cascadeRecordDelete triggers cascade deletion for the provided references. NB! This method is expected to be called from inside of a transaction.
(app App, mainRecord *Record, refs map[*Collection][]Field)
| 1504 | // |
| 1505 | // NB! This method is expected to be called from inside of a transaction. |
| 1506 | func cascadeRecordDelete(app App, mainRecord *Record, refs map[*Collection][]Field) error { |
| 1507 | // Sort the refs keys to ensure that the cascade events firing order is always the same. |
| 1508 | // This is not necessary for the operation to function correctly but it helps having deterministic output during testing. |
| 1509 | sortedRefKeys := make([]*Collection, 0, len(refs)) |
| 1510 | for k := range refs { |
| 1511 | sortedRefKeys = append(sortedRefKeys, k) |
| 1512 | } |
| 1513 | sort.Slice(sortedRefKeys, func(i, j int) bool { |
| 1514 | return sortedRefKeys[i].Name < sortedRefKeys[j].Name |
| 1515 | }) |
| 1516 | |
| 1517 | for _, refCollection := range sortedRefKeys { |
| 1518 | fields, ok := refs[refCollection] |
| 1519 | |
| 1520 | if !ok || refCollection.IsView() { |
| 1521 | continue // skip missing or view collections |
| 1522 | } |
| 1523 | |
| 1524 | recordTableName := inflector.Columnify(refCollection.Name) |
| 1525 | |
| 1526 | for _, field := range fields { |
| 1527 | prefixedFieldName := recordTableName + "." + inflector.Columnify(field.GetName()) |
| 1528 | |
| 1529 | query := app.RecordQuery(refCollection) |
| 1530 | |
| 1531 | if opt, ok := field.(MultiValuer); !ok || !opt.IsMultiple() { |
| 1532 | query.AndWhere(dbx.HashExp{prefixedFieldName: mainRecord.Id}) |
| 1533 | } else { |
| 1534 | query.AndWhere(dbx.Exists(dbx.NewExp(fmt.Sprintf( |
| 1535 | `SELECT 1 FROM %s {{__je__}} WHERE [[__je__.value]]={:jevalue}`, |
| 1536 | dbutils.JSONEach(prefixedFieldName), |
| 1537 | ), dbx.Params{ |
| 1538 | "jevalue": mainRecord.Id, |
| 1539 | }))) |
| 1540 | } |
| 1541 | |
| 1542 | if refCollection.Id == mainRecord.Collection().Id { |
| 1543 | query.AndWhere(dbx.Not(dbx.HashExp{recordTableName + ".id": mainRecord.Id})) |
| 1544 | } |
| 1545 | |
| 1546 | // trigger cascade for each batchSize rel items until there is none |
| 1547 | batchSize := 4000 |
| 1548 | rows := make([]*Record, 0, batchSize) |
| 1549 | for { |
| 1550 | if err := query.Limit(int64(batchSize)).All(&rows); err != nil { |
| 1551 | return err |
| 1552 | } |
| 1553 | |
| 1554 | total := len(rows) |
| 1555 | if total == 0 { |
| 1556 | break |
| 1557 | } |
| 1558 | |
| 1559 | err := deleteRefRecords(app, mainRecord, rows, field) |
| 1560 | if err != nil { |
| 1561 | return err |
| 1562 | } |
| 1563 |
no test coverage detected
searching dependent graphs…