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