Change the interface to sqlite3GenerateConstraintChecks() for improved lucidity

and to fix issues in dealing with UPDATEs for WITHOUT ROWID tables.  Make sure
iDataCur and iIdxCur are initialized when processing DELETEs of a VIEW.
UPDATE processing distinguishes between changes to ROWID and PRIMARY KEY.

FossilOrigin-Name: c525ac5630d6bcd51842cfc84f2c2c50be9cec1c
This commit is contained in:
drh 2013-11-01 17:08:56 +00:00
parent f82b9afcba
commit f8ffb27850
7 changed files with 121 additions and 75 deletions

View File

@ -1,5 +1,5 @@
C Improved\sVDBE\scomments\son\sthe\sconstraint\schecker.\s\sFix\sa\smissing\swrite\nlock\sin\sthe\sUPDATE\slogic.
D 2013-11-01T14:03:20.023
C Change\sthe\sinterface\sto\ssqlite3GenerateConstraintChecks()\sfor\simproved\slucidity\nand\sto\sfix\sissues\sin\sdealing\swith\sUPDATEs\sfor\sWITHOUT\sROWID\stables.\s\sMake\ssure\niDataCur\sand\siIdxCur\sare\sinitialized\swhen\sprocessing\sDELETEs\sof\sa\sVIEW.\nUPDATE\sprocessing\sdistinguishes\sbetween\schanges\sto\sROWID\sand\sPRIMARY\sKEY.
D 2013-11-01T17:08:56.112
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -173,7 +173,7 @@ F src/callback.c f99a8957ba2adf369645fac0db09ad8adcf1caa2
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
F src/delete.c df1775ee33f40bc8feb37b2bfb59d792ff496359
F src/delete.c c58e07fb07878c4b4c4b106cee7f2799a48351f8
F src/expr.c ecc2b98eb75fe5533cfdfcca6b04cfe5f0c6001f
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 628f81177299660a86e40359b3689b81f517e125
@ -182,7 +182,7 @@ F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
F src/insert.c da74aded06680ff30a87065df70fda16af3ecda3
F src/insert.c 4694b23067650659f822d989426116808c0c8e05
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
@ -223,7 +223,7 @@ F src/shell.c d5eebdc6034014103de2b9d58e1d3f6f7de0fb50
F src/sqlite.h.in 547a44dd4ff4d975e92a645ea2d609e543a83d0f
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
F src/sqliteInt.h 69c16a6a7f45fbe5d0293b753fe8022bef6b1f09
F src/sqliteInt.h bc4588b0d2eac8429d102609af6cfad583bfb41f
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@ -275,7 +275,7 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2
F src/trigger.c 53d6b5d50b3b23d4fcd0a36504feb5cff9aed716
F src/update.c fff9ac57e36e54ac939e22aac077326224759372
F src/update.c 94d63d3e06b09df3618655a841dc95d5b9466dc6
F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
@ -1076,7 +1076,7 @@ F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
F test/win32longpath.test e2aafc07e6990fe86c69be22a3d1a0e210cd329b
F test/without_rowid1.test e4f023e49a57c90f6d25afd4b3e8b0305ad06e44
F test/without_rowid1.test ad5764bbe2784a91f7106143e7d1bd8ccfb61ffd
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
F tool/build-all-msvc.bat 38623a30fd58288fda5cc7f7df2682aaab75c9d5 x
@ -1128,7 +1128,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
P 5c0eaea6a26b5c3310d96b3c896ac3068a3ebad1
R 91e4e19c708f99c7ec09052b49466395
P 3bed599e74d354bf1513e4fb0e8665376ba44d0b
R 67b4771869ffd7159304bf29f7d68635
U drh
Z 7a82973f52b628c87c749d18c8301e86
Z f06e68ba1ad92e0ce7abe5fdcd557daa

View File

@ -1 +1 @@
3bed599e74d354bf1513e4fb0e8665376ba44d0b
c525ac5630d6bcd51842cfc84f2c2c50be9cec1c

View File

@ -324,6 +324,7 @@ void sqlite3DeleteFrom(
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
if( isView ){
sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
iDataCur = iIdxCur = iTabCur;
}
#endif

View File

@ -1039,8 +1039,8 @@ void sqlite3Insert(
#endif
{
int isReplace; /* Set to true if constraints may cause a replace */
sqlite3GenerateConstraintChecks(pParse, pTab, iDataCur, iIdxCur,
regIns, aRegIdx, ipkColumn>=0, 0, onError, endOfLoop, &isReplace
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace
);
sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
@ -1134,22 +1134,22 @@ insert_cleanup:
** contain the content of the first table column. The third register will
** contain the content of the second table column. And so forth.
**
** For an UPDATE (isUpdate!=0), if pkChng is non-zero then it contains
** the address of a range of registers containing the rowid and table
** data from before the change. In other words, pkChng is like
** regNewData except that it describes the row before the update rather
** than afterwards. If pkChng is zero, that means that the rowid does
** not change (for a normal rowid table) or the PRIMARY KEY does not
** change (for a WITHOUT ROWID table) in which case the old data is
** not needed.
** The regOldData parameter is similar to regNewData except that it contains
** the data prior to an UPDATE rather than afterwards. regOldData is zero
** for an INSERT. This routine can distinguish between UPDATE and INSERT by
** checking regOldData for zero.
**
** For an INSERT (isUpdate==0), pkChng is just a boolean that indicates
** whether or not the rowid was explicitly specified as part of the
** INSERT statement. If pkChng is zero, it means that the either rowid
** is computed automatically or that the table is a WITHOUT ROWID table
** and has no rowid. On an INSERT, pkChng will only be true if the
** INSERT statement provides an integer value for either the rowid
** column or its INTEGER PRIMARY KEY alias.
** For an UPDATE, the pkChng boolean is true if the true primary key (the
** rowid for a normal table or the PRIMARY KEY for a WITHOUT ROWID table)
** might be modified by the UPDATE. If pkChng is false, then the key of
** the iDataCur content table is guaranteed to be unchanged by the UPDATE.
**
** For an INSERT, the pkChng boolean indicates whether or not the rowid
** was explicitly specified as part of the INSERT statement. If pkChng
** is zero, it means that the either rowid is computed automatically or
** that the table is a WITHOUT ROWID table and has no rowid. On an INSERT,
** pkChng will only be true if the INSERT statement provides an integer
** value for either the rowid column or its INTEGER PRIMARY KEY alias.
**
** The code generated by this routine will store new index entries into
** registers identified by aRegIdx[]. No index entry is created for
@ -1208,30 +1208,30 @@ insert_cleanup:
void sqlite3GenerateConstraintChecks(
Parse *pParse, /* The parser context */
Table *pTab, /* The table being inserted or updated */
int *aRegIdx, /* Use register aRegIdx[i] for index i. 0 for unused */
int iDataCur, /* Canonical data cursor (main table or PK index) */
int iIdxCur, /* First index cursor */
int regNewData, /* First register in a range holding values to insert */
int *aRegIdx, /* Register used by each index. 0 for unused indices */
int pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */
int isUpdate, /* True for UPDATE, False for INSERT */
int overrideError, /* Override onError to this if not OE_Default */
int regOldData, /* Previous content. 0 for INSERTs */
u8 pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */
u8 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 */
){
int i; /* loop counter */
Vdbe *v; /* VDBE under constrution */
int nCol; /* Number of columns */
int onError; /* Conflict resolution strategy */
int j1; /* Addresss of jump instruction */
int ix; /* Index loop counter */
Index *pIdx; /* Pointer to one of the indices */
Index *pPk = 0; /* The PRIMARY KEY index */
sqlite3 *db; /* Database connection */
int i; /* loop counter */
int ix; /* Index loop counter */
int nCol; /* Number of columns */
int onError; /* Conflict resolution strategy */
int j1; /* Addresss of jump instruction */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
int regOldData; /* Previous rowid and table data */
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
u8 isUpdate;
regOldData = (pkChng && isUpdate) ? pkChng : regNewData;
isUpdate = regOldData!=0;
db = pParse->db;
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
@ -1328,16 +1328,13 @@ void sqlite3GenerateConstraintChecks(
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
/* If there is an INTEGER PRIMARY KEY, make sure the primary key
** of the new record does not previously exist. Except, if this
** is an UPDATE and the primary key is not changing, then obviously
** it is OK for the previous rowid to exist in that case.
**
** This block only runs for tables that have a rowid.
/* If rowid is changing, make sure the new rowid does not previously
** exist in the table.
*/
if( pkChng && pPk==0 ){
int addrRowidOk = sqlite3VdbeMakeLabel(v);
/* Figure out what action to take in case of a rowid collision */
onError = pTab->keyConf;
if( overrideError!=OE_Default ){
onError = overrideError;
@ -1346,9 +1343,17 @@ void sqlite3GenerateConstraintChecks(
}
if( isUpdate ){
/* pkChng!=0 does not mean that the rowid has change, only that
** it might have changed. Skip the conflict logic below if the rowid
** is unchanged. */
sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData);
}
/* Check to see if the new rowid already exists in the table. Skip
** the following conflict logic if it does not. */
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
/* Generate code that deals with a rowid collision */
switch( onError ){
default: {
onError = OE_Abort;
@ -1411,6 +1416,9 @@ void sqlite3GenerateConstraintChecks(
/* Test all UNIQUE constraints by creating entries for each UNIQUE
** index and making sure that duplicate entries do not already exist.
** Compute the revised record entries for indices as we go.
**
** This loop also handles the case of the PRIMARY KEY index for a
** WITHOUT ROWID table.
*/
for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
int regIdx; /* Range of registers hold conent for pIdx */
@ -1422,6 +1430,7 @@ void sqlite3GenerateConstraintChecks(
iThisCur = iIdxCur+ix;
addrUniqueOk = sqlite3VdbeMakeLabel(v);
/* Skip partial indices for which the WHERE clause is not true */
if( pIdx->pPartIdxWhere ){
sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]);
pParse->ckBase = regNewData+1;
@ -1431,7 +1440,8 @@ void sqlite3GenerateConstraintChecks(
}
/* Create a record for this index entry as it should appear after
** the insert or update. */
** the insert or update. Store that record in the aRegIdx[ix] register
*/
regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
for(i=0; i<pIdx->nColumn; i++){
int iField = pIdx->aiColumn[i];
@ -1449,6 +1459,12 @@ void sqlite3GenerateConstraintChecks(
VdbeComment((v, "for %s", pIdx->zName));
sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);
/* In an UPDATE operation, if this index is the PRIMARY KEY index
** of a WITHOUT ROWID table and there has been no change the
** primary key, then no collision is possible. The collision detection
** logic below can all be skipped. */
if( isUpdate && pPk && pkChng==0 ) continue;
/* Find out what action to take in case there is a uniqueness conflict */
onError = pIdx->onError;
if( onError==OE_None ){
@ -1470,11 +1486,15 @@ void sqlite3GenerateConstraintChecks(
regR = sqlite3GetTempRange(pParse, nPkField);
sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
regIdx, pIdx->nKeyCol);
/* Generate code to handle collisions */
if( HasRowid(pTab) ){
sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
/* Conflict only if the rowid of the existing index entry
** is different from old-rowid */
sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);
if( isUpdate ){
sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);
}
}else{
int x;
/* Extract the PRIMARY KEY from the end of the index entry and

View File

@ -2922,8 +2922,8 @@ int sqlite3IsRowid(const char*);
void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8);
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*);
void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,int,
int*,int,int,int,int,int*);
void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
u8,u8,int,int*);
void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, int*, int*);
void sqlite3BeginWriteOperation(Parse*, int, int);

View File

@ -108,7 +108,9 @@ void sqlite3Update(
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. */
int chngPk; /* True if the rowid or PRIMARY KEY is changed */
u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */
u8 chngRowid; /* Rowid changed in a normal table */
u8 chngKey; /* Either chngPk or chngRowid */
Expr *pRowidExpr = 0; /* Expression defining the new record number */
int openAll = 0; /* True if all indices need to be opened */
AuthContext sContext; /* The authorization context */
@ -201,11 +203,9 @@ void sqlite3Update(
** of the UPDATE statement. Also find the column index
** for each column to be updated in the pChanges array. For each
** column to be updated, make sure we have authorization to change
** that column. Set chngPk if the iDataCur key changes. Note that
** for WITHOUT ROWID columns, the iDataCur key contains all columns of
** the table and so it will always change.
** that column.
*/
chngPk = (pPk!=0);
chngRowid = chngPk = 0;
for(i=0; i<pChanges->nExpr; i++){
if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
goto update_cleanup;
@ -213,8 +213,10 @@ void sqlite3Update(
for(j=0; j<pTab->nCol; j++){
if( sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
if( j==pTab->iPKey ){
chngPk = 1;
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
}else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
chngPk = 1;
}
aXRef[j] = i;
break;
@ -223,7 +225,7 @@ void sqlite3Update(
if( j>=pTab->nCol ){
if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){
j = -1;
chngPk = 1;
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
}else{
sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);
@ -245,8 +247,12 @@ void sqlite3Update(
}
#endif
}
assert( (chngRowid & chngPk)==0 );
assert( chngRowid==0 || chngRowid==1 );
assert( chngPk==0 || chngPk==1 );
chngKey = chngRowid + chngPk;
hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngPk);
hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey);
/* Allocate memory for the array aRegIdx[]. There is one entry in the
** array for each index associated with table being updated. Fill in
@ -259,11 +265,11 @@ void sqlite3Update(
}
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
if( hasFK || chngPk || pIdx->pPartIdxWhere ){
if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){
reg = ++pParse->nMem;
}else{
reg = 0;
for(i=0; i<pIdx->nColumn; i++){
for(i=0; i<pIdx->nKeyCol; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ){
reg = ++pParse->nMem;
break;
@ -293,11 +299,11 @@ void sqlite3Update(
/* Allocate required registers. */
regRowSet = ++pParse->nMem;
regOldRowid = regNewRowid = ++pParse->nMem;
if( pTrigger || hasFK ){
if( chngPk || pTrigger || hasFK ){
regOld = pParse->nMem + 1;
pParse->nMem += pTab->nCol;
}
if( chngPk || pTrigger || hasFK ){
if( chngKey || pTrigger || hasFK ){
regNewRowid = ++pParse->nMem;
}
regNew = pParse->nMem + 1;
@ -432,27 +438,30 @@ void sqlite3Update(
** contain the new value. If the record number is not being modified,
** then regNewRowid is the same register as regOldRowid, which is
** already populated. */
assert( chngPk || pTrigger || hasFK || regOldRowid==regNewRowid );
if( chngPk && pPk==0 ){
assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
if( chngRowid ){
sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
}
/* If there are triggers on this table, populate an array of registers
** with the required old.* column data. */
if( hasFK || pTrigger ){
/* Compute the old pre-UPDATE content of the row being changed, if that
** information is needed */
if( chngPk || hasFK || pTrigger ){
u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
oldmask |= sqlite3TriggerColmask(pParse,
pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
);
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){
if( oldmask==0xffffffff
|| (i<32 && (oldmask & (1<<i)))
|| (chngPk && (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0)
){
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
}
}
if( chngPk==0 ){
if( chngRowid==0 && pPk==0 ){
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
}
}
@ -509,7 +518,11 @@ void sqlite3Update(
** is deleted or renamed by a BEFORE trigger - is left undefined in the
** documentation.
*/
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addr, regOldRowid);
if( pPk ){
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
}else{
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addr, regOldRowid);
}
/* If it did not delete it, the row-trigger may still have modified
** some of the columns of the row being updated. Load the values for
@ -527,12 +540,13 @@ void sqlite3Update(
int j1; /* Address of jump instruction */
/* Do constraint checks. */
sqlite3GenerateConstraintChecks(pParse, pTab, iDataCur, iIdxCur,
regNewRowid, aRegIdx, (chngPk?regOldRowid:0), 1, onError, addr, 0);
assert( regOldRowid>0 );
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
regNewRowid, regOldRowid, chngKey, onError, addr, 0);
/* Do FK constraint checks. */
if( hasFK ){
sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngPk);
sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
}
/* Delete the index entries associated with the current record. */
@ -544,7 +558,7 @@ void sqlite3Update(
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
/* If changing the record number, delete the old record. */
if( hasFK || chngPk ){
if( hasFK || chngKey || pPk!=0 ){
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
}
if( sqlite3VdbeCurrentAddr(v)==j1+1 ){
@ -554,7 +568,7 @@ void sqlite3Update(
}
if( hasFK ){
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngPk);
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
}
/* Insert the new index entries and the new record. */
@ -565,7 +579,7 @@ void sqlite3Update(
** handle rows (possibly in other tables) that refer via a foreign key
** to the row just updated. */
if( hasFK ){
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngPk);
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey);
}
}

View File

@ -61,4 +61,15 @@ do_execsql_test without_rowid1-1.22 {
SELECT *, '|' FROM t1 ORDER BY c, a;
} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic phone flipper harvard | journal sherman gamma patriot |}
do_execsql_test without_rowid1-1.23 {
SELECT *, '|' FROM t1 ORDER BY b, d;
} {dynamic phone flipper harvard | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
# UPDATE statements.
#
do_execsql_test without_rowid1-1.31 {
UPDATE t1 SET d=3.1415926 WHERE a='journal';
SELECT *, '|' FROM t1 ORDER BY c, a;
} {arctic sleep ammonia helena | journal sherman ammonia 3.1415926 | dynamic phone flipper harvard | journal sherman gamma 3.1415926 |}
finish_test