diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/auth.c | 18 | ||||
-rw-r--r-- | src/fkey.c | 132 | ||||
-rw-r--r-- | src/sqliteInt.h | 2 |
3 files changed, 80 insertions, 72 deletions
diff --git a/src/auth.c b/src/auth.c index 042fc6615..b17d1f53f 100644 --- a/src/auth.c +++ b/src/auth.c @@ -100,30 +100,28 @@ static void sqliteAuthBadReturnCode(Parse *pParse){ ** 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 @@ void sqlite3AuthRead( 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 dc5b4199c..45a00d209 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -319,7 +319,8 @@ static void fkLookupParent( 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 @@ static void fkLookupParent( 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 @@ void sqlite3FkCheck( 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 @@ void sqlite3FkCheck( 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 @@ void sqlite3FkCheck( /* 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 a82bbdfc8..03ff1be11 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2734,7 +2734,7 @@ void sqlite3DeferForeignKey(Parse*, int); 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 |