SQLite

Check-in [04d5b637]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix a problem with "ON DELETE RESTRICT" and "ON UPDATE RESTRICT" foreign keys in attached databases scanning child tables in the wrong schema.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 04d5b637f087520cd58211505f9b5c086ff96d864a1908f60464b6fe22c62b7d
User & Date: dan 2022-04-16 15:46:23
Context
2022-04-16
17:53
Make shell auto-column work with lots of columns when log() is missing. (check-in: 82366436 user: larrybr tags: trunk)
15:46
Fix a problem with "ON DELETE RESTRICT" and "ON UPDATE RESTRICT" foreign keys in attached databases scanning child tables in the wrong schema. (check-in: 04d5b637 user: dan tags: trunk)
2022-04-14
19:48
Cherry pick subroutine indentation improvements and the hardening of OP_Gosub from the right-join branch back into trunk. (check-in: 12645f10 user: drh tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/fkey.c.

530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
** The code generated by this function scans through the rows in the child
** table that correspond to the parent table row being deleted or inserted.
** For each child row found, one of the following actions is taken:
**
**   Operation | FK type   | Action taken
**   --------------------------------------------------------------------------
**   DELETE      immediate   Increment the "immediate constraint counter".
**                           Or, if the ON (UPDATE|DELETE) action is RESTRICT,
**                           throw a "FOREIGN KEY constraint failed" exception.
**
**   INSERT      immediate   Decrement the "immediate constraint counter".
**
**   DELETE      deferred    Increment the "deferred constraint counter".
**                           Or, if the ON (UPDATE|DELETE) action is RESTRICT,
**                           throw a "FOREIGN KEY constraint failed" exception.
**
**   INSERT      deferred    Decrement the "deferred constraint counter".
**
** These operations are identified in the comment at the top of this file 
** (fkey.c) as "I.2" and "D.2".
*/
static void fkScanChildren(







<
<




<
<







530
531
532
533
534
535
536


537
538
539
540


541
542
543
544
545
546
547
** The code generated by this function scans through the rows in the child
** table that correspond to the parent table row being deleted or inserted.
** For each child row found, one of the following actions is taken:
**
**   Operation | FK type   | Action taken
**   --------------------------------------------------------------------------
**   DELETE      immediate   Increment the "immediate constraint counter".


**
**   INSERT      immediate   Decrement the "immediate constraint counter".
**
**   DELETE      deferred    Increment the "deferred constraint counter".


**
**   INSERT      deferred    Decrement the "deferred constraint counter".
**
** These operations are identified in the comment at the top of this file 
** (fkey.c) as "I.2" and "D.2".
*/
static void fkScanChildren(
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
** compiled on table pTab, which is the parent table of foreign-key pFKey.
** If the current operation is an UPDATE, then the pChanges parameter is
** passed a pointer to the list of columns being modified. If it is a
** DELETE, pChanges is passed a NULL pointer.
**
** It returns a pointer to a Trigger structure containing a trigger
** equivalent to the ON UPDATE or ON DELETE action specified by pFKey.
** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is
** returned (these actions require no special handling by the triggers
** sub-system, code for them is created by fkScanChildren()).
**
** For example, if pFKey is the foreign key and pTab is table "p" in 
** the following schema:
**
**   CREATE TABLE p(pk PRIMARY KEY);
**   CREATE TABLE c(ck REFERENCES p ON DELETE CASCADE);
**







|
|
|







1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
** compiled on table pTab, which is the parent table of foreign-key pFKey.
** If the current operation is an UPDATE, then the pChanges parameter is
** passed a pointer to the list of columns being modified. If it is a
** DELETE, pChanges is passed a NULL pointer.
**
** It returns a pointer to a Trigger structure containing a trigger
** equivalent to the ON UPDATE or ON DELETE action specified by pFKey.
** If the action is "NO ACTION" then a NULL pointer is returned (these actions
** require no special handling by the triggers sub-system, code for them is
** created by fkScanChildren()).
**
** For example, if pFKey is the foreign key and pTab is table "p" in 
** the following schema:
**
**   CREATE TABLE p(pk PRIMARY KEY);
**   CREATE TABLE c(ck REFERENCES p ON DELETE CASCADE);
**
1316
1317
1318
1319
1320
1321
1322

1323

1324
1325
1326
1327



1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
    }
    sqlite3DbFree(db, aiCol);

    zFrom = pFKey->pFrom->zName;
    nFrom = sqlite3Strlen30(zFrom);

    if( action==OE_Restrict ){

      Token tFrom;

      Expr *pRaise; 

      tFrom.z = zFrom;
      tFrom.n = nFrom;



      pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
      if( pRaise ){
        pRaise->affExpr = OE_Abort;
      }
      pSelect = sqlite3SelectNew(pParse, 
          sqlite3ExprListAppend(pParse, 0, pRaise),
          sqlite3SrcListAppend(pParse, 0, &tFrom, 0),
          pWhere,
          0, 0, 0, 0, 0
      );
      pWhere = 0;
    }

    /* Disable lookaside memory allocation */







>

>




>
>
>






|







1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
    }
    sqlite3DbFree(db, aiCol);

    zFrom = pFKey->pFrom->zName;
    nFrom = sqlite3Strlen30(zFrom);

    if( action==OE_Restrict ){
      int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
      Token tFrom;
      Token tDb;
      Expr *pRaise; 

      tFrom.z = zFrom;
      tFrom.n = nFrom;
      tDb.z = db->aDb[iDb].zDbSName;
      tDb.n = sqlite3Strlen30(tDb.z);

      pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
      if( pRaise ){
        pRaise->affExpr = OE_Abort;
      }
      pSelect = sqlite3SelectNew(pParse, 
          sqlite3ExprListAppend(pParse, 0, pRaise),
          sqlite3SrcListAppend(pParse, 0, &tDb, &tFrom),
          pWhere,
          0, 0, 0, 0, 0
      );
      pWhere = 0;
    }

    /* Disable lookaside memory allocation */

Changes to test/fkey8.test.

223
224
225
226
227
228
229






















230
231
    INSERT INTO child VALUES(456);
    UPDATE parent SET p = '456' WHERE p=1200;
  COMMIT;
}
do_execsql_test 5.3 {
  PRAGMA integrity_check;
} {ok}























finish_test







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
    INSERT INTO child VALUES(456);
    UPDATE parent SET p = '456' WHERE p=1200;
  COMMIT;
}
do_execsql_test 5.3 {
  PRAGMA integrity_check;
} {ok}

#-------------------------------------------------------------------------
reset_db
forcedelete test.db2
do_execsql_test 6.1 {
  PRAGMA foreign_keys = on;
  CREATE TABLE c1(b);
  INSERT INTO c1 VALUES(123);
}

do_execsql_test 6.2 {
  ATTACH 'test.db2' AS aux;
  CREATE TABLE aux.p1(a INTEGER PRIMARY KEY);
  CREATE TABLE aux.c1(b REFERENCES p1(a) ON DELETE RESTRICT);

  INSERT INTO aux.p1 VALUES(123);
}

do_execsql_test 6.3 {
  DELETE FROM aux.p1 WHERE a=123;
}


finish_test