[#1689] fixed cascade delete condition on rel records with the same id as the main record
This commit is contained in:
parent
a74d227418
commit
eb1246fc41
|
@ -98,6 +98,7 @@ func (dao *Dao) FindRecordsByIds(
|
||||||
// Returns an empty slice if no records are found.
|
// Returns an empty slice if no records are found.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
|
//
|
||||||
// expr1 := dbx.HashExp{"email": "test@example.com"}
|
// expr1 := dbx.HashExp{"email": "test@example.com"}
|
||||||
// expr2 := dbx.NewExp("LOWER(username) = {:username}", dbx.Params{"username": "test"})
|
// expr2 := dbx.NewExp("LOWER(username) = {:username}", dbx.Params{"username": "test"})
|
||||||
// dao.FindRecordsByExpr("example", expr1, expr2)
|
// dao.FindRecordsByExpr("example", expr1, expr2)
|
||||||
|
@ -402,13 +403,16 @@ func (dao *Dao) cascadeRecordDelete(mainRecord *models.Record, refs map[*models.
|
||||||
// @todo optimize single relation lookup in v0.12+
|
// @todo optimize single relation lookup in v0.12+
|
||||||
query := dao.RecordQuery(refCollection).
|
query := dao.RecordQuery(refCollection).
|
||||||
Distinct(true).
|
Distinct(true).
|
||||||
AndWhere(dbx.Not(dbx.HashExp{recordTableName + ".id": mainRecord.Id})).
|
|
||||||
InnerJoin(fmt.Sprintf(
|
InnerJoin(fmt.Sprintf(
|
||||||
// note: the case is used to normalize the value access
|
// note: the case is used to normalize the value access
|
||||||
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END) as {{%s}}`,
|
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END) as {{%s}}`,
|
||||||
prefixedFieldName, prefixedFieldName, prefixedFieldName, uniqueJsonEachAlias,
|
prefixedFieldName, prefixedFieldName, prefixedFieldName, uniqueJsonEachAlias,
|
||||||
), dbx.HashExp{uniqueJsonEachAlias + ".value": mainRecord.Id})
|
), dbx.HashExp{uniqueJsonEachAlias + ".value": mainRecord.Id})
|
||||||
|
|
||||||
|
if refCollection.Id == mainRecord.Collection().Id {
|
||||||
|
query.AndWhere(dbx.Not(dbx.HashExp{recordTableName + ".id": mainRecord.Id}))
|
||||||
|
}
|
||||||
|
|
||||||
// trigger cascade for each batchSize rel items until there is none
|
// trigger cascade for each batchSize rel items until there is none
|
||||||
batchSize := 4000
|
batchSize := 4000
|
||||||
rows := make([]dbx.NullStringMap, 0, batchSize)
|
rows := make([]dbx.NullStringMap, 0, batchSize)
|
||||||
|
|
|
@ -693,6 +693,12 @@ func TestDeleteRecordBatchProcessing(t *testing.T) {
|
||||||
t.Fatal("The main record wasn't deleted")
|
t.Fatal("The main record wasn't deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if the c1 b rel field were updated
|
||||||
|
c1RecordB, err := app.Dao().FindRecordById("c1", "b")
|
||||||
|
if err != nil || c1RecordB.GetString("rel") != "" {
|
||||||
|
t.Fatalf("Expected c1RecordB.rel to be nil, got %v", c1RecordB.GetString("rel"))
|
||||||
|
}
|
||||||
|
|
||||||
// check if the c2 rel fields were updated
|
// check if the c2 rel fields were updated
|
||||||
c2Records, err := app.Dao().FindRecordsByExpr("c2", nil)
|
c2Records, err := app.Dao().FindRecordsByExpr("c2", nil)
|
||||||
if err != nil || len(c2Records) == 0 {
|
if err != nil || len(c2Records) == 0 {
|
||||||
|
@ -725,6 +731,16 @@ func createMockBatchProcessingData(dao *daos.Dao) error {
|
||||||
Name: "text",
|
Name: "text",
|
||||||
Type: schema.FieldTypeText,
|
Type: schema.FieldTypeText,
|
||||||
},
|
},
|
||||||
|
// self reference
|
||||||
|
&schema.SchemaField{
|
||||||
|
Name: "rel",
|
||||||
|
Type: schema.FieldTypeRelation,
|
||||||
|
Options: &schema.RelationOptions{
|
||||||
|
MaxSelect: types.Pointer(1),
|
||||||
|
CollectionId: "c1",
|
||||||
|
CascadeDelete: false, // should unset all rel fields
|
||||||
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err := dao.SaveCollection(c1); err != nil {
|
if err := dao.SaveCollection(c1); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -771,15 +787,17 @@ func createMockBatchProcessingData(dao *daos.Dao) error {
|
||||||
// insert mock records
|
// insert mock records
|
||||||
c1RecordA := models.NewRecord(c1)
|
c1RecordA := models.NewRecord(c1)
|
||||||
c1RecordA.Id = "a"
|
c1RecordA.Id = "a"
|
||||||
|
c1RecordA.Set("rel", c1RecordA.Id) // self reference
|
||||||
if err := dao.Save(c1RecordA); err != nil {
|
if err := dao.Save(c1RecordA); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c1RecordB := models.NewRecord(c1)
|
c1RecordB := models.NewRecord(c1)
|
||||||
c1RecordB.Id = "b"
|
c1RecordB.Id = "b"
|
||||||
|
c1RecordB.Set("rel", c1RecordA.Id) // rel to another record from the same collection
|
||||||
if err := dao.Save(c1RecordB); err != nil {
|
if err := dao.Save(c1RecordB); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := 0; i < 2400; i++ {
|
for i := 0; i < 4500; i++ {
|
||||||
c2Record := models.NewRecord(c2)
|
c2Record := models.NewRecord(c2)
|
||||||
c2Record.Set("rel", []string{c1RecordA.Id, c1RecordB.Id})
|
c2Record.Set("rel", []string{c1RecordA.Id, c1RecordB.Id})
|
||||||
if err := dao.Save(c2Record); err != nil {
|
if err := dao.Save(c2Record); err != nil {
|
||||||
|
@ -793,5 +811,14 @@ func createMockBatchProcessingData(dao *daos.Dao) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set the same id as the relation for at least 1 record
|
||||||
|
// to check whether the correct condition will be added
|
||||||
|
c3Record := models.NewRecord(c3)
|
||||||
|
c3Record.Set("rel", c1RecordA.Id)
|
||||||
|
c3Record.Id = c1RecordA.Id
|
||||||
|
if err := dao.Save(c3Record); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue