Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fixes to foreign key logic. And other things. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | primary-keys |
Files: | files | file ages | folders |
SHA1: |
69cf7caf80f1b55fa708147a2912d124 |
User & Date: | dan 2012-04-14 19:38:34.612 |
Context
2012-04-16
| ||
19:04 | Get most of the trigger logic working again. Still some problems. check-in: b480943c48 user: dan tags: primary-keys | |
2012-04-14
| ||
19:38 | Fixes to foreign key logic. And other things. check-in: 69cf7caf80 user: dan tags: primary-keys | |
2012-04-13
| ||
19:17 | Fix sub-transaction commit in kvmem.c. Also various aspects of constraint handling. check-in: 365eb3c6de user: dan tags: primary-keys | |
Changes
Changes to src/delete.c.
︙ | ︙ | |||
331 332 333 334 335 336 337 | memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pSrcList = pTabList; if( sqlite4ResolveExprNames(&sNC, pWhere) ){ goto delete_from_cleanup; } | < < < < < < < < | | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pSrcList = pTabList; if( sqlite4ResolveExprNames(&sNC, pWhere) ){ goto delete_from_cleanup; } #ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION /* Special case: A DELETE without a WHERE clause deletes everything. ** It is easier just to erase the whole table. Prior to version 3.6.5, ** this optimization caused the row change count (the value returned by ** API function sqlite4_count_changes) to be set incorrectly. */ if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) && 0==sqlite4FkRequired(pParse, pTab, 0) ){ assert( !isView ); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->pSchema==pTab->pSchema ); sqlite4VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); } }else |
︙ | ︙ | |||
477 478 479 480 481 482 483 | int baseCur, /* Base cursor number */ int regKey, /* Register containing PK of row to delete */ int bCount, /* True to increment the row change counter */ Trigger *pTrigger, /* List of triggers to (potentially) fire */ int onconf /* Default ON CONFLICT policy for triggers */ ){ Vdbe *v = pParse->pVdbe; /* Vdbe */ | | > > | | < | | > > > | > > > > > > > | | < < < < | | | | < | > | | | 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 | int baseCur, /* Base cursor number */ int regKey, /* Register containing PK of row to delete */ int bCount, /* True to increment the row change counter */ Trigger *pTrigger, /* List of triggers to (potentially) fire */ int onconf /* Default ON CONFLICT policy for triggers */ ){ Vdbe *v = pParse->pVdbe; /* Vdbe */ int regOld = 0; /* First register in OLD.* array */ int iLabel; /* Label resolved to end of generated code */ int iPk; int iPkCsr; /* Primary key cursor number */ /* Vdbe is guaranteed to have been allocated by this stage. */ assert( v ); sqlite4FindPrimaryKey(pTab, &iPk); iPkCsr = baseCur + iPk; /* Seek the PK cursor to the row to delete. If this row no longer exists ** (this can happen if a trigger program has already deleted it), do ** not attempt to delete it or fire any DELETE triggers. */ iLabel = sqlite4VdbeMakeLabel(v); sqlite4VdbeAddOp4Int(v, OP_NotFound, iPkCsr, iLabel, regKey, 0); /* If there are any triggers to fire, allocate a range of registers to ** use for the old.* references in the triggers. */ if( sqlite4FkRequired(pParse, pTab, 0) || pTrigger ){ u32 mask; /* Mask of OLD.* columns in use */ int iCol; /* Iterator used while populating OLD.* */ /* Determine which table columns may be required by either foreign key ** logic or triggers. This block sets stack variable mask to a 32-bit mask ** where bit 0 corresponds to the left-most table column, bit 1 to the ** second left-most, and so on. If an OLD.* column may be required, then ** the corresponding bit is set. ** ** Or, if the table contains more than 32 columns and at least one of ** the columns following the 32nd is required, set mask to 0xffffffff. */ mask = sqlite4TriggerColmask( pParse, pTrigger, 0, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onconf ); mask |= sqlite4FkOldmask(pParse, pTab); /* Allocate an array of registers - one for each column in the table. ** Then populate those array elements that may be used by FK or trigger ** logic with the OLD.* values. */ regOld = pParse->nMem+1; pParse->nMem += pTab->nCol; for(iCol=0; iCol<pTab->nCol; iCol++){ if( mask==0xffffffff || mask&(1<<iCol) ){ sqlite4ExprCodeGetColumnOfTable(v, pTab, iPkCsr, iCol, regOld+iCol); } } /* Invoke BEFORE DELETE trigger programs. */ sqlite4CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, TRIGGER_BEFORE, pTab, regOld, onconf, iLabel ); /* Seek the cursor to the row to be deleted again. It may be that ** the BEFORE triggers coded above have already removed the row ** being deleted. Do not attempt to delete the row a second time, and ** do not fire AFTER triggers. */ sqlite4VdbeAddOp4Int(v, OP_NotFound, iPkCsr, iLabel, regKey, 0); /* Do FK processing. This call checks that any FK constraints that ** refer to this table (i.e. constraints attached to other tables) ** are not violated by deleting this row. */ sqlite4FkCheck(pParse, pTab, regOld, 0); } /* Delete the index and table entries. Skip this step if pTab is really ** a view (in which case the only effect of the DELETE statement is to ** fire the INSTEAD OF triggers). */ if( pTab->pSelect==0 ){ sqlite4GenerateRowIndexDelete(pParse, pTab, baseCur, 0); #if 0 if( count ){ sqlite4VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); } #endif } /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key ** to the row just deleted. This is a no-op if there are no configured ** foreign keys that use this table as a parent table. */ sqlite4FkActions(pParse, pTab, 0, regOld); /* Invoke AFTER DELETE trigger programs. */ sqlite4CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, TRIGGER_AFTER, pTab, regOld, onconf, iLabel ); /* Jump here if the row had already been deleted before any BEFORE ** trigger programs were invoked. Or if a trigger program throws a ** RAISE(IGNORE) exception. */ sqlite4VdbeResolveLabel(v, iLabel); } |
︙ | ︙ |
Changes to src/fkey.c.
︙ | ︙ | |||
329 330 331 332 333 334 335 | if( isIgnore==0 ){ int nCol = pFKey->nCol; int regTemp = sqlite4GetTempRange(pParse, nCol); int regRec = sqlite4GetTempReg(pParse); sqlite4OpenIndex(pParse, iCur, iDb, pIdx, OP_OpenRead); | | > > > | > < | < > > | 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | if( isIgnore==0 ){ int nCol = pFKey->nCol; int regTemp = sqlite4GetTempRange(pParse, nCol); int regRec = sqlite4GetTempReg(pParse); sqlite4OpenIndex(pParse, iCur, iDb, pIdx, OP_OpenRead); /* Assemble the child key values in a contiguous array of registers. ** Then apply the affinity transformation for the parent index. */ for(i=0; i<nCol; i++){ sqlite4VdbeAddOp2(v, OP_Copy, aiCol[i]+regContent, regTemp+i); } sqlite4VdbeAddOp2(v, OP_Affinity, regTemp, nCol); sqlite4VdbeChangeP4(v, -1, sqlite4IndexAffinityStr(v, pIdx), P4_TRANSIENT); /* If the parent table is the same as the child table, and we are about ** to increment the constraint-counter (i.e. this is an INSERT operation), ** then check if the row being inserted matches itself. If so, do not ** increment the constraint-counter. ** ** If any of the parent-key values are NULL, then the row cannot match ** itself. So set JUMPIFNULL to make sure we do the OP_Found if any ** of the parent-key values are NULL (at this point it is known that ** none of the child key values are). */ if( pTab==pFKey->pFrom && nIncr==1 ){ int iJump = sqlite4VdbeCurrentAddr(v) + nCol + 1; for(i=0; i<nCol; i++){ int iChild = regTemp+i; int iParent = pIdx->aiColumn[i]+regContent; sqlite4VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); sqlite4VdbeChangeP5(v, SQLITE_JUMPIFNULL); assert( iChild<=pParse->nMem && iParent<=pParse->nMem ); } sqlite4VdbeAddOp2(v, OP_Goto, 0, iOk); } sqlite4VdbeAddOp3(v, OP_MakeIdxKey, iCur, regTemp, regRec); sqlite4VdbeChangeP5(v, 1); sqlite4VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); #if 0 sqlite4VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); sqlite4VdbeChangeP4(v, -1, sqlite4IndexAffinityStr(v,pIdx), P4_TRANSIENT); sqlite4VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); #endif sqlite4ReleaseTempReg(pParse, regRec); sqlite4ReleaseTempRange(pParse, regTemp, nCol); } if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){ /* Special case: If this is an INSERT statement that will insert exactly |
︙ | ︙ | |||
389 390 391 392 393 394 395 | } sqlite4VdbeResolveLabel(v, iOk); sqlite4VdbeAddOp1(v, OP_Close, iCur); } /* | | | < | | > | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | } sqlite4VdbeResolveLabel(v, iOk); sqlite4VdbeAddOp1(v, OP_Close, iCur); } /* ** This function is called to generate code executed when a row is inserted ** into or deleted from the parent table of foreign key constraint pFKey. ** When generating code for an SQL UPDATE operation, this function may be ** called twice - once to "delete" the old row and once to "insert" the ** new row. ** ** 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 ** -------------------------------------------------------------------------- |
︙ | ︙ | |||
434 435 436 437 438 439 440 | int i; /* Iterator variable */ Expr *pWhere = 0; /* WHERE clause to scan with */ NameContext sNameContext; /* Context used to resolve WHERE clause */ WhereInfo *pWInfo; /* Context used by sqlite4WhereXXX() */ int iFkIfZero = 0; /* Address of OP_FkIfZero */ Vdbe *v = sqlite4GetVdbe(pParse); | | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | int i; /* Iterator variable */ Expr *pWhere = 0; /* WHERE clause to scan with */ NameContext sNameContext; /* Context used to resolve WHERE clause */ WhereInfo *pWInfo; /* Context used by sqlite4WhereXXX() */ int iFkIfZero = 0; /* Address of OP_FkIfZero */ Vdbe *v = sqlite4GetVdbe(pParse); assert( pIdx && pIdx->pTable==pTab ); if( nIncr<0 ){ iFkIfZero = sqlite4VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0); } /* Create an Expr object representing an SQL expression like: ** |
︙ | ︙ | |||
459 460 461 462 463 464 465 | int iCol; /* Index of column in child table */ const char *zCol; /* Name of column in child table */ pLeft = sqlite4Expr(db, TK_REGISTER, 0); if( pLeft ){ /* Set the collation sequence and affinity of the LHS of each TK_EQ ** expression to the parent key column defaults. */ | < | | | | | | < < < < | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | int iCol; /* Index of column in child table */ const char *zCol; /* Name of column in child table */ pLeft = sqlite4Expr(db, TK_REGISTER, 0); if( pLeft ){ /* Set the collation sequence and affinity of the LHS of each TK_EQ ** expression to the parent key column defaults. */ Column *pCol; iCol = pIdx->aiColumn[i]; pCol = &pTab->aCol[iCol]; pLeft->iTable = regData+iCol; pLeft->affinity = pCol->affinity; pLeft->pColl = sqlite4LocateCollSeq(pParse, pCol->zColl); } iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iCol>=0 ); zCol = pFKey->pFrom->aCol[iCol].zName; pRight = sqlite4Expr(db, TK_ID, zCol); pEq = sqlite4PExpr(pParse, TK_EQ, pLeft, pRight, 0); pWhere = sqlite4ExprAnd(db, pWhere, pEq); |
︙ | ︙ | |||
506 507 508 509 510 511 512 | /* Resolve the references in the WHERE clause. */ memset(&sNameContext, 0, sizeof(NameContext)); sNameContext.pSrcList = pSrc; sNameContext.pParse = pParse; sqlite4ResolveExprNames(&sNameContext, pWhere); /* Create VDBE to loop through the entries in pSrc that match the WHERE | < | | | 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | /* Resolve the references in the WHERE clause. */ memset(&sNameContext, 0, sizeof(NameContext)); sNameContext.pSrcList = pSrc; sNameContext.pParse = pParse; sqlite4ResolveExprNames(&sNameContext, pWhere); /* Create VDBE to loop through the entries in pSrc that match the WHERE ** clause. For each row found, increment the relevant constraint counter ** by nIncr. */ pWInfo = sqlite4WhereBegin(pParse, pSrc, pWhere, 0, 0, 0); if( nIncr>0 && pFKey->isDeferred==0 ){ sqlite4ParseToplevel(pParse)->mayAbort = 1; } sqlite4VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); if( pWInfo ){ sqlite4WhereEnd(pWInfo); |
︙ | ︙ | |||
799 800 801 802 803 804 805 | } } #define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x))) /* ** This function is called before generating code to update or delete a | | | 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 | } } #define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x))) /* ** This function is called before generating code to update or delete a ** row contained in table pTab. */ u32 sqlite4FkOldmask( Parse *pParse, /* Parse context */ Table *pTab /* Table being modified */ ){ u32 mask = 0; if( pParse->db->flags&SQLITE_ForeignKeys ){ |
︙ | ︙ | |||
840 841 842 843 844 845 846 | ** If any foreign key processing will be required, this function returns ** true. If there is no foreign key related processing, this function ** returns false. */ int sqlite4FkRequired( Parse *pParse, /* Parse context */ Table *pTab, /* Table being modified */ | | < | 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 | ** If any foreign key processing will be required, this function returns ** true. If there is no foreign key related processing, this function ** returns false. */ int sqlite4FkRequired( Parse *pParse, /* Parse context */ Table *pTab, /* Table being modified */ int *aChange /* Non-NULL for UPDATE operations */ ){ if( pParse->db->flags&SQLITE_ForeignKeys ){ if( !aChange ){ /* A DELETE operation. Foreign key processing is required if the ** table in question is either the child or parent table for any ** foreign key constraint. */ return (sqlite4FkReferences(pTab) || pTab->pFKey); |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
784 785 786 787 788 789 790 | pTabList, 0, pColumn->a[i].zName); pParse->checkSchema = 1; goto insert_cleanup; } } } | < < < < < < < | 784 785 786 787 788 789 790 791 792 793 794 795 796 797 | pTabList, 0, pColumn->a[i].zName); pParse->checkSchema = 1; goto insert_cleanup; } } } /* If this is not a view, open a write cursor on each index. Allocate ** a contiguous array of (nIdx+1) registers, where nIdx is the total ** number of indexes (including the PRIMARY KEY index). ** ** Register aRegIdx[0]: The PRIMARY KEY index key ** Register aRegIdx[1..nIdx-1]: Keys for other table indexes ** Register aRegIdx[nIdx]: Data record for table row. |
︙ | ︙ | |||
980 981 982 983 984 985 986 | sqlite4VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc); appendFlag = 1; } autoIncStep(pParse, regAutoinc, regRowid); #endif /* Push onto the stack, data for all columns of the new entry, beginning | | < | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | sqlite4VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc); appendFlag = 1; } autoIncStep(pParse, regAutoinc, regRowid); #endif /* Push onto the stack, data for all columns of the new entry, beginning ** with the first column. */ nHidden = 0; for(i=0; i<pTab->nCol; i++){ int iRegStore = regContent + i; if( pColumn==0 ){ if( IsHiddenColumn(&pTab->aCol[i]) ){ assert( IsVirtual(pTab) ); j = -1; |
︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | sqlite4VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore); }else if( pSelect ){ sqlite4VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore); }else{ sqlite4ExprCode(pParse, pList->a[j].pExpr, iRegStore); } } /* Generate code to check constraints and generate index keys and ** do the insertion. */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ const char *pVTab = (const char *)sqlite4GetVTable(db, pTab); sqlite4VtabMakeWritable(pParse, pTab); sqlite4VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); sqlite4VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite4MayAbort(pParse); }else #endif { int isReplace; /* Set to true if constraints may cause a replace */ sqlite4GenerateConstraintChecks(pParse, pTab, baseCur, | > > > | < < < < < < | 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 | sqlite4VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore); }else if( pSelect ){ sqlite4VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore); }else{ sqlite4ExprCode(pParse, pList->a[j].pExpr, iRegStore); } } sqlite4VdbeAddOp2(v, OP_Affinity, regContent, pTab->nCol); sqlite4TableAffinityStr(v, pTab); /* Generate code to check constraints and generate index keys and ** do the insertion. */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ const char *pVTab = (const char *)sqlite4GetVTable(db, pTab); sqlite4VtabMakeWritable(pParse, pTab); sqlite4VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); sqlite4VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite4MayAbort(pParse); }else #endif { int isReplace; /* Set to true if constraints may cause a replace */ sqlite4GenerateConstraintChecks(pParse, pTab, baseCur, regContent, aRegIdx, 0, 0, onError, endOfLoop, &isReplace ); sqlite4FkCheck(pParse, pTab, 0, regContent); sqlite4CompleteInsertion(pParse, pTab, baseCur, regContent, aRegIdx, 0, appendFlag, isReplace==0 ); } } if( pTrigger ){ /* Code AFTER triggers */ sqlite4CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, pTab, regData-2-pTab->nCol, onError, endOfLoop); } /* The bottom of the main insertion loop, if the data source |
︙ | ︙ | |||
1077 1078 1079 1080 1081 1082 1083 | ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite4AutoincrementEnd(pParse); } | < < < < < < < < < < < | 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 | ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite4AutoincrementEnd(pParse); } insert_cleanup: sqlite4SrcListDelete(db, pTabList); sqlite4ExprListDelete(db, pList); sqlite4SelectDelete(db, pSelect); sqlite4IdListDelete(db, pColumn); sqlite4DbFree(db, aRegIdx); } |
︙ | ︙ | |||
1365 1366 1367 1368 1369 1370 1371 | */ void sqlite4GenerateConstraintChecks( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int baseCur, /* First in array of cursors for pTab indexes */ int regContent, /* Index of the range of input registers */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ | | < < | 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 | */ void sqlite4GenerateConstraintChecks( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int baseCur, /* First in array of cursors for pTab indexes */ int regContent, /* Index of the range of input registers */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ int regOldKey, /* For an update, the original encoded PK */ int isUpdate, /* True for UPDATE, False for INSERT */ int overrideError, /* Override onError to this if not OE_Default */ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */ ){ u8 aPkRoot[10]; /* Root page number for pPk as a varint */ int nPkRoot; /* Size of aPkRoot in bytes */ Index *pPk; /* Primary key index for table pTab */ |
︙ | ︙ | |||
1437 1438 1439 1440 1441 1442 1443 | /* If Index.onError==OE_None, then pIdx is not a UNIQUE or PRIMARY KEY ** index. In this case there is no need to test the index for uniqueness ** - all that is required is to populate the aRegIdx[iCur] register. Jump ** to the next iteration of the loop if this is the case. */ onError = pIdx->onError; if( onError!=OE_None ){ int iTest; /* Address of OP_IsUnique instruction */ | > | > > > | 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 | /* If Index.onError==OE_None, then pIdx is not a UNIQUE or PRIMARY KEY ** index. In this case there is no need to test the index for uniqueness ** - all that is required is to populate the aRegIdx[iCur] register. Jump ** to the next iteration of the loop if this is the case. */ onError = pIdx->onError; if( onError!=OE_None ){ int iTest; /* Address of OP_IsUnique instruction */ int iTest2 = 0; /* Address of OP_Eq instruction */ int regOut = 0; /* PK of row to replace */ /* Figure out what to do if a UNIQUE constraint is encountered. ** ** TODO: If a previous constraint is a REPLACE, why change IGNORE to ** REPLACE and FAIL to ABORT here? */ if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } if( seenReplace ){ if( onError==OE_Ignore ) onError = OE_Replace; else if( onError==OE_Fail ) onError = OE_Abort; } if( onError==OE_Replace ){ sqlite4VdbeAddOp3(v, OP_Blob, nPkRoot, regPk, 0); sqlite4VdbeChangeP4(v, -1, aPkRoot, nPkRoot); regOut = regPk; } if( regOldKey && pIdx==pPk ){ iTest2 = sqlite4VdbeAddOp3(v, OP_Eq, regOldKey, 0, aRegIdx[iCur]); } iTest = sqlite4VdbeAddOp3(v, OP_IsUnique, baseCur+iCur, 0, aRegIdx[iCur]); sqlite4VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(regOut), P4_INT32); switch( onError ){ case OE_Rollback: case OE_Abort: case OE_Fail: { |
︙ | ︙ | |||
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 | seenReplace = 1; break; } } /* If the OP_IsUnique passes (no constraint violation) jump here */ sqlite4VdbeJumpHere(v, iTest); } sqlite4ReleaseTempRange(pParse, regTmp, nTmpReg); } if( pbMayReplace ){ *pbMayReplace = seenReplace; | > | 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 | seenReplace = 1; break; } } /* If the OP_IsUnique passes (no constraint violation) jump here */ sqlite4VdbeJumpHere(v, iTest); if( iTest2 ) sqlite4VdbeJumpHere(v, iTest2); } sqlite4ReleaseTempRange(pParse, regTmp, nTmpReg); } if( pbMayReplace ){ *pbMayReplace = seenReplace; |
︙ | ︙ | |||
1844 1845 1846 1847 1848 1849 1850 | ** the extra complication to make this rule less restrictive is probably ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] */ if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ return 0; } #endif | < < < | 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 | ** the extra complication to make this rule less restrictive is probably ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] */ if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ return 0; } #endif /* If we get this far, it means that the xfer optimization is at ** least a possibility, though it might only work if the destination ** table (tab1) is initially empty. */ #ifdef SQLITE_TEST sqlite4_xferopt_count++; |
︙ | ︙ |
Changes to src/kvmem.c.
︙ | ︙ | |||
388 389 390 391 392 393 394 | *ppParent = 0; pBalance = pOld->pUp; }else if( pOld->pBefore && pOld->pAfter ){ KVMemNode *pX; /* Smallest node that is larger than pOld */ KVMemNode *pY; /* Left-hand child of pOld */ pX = kvmemFirst(pOld->pAfter); assert( pX->pBefore==0 ); | > > > | | | | | | | | | > > > | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | *ppParent = 0; pBalance = pOld->pUp; }else if( pOld->pBefore && pOld->pAfter ){ KVMemNode *pX; /* Smallest node that is larger than pOld */ KVMemNode *pY; /* Left-hand child of pOld */ pX = kvmemFirst(pOld->pAfter); assert( pX->pBefore==0 ); if( pX==pOld->pAfter ){ pBalance = pX; }else{ *kvmemFromPtr(pX, 0) = pX->pAfter; if( pX->pAfter ) pX->pAfter->pUp = pX->pUp; pBalance = pX->pUp; pX->pAfter = pOld->pAfter; if( pX->pAfter ){ pX->pAfter->pUp = pX; }else{ assert( pBalance==pOld ); pBalance = pX; } } pX->pBefore = pY = pOld->pBefore; if( pY ) pY->pUp = pX; pX->pUp = pOld->pUp; *ppParent = pX; }else if( pOld->pBefore==0 ){ *ppParent = pBalance = pOld->pAfter; pBalance->pUp = pOld->pUp; }else if( pOld->pAfter==0 ){ *ppParent = pBalance = pOld->pBefore; pBalance->pUp = pOld->pUp; } assertUpPointers(p->pRoot); p->pRoot = kvmemBalance(pBalance); assertUpPointers(p->pRoot); kvmemNodeUnref(pOld); } /* ** End of low-level access routines *************************************************************************** ** Interface routines follow |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
3182 3183 3184 3185 3186 3187 3188 | ** this case foreign keys are parsed, but no other functionality is ** provided (enforcement of FK constraints requires the triggers sub-system). */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) void sqlite4FkCheck(Parse*, Table*, int, int); void sqlite4FkDropTable(Parse*, SrcList *, Table*); void sqlite4FkActions(Parse*, Table*, ExprList*, int); | | | | 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 | ** this case foreign keys are parsed, but no other functionality is ** provided (enforcement of FK constraints requires the triggers sub-system). */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) void sqlite4FkCheck(Parse*, Table*, int, int); void sqlite4FkDropTable(Parse*, SrcList *, Table*); void sqlite4FkActions(Parse*, Table*, ExprList*, int); int sqlite4FkRequired(Parse*, Table*, int*); u32 sqlite4FkOldmask(Parse*, Table*); FKey *sqlite4FkReferences(Table *); #else #define sqlite4FkActions(a,b,c,d) #define sqlite4FkCheck(a,b,c,d) #define sqlite4FkDropTable(a,b,c) #define sqlite4FkOldmask(a,b) 0 #define sqlite4FkRequired(a,b,c) 0 #endif #ifndef SQLITE_OMIT_FOREIGN_KEY void sqlite4FkDelete(sqlite4 *, Table*); #else #define sqlite4FkDelete(a,b) #endif |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
102 103 104 105 106 107 108 | int nIdx; /* Number of indices that need updating */ int iCur; /* VDBE Cursor number of pTab */ sqlite4 *db; /* The database structure */ int *aRegIdx = 0; /* One register assigned to each index to be updated */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ | < | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | int nIdx; /* Number of indices that need updating */ int iCur; /* VDBE Cursor number of pTab */ sqlite4 *db; /* The database structure */ int *aRegIdx = 0; /* One register assigned to each index to be updated */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ Expr *pRowidExpr = 0; /* Expression defining the new record number */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ int okOnePass; /* True for one-pass algorithm without the FIFO */ int hasFK; /* True if foreign key processing is required */ |
︙ | ︙ | |||
252 253 254 255 256 257 258 | pWhere, onError); pWhere = 0; pSrc = 0; goto update_cleanup; } #endif | | | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | pWhere, onError); pWhere = 0; pSrc = 0; goto update_cleanup; } #endif hasFK = sqlite4FkRequired(pParse, pTab, aXRef); /* Allocate memory for the array aRegIdx[]. There is one entry in the ** array for each index associated with table being updated. Fill in ** the value with a register number for indices that are to be used ** and with zero for unused indices. */ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} aRegIdx = sqlite4DbMallocZero(db, sizeof(Index*) * nIdx ); |
︙ | ︙ | |||
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 | for(i=0; i<pTab->nCol; i++){ if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){ sqlite4ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i); }else{ sqlite4VdbeAddOp2(v, OP_Null, 0, regOld+i); } } if( chngRowid==0 ){ sqlite4VdbeAddOp2(v, OP_Copy, regOldKey, regNewRowid); } } /* Populate the array of registers beginning at regNew with the new ** row data. This array is used to check constaints, create the new ** table and index records, and as the values for any new.* references ** made by triggers. ** | > > | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | for(i=0; i<pTab->nCol; i++){ if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){ sqlite4ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i); }else{ sqlite4VdbeAddOp2(v, OP_Null, 0, regOld+i); } } #if 0 if( chngRowid==0 ){ sqlite4VdbeAddOp2(v, OP_Copy, regOldKey, regNewRowid); } #endif } /* Populate the array of registers beginning at regNew with the new ** row data. This array is used to check constaints, create the new ** table and index records, and as the values for any new.* references ** made by triggers. ** |
︙ | ︙ | |||
478 479 480 481 482 483 484 | } } } if( !isView ){ int j1; /* Address of jump instruction */ | < | | | | 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | } } } if( !isView ){ int j1; /* Address of jump instruction */ /* Do constraint checks. */ assert( bChngPk==0 || bImplicitPk==0 ); if( bChngPk==0 ) aRegIdx[iPk] = 0; sqlite4GenerateConstraintChecks( pParse, pTab, iCur, regNew, aRegIdx, regOldKey, 1, onError, addr, 0 ); if( bChngPk==0 ) aRegIdx[iPk] = regOldKey; /* Do FK constraint checks. */ if( hasFK ){ sqlite4FkCheck(pParse, pTab, regOld, 0); } /* Delete the index entries associated with the current record. */ j1 = sqlite4VdbeAddOp4(v, OP_NotFound, iCur+iPk, 0, regOldKey, 0, P4_INT32); sqlite4GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); /* Delete the old record */ if( hasFK || bChngPk ){ sqlite4VdbeAddOp2(v, OP_Delete, iCur, 0); } sqlite4VdbeJumpHere(v, j1); if( hasFK ){ sqlite4FkCheck(pParse, pTab, 0, regNew); } /* Insert the new index entries and the new record. */ sqlite4CompleteInsertion(pParse, pTab, iCur, regNew, aRegIdx, 1, 0, 0); /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 | ** P4 is a string that is P2 characters long. The nth character of the ** string indicates the column affinity that should be used for the nth ** memory cell in the range. */ case OP_Affinity: { const char *zAffinity; /* The affinity to be applied */ char cAff; /* A single character of affinity */ zAffinity = pOp->p4.z; assert( zAffinity!=0 ); | > > | | < | > | | > | 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 | ** P4 is a string that is P2 characters long. The nth character of the ** string indicates the column affinity that should be used for the nth ** memory cell in the range. */ case OP_Affinity: { const char *zAffinity; /* The affinity to be applied */ char cAff; /* A single character of affinity */ Mem *pEnd; zAffinity = pOp->p4.z; assert( zAffinity!=0 ); assert( sqlite4Strlen30(zAffinity)>=pOp->p2 ); pEnd = &aMem[pOp->p2+pOp->p1]; for(pIn1=&aMem[pOp->p1]; pIn1<pEnd; pIn1++){ assert( memIsValid(pIn1) ); memAboutToChange(p, pIn1); applyAffinity(pIn1, *(zAffinity++), encoding); REGISTER_TRACE(pIn1-aMem, pIn1); } break; } /* Opcode: MakeIdxKey P1 P2 P3 * P5 ** ** P1 is an open cursor. P2 is the first register in a contiguous array ** of N registers containing values to encode into a database key. Normally, |
︙ | ︙ | |||
2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 | */ case OP_MakeIdxKey: { VdbeCursor *pC; KeyInfo *pKeyInfo; Mem *pData0; /* First in array of input registers */ u8 *aRec; /* The constructed database key */ int nRec; /* Size of aRec[] in bytes */ pC = p->apCsr[pOp->p1]; pKeyInfo = pC->pKeyInfo; pData0 = &aMem[pOp->p2]; pOut = &aMem[pOp->p3]; aRec = 0; memAboutToChange(p, pOut); | > | | | 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 | */ case OP_MakeIdxKey: { VdbeCursor *pC; KeyInfo *pKeyInfo; Mem *pData0; /* First in array of input registers */ u8 *aRec; /* The constructed database key */ int nRec; /* Size of aRec[] in bytes */ int nField; /* Number of fields in encoded record */ pC = p->apCsr[pOp->p1]; pKeyInfo = pC->pKeyInfo; pData0 = &aMem[pOp->p2]; pOut = &aMem[pOp->p3]; aRec = 0; memAboutToChange(p, pOut); nField = pKeyInfo->nField - (pOp->p5 ? pKeyInfo->nPK : 0); rc = sqlite4VdbeEncodeKey( db, pData0, nField, pC->iRoot, pKeyInfo, &aRec, &nRec, 0 ); if( rc ){ sqlite4DbFree(db, aRec); }else{ rc = sqlite4VdbeMemSetStr(pOut, aRec, nRec, 0, SQLITE_DYNAMIC); REGISTER_TRACE(pOp->p3, pOut); |
︙ | ︙ | |||
2287 2288 2289 2290 2291 2292 2293 | sqlite4VdbeMemExpandBlob(pMem); } } /* Compute the key (if this is a MakeKey opcode) */ if( pC ){ aRec = 0; | | | > | 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 | sqlite4VdbeMemExpandBlob(pMem); } } /* Compute the key (if this is a MakeKey opcode) */ if( pC ){ aRec = 0; rc = sqlite4VdbeEncodeKey(db, pData0, pC->pKeyInfo->nField, pC->iRoot, pC->pKeyInfo, &aRec, &nRec, 0 ); if( rc ){ sqlite4DbFree(db, aRec); }else{ rc = sqlite4VdbeMemSetStr(pKeyOut, aRec, nRec, 0, SQLITE_DYNAMIC); REGISTER_TRACE(keyReg, pKeyOut); UPDATE_MAX_BLOBSIZE(pKeyOut); } |
︙ | ︙ | |||
2384 2385 2386 2387 2388 2389 2390 | } else if( bAutoCommit==0 ){ db->autoCommit = 0; }else{ if( bRollback ){ sqlite4RollbackAll(db); | | > > > | 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 | } else if( bAutoCommit==0 ){ db->autoCommit = 0; }else{ if( bRollback ){ sqlite4RollbackAll(db); }else if( rc = sqlite4VdbeCheckFk(p, 1) ){ rc = SQLITE_ERROR; goto vdbe_return; } db->autoCommit = 1; sqlite4VdbeHalt(p); rc = (p->rc ? SQLITE_DONE : SQLITE_ERROR); goto vdbe_return; } break; } /* Opcode: Transaction P1 P2 * * * ** |
︙ | ︙ | |||
2432 2433 2434 2435 2436 2437 2438 | if( pOp->p2==0 ){ /* Read transaction needed. Start if we are not already in one. */ if( pKV->iTransLevel==0 ){ rc = sqlite4KVStoreBegin(pKV, 1); } }else{ /* A write transaction is needed */ | | > | | 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 | if( pOp->p2==0 ){ /* Read transaction needed. Start if we are not already in one. */ if( pKV->iTransLevel==0 ){ rc = sqlite4KVStoreBegin(pKV, 1); } }else{ /* A write transaction is needed */ needStmt = db->autoCommit==0 && (p->needSavepoint || db->activeVdbeCnt>1); if( pKV->iTransLevel<2 ){ rc = sqlite4KVStoreBegin(pKV, 2); } if( needStmt ){ rc = sqlite4KVStoreBegin(pKV, pKV->iTransLevel+1); if( rc==SQLITE_OK ){ p->stmtTransMask |= ((yDbMask)1)<<pOp->p1; } } } break; |
︙ | ︙ | |||
2988 2989 2990 2991 2992 2993 2994 | nProbe = pIn3->n; pFree = 0; } if( rc==SQLITE_OK ){ rc = sqlite4KVCursorSeek(pC->pKVCur, pProbe, nProbe, +1); if( rc==SQLITE_INEXACT || rc==SQLITE_OK ){ rc = sqlite4KVCursorKey(pC->pKVCur, &pKey, &nKey); | | | 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 | nProbe = pIn3->n; pFree = 0; } if( rc==SQLITE_OK ){ rc = sqlite4KVCursorSeek(pC->pKVCur, pProbe, nProbe, +1); if( rc==SQLITE_INEXACT || rc==SQLITE_OK ){ rc = sqlite4KVCursorKey(pC->pKVCur, &pKey, &nKey); if( rc==SQLITE_OK && nKey>=nProbe && memcmp(pKey, pProbe, nProbe)==0 ){ alreadyExists = 1; pC->nullRow = 0; } }else if( rc==SQLITE_NOTFOUND ){ rc = SQLITE_OK; } } |
︙ | ︙ |
Changes to src/vdbecodec.c.
︙ | ︙ | |||
644 645 646 647 648 649 650 | int *pnShort /* Number of bytes without the primary key */ ){ int i; int rc = SQLITE_OK; KeyEncoder x; u8 *so; int iShort; | < > > > > < < | | | < < < < < < < | | 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | int *pnShort /* Number of bytes without the primary key */ ){ int i; int rc = SQLITE_OK; KeyEncoder x; u8 *so; int iShort; CollSeq **aColl; CollSeq *xColl; static const CollSeq defaultColl; assert( pKeyInfo ); assert( nIn<=pKeyInfo->nField ); x.db = db; x.aOut = 0; x.nOut = 0; x.nAlloc = 0; *paOut = 0; *pnOut = 0; if( enlargeEncoderAllocation(&x, (nIn+1)*10) ) return SQLITE_NOMEM; x.nOut = sqlite4PutVarint64(x.aOut, iTabno); iShort = pKeyInfo->nField - pKeyInfo->nPK; aColl = pKeyInfo->aColl; so = pKeyInfo->aSortOrder; for(i=0; i<nIn && rc==SQLITE_OK; i++){ rc = encodeOneKeyValue(&x, aIn+i, so ? so[i] : SQLITE_SO_ASC, aColl[i]); if( pnShort && i+1==iShort ) *pnShort = x.nOut; } if( rc ){ sqlite4DbFree(db, x.aOut); }else{ *paOut = x.aOut; |
︙ | ︙ |
Changes to test/conflict.test.
︙ | ︙ | |||
356 357 358 359 360 361 362 | } execsql { SELECT count(*), min(a), max(b) FROM t1; } } {50 1 51} do_test conflict-7.2 { execsql { | < | < | | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | } execsql { SELECT count(*), min(a), max(b) FROM t1; } } {50 1 51} do_test conflict-7.2 { execsql { UPDATE OR IGNORE t1 SET a=1000; } } {} do_test conflict-7.2.1 { db changes } {1} do_test conflict-7.3 { execsql { SELECT b FROM t1 WHERE +a=1000; } } {2} do_test conflict-7.4 { execsql { SELECT count(*) FROM t1; } } {50} do_test conflict-7.5 { execsql { UPDATE OR REPLACE t1 SET a=1001; } } {} do_test conflict-7.5.1 { db changes } {50} do_test conflict-7.6 { execsql { SELECT b FROM t1 WHERE +a=1001; } |
︙ | ︙ | |||
410 411 412 413 414 415 416 | execsql { DELETE FROM t1; INSERT INTO t1 VALUES(1,2); } execsql { INSERT OR IGNORE INTO t1 VALUES(2,3); } | | | | | | | < | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | execsql { DELETE FROM t1; INSERT INTO t1 VALUES(1,2); } execsql { INSERT OR IGNORE INTO t1 VALUES(2,3); } } {} do_test conflict-8.1.1 { db changes } {1} do_test conflict-8.2 { execsql { INSERT OR IGNORE INTO t1 VALUES(2,4); } } {} do_test conflict-8.2.1 { db changes } {0} do_test conflict-8.3 { execsql { INSERT OR REPLACE INTO t1 VALUES(2,4); } } {} do_test conflict-8.3.1 { db changes } {1} do_test conflict-8.4 { execsql { INSERT OR IGNORE INTO t1 SELECT * FROM t1; } } {} do_test conflict-8.4.1 { db changes } {0} do_test conflict-8.5 { execsql { INSERT OR IGNORE INTO t1 SELECT a+2,b+2 FROM t1; } } {} do_test conflict-8.5.1 { db changes } {2} do_test conflict-8.6 { execsql { INSERT OR IGNORE INTO t1 SELECT a+3,b+3 FROM t1; } } {} do_test conflict-8.6.1 { db changes } {3} integrity_check conflict-8.99 do_test conflict-9.1 { execsql { CREATE TABLE t2( a INTEGER UNIQUE ON CONFLICT IGNORE, b INTEGER UNIQUE ON CONFLICT FAIL, c INTEGER UNIQUE ON CONFLICT REPLACE, d INTEGER UNIQUE ON CONFLICT ABORT, e INTEGER UNIQUE ON CONFLICT ROLLBACK ); |
︙ | ︙ |
Changes to test/fkey2.test.
︙ | ︙ | |||
22 23 24 25 26 27 28 | } #------------------------------------------------------------------------- # Test structure: # # fkey2-1.*: Simple tests to check that immediate and deferred foreign key # constraints work when not inside a transaction. | < < | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | } #------------------------------------------------------------------------- # Test structure: # # fkey2-1.*: Simple tests to check that immediate and deferred foreign key # constraints work when not inside a transaction. # explicit transactions (i.e that processing really is deferred). # # fkey2-3.*: Tests that a statement transaction is rolled back if an # immediate foreign key constraint is violated. # # fkey2-4.*: Test that FK actions may recurse even when recursive triggers # are disabled. |
︙ | ︙ | |||
131 132 133 134 135 136 137 | 4.9 "UPDATE t8 SET c=1 WHERE d=4" {0 {}} 4.10 "UPDATE t8 SET c=NULL WHERE d=4" {0 {}} 4.11 "DELETE FROM t7 WHERE b=1" {1 {foreign key constraint failed}} 4.12 "UPDATE t7 SET b = 2" {1 {foreign key constraint failed}} 4.13 "UPDATE t7 SET b = 1" {0 {}} 4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {foreign key constraint failed}} 4.15 "UPDATE t7 SET b = 5" {1 {foreign key constraint failed}} | < > > > > > > > < < > > < > > > < < < | | < > | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | 4.9 "UPDATE t8 SET c=1 WHERE d=4" {0 {}} 4.10 "UPDATE t8 SET c=NULL WHERE d=4" {0 {}} 4.11 "DELETE FROM t7 WHERE b=1" {1 {foreign key constraint failed}} 4.12 "UPDATE t7 SET b = 2" {1 {foreign key constraint failed}} 4.13 "UPDATE t7 SET b = 1" {0 {}} 4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {foreign key constraint failed}} 4.15 "UPDATE t7 SET b = 5" {1 {foreign key constraint failed}} 4.17 "UPDATE t7 SET a = 10" {0 {}} 5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}} 5.2 "INSERT INTO t10 VALUES(1, 3)" {1 {foreign key mismatch}} } # Run the simple tests with all FK constraints immediate. # do_test fkey2-1.1.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { do_test fkey2-1.1.$tn { catchsql $zSql } $res } drop_all_tables # Run the simple tests with all FK constraints deferred. # do_test fkey2-1.2.0 { execsql [string map {/D/ {DEFERRABLE INITIALLY DEFERRED}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { do_test fkey2-1.2.$tn { catchsql $zSql } $res } drop_all_tables # Run the simple tests with all FK constraints immediate. Put a # BEGIN/COMMIT block around each write to the database. # do_test fkey2-1.3.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { execsql BEGIN do_test fkey2-1.3.$tn { catchsql $zSql } $res execsql COMMIT } drop_all_tables # Run the simple tests with all FK constraints deferred. Put a # BEGIN/COMMIT block around each write to the database. # do_test fkey2-1.4.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { do_test fkey2-1.4.$tn { catchsql "BEGIN; $zSql; COMMIT;" } $res catchsql commit } drop_all_tables # Special test: When the parent key is an IPK, make sure the affinity of # the IPK is not applied to the child key value before it is inserted # into the child table. do_test fkey2-1.5.1 { execsql { CREATE TABLE i(i INTEGER PRIMARY KEY); |
︙ | ︙ | |||
212 213 214 215 216 217 218 219 220 221 222 223 224 225 | } {35.0 text 35 integer} do_test fkey2-1.6.2 { catchsql { DELETE FROM i } } {1 {foreign key constraint failed}} # Use a collation sequence on the parent key. drop_all_tables do_test fkey2-1.7.1 { execsql { CREATE TABLE i(i TEXT COLLATE nocase PRIMARY KEY); CREATE TABLE j(j TEXT COLLATE binary REFERENCES i(i)); INSERT INTO i VALUES('SQLite'); INSERT INTO j VALUES('sqlite'); } | > | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | } {35.0 text 35 integer} do_test fkey2-1.6.2 { catchsql { DELETE FROM i } } {1 {foreign key constraint failed}} # Use a collation sequence on the parent key. drop_all_tables puts "src4: the following requires collation support" do_test fkey2-1.7.1 { execsql { CREATE TABLE i(i TEXT COLLATE nocase PRIMARY KEY); CREATE TABLE j(j TEXT COLLATE binary REFERENCES i(i)); INSERT INTO i VALUES('SQLite'); INSERT INTO j VALUES('sqlite'); } |
︙ | ︙ | |||
274 275 276 277 278 279 280 281 282 283 284 285 286 287 | parent REFERENCES node DEFERRABLE INITIALLY DEFERRED ); } fkey2-2-test 1 0 "INSERT INTO node VALUES(1, 0)" FKV fkey2-2-test 2 0 "BEGIN" fkey2-2-test 3 1 "INSERT INTO node VALUES(1, 0)" fkey2-2-test 4 0 "UPDATE node SET parent = NULL" fkey2-2-test 5 0 "COMMIT" fkey2-2-test 6 0 "SELECT * FROM node" {1 {}} fkey2-2-test 7 0 "BEGIN" fkey2-2-test 8 1 "INSERT INTO leaf VALUES('a', 2)" fkey2-2-test 9 1 "INSERT INTO node VALUES(2, 0)" | > | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | parent REFERENCES node DEFERRABLE INITIALLY DEFERRED ); } fkey2-2-test 1 0 "INSERT INTO node VALUES(1, 0)" FKV fkey2-2-test 2 0 "BEGIN" fkey2-2-test 3 1 "INSERT INTO node VALUES(1, 0)" fkey2-2-test 4 0 "UPDATE node SET parent = NULL" fkey2-2-test 5 0 "COMMIT" fkey2-2-test 6 0 "SELECT * FROM node" {1 {}} fkey2-2-test 7 0 "BEGIN" fkey2-2-test 8 1 "INSERT INTO leaf VALUES('a', 2)" fkey2-2-test 9 1 "INSERT INTO node VALUES(2, 0)" |
︙ | ︙ |
Changes to test/simple.test.
︙ | ︙ | |||
417 418 419 420 421 422 423 424 | SELECT count(*) FROM def; } {32} do_execsql_test 20.2 { ROLLBACK } do_execsql_test 20.3 { SELECT count(*) FROM def } 0 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 | SELECT count(*) FROM def; } {32} do_execsql_test 20.2 { ROLLBACK } do_execsql_test 20.3 { SELECT count(*) FROM def } 0 #------------------------------------------------------------------------- reset_db do_execsql_test 21.1 { PRAGMA foreign_keys = on; CREATE TABLE t1(a PRIMARY KEY, b); CREATE TABLE t2(c REFERENCES t1(a), d); } do_execsql_test 21.2 { INSERT INTO t1 VALUES(1, 2); INSERT INTO t2 VALUES(1, 3); INSERT INTO t2 VALUES(NULL, 4); } do_catchsql_test 21.3 { UPDATE t2 SET c=2 WHERE d=4; } {1 {foreign key constraint failed}} #------------------------------------------------------------------------- reset_db do_execsql_test 22.1 { CREATE TABLE t1(x PRIMARY KEY); INSERT INTO t1 VALUES('abc'); } do_execsql_test 22.2 { UPDATE t1 SET x = 'abc' } do_execsql_test 22.3 { SELECT * FROM t1 } {abc} #------------------------------------------------------------------------- reset_db do_execsql_test 23.1 { PRAGMA foreign_keys = on; CREATE TABLE t1(a PRIMARY KEY, b); CREATE TABLE t2(c REFERENCES t1(a), d); } proc out {} { set t1 [execsql {SELECT a FROM t1}] set t2 [execsql {SELECT c FROM t2}] puts "t1: $t1 t2: $t2" } do_test 23.2 { catchsql "BEGIN; INSERT INTO t2 VALUES(1, 3)" ; execsql COMMIT } {} do_test 23.3 { catchsql "BEGIN; INSERT INTO t1 VALUES(1, 2)" ; execsql COMMIT } {} do_test 23.4 { catchsql "BEGIN; INSERT INTO t2 VALUES(1, 3)" ; execsql COMMIT } {} do_test 23.5 { catchsql "BEGIN; INSERT INTO t2 VALUES(2, 4)" ; execsql COMMIT } {} do_test 23.6 { catchsql "BEGIN; INSERT INTO t2 VALUES(NULL, 4)" ; execsql COMMIT } {} do_test 23.7 { catchsql "BEGIN; UPDATE t2 SET c=2 WHERE d=4" ; execsql COMMIT } {} do_test 23.8 { catchsql "BEGIN; UPDATE t2 SET c=1 WHERE d=4" ; execsql COMMIT } {} do_test 23.9 { catchsql "BEGIN; UPDATE t2 SET c=1 WHERE d=4" ; execsql COMMIT } {} do_test 23.10 { catchsql "BEGIN; UPDATE t2 SET c=NULL WHERE d=4" ; execsql COMMIT } {} do_test 23.11 { execsql BEGIN catchsql "DELETE FROM t1 WHERE a=1" execsql COMMIT } {} do_catchsql_test 23.3 { BEGIN; UPDATE t1 SET a = 2; COMMIT; } {1 {foreign key constraint failed}} #------------------------------------------------------------------------- reset_db do_execsql_test 24.1 { CREATE TABLE p(x INTEGER); INSERT INTO p VALUES(35.0); SELECT typeof(x) FROM p; } {integer} do_execsql_test 24.2 { CREATE TABLE p2(x INTEGER PRIMARY KEY); INSERT INTO p2 VALUES(35.0); SELECT typeof(x) FROM p2; } {integer} #------------------------------------------------------------------------- reset_db do_execsql_test 25.1 { PRAGMA foreign_keys = on; CREATE TABLE p(x INT PRIMARY KEY); CREATE TABLE c(y REFERENCES p); INSERT INTO p VALUES(35); PRAGMA vdbe_trace = 1; INSERT INTO c VALUES(35.0); } execsql { PRAGMA vdbe_trace = 0; } explain { INSERT INTO c VALUES(35.0); } finish_test #------------------------------------------------------------------------- reset_db do_execsql_test 26.1 { PRAGMA foreign_keys = on; CREATE TABLE p(x INT PRIMARY KEY); CREATE TABLE c(y REFERENCES p); INSERT INTO p VALUES(35.0); INSERT INTO c VALUES(35.0); } finish_test #proc populate_t1 {} { # db eval { # INSERT INTO t1(a, b) VALUES(4, 'four'); # INSERT INTO t1(a, b) VALUES(9, 'nine'); |
︙ | ︙ |