synced with master
This commit is contained in:
commit
7d4017225c
|
@ -85,6 +85,11 @@
|
||||||
- (@todo docs) Added `record.ExpandedOne(rel)` and `record.ExpandedAll(rel)` helpers to retrieve casted single or multiple expand relations from the already loaded "expand" Record data.
|
- (@todo docs) Added `record.ExpandedOne(rel)` and `record.ExpandedAll(rel)` helpers to retrieve casted single or multiple expand relations from the already loaded "expand" Record data.
|
||||||
|
|
||||||
|
|
||||||
|
## v0.16.10
|
||||||
|
|
||||||
|
- Added multiple valued fields (`relation`, `select`, `file`) normalizations to ensure that the zero-default value of a newly created multiple field is applied for already existing data ([#2930](https://github.com/pocketbase/pocketbase/issues/2930)).
|
||||||
|
|
||||||
|
|
||||||
## v0.16.9
|
## v0.16.9
|
||||||
|
|
||||||
- Register the `eagerRequestDataCache` middleware only for the internal `api` group routes to avoid conflicts with custom route handlers ([#2914](https://github.com/pocketbase/pocketbase/issues/2914)).
|
- Register the `eagerRequestDataCache` middleware only for the internal `api` group routes to avoid conflicts with custom route handlers ([#2914](https://github.com/pocketbase/pocketbase/issues/2914)).
|
||||||
|
|
|
@ -174,9 +174,13 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
|
||||||
|
|
||||||
return dao.RunInTransaction(func(txDao *Dao) error {
|
return dao.RunInTransaction(func(txDao *Dao) error {
|
||||||
for _, newField := range newCollection.Schema.Fields() {
|
for _, newField := range newCollection.Schema.Fields() {
|
||||||
oldField := oldCollection.Schema.GetFieldById(newField.Id)
|
// allow to continue even if there is no old field for the cases
|
||||||
if oldField == nil {
|
// when a new field is added and there are already inserted data
|
||||||
continue
|
var isOldMultiple bool
|
||||||
|
if oldField := oldCollection.Schema.GetFieldById(newField.Id); oldField != nil {
|
||||||
|
if opt, ok := oldField.Options.(schema.MultiValuer); ok {
|
||||||
|
isOldMultiple = opt.IsMultiple()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isNewMultiple bool
|
var isNewMultiple bool
|
||||||
|
@ -184,11 +188,6 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
|
||||||
isNewMultiple = opt.IsMultiple()
|
isNewMultiple = opt.IsMultiple()
|
||||||
}
|
}
|
||||||
|
|
||||||
var isOldMultiple bool
|
|
||||||
if opt, ok := oldField.Options.(schema.MultiValuer); ok {
|
|
||||||
isOldMultiple = opt.IsMultiple()
|
|
||||||
}
|
|
||||||
|
|
||||||
if isOldMultiple == isNewMultiple {
|
if isOldMultiple == isNewMultiple {
|
||||||
continue // no change
|
continue // no change
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,18 +171,31 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
||||||
opt := relManyField.Options.(*schema.RelationOptions)
|
opt := relManyField.Options.(*schema.RelationOptions)
|
||||||
opt.MaxSelect = types.Pointer(1)
|
opt.MaxSelect = types.Pointer(1)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// new multivaluer field to check whether the array normalization
|
||||||
|
// will be applied for already inserted data
|
||||||
|
collection.Schema.AddField(&schema.SchemaField{
|
||||||
|
Name: "new_multiple",
|
||||||
|
Type: schema.FieldTypeSelect,
|
||||||
|
Options: &schema.SelectOptions{
|
||||||
|
Values: []string{"a", "b", "c"},
|
||||||
|
MaxSelect: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if err := app.Dao().SaveCollection(collection); err != nil {
|
if err := app.Dao().SaveCollection(collection); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
type expectation struct {
|
type expectation struct {
|
||||||
SelectOne string `db:"select_one"`
|
SelectOne string `db:"select_one"`
|
||||||
SelectMany string `db:"select_many"`
|
SelectMany string `db:"select_many"`
|
||||||
FileOne string `db:"file_one"`
|
FileOne string `db:"file_one"`
|
||||||
FileMany string `db:"file_many"`
|
FileMany string `db:"file_many"`
|
||||||
RelOne string `db:"rel_one"`
|
RelOne string `db:"rel_one"`
|
||||||
RelMany string `db:"rel_many"`
|
RelMany string `db:"rel_many"`
|
||||||
|
NewMultiple string `db:"new_multiple"`
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
|
@ -192,34 +205,37 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
||||||
{
|
{
|
||||||
"imy661ixudk5izi",
|
"imy661ixudk5izi",
|
||||||
expectation{
|
expectation{
|
||||||
SelectOne: `[]`,
|
SelectOne: `[]`,
|
||||||
SelectMany: ``,
|
SelectMany: ``,
|
||||||
FileOne: `[]`,
|
FileOne: `[]`,
|
||||||
FileMany: ``,
|
FileMany: ``,
|
||||||
RelOne: `[]`,
|
RelOne: `[]`,
|
||||||
RelMany: ``,
|
RelMany: ``,
|
||||||
|
NewMultiple: `[]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"al1h9ijdeojtsjy",
|
"al1h9ijdeojtsjy",
|
||||||
expectation{
|
expectation{
|
||||||
SelectOne: `["optionB"]`,
|
SelectOne: `["optionB"]`,
|
||||||
SelectMany: `optionB`,
|
SelectMany: `optionB`,
|
||||||
FileOne: `["300_Jsjq7RdBgA.png"]`,
|
FileOne: `["300_Jsjq7RdBgA.png"]`,
|
||||||
FileMany: ``,
|
FileMany: ``,
|
||||||
RelOne: `["84nmscqy84lsi1t"]`,
|
RelOne: `["84nmscqy84lsi1t"]`,
|
||||||
RelMany: `oap640cot4yru2s`,
|
RelMany: `oap640cot4yru2s`,
|
||||||
|
NewMultiple: `[]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"84nmscqy84lsi1t",
|
"84nmscqy84lsi1t",
|
||||||
expectation{
|
expectation{
|
||||||
SelectOne: `["optionB"]`,
|
SelectOne: `["optionB"]`,
|
||||||
SelectMany: `optionC`,
|
SelectMany: `optionC`,
|
||||||
FileOne: `["test_d61b33QdDU.txt"]`,
|
FileOne: `["test_d61b33QdDU.txt"]`,
|
||||||
FileMany: `test_tC1Yc87DfC.txt`,
|
FileMany: `test_tC1Yc87DfC.txt`,
|
||||||
RelOne: `[]`,
|
RelOne: `[]`,
|
||||||
RelMany: `oap640cot4yru2s`,
|
RelMany: `oap640cot4yru2s`,
|
||||||
|
NewMultiple: `[]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -234,6 +250,7 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
||||||
"file_many",
|
"file_many",
|
||||||
"rel_one",
|
"rel_one",
|
||||||
"rel_many",
|
"rel_many",
|
||||||
|
"new_multiple",
|
||||||
).From(collection.Name).Where(dbx.HashExp{"id": s.recordId}).One(result)
|
).From(collection.Name).Where(dbx.HashExp{"id": s.recordId}).One(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("[%s] Failed to load record: %v", s.recordId, err)
|
t.Errorf("[%s] Failed to load record: %v", s.recordId, err)
|
||||||
|
|
|
@ -12,93 +12,97 @@ import (
|
||||||
// Normalizes old single and multiple values of MultiValuer fields (file, select, relation).
|
// Normalizes old single and multiple values of MultiValuer fields (file, select, relation).
|
||||||
func init() {
|
func init() {
|
||||||
AppMigrations.Register(func(db dbx.Builder) error {
|
AppMigrations.Register(func(db dbx.Builder) error {
|
||||||
dao := daos.New(db)
|
return normalizeMultivaluerFields(db)
|
||||||
|
|
||||||
collections := []*models.Collection{}
|
|
||||||
if err := dao.CollectionQuery().All(&collections); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range collections {
|
|
||||||
if c.IsView() {
|
|
||||||
// skip view collections
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range c.Schema.Fields() {
|
|
||||||
opt, ok := f.Options.(schema.MultiValuer)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateQuery *dbx.Query
|
|
||||||
|
|
||||||
if opt.IsMultiple() {
|
|
||||||
updateQuery = dao.DB().NewQuery(fmt.Sprintf(
|
|
||||||
`UPDATE {{%s}} set [[%s]] = (
|
|
||||||
CASE
|
|
||||||
WHEN COALESCE([[%s]], '') = ''
|
|
||||||
THEN '[]'
|
|
||||||
ELSE (
|
|
||||||
CASE
|
|
||||||
WHEN json_valid([[%s]]) AND json_type([[%s]]) == 'array'
|
|
||||||
THEN [[%s]]
|
|
||||||
ELSE json_array([[%s]])
|
|
||||||
END
|
|
||||||
)
|
|
||||||
END
|
|
||||||
)`,
|
|
||||||
c.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
updateQuery = dao.DB().NewQuery(fmt.Sprintf(
|
|
||||||
`UPDATE {{%s}} set [[%s]] = (
|
|
||||||
CASE
|
|
||||||
WHEN COALESCE([[%s]], '[]') = '[]'
|
|
||||||
THEN ''
|
|
||||||
ELSE (
|
|
||||||
CASE
|
|
||||||
WHEN json_valid([[%s]]) AND json_type([[%s]]) == 'array'
|
|
||||||
THEN COALESCE(json_extract([[%s]], '$[#-1]'), '')
|
|
||||||
ELSE [[%s]]
|
|
||||||
END
|
|
||||||
)
|
|
||||||
END
|
|
||||||
)`,
|
|
||||||
c.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
f.Name,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := updateQuery.Execute(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// trigger view query update after the records normalization
|
|
||||||
// (ignore save error in case of invalid query to allow users to change it from the UI)
|
|
||||||
for _, c := range collections {
|
|
||||||
if !c.IsView() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dao.SaveCollection(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, func(db dbx.Builder) error {
|
}, func(db dbx.Builder) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeMultivaluerFields(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db)
|
||||||
|
|
||||||
|
collections := []*models.Collection{}
|
||||||
|
if err := dao.CollectionQuery().All(&collections); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range collections {
|
||||||
|
if c.IsView() {
|
||||||
|
// skip view collections
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range c.Schema.Fields() {
|
||||||
|
opt, ok := f.Options.(schema.MultiValuer)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var updateQuery *dbx.Query
|
||||||
|
|
||||||
|
if opt.IsMultiple() {
|
||||||
|
updateQuery = dao.DB().NewQuery(fmt.Sprintf(
|
||||||
|
`UPDATE {{%s}} set [[%s]] = (
|
||||||
|
CASE
|
||||||
|
WHEN COALESCE([[%s]], '') = ''
|
||||||
|
THEN '[]'
|
||||||
|
ELSE (
|
||||||
|
CASE
|
||||||
|
WHEN json_valid([[%s]]) AND json_type([[%s]]) == 'array'
|
||||||
|
THEN [[%s]]
|
||||||
|
ELSE json_array([[%s]])
|
||||||
|
END
|
||||||
|
)
|
||||||
|
END
|
||||||
|
)`,
|
||||||
|
c.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
updateQuery = dao.DB().NewQuery(fmt.Sprintf(
|
||||||
|
`UPDATE {{%s}} set [[%s]] = (
|
||||||
|
CASE
|
||||||
|
WHEN COALESCE([[%s]], '[]') = '[]'
|
||||||
|
THEN ''
|
||||||
|
ELSE (
|
||||||
|
CASE
|
||||||
|
WHEN json_valid([[%s]]) AND json_type([[%s]]) == 'array'
|
||||||
|
THEN COALESCE(json_extract([[%s]], '$[#-1]'), '')
|
||||||
|
ELSE [[%s]]
|
||||||
|
END
|
||||||
|
)
|
||||||
|
END
|
||||||
|
)`,
|
||||||
|
c.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := updateQuery.Execute(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger view query update after the records normalization
|
||||||
|
// (ignore save error in case of invalid query to allow users to change it from the UI)
|
||||||
|
for _, c := range collections {
|
||||||
|
if !c.IsView() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dao.SaveCollection(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Renormalizes old single and multiple values of MultiValuer fields (file, select, relation)
|
||||||
|
// (see https://github.com/pocketbase/pocketbase/issues/2930).
|
||||||
|
func init() {
|
||||||
|
AppMigrations.Register(func(db dbx.Builder) error {
|
||||||
|
return normalizeMultivaluerFields(db)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
|
@ -580,8 +580,8 @@ func (r *runner) processActiveProps() (*search.ResolverResult, error) {
|
||||||
func jsonArrayLength(tableColumnPair string) string {
|
func jsonArrayLength(tableColumnPair string) string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
// note: the case is used to normalize value access for single and multiple relations.
|
// note: the case is used to normalize value access for single and multiple relations.
|
||||||
`json_array_length(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END)`,
|
`json_array_length(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE (CASE WHEN [[%s]] = '' OR [[%s]] IS NULL THEN json_array() ELSE json_array([[%s]]) END) END)`,
|
||||||
tableColumnPair, tableColumnPair, tableColumnPair,
|
tableColumnPair, tableColumnPair, tableColumnPair, tableColumnPair, tableColumnPair,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -294,14 +294,14 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
|
||||||
"self_rel_one.rel_many_cascade.files:length != 7 &&" +
|
"self_rel_one.rel_many_cascade.files:length != 7 &&" +
|
||||||
"self_rel_one.rel_many_cascade.files:length ?!= 8",
|
"self_rel_one.rel_many_cascade.files:length ?!= 8",
|
||||||
false,
|
false,
|
||||||
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `demo4` `__data_demo4` ON [[__data_demo4.id]]={:TEST} LEFT JOIN `demo3` `__data_demo3` ON [[__data_demo3.id]] IN ({:TEST}, {:TEST}) LEFT JOIN `demo4` `demo4_self_rel_one` ON [[demo4_self_rel_one.id]] = [[demo4.self_rel_one]] LEFT JOIN json_each(CASE WHEN json_valid([[demo4_self_rel_one.rel_many_cascade]]) THEN [[demo4_self_rel_one.rel_many_cascade]] ELSE json_array([[demo4_self_rel_one.rel_many_cascade]]) END) `demo4_self_rel_one_rel_many_cascade_je` LEFT JOIN `demo3` `demo4_self_rel_one_rel_many_cascade` ON [[demo4_self_rel_one_rel_many_cascade.id]] = [[demo4_self_rel_one_rel_many_cascade_je.value]] WHERE (json_array_length(CASE WHEN json_valid([[__data_demo4.self_rel_many]]) THEN [[__data_demo4.self_rel_many]] ELSE json_array([[__data_demo4.self_rel_many]]) END) > {:TEST} AND json_array_length(CASE WHEN json_valid([[__data_demo4.self_rel_many]]) THEN [[__data_demo4.self_rel_many]] ELSE json_array([[__data_demo4.self_rel_many]]) END) > {:TEST} AND json_array_length(CASE WHEN json_valid([[__data_demo3.files]]) THEN [[__data_demo3.files]] ELSE json_array([[__data_demo3.files]]) END) < {:TEST} AND ((json_array_length(CASE WHEN json_valid([[__data_demo3.files]]) THEN [[__data_demo3.files]] ELSE json_array([[__data_demo3.files]]) END) < {:TEST}) AND (NOT EXISTS (SELECT 1 FROM (SELECT json_array_length(CASE WHEN json_valid([[__data_mm_demo3.files]]) THEN [[__data_mm_demo3.files]] ELSE json_array([[__data_mm_demo3.files]]) END) as [[multiMatchValue]] FROM `demo4` `__mm_demo4` LEFT JOIN `demo3` `__data_mm_demo3` ON `__data_mm_demo3`.`id` IN ({:TEST}, {:TEST}) WHERE `__mm_demo4`.`id` = `demo4`.`id`) {{__smTEST}} WHERE ((NOT ([[__smTEST.multiMatchValue]] < {:TEST})) OR ([[__smTEST.multiMatchValue]] IS NULL))))) AND json_array_length(CASE WHEN json_valid([[demo4_self_rel_one.self_rel_many]]) THEN [[demo4_self_rel_one.self_rel_many]] ELSE json_array([[demo4_self_rel_one.self_rel_many]]) END) = {:TEST} AND json_array_length(CASE WHEN json_valid([[demo4_self_rel_one.self_rel_many]]) THEN [[demo4_self_rel_one.self_rel_many]] ELSE json_array([[demo4_self_rel_one.self_rel_many]]) END) = {:TEST} AND ((json_array_length(CASE WHEN json_valid([[demo4_self_rel_one_rel_many_cascade.files]]) THEN [[demo4_self_rel_one_rel_many_cascade.files]] ELSE json_array([[demo4_self_rel_one_rel_many_cascade.files]]) END) != {:TEST}) AND (NOT EXISTS (SELECT 1 FROM (SELECT json_array_length(CASE WHEN json_valid([[__mm_demo4_self_rel_one_rel_many_cascade.files]]) THEN [[__mm_demo4_self_rel_one_rel_many_cascade.files]] ELSE json_array([[__mm_demo4_self_rel_one_rel_many_cascade.files]]) END) as [[multiMatchValue]] FROM `demo4` `__mm_demo4` LEFT JOIN `demo4` `__mm_demo4_self_rel_one` ON [[__mm_demo4_self_rel_one.id]] = [[__mm_demo4.self_rel_one]] LEFT JOIN json_each(CASE WHEN json_valid([[__mm_demo4_self_rel_one.rel_many_cascade]]) THEN [[__mm_demo4_self_rel_one.rel_many_cascade]] ELSE json_array([[__mm_demo4_self_rel_one.rel_many_cascade]]) END) `__mm_demo4_self_rel_one_rel_many_cascade_je` LEFT JOIN `demo3` `__mm_demo4_self_rel_one_rel_many_cascade` ON [[__mm_demo4_self_rel_one_rel_many_cascade.id]] = [[__mm_demo4_self_rel_one_rel_many_cascade_je.value]] WHERE `__mm_demo4`.`id` = `demo4`.`id`) {{__smTEST}} WHERE ((NOT ([[__smTEST.multiMatchValue]] != {:TEST})) OR ([[__smTEST.multiMatchValue]] IS NULL))))) AND json_array_length(CASE WHEN json_valid([[demo4_self_rel_one_rel_many_cascade.files]]) THEN [[demo4_self_rel_one_rel_many_cascade.files]] ELSE json_array([[demo4_self_rel_one_rel_many_cascade.files]]) END) != {:TEST})",
|
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `demo4` `__data_demo4` ON [[__data_demo4.id]]={:TEST} LEFT JOIN `demo3` `__data_demo3` ON [[__data_demo3.id]] IN ({:TEST}, {:TEST}) LEFT JOIN `demo4` `demo4_self_rel_one` ON [[demo4_self_rel_one.id]] = [[demo4.self_rel_one]] LEFT JOIN json_each(CASE WHEN json_valid([[demo4_self_rel_one.rel_many_cascade]]) THEN [[demo4_self_rel_one.rel_many_cascade]] ELSE json_array([[demo4_self_rel_one.rel_many_cascade]]) END) `demo4_self_rel_one_rel_many_cascade_je` LEFT JOIN `demo3` `demo4_self_rel_one_rel_many_cascade` ON [[demo4_self_rel_one_rel_many_cascade.id]] = [[demo4_self_rel_one_rel_many_cascade_je.value]] WHERE (json_array_length(CASE WHEN json_valid([[__data_demo4.self_rel_many]]) THEN [[__data_demo4.self_rel_many]] ELSE (CASE WHEN [[__data_demo4.self_rel_many]] = '' OR [[__data_demo4.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[__data_demo4.self_rel_many]]) END) END) > {:TEST} AND json_array_length(CASE WHEN json_valid([[__data_demo4.self_rel_many]]) THEN [[__data_demo4.self_rel_many]] ELSE (CASE WHEN [[__data_demo4.self_rel_many]] = '' OR [[__data_demo4.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[__data_demo4.self_rel_many]]) END) END) > {:TEST} AND json_array_length(CASE WHEN json_valid([[__data_demo3.files]]) THEN [[__data_demo3.files]] ELSE (CASE WHEN [[__data_demo3.files]] = '' OR [[__data_demo3.files]] IS NULL THEN json_array() ELSE json_array([[__data_demo3.files]]) END) END) < {:TEST} AND ((json_array_length(CASE WHEN json_valid([[__data_demo3.files]]) THEN [[__data_demo3.files]] ELSE (CASE WHEN [[__data_demo3.files]] = '' OR [[__data_demo3.files]] IS NULL THEN json_array() ELSE json_array([[__data_demo3.files]]) END) END) < {:TEST}) AND (NOT EXISTS (SELECT 1 FROM (SELECT json_array_length(CASE WHEN json_valid([[__data_mm_demo3.files]]) THEN [[__data_mm_demo3.files]] ELSE (CASE WHEN [[__data_mm_demo3.files]] = '' OR [[__data_mm_demo3.files]] IS NULL THEN json_array() ELSE json_array([[__data_mm_demo3.files]]) END) END) as [[multiMatchValue]] FROM `demo4` `__mm_demo4` LEFT JOIN `demo3` `__data_mm_demo3` ON `__data_mm_demo3`.`id` IN ({:TEST}, {:TEST}) WHERE `__mm_demo4`.`id` = `demo4`.`id`) {{__smTEST}} WHERE ((NOT ([[__smTEST.multiMatchValue]] < {:TEST})) OR ([[__smTEST.multiMatchValue]] IS NULL))))) AND json_array_length(CASE WHEN json_valid([[demo4_self_rel_one.self_rel_many]]) THEN [[demo4_self_rel_one.self_rel_many]] ELSE (CASE WHEN [[demo4_self_rel_one.self_rel_many]] = '' OR [[demo4_self_rel_one.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[demo4_self_rel_one.self_rel_many]]) END) END) = {:TEST} AND json_array_length(CASE WHEN json_valid([[demo4_self_rel_one.self_rel_many]]) THEN [[demo4_self_rel_one.self_rel_many]] ELSE (CASE WHEN [[demo4_self_rel_one.self_rel_many]] = '' OR [[demo4_self_rel_one.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[demo4_self_rel_one.self_rel_many]]) END) END) = {:TEST} AND ((json_array_length(CASE WHEN json_valid([[demo4_self_rel_one_rel_many_cascade.files]]) THEN [[demo4_self_rel_one_rel_many_cascade.files]] ELSE (CASE WHEN [[demo4_self_rel_one_rel_many_cascade.files]] = '' OR [[demo4_self_rel_one_rel_many_cascade.files]] IS NULL THEN json_array() ELSE json_array([[demo4_self_rel_one_rel_many_cascade.files]]) END) END) != {:TEST}) AND (NOT EXISTS (SELECT 1 FROM (SELECT json_array_length(CASE WHEN json_valid([[__mm_demo4_self_rel_one_rel_many_cascade.files]]) THEN [[__mm_demo4_self_rel_one_rel_many_cascade.files]] ELSE (CASE WHEN [[__mm_demo4_self_rel_one_rel_many_cascade.files]] = '' OR [[__mm_demo4_self_rel_one_rel_many_cascade.files]] IS NULL THEN json_array() ELSE json_array([[__mm_demo4_self_rel_one_rel_many_cascade.files]]) END) END) as [[multiMatchValue]] FROM `demo4` `__mm_demo4` LEFT JOIN `demo4` `__mm_demo4_self_rel_one` ON [[__mm_demo4_self_rel_one.id]] = [[__mm_demo4.self_rel_one]] LEFT JOIN json_each(CASE WHEN json_valid([[__mm_demo4_self_rel_one.rel_many_cascade]]) THEN [[__mm_demo4_self_rel_one.rel_many_cascade]] ELSE json_array([[__mm_demo4_self_rel_one.rel_many_cascade]]) END) `__mm_demo4_self_rel_one_rel_many_cascade_je` LEFT JOIN `demo3` `__mm_demo4_self_rel_one_rel_many_cascade` ON [[__mm_demo4_self_rel_one_rel_many_cascade.id]] = [[__mm_demo4_self_rel_one_rel_many_cascade_je.value]] WHERE `__mm_demo4`.`id` = `demo4`.`id`) {{__smTEST}} WHERE ((NOT ([[__smTEST.multiMatchValue]] != {:TEST})) OR ([[__smTEST.multiMatchValue]] IS NULL))))) AND json_array_length(CASE WHEN json_valid([[demo4_self_rel_one_rel_many_cascade.files]]) THEN [[demo4_self_rel_one_rel_many_cascade.files]] ELSE (CASE WHEN [[demo4_self_rel_one_rel_many_cascade.files]] = '' OR [[demo4_self_rel_one_rel_many_cascade.files]] IS NULL THEN json_array() ELSE json_array([[demo4_self_rel_one_rel_many_cascade.files]]) END) END) != {:TEST})",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"json_extract and json_array_length COALESCE equal normalizations",
|
"json_extract and json_array_length COALESCE equal normalizations",
|
||||||
"demo4",
|
"demo4",
|
||||||
"json_object.a.b = '' && self_rel_many:length != 2 && json_object.a.b > 3 && self_rel_many:length <= 4",
|
"json_object.a.b = '' && self_rel_many:length != 2 && json_object.a.b > 3 && self_rel_many:length <= 4",
|
||||||
false,
|
false,
|
||||||
"SELECT `demo4`.* FROM `demo4` WHERE ((JSON_EXTRACT([[demo4.json_object]], '$.a.b') = '' OR JSON_EXTRACT([[demo4.json_object]], '$.a.b') IS NULL) AND json_array_length(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE json_array([[demo4.self_rel_many]]) END) != {:TEST} AND JSON_EXTRACT([[demo4.json_object]], '$.a.b') > {:TEST} AND json_array_length(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE json_array([[demo4.self_rel_many]]) END) <= {:TEST})",
|
"SELECT `demo4`.* FROM `demo4` WHERE ((JSON_EXTRACT([[demo4.json_object]], '$.a.b') = '' OR JSON_EXTRACT([[demo4.json_object]], '$.a.b') IS NULL) AND json_array_length(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE (CASE WHEN [[demo4.self_rel_many]] = '' OR [[demo4.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[demo4.self_rel_many]]) END) END) != {:TEST} AND JSON_EXTRACT([[demo4.json_object]], '$.a.b') > {:TEST} AND json_array_length(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE (CASE WHEN [[demo4.self_rel_many]] = '' OR [[demo4.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[demo4.self_rel_many]]) END) END) <= {:TEST})",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue