Correctly handle self-referential foreign keys on WITHOUT ROWID tables.
FossilOrigin-Name: af128862ab6008df9dda1ee90f93f9efd629e259
This commit is contained in:
parent
bd50a926ef
commit
90e758ff1f
12
manifest
12
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
1315d9109c7105f4a62bb2d43ca6948d41245129
|
||||
af128862ab6008df9dda1ee90f93f9efd629e259
|
119
src/fkey.c
119
src/fkey.c
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user