From: dan Date: Sat, 3 Oct 2009 07:04:11 +0000 (+0000) Subject: Handle an SQLITE_IGNORE returned when requesting authorization to read parent key... X-Git-Tag: fts3-refactor~128 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=02470b20f30e37f6fc046a76671f19daecd61739;p=thirdparty%2Fsqlite.git 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/manifest b/manifest index 1eed7c7101..769ce8681f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sone\smore\sauthentication\stest\sto\sfkey2.test\sto\scover\san\suntested\sbranch. -D 2009-10-02T15:29:10 +C Handle\san\sSQLITE_IGNORE\sreturned\swhen\srequesting\sauthorization\sto\sread\sparent\skey\scolumns\sby\spretending\sthe\sparent\skey\scolumns\scontain\sNULL\svalues. +D 2009-10-03T07:04:12 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -102,7 +102,7 @@ F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F src/alter.c 1b63d4f2a6e76af9e2964d4d86ccc8e74c3fae4a F src/analyze.c 5a8b8aa3d170eac5e71af45458cec61f83c623ee F src/attach.c 13995348fc5a26cdd136a50806faf292aabc173f -F src/auth.c 50a4fd3ad4a0d3dec95cec7c022a6ca9d5ca3f4e +F src/auth.c a5471a6951a18f79d783da34be22cd94dfbe603a F src/backup.c 6f1c2d9862c8a3feb7739dfcca02c1f5352e37f3 F src/bitvec.c ed215b95734045e58358c3b3e16448f8fe6a235a F src/btmutex.c 0f43a75bb5b8147b386e8e1c3e71ba734e3863b7 @@ -116,7 +116,7 @@ F src/date.c 657ff12ca0f1195b531561afacbb38b772d16638 F src/delete.c 308e300d599d2d11b838687e2cf7309d42f29a1a F src/expr.c c7f3f718bd5c392344ec8694a41c1824f30cf375 F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff -F src/fkey.c bcc2af88a4f88cb2127cd5fb68d4e09e73e03063 +F src/fkey.c 720f049dc4615a568b07cd7d65bbc499b8604f8a F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606 F src/global.c 271952d199a8cc59d4ce840b3bbbfd2f30c8ba32 F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7 @@ -164,7 +164,7 @@ F src/select.c 1d0a13137532321b4364f964e46f057d271691e3 F src/shell.c d6e64471aafb81f355262533393169a70529847a F src/sqlite.h.in 5af8181f815831a8672c3834c60e6b4418448bcc F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h 20ca4172804c85745445bd3291d7884446b67bd7 +F src/sqliteInt.h 61c55f5f83c63813903f374e9b33173572f0559a F src/sqliteLimit.h 504a3161886d2938cbd163054ad620b8356df758 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d @@ -330,7 +330,7 @@ F test/expr.test 9f521ae22f00e074959f72ce2e55d46b9ed23f68 F test/filectrl.test 8923a6dc7630f31c8a9dd3d3d740aa0922df7bf8 F test/filefmt.test 84e3d0fe9f12d0d2ac852465c6f8450aea0d6f43 F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da -F test/fkey2.test 10ac5a185bd82fccebc1644960baee89a9a24bcc +F test/fkey2.test 2220d7384624d792b692a94c65239f6359480312 F test/fkey3.test 42f88d6048d8dc079e2a8cf7baad1cc1483a7620 F test/fkey_malloc.test a5ede29bd2f6e56dea78c3d43fb86dd696c068c8 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb @@ -755,7 +755,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 9842f2d5f606eb8f641ecae9fbc5368b8d7e4286 -R 3f4f5605b427a8a3e05e79ea9061feda +P e4fa8be770eb79806d489be46f68f969de17378b +R 2ef0920c3026f22dce7b0a365b2419f0 U dan -Z 6f95349be215c026d8008f1f1d0bb69d +Z c958d9e98d78b6ad7096dbaf02193139 diff --git a/manifest.uuid b/manifest.uuid index cec13b3ae1..db4933029d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e4fa8be770eb79806d489be46f68f969de17378b \ No newline at end of file +3c24df38e6ae5dfe999bbf3133b65df0074c6a50 \ No newline at end of file diff --git a/src/auth.c b/src/auth.c index 042fc66150..b17d1f53f7 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 && iDbnDb ); - 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 dc5b4199cc..45a00d2095 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; ipFrom && nIncr==1 ){ - int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1; - for(i=0; iaiColumn[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; ipFrom && nIncr==1 ){ + int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1; + for(i=0; iaiColumn[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 a82bbdfc8a..03ff1be11d 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 diff --git a/test/fkey2.test b/test/fkey2.test index 62d5fbdf05..78ccddf697 100644 --- a/test/fkey2.test +++ b/test/fkey2.test @@ -1421,6 +1421,9 @@ ifcapable auth { set authargs } {SQLITE_INSERT two {} main {} SQLITE_READ one a main {}} + # Return SQLITE_IGNORE to requests to read from the parent table. This + # causes inserts of non-NULL keys into the child table to fail. + # rename auth {} proc auth {args} { if {[lindex $args 1] == "long"} {return SQLITE_IGNORE} @@ -1428,7 +1431,16 @@ ifcapable auth { } do_test fkey2-18.8 { catchsql { INSERT INTO short VALUES(1, 3, 2) } - } {1 {access to long.b is prohibited}} + } {1 {foreign key constraint failed}} + do_test fkey2-18.9 { + execsql { INSERT INTO short VALUES(1, 3, NULL) } + } {} + do_test fkey2-18.10 { + execsql { SELECT * FROM short } + } {1 3 2 1 3 {}} + do_test fkey2-18.11 { + catchsql { UPDATE short SET f = 2 WHERE f IS NULL } + } {1 {foreign key constraint failed}} db auth {} unset authargs