Correctly handle self-referential foreign keys on WITHOUT ROWID tables.

FossilOrigin-Name: af128862ab6008df9dda1ee90f93f9efd629e259
This commit is contained in:
drh 2013-11-04 13:56:00 +00:00
parent bd50a926ef
commit 90e758ff1f
3 changed files with 93 additions and 40 deletions

View File

@ -1,5 +1,5 @@
C Improved\scomments\son\sforeign\skey\slogic.
D 2013-11-03T02:27:58.561
C Correctly\shandle\sself-referential\sforeign\skeys\son\sWITHOUT\sROWID\stables.
D 2013-11-04T13:56:00.182
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -176,7 +176,7 @@ F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
F src/delete.c 605be39dc72a56768a55ee7245d92774ed7ae343
F src/expr.c ecc2b98eb75fe5533cfdfcca6b04cfe5f0c6001f
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c df809cab5485c16ab146cc13246afd41900ece84
F src/fkey.c 24bbd27a963c8e022a79579964885cc638567c88
F src/func.c 2c47b65e6e00e3e9374942f28254faf8adafe398
F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
@ -1130,7 +1130,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 a7a18b65fa34dfdf6117fa21db3e576f96876617
R c871a047a9d8bb37aa43632b24cb0302
P 1315d9109c7105f4a62bb2d43ca6948d41245129
R 64353718434772b548f438b0d879d511
U drh
Z 938c2bb15557c0ae50def104b2712b1c
Z 7428ec81c08443a72ec23535cca14f03

View File

@ -1 +1 @@
1315d9109c7105f4a62bb2d43ca6948d41245129
af128862ab6008df9dda1ee90f93f9efd629e259

View File

@ -445,6 +445,62 @@ static void fkLookupParent(
sqlite3VdbeAddOp1(v, OP_Close, iCur);
}
/*
** Return an Expr object that refers to a memory register corresponding
** to column iCol of table pTab.
**
** regBase is the first of an array of register that contains the data
** for pTab. regBase itself holds the rowid. regBase+1 holds the first
** column. regBase+2 holds the second column, and so forth.
*/
static Expr *exprTableRegister(
Parse *pParse, /* Parsing and code generating context */
Table *pTab, /* The table whose content is at r[regBase]... */
int regBase, /* Contents of table pTab */
i16 iCol /* Which column of pTab is desired */
){
Expr *pExpr;
Column *pCol;
const char *zColl;
sqlite3 *db = pParse->db;
pExpr = sqlite3Expr(db, TK_REGISTER, 0);
if( pExpr ){
if( iCol>=0 && iCol!=pTab->iPKey ){
pCol = &pTab->aCol[iCol];
pExpr->iTable = regBase + iCol + 1;
pExpr->affinity = pCol->affinity;
zColl = pCol->zColl;
if( zColl==0 ) zColl = db->pDfltColl->zName;
pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
}else{
pExpr->iTable = regBase;
pExpr->affinity = SQLITE_AFF_INTEGER;
}
}
return pExpr;
}
/*
** Return an Expr object that refers to column iCol of table pTab which
** has cursor iCur.
*/
static Expr *exprTableColumn(
sqlite3 *db, /* The database connection */
Table *pTab, /* The table whose column is desired */
int iCursor, /* The open cursor on the table */
i16 iCol /* The column that is wanted */
){
Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
if( pExpr ){
pExpr->pTab = pTab;
pExpr->iTable = iCursor;
pExpr->iColumn = iCol;
}
return pExpr;
}
/*
** This function is called to generate code executed when a row is deleted
** from the parent table of foreign key constraint pFKey and, if pFKey is
@ -514,26 +570,8 @@ static void fkScanChildren(
i16 iCol; /* Index of column in child table */
const char *zCol; /* Name of column in child table */
pLeft = sqlite3Expr(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. */
if( pIdx ){
Column *pCol;
const char *zColl;
iCol = pIdx->aiColumn[i];
pCol = &pTab->aCol[iCol];
if( pTab->iPKey==iCol ) iCol = -1;
pLeft->iTable = regData+iCol+1;
pLeft->affinity = pCol->affinity;
zColl = pCol->zColl;
if( zColl==0 ) zColl = db->pDfltColl->zName;
pLeft = sqlite3ExprAddCollateString(pParse, pLeft, zColl);
}else{
pLeft->iTable = regData;
pLeft->affinity = SQLITE_AFF_INTEGER;
}
}
iCol = pIdx ? pIdx->aiColumn[i] : -1;
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
assert( iCol>=0 );
zCol = pFKey->pFrom->aCol[iCol].zName;
@ -542,23 +580,38 @@ static void fkScanChildren(
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
}
/* If the child table is the same as the parent table, and this scan
** is taking place as part of a DELETE operation (operation D.2), omit the
** row being deleted from the scan by adding ($rowid != rowid) to the WHERE
** clause, where $rowid is the rowid of the row being deleted. */
if( pTab==pFKey->pFrom && nIncr>0 && HasRowid(pTab) /*FIXME*/ ){
/* If the child table is the same as the parent table, then add terms
** to the WHERE clause that prevent this entry from being scanned.
** The added WHERE clause terms are like this:
**
** $current_rowid!=rowid
** NOT( $current_a==a AND $current_b==b AND ... )
**
** The first form is used for rowid tables. The second form is used
** for WITHOUT ROWID tables. In the second form, the primary key is
** (a,b,...)
*/
if( pTab==pFKey->pFrom && nIncr>0 ){
Expr *pNe; /* Expression (pLeft != pRight) */
Expr *pLeft; /* Value from parent table row */
Expr *pRight; /* Column ref to child table */
pLeft = sqlite3Expr(db, TK_REGISTER, 0);
pRight = sqlite3Expr(db, TK_COLUMN, 0);
if( pLeft && pRight ){
pLeft->iTable = regData;
pLeft->affinity = SQLITE_AFF_INTEGER;
pRight->iTable = pSrc->a[0].iCursor;
pRight->iColumn = -1;
if( HasRowid(pTab) ){
pLeft = exprTableRegister(pParse, pTab, regData, -1);
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1);
pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
}else{
int i;
Expr *pEq, *pAll = 0;
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
for(i=0; i<pPk->nKeyCol; i++){
i16 iCol = pIdx->aiColumn[i];
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
pAll = sqlite3ExprAnd(db, pAll, pEq);
}
pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0);
}
pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
pWhere = sqlite3ExprAnd(db, pWhere, pNe);
}