MCPcopy
hub / github.com/pocketbase/pocketbase / cascadeRecordDelete

Function cascadeRecordDelete

core/record_model.go:1506–1574  ·  view source on GitHub ↗

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)

Source from the content-addressed store, hash-verified

1504//
1505// NB! This method is expected to be called from inside of a transaction.
1506func 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

Callers 1

onRecordDeleteExecuteFunction · 0.85

Calls 9

ColumnifyFunction · 0.92
JSONEachFunction · 0.92
deleteRefRecordsFunction · 0.85
IsViewMethod · 0.80
CollectionMethod · 0.80
GetNameMethod · 0.65
RecordQueryMethod · 0.65
IsMultipleMethod · 0.65
ExistsMethod · 0.45

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…