]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Correctly handle self-referential foreign keys on WITHOUT ROWID tables.
authordrh <drh@noemail.net>
Mon, 4 Nov 2013 13:56:00 +0000 (13:56 +0000)
committerdrh <drh@noemail.net>
Mon, 4 Nov 2013 13:56:00 +0000 (13:56 +0000)
FossilOrigin-Name: af128862ab6008df9dda1ee90f93f9efd629e259

manifest
manifest.uuid
src/fkey.c

index 9c0e62b6e4242a0a5179728555911f499a49b3a0..64a0c82308ba266b0fd7d2546d805bad9857aacd 100644 (file)
--- a/manifest
+++ b/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
index 677a3fb2418b4c032161fd2884f4587efc15d431..55191530befb7402d0dc20322c434a487b027560 100644 (file)
@@ -1 +1 @@
-1315d9109c7105f4a62bb2d43ca6948d41245129
\ No newline at end of file
+af128862ab6008df9dda1ee90f93f9efd629e259
\ No newline at end of file
index c78f8c3e5e0e2f88d9cb7fdd538cbe1bda874506..0ef76800ce3d58c8a9326b37584a432a6be9c5ed 100644 (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);
   }