[#1220] fixed field column name conflict on cascade record deletion

This commit is contained in:
Gani Georgiev
2022-12-09 19:09:43 +02:00
parent 59b41c8202
commit 9c7f48a66e
5 changed files with 34 additions and 22 deletions
+5 -4
View File
@@ -379,6 +379,7 @@ func (dao *Dao) DeleteRecord(record *models.Record) error {
// check if related records has to be deleted (if `CascadeDelete` is set)
// OR
// just unset the record id from any relation field values (if they are not required)
uniqueJsonEachAlias := "__je__" + security.PseudorandomString(5)
for refCollection, fields := range refs {
for _, field := range fields {
options, _ := field.Options.(*schema.RelationOptions)
@@ -387,16 +388,16 @@ func (dao *Dao) DeleteRecord(record *models.Record) error {
// fetch all referenced records
recordTableName := inflector.Columnify(refCollection.Name)
fieldColumnName := inflector.Columnify(field.Name)
prefixedFieldName := recordTableName + "." + inflector.Columnify(field.Name)
err := txDao.RecordQuery(refCollection).
Distinct(true).
LeftJoin(fmt.Sprintf(
// note: the case is used to normalize value access for single and multiple relations.
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END) as {{__je__}}`,
fieldColumnName, fieldColumnName, fieldColumnName,
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END) as {{%s}}`,
prefixedFieldName, prefixedFieldName, prefixedFieldName, uniqueJsonEachAlias,
), nil).
AndWhere(dbx.Not(dbx.HashExp{recordTableName + ".id": record.Id})).
AndWhere(dbx.HashExp{"__je__.value": record.Id}).
AndWhere(dbx.HashExp{uniqueJsonEachAlias + ".value": record.Id}).
All(&rows)
if err != nil {
return err
+21
View File
@@ -1,11 +1,14 @@
package daos_test
import (
"context"
"database/sql"
"errors"
"fmt"
"regexp"
"strings"
"testing"
"time"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/models"
@@ -628,7 +631,15 @@ func TestDeleteRecord(t *testing.T) {
// delete existing record + cascade
// ---
calledQueries := []string{}
app.DB().QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {
calledQueries = append(calledQueries, sql)
}
app.DB().ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {
calledQueries = append(calledQueries, sql)
}
rec3, _ := app.Dao().FindRecordById("users", "oap640cot4yru2s")
// delete
if err := app.Dao().DeleteRecord(rec3); err != nil {
t.Fatalf("(rec3) Expected nil, got error %v", err)
}
@@ -642,6 +653,16 @@ func TestDeleteRecord(t *testing.T) {
if rel != nil {
t.Fatalf("(rec3) Expected the delete to cascade, found relation %v", rel)
}
// ensure that the json rel fields were prefixed
joinedQueries := strings.Join(calledQueries, " ")
expectedRelManyJoin := "`demo1` LEFT JOIN json_each(CASE WHEN json_valid([[demo1.rel_many]]) THEN [[demo1.rel_many]] ELSE json_array([[demo1.rel_many]]) END)"
if !strings.Contains(joinedQueries, expectedRelManyJoin) {
t.Fatalf("(rec3) Expected the cascade delete to call the query \n%v, got \n%v", expectedRelManyJoin, calledQueries)
}
expectedRelOneJoin := "`demo1` LEFT JOIN json_each(CASE WHEN json_valid([[demo1.rel_one]]) THEN [[demo1.rel_one]] ELSE json_array([[demo1.rel_one]]) END)"
if !strings.Contains(joinedQueries, expectedRelOneJoin) {
t.Fatalf("(rec3) Expected the cascade delete to call the query \n%v, got \n%v", expectedRelOneJoin, calledQueries)
}
}
func TestSyncRecordTableSchema(t *testing.T) {