Handle an SQLITE_IGNORE returned when requesting authorization to read parent key columns by pretending the parent key columns contain NULL values.

FossilOrigin-Name: 3c24df38e6ae5dfe999bbf3133b65df0074c6a50
diff --git a/src/auth.c b/src/auth.c
index 042fc66..b17d1f5 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -100,30 +100,28 @@
 ** to an SQL NULL expression. Otherwise, if pExpr is NULL, then SQLITE_IGNORE
 ** is treated as SQLITE_DENY. In this case an error is left in pParse.
 */
-void sqlite3AuthReadCol(
+int sqlite3AuthReadCol(
   Parse *pParse,                  /* The parser context */
   const char *zTab,               /* Table name */
   const char *zCol,               /* Column name */
-  int iDb,                        /* Index of containing database. */
-  Expr *pExpr                     /* Optional expression */
+  int iDb                         /* Index of containing database. */
 ){
   sqlite3 *db = pParse->db;       /* Database handle */
   char *zDb = db->aDb[iDb].zName; /* Name of attached database */
   int rc;                         /* Auth callback return code */
 
   rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext);
-  if( rc!=SQLITE_IGNORE && rc!=SQLITE_DENY && rc!=SQLITE_OK ){
-    sqliteAuthBadReturnCode(pParse);
-  }else if( rc==SQLITE_IGNORE && pExpr ){
-    pExpr->op = TK_NULL;
-  }else if( rc!=SQLITE_OK ){
+  if( rc==SQLITE_DENY ){
     if( db->nDb>2 || iDb!=0 ){
       sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",zDb,zTab,zCol);
     }else{
       sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited", zTab, zCol);
     }
     pParse->rc = SQLITE_AUTH;
+  }else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){
+    sqliteAuthBadReturnCode(pParse);
   }
+  return rc;
 }
 
 /*
@@ -181,7 +179,9 @@
     zCol = "ROWID";
   }
   assert( iDb>=0 && iDb<db->nDb );
-  sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb, pExpr);
+  if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){
+    pExpr->op = TK_NULL;
+  }
 }
 
 /*
diff --git a/src/fkey.c b/src/fkey.c
index dc5b419..45a00d2 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -319,7 +319,8 @@
   FKey *pFKey,          /* Foreign key constraint */
   int *aiCol,           /* Map from parent key columns to child table columns */
   int regData,          /* Address of array containing child table row */
-  int nIncr             /* Increment constraint counter by this */
+  int nIncr,            /* Increment constraint counter by this */
+  int isIgnore          /* If true, pretend pTab contains all NULL values */
 ){
   int i;                                    /* Iterator variable */
   Vdbe *v = sqlite3GetVdbe(pParse);         /* Vdbe to add code to */
@@ -341,66 +342,68 @@
     sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk);
   }
 
-  if( pIdx==0 ){
-    /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY
-    ** column of the parent table (table pTab).  */
-    int iMustBeInt;               /* Address of MustBeInt instruction */
-    int regTemp = sqlite3GetTempReg(pParse);
-
-    /* Invoke MustBeInt to coerce the child key value to an integer (i.e. 
-    ** apply the affinity of the parent key). If this fails, then there
-    ** is no matching parent key. Before using MustBeInt, make a copy of
-    ** the value. Otherwise, the value inserted into the child key column
-    ** will have INTEGER affinity applied to it, which may not be correct.  */
-    sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp);
-    iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0);
-
-    /* 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( pTab==pFKey->pFrom && nIncr==1 ){
-      sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp);
-    }
-
-    sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
-    sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp);
-    sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
-    sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
-    sqlite3VdbeJumpHere(v, iMustBeInt);
-    sqlite3ReleaseTempReg(pParse, regTemp);
-  }else{
-    int nCol = pFKey->nCol;
-    int regTemp = sqlite3GetTempRange(pParse, nCol);
-    int regRec = sqlite3GetTempReg(pParse);
-    KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
-
-    sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
-    sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
-    for(i=0; i<nCol; i++){
-      sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[i]+1+regData, regTemp+i);
-    }
-
-    /* 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( pTab==pFKey->pFrom && nIncr==1 ){
-      int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1;
-      for(i=0; i<nCol; i++){
-        int iChild = aiCol[i]+1+regData;
-        int iParent = pIdx->aiColumn[i]+1+regData;
-        sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
+  if( isIgnore==0 ){
+    if( pIdx==0 ){
+      /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY
+      ** column of the parent table (table pTab).  */
+      int iMustBeInt;               /* Address of MustBeInt instruction */
+      int regTemp = sqlite3GetTempReg(pParse);
+  
+      /* Invoke MustBeInt to coerce the child key value to an integer (i.e. 
+      ** apply the affinity of the parent key). If this fails, then there
+      ** is no matching parent key. Before using MustBeInt, make a copy of
+      ** the value. Otherwise, the value inserted into the child key column
+      ** will have INTEGER affinity applied to it, which may not be correct.  */
+      sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp);
+      iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0);
+  
+      /* 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( pTab==pFKey->pFrom && nIncr==1 ){
+        sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp);
       }
+  
+      sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
+      sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp);
       sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
+      sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
+      sqlite3VdbeJumpHere(v, iMustBeInt);
+      sqlite3ReleaseTempReg(pParse, regTemp);
+    }else{
+      int nCol = pFKey->nCol;
+      int regTemp = sqlite3GetTempRange(pParse, nCol);
+      int regRec = sqlite3GetTempReg(pParse);
+      KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
+  
+      sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
+      sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
+      for(i=0; i<nCol; i++){
+        sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[i]+1+regData, regTemp+i);
+      }
+  
+      /* 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( pTab==pFKey->pFrom && nIncr==1 ){
+        int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1;
+        for(i=0; i<nCol; i++){
+          int iChild = aiCol[i]+1+regData;
+          int iParent = pIdx->aiColumn[i]+1+regData;
+          sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
+        }
+        sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
+      }
+  
+      sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
+      sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0);
+      sqlite3VdbeAddOp3(v, OP_Found, iCur, iOk, regRec);
+  
+      sqlite3ReleaseTempReg(pParse, regRec);
+      sqlite3ReleaseTempRange(pParse, regTemp, nCol);
     }
-
-    sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
-    sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0);
-    sqlite3VdbeAddOp3(v, OP_Found, iCur, iOk, regRec);
-
-    sqlite3ReleaseTempReg(pParse, regRec);
-    sqlite3ReleaseTempRange(pParse, regTemp, nCol);
   }
 
   if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
@@ -706,6 +709,7 @@
     int *aiCol;
     int iCol;
     int i;
+    int isIgnore = 0;
 
     /* Find the parent table of this foreign key. Also find a unique index 
     ** on the parent key columns in the parent table. If either of these 
@@ -733,10 +737,14 @@
         aiCol[i] = -1;
       }
 #ifndef SQLITE_OMIT_AUTHORIZATION
-      /* Request permission to read the parent key columns. */
+      /* Request permission to read the parent key columns. If the 
+      ** authorization callback returns SQLITE_IGNORE, behave as if any
+      ** values read from the parent table are NULL. */
       if( db->xAuth ){
+        int rcauth;
         char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName;
-        sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb, 0);
+        rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb);
+        isIgnore = (rcauth==SQLITE_IGNORE);
       }
 #endif
     }
@@ -751,12 +759,12 @@
       /* A row is being removed from the child table. Search for the parent.
       ** If the parent does not exist, removing the child row resolves an 
       ** outstanding foreign key constraint violation. */
-      fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1);
+      fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore);
     }
     if( regNew!=0 ){
       /* A row is being added to the child table. If a parent row cannot
       ** be found, adding the child row has violated the FK constraint. */ 
-      fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1);
+      fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore);
     }
 
     sqlite3DbFree(db, aiFree);
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index a82bbdf..03ff1be 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2734,7 +2734,7 @@
   int sqlite3AuthCheck(Parse*,int, const char*, const char*, const char*);
   void sqlite3AuthContextPush(Parse*, AuthContext*, const char*);
   void sqlite3AuthContextPop(AuthContext*);
-  void sqlite3AuthReadCol(Parse*, const char *, const char *, int, Expr *);
+  int sqlite3AuthReadCol(Parse*, const char *, const char *, int);
 #else
 # define sqlite3AuthRead(a,b,c,d)
 # define sqlite3AuthCheck(a,b,c,d,e)    SQLITE_OK