OLD | NEW |
1 /* | 1 /* |
2 ** | 2 ** |
3 ** The author disclaims copyright to this source code. In place of | 3 ** The author disclaims copyright to this source code. In place of |
4 ** a legal notice, here is a blessing: | 4 ** a legal notice, here is a blessing: |
5 ** | 5 ** |
6 ** May you do good and not evil. | 6 ** May you do good and not evil. |
7 ** May you find forgiveness for yourself and forgive others. | 7 ** May you find forgiveness for yourself and forgive others. |
8 ** May you share freely, never taking more than you give. | 8 ** May you share freely, never taking more than you give. |
9 ** | 9 ** |
10 ************************************************************************* | 10 ************************************************************************* |
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 break; | 242 break; |
243 } | 243 } |
244 }else{ | 244 }else{ |
245 /* If zKey is non-NULL, then this foreign key was declared to | 245 /* If zKey is non-NULL, then this foreign key was declared to |
246 ** map to an explicit list of columns in table pParent. Check if this | 246 ** map to an explicit list of columns in table pParent. Check if this |
247 ** index matches those columns. Also, check that the index uses | 247 ** index matches those columns. Also, check that the index uses |
248 ** the default collation sequences for each column. */ | 248 ** the default collation sequences for each column. */ |
249 int i, j; | 249 int i, j; |
250 for(i=0; i<nCol; i++){ | 250 for(i=0; i<nCol; i++){ |
251 i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */ | 251 i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */ |
252 char *zDfltColl; /* Def. collation for column */ | 252 const char *zDfltColl; /* Def. collation for column */ |
253 char *zIdxCol; /* Name of indexed column */ | 253 char *zIdxCol; /* Name of indexed column */ |
254 | 254 |
| 255 if( iCol<0 ) break; /* No foreign keys against expression indexes */ |
| 256 |
255 /* If the index uses a collation sequence that is different from | 257 /* If the index uses a collation sequence that is different from |
256 ** the default collation sequence for the column, this index is | 258 ** the default collation sequence for the column, this index is |
257 ** unusable. Bail out early in this case. */ | 259 ** unusable. Bail out early in this case. */ |
258 zDfltColl = pParent->aCol[iCol].zColl; | 260 zDfltColl = pParent->aCol[iCol].zColl; |
259 if( !zDfltColl ){ | 261 if( !zDfltColl ) zDfltColl = sqlite3StrBINARY; |
260 zDfltColl = "BINARY"; | |
261 } | |
262 if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; | 262 if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; |
263 | 263 |
264 zIdxCol = pParent->aCol[iCol].zName; | 264 zIdxCol = pParent->aCol[iCol].zName; |
265 for(j=0; j<nCol; j++){ | 265 for(j=0; j<nCol; j++){ |
266 if( sqlite3StrICmp(pFKey->aCol[j].zCol, zIdxCol)==0 ){ | 266 if( sqlite3StrICmp(pFKey->aCol[j].zCol, zIdxCol)==0 ){ |
267 if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; | 267 if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; |
268 break; | 268 break; |
269 } | 269 } |
270 } | 270 } |
271 if( j==nCol ) break; | 271 if( j==nCol ) break; |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
367 ** to increment the constraint-counter (i.e. this is an INSERT operation), | 367 ** to increment the constraint-counter (i.e. this is an INSERT operation), |
368 ** then check if the row being inserted matches itself. If so, do not | 368 ** then check if the row being inserted matches itself. If so, do not |
369 ** increment the constraint-counter. */ | 369 ** increment the constraint-counter. */ |
370 if( pTab==pFKey->pFrom && nIncr==1 ){ | 370 if( pTab==pFKey->pFrom && nIncr==1 ){ |
371 sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); VdbeCoverage(v); | 371 sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); VdbeCoverage(v); |
372 sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); | 372 sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); |
373 } | 373 } |
374 | 374 |
375 sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); | 375 sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); |
376 sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v); | 376 sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v); |
377 sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); | 377 sqlite3VdbeGoto(v, iOk); |
378 sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); | 378 sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); |
379 sqlite3VdbeJumpHere(v, iMustBeInt); | 379 sqlite3VdbeJumpHere(v, iMustBeInt); |
380 sqlite3ReleaseTempReg(pParse, regTemp); | 380 sqlite3ReleaseTempReg(pParse, regTemp); |
381 }else{ | 381 }else{ |
382 int nCol = pFKey->nCol; | 382 int nCol = pFKey->nCol; |
383 int regTemp = sqlite3GetTempRange(pParse, nCol); | 383 int regTemp = sqlite3GetTempRange(pParse, nCol); |
384 int regRec = sqlite3GetTempReg(pParse); | 384 int regRec = sqlite3GetTempReg(pParse); |
385 | 385 |
386 sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); | 386 sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); |
387 sqlite3VdbeSetP4KeyInfo(pParse, pIdx); | 387 sqlite3VdbeSetP4KeyInfo(pParse, pIdx); |
388 for(i=0; i<nCol; i++){ | 388 for(i=0; i<nCol; i++){ |
389 sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i); | 389 sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i); |
390 } | 390 } |
391 | 391 |
392 /* If the parent table is the same as the child table, and we are about | 392 /* If the parent table is the same as the child table, and we are about |
393 ** to increment the constraint-counter (i.e. this is an INSERT operation), | 393 ** to increment the constraint-counter (i.e. this is an INSERT operation), |
394 ** then check if the row being inserted matches itself. If so, do not | 394 ** then check if the row being inserted matches itself. If so, do not |
395 ** increment the constraint-counter. | 395 ** increment the constraint-counter. |
396 ** | 396 ** |
397 ** If any of the parent-key values are NULL, then the row cannot match | 397 ** If any of the parent-key values are NULL, then the row cannot match |
398 ** itself. So set JUMPIFNULL to make sure we do the OP_Found if any | 398 ** itself. So set JUMPIFNULL to make sure we do the OP_Found if any |
399 ** of the parent-key values are NULL (at this point it is known that | 399 ** of the parent-key values are NULL (at this point it is known that |
400 ** none of the child key values are). | 400 ** none of the child key values are). |
401 */ | 401 */ |
402 if( pTab==pFKey->pFrom && nIncr==1 ){ | 402 if( pTab==pFKey->pFrom && nIncr==1 ){ |
403 int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1; | 403 int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1; |
404 for(i=0; i<nCol; i++){ | 404 for(i=0; i<nCol; i++){ |
405 int iChild = aiCol[i]+1+regData; | 405 int iChild = aiCol[i]+1+regData; |
406 int iParent = pIdx->aiColumn[i]+1+regData; | 406 int iParent = pIdx->aiColumn[i]+1+regData; |
| 407 assert( pIdx->aiColumn[i]>=0 ); |
407 assert( aiCol[i]!=pTab->iPKey ); | 408 assert( aiCol[i]!=pTab->iPKey ); |
408 if( pIdx->aiColumn[i]==pTab->iPKey ){ | 409 if( pIdx->aiColumn[i]==pTab->iPKey ){ |
409 /* The parent key is a composite key that includes the IPK column */ | 410 /* The parent key is a composite key that includes the IPK column */ |
410 iParent = regData; | 411 iParent = regData; |
411 } | 412 } |
412 sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); VdbeCoverage(v); | 413 sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); VdbeCoverage(v); |
413 sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); | 414 sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); |
414 } | 415 } |
415 sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); | 416 sqlite3VdbeGoto(v, iOk); |
416 } | 417 } |
417 | 418 |
418 sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec, | 419 sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec, |
419 sqlite3IndexAffinityStr(v,pIdx), nCol); | 420 sqlite3IndexAffinityStr(pParse->db,pIdx), nCol); |
420 sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v); | 421 sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v); |
421 | 422 |
422 sqlite3ReleaseTempReg(pParse, regRec); | 423 sqlite3ReleaseTempReg(pParse, regRec); |
423 sqlite3ReleaseTempRange(pParse, regTemp, nCol); | 424 sqlite3ReleaseTempRange(pParse, regTemp, nCol); |
424 } | 425 } |
425 } | 426 } |
426 | 427 |
427 if( !pFKey->isDeferred && !(pParse->db->flags & SQLITE_DeferFKs) | 428 if( !pFKey->isDeferred && !(pParse->db->flags & SQLITE_DeferFKs) |
428 && !pParse->pToplevel | 429 && !pParse->pToplevel |
429 && !pParse->isMultiWrite | 430 && !pParse->isMultiWrite |
430 ){ | 431 ){ |
431 /* Special case: If this is an INSERT statement that will insert exactly | 432 /* Special case: If this is an INSERT statement that will insert exactly |
432 ** one row into the table, raise a constraint immediately instead of | 433 ** one row into the table, raise a constraint immediately instead of |
433 ** incrementing a counter. This is necessary as the VM code is being | 434 ** incrementing a counter. This is necessary as the VM code is being |
434 ** generated for will not open a statement transaction. */ | 435 ** generated for will not open a statement transaction. */ |
435 assert( nIncr==1 ); | 436 assert( nIncr==1 ); |
436 sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, | 437 sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, |
437 OE_Abort, 0, P4_STATIC, P5_ConstraintFK); | 438 OE_Abort, 0, P4_STATIC, P5_ConstraintFK); |
438 }else{ | 439 }else{ |
439 if( nIncr>0 && pFKey->isDeferred==0 ){ | 440 if( nIncr>0 && pFKey->isDeferred==0 ){ |
440 sqlite3ParseToplevel(pParse)->mayAbort = 1; | 441 sqlite3MayAbort(pParse); |
441 } | 442 } |
442 sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); | 443 sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); |
443 } | 444 } |
444 | 445 |
445 sqlite3VdbeResolveLabel(v, iOk); | 446 sqlite3VdbeResolveLabel(v, iOk); |
446 sqlite3VdbeAddOp1(v, OP_Close, iCur); | 447 sqlite3VdbeAddOp1(v, OP_Close, iCur); |
447 } | 448 } |
448 | 449 |
449 | 450 |
450 /* | 451 /* |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
502 return pExpr; | 503 return pExpr; |
503 } | 504 } |
504 | 505 |
505 /* | 506 /* |
506 ** This function is called to generate code executed when a row is deleted | 507 ** This function is called to generate code executed when a row is deleted |
507 ** from the parent table of foreign key constraint pFKey and, if pFKey is | 508 ** from the parent table of foreign key constraint pFKey and, if pFKey is |
508 ** deferred, when a row is inserted into the same table. When generating | 509 ** deferred, when a row is inserted into the same table. When generating |
509 ** code for an SQL UPDATE operation, this function may be called twice - | 510 ** code for an SQL UPDATE operation, this function may be called twice - |
510 ** once to "delete" the old row and once to "insert" the new row. | 511 ** once to "delete" the old row and once to "insert" the new row. |
511 ** | 512 ** |
| 513 ** Parameter nIncr is passed -1 when inserting a row (as this may decrease |
| 514 ** the number of FK violations in the db) or +1 when deleting one (as this |
| 515 ** may increase the number of FK constraint problems). |
| 516 ** |
512 ** The code generated by this function scans through the rows in the child | 517 ** The code generated by this function scans through the rows in the child |
513 ** table that correspond to the parent table row being deleted or inserted. | 518 ** table that correspond to the parent table row being deleted or inserted. |
514 ** For each child row found, one of the following actions is taken: | 519 ** For each child row found, one of the following actions is taken: |
515 ** | 520 ** |
516 ** Operation | FK type | Action taken | 521 ** Operation | FK type | Action taken |
517 ** -------------------------------------------------------------------------- | 522 ** -------------------------------------------------------------------------- |
518 ** DELETE immediate Increment the "immediate constraint counter". | 523 ** DELETE immediate Increment the "immediate constraint counter". |
519 ** Or, if the ON (UPDATE|DELETE) action is RESTRICT, | 524 ** Or, if the ON (UPDATE|DELETE) action is RESTRICT, |
520 ** throw a "FOREIGN KEY constraint failed" exception. | 525 ** throw a "FOREIGN KEY constraint failed" exception. |
521 ** | 526 ** |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
601 if( HasRowid(pTab) ){ | 606 if( HasRowid(pTab) ){ |
602 pLeft = exprTableRegister(pParse, pTab, regData, -1); | 607 pLeft = exprTableRegister(pParse, pTab, regData, -1); |
603 pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1); | 608 pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1); |
604 pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0); | 609 pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0); |
605 }else{ | 610 }else{ |
606 Expr *pEq, *pAll = 0; | 611 Expr *pEq, *pAll = 0; |
607 Index *pPk = sqlite3PrimaryKeyIndex(pTab); | 612 Index *pPk = sqlite3PrimaryKeyIndex(pTab); |
608 assert( pIdx!=0 ); | 613 assert( pIdx!=0 ); |
609 for(i=0; i<pPk->nKeyCol; i++){ | 614 for(i=0; i<pPk->nKeyCol; i++){ |
610 i16 iCol = pIdx->aiColumn[i]; | 615 i16 iCol = pIdx->aiColumn[i]; |
| 616 assert( iCol>=0 ); |
611 pLeft = exprTableRegister(pParse, pTab, regData, iCol); | 617 pLeft = exprTableRegister(pParse, pTab, regData, iCol); |
612 pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol); | 618 pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol); |
613 pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0); | 619 pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0); |
614 pAll = sqlite3ExprAnd(db, pAll, pEq); | 620 pAll = sqlite3ExprAnd(db, pAll, pEq); |
615 } | 621 } |
616 pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0); | 622 pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0); |
617 } | 623 } |
618 pWhere = sqlite3ExprAnd(db, pWhere, pNe); | 624 pWhere = sqlite3ExprAnd(db, pWhere, pNe); |
619 } | 625 } |
620 | 626 |
621 /* Resolve the references in the WHERE clause. */ | 627 /* Resolve the references in the WHERE clause. */ |
622 memset(&sNameContext, 0, sizeof(NameContext)); | 628 memset(&sNameContext, 0, sizeof(NameContext)); |
623 sNameContext.pSrcList = pSrc; | 629 sNameContext.pSrcList = pSrc; |
624 sNameContext.pParse = pParse; | 630 sNameContext.pParse = pParse; |
625 sqlite3ResolveExprNames(&sNameContext, pWhere); | 631 sqlite3ResolveExprNames(&sNameContext, pWhere); |
626 | 632 |
627 /* Create VDBE to loop through the entries in pSrc that match the WHERE | 633 /* Create VDBE to loop through the entries in pSrc that match the WHERE |
628 ** clause. If the constraint is not deferred, throw an exception for | 634 ** clause. For each row found, increment either the deferred or immediate |
629 ** each row found. Otherwise, for deferred constraints, increment the | 635 ** foreign key constraint counter. */ |
630 ** deferred constraint counter by nIncr for each row selected. */ | |
631 pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); | 636 pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); |
632 if( nIncr>0 && pFKey->isDeferred==0 ){ | |
633 sqlite3ParseToplevel(pParse)->mayAbort = 1; | |
634 } | |
635 sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); | 637 sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); |
636 if( pWInfo ){ | 638 if( pWInfo ){ |
637 sqlite3WhereEnd(pWInfo); | 639 sqlite3WhereEnd(pWInfo); |
638 } | 640 } |
639 | 641 |
640 /* Clean up the WHERE clause constructed above. */ | 642 /* Clean up the WHERE clause constructed above. */ |
641 sqlite3ExprDelete(db, pWhere); | 643 sqlite3ExprDelete(db, pWhere); |
642 if( iFkIfZero ){ | 644 if( iFkIfZero ){ |
643 sqlite3VdbeJumpHere(v, iFkIfZero); | 645 sqlite3VdbeJumpHere(v, iFkIfZero); |
644 } | 646 } |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
804 }else if( pCol->colFlags & COLFLAG_PRIMKEY ){ | 806 }else if( pCol->colFlags & COLFLAG_PRIMKEY ){ |
805 return 1; | 807 return 1; |
806 } | 808 } |
807 } | 809 } |
808 } | 810 } |
809 } | 811 } |
810 return 0; | 812 return 0; |
811 } | 813 } |
812 | 814 |
813 /* | 815 /* |
| 816 ** Return true if the parser passed as the first argument is being |
| 817 ** used to code a trigger that is really a "SET NULL" action belonging |
| 818 ** to trigger pFKey. |
| 819 */ |
| 820 static int isSetNullAction(Parse *pParse, FKey *pFKey){ |
| 821 Parse *pTop = sqlite3ParseToplevel(pParse); |
| 822 if( pTop->pTriggerPrg ){ |
| 823 Trigger *p = pTop->pTriggerPrg->pTrigger; |
| 824 if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull) |
| 825 || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull) |
| 826 ){ |
| 827 return 1; |
| 828 } |
| 829 } |
| 830 return 0; |
| 831 } |
| 832 |
| 833 /* |
814 ** This function is called when inserting, deleting or updating a row of | 834 ** This function is called when inserting, deleting or updating a row of |
815 ** table pTab to generate VDBE code to perform foreign key constraint | 835 ** table pTab to generate VDBE code to perform foreign key constraint |
816 ** processing for the operation. | 836 ** processing for the operation. |
817 ** | 837 ** |
818 ** For a DELETE operation, parameter regOld is passed the index of the | 838 ** For a DELETE operation, parameter regOld is passed the index of the |
819 ** first register in an array of (pTab->nCol+1) registers containing the | 839 ** first register in an array of (pTab->nCol+1) registers containing the |
820 ** rowid of the row being deleted, followed by each of the column values | 840 ** rowid of the row being deleted, followed by each of the column values |
821 ** of the row being deleted, from left to right. Parameter regNew is passed | 841 ** of the row being deleted, from left to right. Parameter regNew is passed |
822 ** zero in this case. | 842 ** zero in this case. |
823 ** | 843 ** |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
855 | 875 |
856 /* Loop through all the foreign key constraints for which pTab is the | 876 /* Loop through all the foreign key constraints for which pTab is the |
857 ** child table (the table that the foreign key definition is part of). */ | 877 ** child table (the table that the foreign key definition is part of). */ |
858 for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ | 878 for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ |
859 Table *pTo; /* Parent table of foreign key pFKey */ | 879 Table *pTo; /* Parent table of foreign key pFKey */ |
860 Index *pIdx = 0; /* Index on key columns in pTo */ | 880 Index *pIdx = 0; /* Index on key columns in pTo */ |
861 int *aiFree = 0; | 881 int *aiFree = 0; |
862 int *aiCol; | 882 int *aiCol; |
863 int iCol; | 883 int iCol; |
864 int i; | 884 int i; |
865 int isIgnore = 0; | 885 int bIgnore = 0; |
866 | 886 |
867 if( aChange | 887 if( aChange |
868 && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0 | 888 && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0 |
869 && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0 | 889 && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0 |
870 ){ | 890 ){ |
871 continue; | 891 continue; |
872 } | 892 } |
873 | 893 |
874 /* Find the parent table of this foreign key. Also find a unique index | 894 /* Find the parent table of this foreign key. Also find a unique index |
875 ** on the parent key columns in the parent table. If either of these | 895 ** on the parent key columns in the parent table. If either of these |
(...skipping 30 matching lines...) Expand all Loading... |
906 if( aiFree ){ | 926 if( aiFree ){ |
907 aiCol = aiFree; | 927 aiCol = aiFree; |
908 }else{ | 928 }else{ |
909 iCol = pFKey->aCol[0].iFrom; | 929 iCol = pFKey->aCol[0].iFrom; |
910 aiCol = &iCol; | 930 aiCol = &iCol; |
911 } | 931 } |
912 for(i=0; i<pFKey->nCol; i++){ | 932 for(i=0; i<pFKey->nCol; i++){ |
913 if( aiCol[i]==pTab->iPKey ){ | 933 if( aiCol[i]==pTab->iPKey ){ |
914 aiCol[i] = -1; | 934 aiCol[i] = -1; |
915 } | 935 } |
| 936 assert( pIdx==0 || pIdx->aiColumn[i]>=0 ); |
916 #ifndef SQLITE_OMIT_AUTHORIZATION | 937 #ifndef SQLITE_OMIT_AUTHORIZATION |
917 /* Request permission to read the parent key columns. If the | 938 /* Request permission to read the parent key columns. If the |
918 ** authorization callback returns SQLITE_IGNORE, behave as if any | 939 ** authorization callback returns SQLITE_IGNORE, behave as if any |
919 ** values read from the parent table are NULL. */ | 940 ** values read from the parent table are NULL. */ |
920 if( db->xAuth ){ | 941 if( db->xAuth ){ |
921 int rcauth; | 942 int rcauth; |
922 char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; | 943 char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; |
923 rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); | 944 rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); |
924 isIgnore = (rcauth==SQLITE_IGNORE); | 945 bIgnore = (rcauth==SQLITE_IGNORE); |
925 } | 946 } |
926 #endif | 947 #endif |
927 } | 948 } |
928 | 949 |
929 /* Take a shared-cache advisory read-lock on the parent table. Allocate | 950 /* Take a shared-cache advisory read-lock on the parent table. Allocate |
930 ** a cursor to use to search the unique index on the parent key columns | 951 ** a cursor to use to search the unique index on the parent key columns |
931 ** in the parent table. */ | 952 ** in the parent table. */ |
932 sqlite3TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName); | 953 sqlite3TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName); |
933 pParse->nTab++; | 954 pParse->nTab++; |
934 | 955 |
935 if( regOld!=0 ){ | 956 if( regOld!=0 ){ |
936 /* A row is being removed from the child table. Search for the parent. | 957 /* A row is being removed from the child table. Search for the parent. |
937 ** If the parent does not exist, removing the child row resolves an | 958 ** If the parent does not exist, removing the child row resolves an |
938 ** outstanding foreign key constraint violation. */ | 959 ** outstanding foreign key constraint violation. */ |
939 fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore); | 960 fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1, bIgnore); |
940 } | 961 } |
941 if( regNew!=0 ){ | 962 if( regNew!=0 && !isSetNullAction(pParse, pFKey) ){ |
942 /* A row is being added to the child table. If a parent row cannot | 963 /* A row is being added to the child table. If a parent row cannot |
943 ** be found, adding the child row has violated the FK constraint. */ | 964 ** be found, adding the child row has violated the FK constraint. |
944 fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore); | 965 ** |
| 966 ** If this operation is being performed as part of a trigger program |
| 967 ** that is actually a "SET NULL" action belonging to this very |
| 968 ** foreign key, then omit this scan altogether. As all child key |
| 969 ** values are guaranteed to be NULL, it is not possible for adding |
| 970 ** this row to cause an FK violation. */ |
| 971 fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1, bIgnore); |
945 } | 972 } |
946 | 973 |
947 sqlite3DbFree(db, aiFree); | 974 sqlite3DbFree(db, aiFree); |
948 } | 975 } |
949 | 976 |
950 /* Loop through all the foreign key constraints that refer to this table. | 977 /* Loop through all the foreign key constraints that refer to this table. |
951 ** (the "child" constraints) */ | 978 ** (the "child" constraints) */ |
952 for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ | 979 for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ |
953 Index *pIdx = 0; /* Foreign key index for pFKey */ | 980 Index *pIdx = 0; /* Foreign key index for pFKey */ |
954 SrcList *pSrc; | 981 SrcList *pSrc; |
955 int *aiCol = 0; | 982 int *aiCol = 0; |
956 | 983 |
957 if( aChange && fkParentIsModified(pTab, pFKey, aChange, bChngRowid)==0 ){ | 984 if( aChange && fkParentIsModified(pTab, pFKey, aChange, bChngRowid)==0 ){ |
958 continue; | 985 continue; |
959 } | 986 } |
960 | 987 |
961 if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs) | 988 if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs) |
962 && !pParse->pToplevel && !pParse->isMultiWrite | 989 && !pParse->pToplevel && !pParse->isMultiWrite |
963 ){ | 990 ){ |
964 assert( regOld==0 && regNew!=0 ); | 991 assert( regOld==0 && regNew!=0 ); |
965 /* Inserting a single row into a parent table cannot cause an immediate | 992 /* Inserting a single row into a parent table cannot cause (or fix) |
966 ** foreign key violation. So do nothing in this case. */ | 993 ** an immediate foreign key violation. So do nothing in this case. */ |
967 continue; | 994 continue; |
968 } | 995 } |
969 | 996 |
970 if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ | 997 if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ |
971 if( !isIgnoreErrors || db->mallocFailed ) return; | 998 if( !isIgnoreErrors || db->mallocFailed ) return; |
972 continue; | 999 continue; |
973 } | 1000 } |
974 assert( aiCol || pFKey->nCol==1 ); | 1001 assert( aiCol || pFKey->nCol==1 ); |
975 | 1002 |
976 /* Create a SrcList structure containing the child table. We need the | 1003 /* Create a SrcList structure containing the child table. We need the |
977 ** child table as a SrcList for sqlite3WhereBegin() */ | 1004 ** child table as a SrcList for sqlite3WhereBegin() */ |
978 pSrc = sqlite3SrcListAppend(db, 0, 0, 0); | 1005 pSrc = sqlite3SrcListAppend(db, 0, 0, 0); |
979 if( pSrc ){ | 1006 if( pSrc ){ |
980 struct SrcList_item *pItem = pSrc->a; | 1007 struct SrcList_item *pItem = pSrc->a; |
981 pItem->pTab = pFKey->pFrom; | 1008 pItem->pTab = pFKey->pFrom; |
982 pItem->zName = pFKey->pFrom->zName; | 1009 pItem->zName = pFKey->pFrom->zName; |
983 pItem->pTab->nRef++; | 1010 pItem->pTab->nRef++; |
984 pItem->iCursor = pParse->nTab++; | 1011 pItem->iCursor = pParse->nTab++; |
985 | 1012 |
986 if( regNew!=0 ){ | 1013 if( regNew!=0 ){ |
987 fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); | 1014 fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); |
988 } | 1015 } |
989 if( regOld!=0 ){ | 1016 if( regOld!=0 ){ |
990 /* If there is a RESTRICT action configured for the current operation | 1017 int eAction = pFKey->aAction[aChange!=0]; |
991 ** on the parent table of this FK, then throw an exception | |
992 ** immediately if the FK constraint is violated, even if this is a | |
993 ** deferred trigger. That's what RESTRICT means. To defer checking | |
994 ** the constraint, the FK should specify NO ACTION (represented | |
995 ** using OE_None). NO ACTION is the default. */ | |
996 fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); | 1018 fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); |
| 1019 /* If this is a deferred FK constraint, or a CASCADE or SET NULL |
| 1020 ** action applies, then any foreign key violations caused by |
| 1021 ** removing the parent key will be rectified by the action trigger. |
| 1022 ** So do not set the "may-abort" flag in this case. |
| 1023 ** |
| 1024 ** Note 1: If the FK is declared "ON UPDATE CASCADE", then the |
| 1025 ** may-abort flag will eventually be set on this statement anyway |
| 1026 ** (when this function is called as part of processing the UPDATE |
| 1027 ** within the action trigger). |
| 1028 ** |
| 1029 ** Note 2: At first glance it may seem like SQLite could simply omit |
| 1030 ** all OP_FkCounter related scans when either CASCADE or SET NULL |
| 1031 ** applies. The trouble starts if the CASCADE or SET NULL action |
| 1032 ** trigger causes other triggers or action rules attached to the |
| 1033 ** child table to fire. In these cases the fk constraint counters |
| 1034 ** might be set incorrectly if any OP_FkCounter related scans are |
| 1035 ** omitted. */ |
| 1036 if( !pFKey->isDeferred && eAction!=OE_Cascade && eAction!=OE_SetNull ){ |
| 1037 sqlite3MayAbort(pParse); |
| 1038 } |
997 } | 1039 } |
998 pItem->zName = 0; | 1040 pItem->zName = 0; |
999 sqlite3SrcListDelete(db, pSrc); | 1041 sqlite3SrcListDelete(db, pSrc); |
1000 } | 1042 } |
1001 sqlite3DbFree(db, aiCol); | 1043 sqlite3DbFree(db, aiCol); |
1002 } | 1044 } |
1003 } | 1045 } |
1004 | 1046 |
1005 #define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x))) | 1047 #define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x))) |
1006 | 1048 |
1007 /* | 1049 /* |
1008 ** This function is called before generating code to update or delete a | 1050 ** This function is called before generating code to update or delete a |
1009 ** row contained in table pTab. | 1051 ** row contained in table pTab. |
1010 */ | 1052 */ |
1011 u32 sqlite3FkOldmask( | 1053 u32 sqlite3FkOldmask( |
1012 Parse *pParse, /* Parse context */ | 1054 Parse *pParse, /* Parse context */ |
1013 Table *pTab /* Table being modified */ | 1055 Table *pTab /* Table being modified */ |
1014 ){ | 1056 ){ |
1015 u32 mask = 0; | 1057 u32 mask = 0; |
1016 if( pParse->db->flags&SQLITE_ForeignKeys ){ | 1058 if( pParse->db->flags&SQLITE_ForeignKeys ){ |
1017 FKey *p; | 1059 FKey *p; |
1018 int i; | 1060 int i; |
1019 for(p=pTab->pFKey; p; p=p->pNextFrom){ | 1061 for(p=pTab->pFKey; p; p=p->pNextFrom){ |
1020 for(i=0; i<p->nCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); | 1062 for(i=0; i<p->nCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); |
1021 } | 1063 } |
1022 for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ | 1064 for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ |
1023 Index *pIdx = 0; | 1065 Index *pIdx = 0; |
1024 sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); | 1066 sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); |
1025 if( pIdx ){ | 1067 if( pIdx ){ |
1026 for(i=0; i<pIdx->nKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); | 1068 for(i=0; i<pIdx->nKeyCol; i++){ |
| 1069 assert( pIdx->aiColumn[i]>=0 ); |
| 1070 mask |= COLUMN_MASK(pIdx->aiColumn[i]); |
| 1071 } |
1027 } | 1072 } |
1028 } | 1073 } |
1029 } | 1074 } |
1030 return mask; | 1075 return mask; |
1031 } | 1076 } |
1032 | 1077 |
1033 | 1078 |
1034 /* | 1079 /* |
1035 ** This function is called before generating code to update or delete a | 1080 ** This function is called before generating code to update or delete a |
1036 ** row contained in table pTab. If the operation is a DELETE, then | 1081 ** row contained in table pTab. If the operation is a DELETE, then |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1138 for(i=0; i<pFKey->nCol; i++){ | 1183 for(i=0; i<pFKey->nCol; i++){ |
1139 Token tOld = { "old", 3 }; /* Literal "old" token */ | 1184 Token tOld = { "old", 3 }; /* Literal "old" token */ |
1140 Token tNew = { "new", 3 }; /* Literal "new" token */ | 1185 Token tNew = { "new", 3 }; /* Literal "new" token */ |
1141 Token tFromCol; /* Name of column in child table */ | 1186 Token tFromCol; /* Name of column in child table */ |
1142 Token tToCol; /* Name of column in parent table */ | 1187 Token tToCol; /* Name of column in parent table */ |
1143 int iFromCol; /* Idx of column in child table */ | 1188 int iFromCol; /* Idx of column in child table */ |
1144 Expr *pEq; /* tFromCol = OLD.tToCol */ | 1189 Expr *pEq; /* tFromCol = OLD.tToCol */ |
1145 | 1190 |
1146 iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; | 1191 iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; |
1147 assert( iFromCol>=0 ); | 1192 assert( iFromCol>=0 ); |
1148 tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid"; | 1193 assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKey<pTab->nCol) ); |
| 1194 assert( pIdx==0 || pIdx->aiColumn[i]>=0 ); |
| 1195 tToCol.z = pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName; |
1149 tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName; | 1196 tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName; |
1150 | 1197 |
1151 tToCol.n = sqlite3Strlen30(tToCol.z); | 1198 tToCol.n = sqlite3Strlen30(tToCol.z); |
1152 tFromCol.n = sqlite3Strlen30(tFromCol.z); | 1199 tFromCol.n = sqlite3Strlen30(tFromCol.z); |
1153 | 1200 |
1154 /* Create the expression "OLD.zToCol = zFromCol". It is important | 1201 /* Create the expression "OLD.zToCol = zFromCol". It is important |
1155 ** that the "OLD.zToCol" term is on the LHS of the = operator, so | 1202 ** that the "OLD.zToCol" term is on the LHS of the = operator, so |
1156 ** that the affinity and collation sequence associated with the | 1203 ** that the affinity and collation sequence associated with the |
1157 ** parent table are used for the comparison. */ | 1204 ** parent table are used for the comparison. */ |
1158 pEq = sqlite3PExpr(pParse, TK_EQ, | 1205 pEq = sqlite3PExpr(pParse, TK_EQ, |
1159 sqlite3PExpr(pParse, TK_DOT, | 1206 sqlite3PExpr(pParse, TK_DOT, |
1160 sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), | 1207 sqlite3ExprAlloc(db, TK_ID, &tOld, 0), |
1161 sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) | 1208 sqlite3ExprAlloc(db, TK_ID, &tToCol, 0) |
1162 , 0), | 1209 , 0), |
1163 sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol) | 1210 sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0) |
1164 , 0); | 1211 , 0); |
1165 pWhere = sqlite3ExprAnd(db, pWhere, pEq); | 1212 pWhere = sqlite3ExprAnd(db, pWhere, pEq); |
1166 | 1213 |
1167 /* For ON UPDATE, construct the next term of the WHEN clause. | 1214 /* For ON UPDATE, construct the next term of the WHEN clause. |
1168 ** The final WHEN clause will be like this: | 1215 ** The final WHEN clause will be like this: |
1169 ** | 1216 ** |
1170 ** WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN) | 1217 ** WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN) |
1171 */ | 1218 */ |
1172 if( pChanges ){ | 1219 if( pChanges ){ |
1173 pEq = sqlite3PExpr(pParse, TK_IS, | 1220 pEq = sqlite3PExpr(pParse, TK_IS, |
1174 sqlite3PExpr(pParse, TK_DOT, | 1221 sqlite3PExpr(pParse, TK_DOT, |
1175 sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), | 1222 sqlite3ExprAlloc(db, TK_ID, &tOld, 0), |
1176 sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), | 1223 sqlite3ExprAlloc(db, TK_ID, &tToCol, 0), |
1177 0), | 1224 0), |
1178 sqlite3PExpr(pParse, TK_DOT, | 1225 sqlite3PExpr(pParse, TK_DOT, |
1179 sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), | 1226 sqlite3ExprAlloc(db, TK_ID, &tNew, 0), |
1180 sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), | 1227 sqlite3ExprAlloc(db, TK_ID, &tToCol, 0), |
1181 0), | 1228 0), |
1182 0); | 1229 0); |
1183 pWhen = sqlite3ExprAnd(db, pWhen, pEq); | 1230 pWhen = sqlite3ExprAnd(db, pWhen, pEq); |
1184 } | 1231 } |
1185 | 1232 |
1186 if( action!=OE_Restrict && (action!=OE_Cascade || pChanges) ){ | 1233 if( action!=OE_Restrict && (action!=OE_Cascade || pChanges) ){ |
1187 Expr *pNew; | 1234 Expr *pNew; |
1188 if( action==OE_Cascade ){ | 1235 if( action==OE_Cascade ){ |
1189 pNew = sqlite3PExpr(pParse, TK_DOT, | 1236 pNew = sqlite3PExpr(pParse, TK_DOT, |
1190 sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), | 1237 sqlite3ExprAlloc(db, TK_ID, &tNew, 0), |
1191 sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) | 1238 sqlite3ExprAlloc(db, TK_ID, &tToCol, 0) |
1192 , 0); | 1239 , 0); |
1193 }else if( action==OE_SetDflt ){ | 1240 }else if( action==OE_SetDflt ){ |
1194 Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt; | 1241 Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt; |
1195 if( pDflt ){ | 1242 if( pDflt ){ |
1196 pNew = sqlite3ExprDup(db, pDflt, 0); | 1243 pNew = sqlite3ExprDup(db, pDflt, 0); |
1197 }else{ | 1244 }else{ |
1198 pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); | 1245 pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); |
1199 } | 1246 } |
1200 }else{ | 1247 }else{ |
1201 pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); | 1248 pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); |
(...skipping 26 matching lines...) Expand all Loading... |
1228 pWhere = 0; | 1275 pWhere = 0; |
1229 } | 1276 } |
1230 | 1277 |
1231 /* Disable lookaside memory allocation */ | 1278 /* Disable lookaside memory allocation */ |
1232 enableLookaside = db->lookaside.bEnabled; | 1279 enableLookaside = db->lookaside.bEnabled; |
1233 db->lookaside.bEnabled = 0; | 1280 db->lookaside.bEnabled = 0; |
1234 | 1281 |
1235 pTrigger = (Trigger *)sqlite3DbMallocZero(db, | 1282 pTrigger = (Trigger *)sqlite3DbMallocZero(db, |
1236 sizeof(Trigger) + /* struct Trigger */ | 1283 sizeof(Trigger) + /* struct Trigger */ |
1237 sizeof(TriggerStep) + /* Single step in trigger program */ | 1284 sizeof(TriggerStep) + /* Single step in trigger program */ |
1238 nFrom + 1 /* Space for pStep->target.z */ | 1285 nFrom + 1 /* Space for pStep->zTarget */ |
1239 ); | 1286 ); |
1240 if( pTrigger ){ | 1287 if( pTrigger ){ |
1241 pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1]; | 1288 pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1]; |
1242 pStep->target.z = (char *)&pStep[1]; | 1289 pStep->zTarget = (char *)&pStep[1]; |
1243 pStep->target.n = nFrom; | 1290 memcpy((char *)pStep->zTarget, zFrom, nFrom); |
1244 memcpy((char *)pStep->target.z, zFrom, nFrom); | |
1245 | 1291 |
1246 pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); | 1292 pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); |
1247 pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); | 1293 pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); |
1248 pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); | 1294 pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); |
1249 if( pWhen ){ | 1295 if( pWhen ){ |
1250 pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0, 0); | 1296 pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0, 0); |
1251 pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); | 1297 pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); |
1252 } | 1298 } |
1253 } | 1299 } |
1254 | 1300 |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1353 #ifndef SQLITE_OMIT_TRIGGER | 1399 #ifndef SQLITE_OMIT_TRIGGER |
1354 fkTriggerDelete(db, pFKey->apTrigger[0]); | 1400 fkTriggerDelete(db, pFKey->apTrigger[0]); |
1355 fkTriggerDelete(db, pFKey->apTrigger[1]); | 1401 fkTriggerDelete(db, pFKey->apTrigger[1]); |
1356 #endif | 1402 #endif |
1357 | 1403 |
1358 pNext = pFKey->pNextFrom; | 1404 pNext = pFKey->pNextFrom; |
1359 sqlite3DbFree(db, pFKey); | 1405 sqlite3DbFree(db, pFKey); |
1360 } | 1406 } |
1361 } | 1407 } |
1362 #endif /* ifndef SQLITE_OMIT_FOREIGN_KEY */ | 1408 #endif /* ifndef SQLITE_OMIT_FOREIGN_KEY */ |
OLD | NEW |