From 6c5b915fcb78e067fc8a6db022d5fc77aafe2e60 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 17 Dec 2012 16:46:37 +0000 Subject: [PATCH 01/15] Prototype for PRAGMA that checks all foreign key constraints on a table. FossilOrigin-Name: 01c980e9bbdf82e0b37667b6403c19ed8c73ee62 --- manifest | 19 +++++----- manifest.uuid | 2 +- src/fkey.c | 12 +++---- src/pragma.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sqliteInt.h | 2 ++ 5 files changed, 112 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index bd226934d5..a7adf8bb64 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Optimize\sIN\soperators\sin\sthe\sWHERE\sclause\sof\squeries\susing\svirtual\stables. -D 2012-12-14T17:54:38.979 +C Prototype\sfor\sPRAGMA\sthat\schecks\sall\sforeign\skey\sconstraints\son\sa\stable. +D 2012-12-17T16:46:37.634 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -132,7 +132,7 @@ F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c 9b8d308979114991e5dc7cee958316e07186941d F src/expr.c 0e41d66d868b37dbc0e041c342e0036fad27e705 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c dcb7c37a4bf526ded7b24a01a60fe071bcd160a2 +F src/fkey.c 21fb59849f5ce409d82736a34c1c21612d029e59 F src/func.c 8147799b048065a1590805be464d05b4913e652c F src/global.c e59ecd2c553ad0d4bfbc84ca71231336f8993a7a F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 @@ -168,7 +168,7 @@ F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c 015723c48072781d2740e310ab04dc92956b76d1 +F src/pragma.c 52adf60a59ee30717ffc24d5dffbf8305e2b0532 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -179,7 +179,7 @@ F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 -F src/sqliteInt.h d1f0866c69d94fe018a32f78c31a043e3fc0d0de +F src/sqliteInt.h fed3071c8d1b7ccfe37f509def6ea5421988577c F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -1025,7 +1025,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 52e755943f87354febe214e5dc3b423a1e38ba80 d2fb7619b063b329b6d7ba9a16a7290e5d868f23 -R fea13d1ea2e8500bc200a6665b5fa212 +P 3d65c70343196b8f69c5293e7703839846fade85 +R d2dffba8202bff8058b0bdfbf2948311 +T *branch * foreign-key-check +T *sym-foreign-key-check * +T -sym-trunk * U drh -Z 55c2c2e92a55016f5394cb88b53de07c +Z c27ac5b11b52c35a742b54038f8a6374 diff --git a/manifest.uuid b/manifest.uuid index e5bf82bc6d..9173b209b7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3d65c70343196b8f69c5293e7703839846fade85 \ No newline at end of file +01c980e9bbdf82e0b37667b6403c19ed8c73ee62 \ No newline at end of file diff --git a/src/fkey.c b/src/fkey.c index 2d01e2524b..c9b7c51af9 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -142,7 +142,7 @@ ** A foreign key constraint requires that the key columns in the parent ** table are collectively subject to a UNIQUE or PRIMARY KEY constraint. ** Given that pParent is the parent table for foreign key constraint pFKey, -** search the schema a unique index on the parent key columns. +** search the schema for a unique index on the parent key columns. ** ** If successful, zero is returned. If the parent key is an INTEGER PRIMARY ** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx @@ -178,7 +178,7 @@ ** into pParse. If an OOM error occurs, non-zero is returned and the ** pParse->db->mallocFailed flag is set. */ -static int locateFkeyIndex( +int sqlite3FkLocateIndex( Parse *pParse, /* Parse context to store any error in */ Table *pParent, /* Parent table of FK constraint pFKey */ FKey *pFKey, /* Foreign key to find index for */ @@ -736,7 +736,7 @@ void sqlite3FkCheck( }else{ pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb); } - if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ + if( !pTo || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) ); if( !isIgnoreErrors || db->mallocFailed ) return; if( pTo==0 ){ @@ -816,7 +816,7 @@ void sqlite3FkCheck( continue; } - if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ + if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ if( !isIgnoreErrors || db->mallocFailed ) return; continue; } @@ -871,7 +871,7 @@ u32 sqlite3FkOldmask( } for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ Index *pIdx = 0; - locateFkeyIndex(pParse, pTab, p, &pIdx, 0); + sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); if( pIdx ){ for(i=0; inColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); } @@ -997,7 +997,7 @@ static Trigger *fkActionTrigger( int i; /* Iterator variable */ Expr *pWhen = 0; /* WHEN clause for the trigger */ - if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; + if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; assert( aiCol || pFKey->nCol==1 ); for(i=0; inCol; i++){ diff --git a/src/pragma.c b/src/pragma.c index d3061b234f..86f521a000 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1114,6 +1114,98 @@ void sqlite3Pragma( }else #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ +#ifndef SQLITE_OMIT_FOREIGN_KEY + if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 && zRight ){ + FKey *pFK; + Table *pTab; + Table *pParent; + Index *pIdx; + int i, j; + int x; + int *aiCols; + + if( sqlite3ReadSchema(pParse) ) goto pragma_out; + pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); + if( pTab && pTab->pFKey ){ + int regResult; + int regRow; + int regKey; + v = sqlite3GetVdbe(pParse); + sqlite3VdbeSetNumCols(v, 2); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "fkid", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC); + sqlite3CodeVerifySchema(pParse, iDb); + sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); + for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); + if( pParent==0 ) break; + pIdx = 0; + aiCols = 0; + x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); + if( x==0 ){ + if( pIdx==0 ){ + sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); + }else{ + KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb); + sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + } + }else{ + break; + } + sqlite3DbFree(db, aiCols); + } + pParse->nTab = i; + if( pFK==0 ){ + int addrTop; + int addrOk; + addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); + regResult = pParse->nMem+1; + pParse->nMem += 2; + regRow = pParse->nMem+1; + pParse->nMem += pTab->nCol; + regKey = ++pParse->nMem; + for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); + assert( pParent!=0 ); + pIdx = 0; + aiCols = 0; + x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); + assert( x==0 ); + addrOk = sqlite3VdbeMakeLabel(v); + if( pIdx==0 ){ + sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, pFK->aCol[0].iFrom, + regRow); + sqlite3VdbeAddOp1(v, OP_MustBeInt, regRow); + sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk); + x = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeJumpHere(v, x-2); + sqlite3VdbeJumpHere(v, x-3); + }else{ + for(j=0; jnCol; j++){ + sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, pFK->aCol[j].iFrom, + regRow+j); + sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); + } + sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey); + sqlite3VdbeChangeP4(v, -1, + sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); + sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); + } + sqlite3DbFree(db, aiCols); + sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult); + sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1); + sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 2); + sqlite3VdbeResolveLabel(v, addrOk); + } + sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); + sqlite3VdbeJumpHere(v, addrTop); + } + } + }else +#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ + #ifndef NDEBUG if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){ if( zRight ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 2b58a808fb..e15b815e12 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3209,8 +3209,10 @@ const char *sqlite3JournalModename(int); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY void sqlite3FkDelete(sqlite3 *, Table*); + int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**); #else #define sqlite3FkDelete(a,b) + #define sqlite3FkLocateIndex(a,b,c,d,e) #endif From a7c2f3e760fcaf5a3d3426bd1688fc5c886c4de5 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 17 Dec 2012 18:05:54 +0000 Subject: [PATCH 02/15] Fix an issue with child keys that are not INTEGER PRIMARY KEY referencing INTEGER PRIMARY KEY in the parent. FossilOrigin-Name: 603b695bf094b9da905f877a13dcb134463db92a --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/pragma.c | 17 +++++++++++------ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index a7adf8bb64..239a8f55d8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Prototype\sfor\sPRAGMA\sthat\schecks\sall\sforeign\skey\sconstraints\son\sa\stable. -D 2012-12-17T16:46:37.634 +C Fix\san\sissue\swith\schild\skeys\sthat\sare\snot\sINTEGER\sPRIMARY\sKEY\nreferencing\sINTEGER\sPRIMARY\sKEY\sin\sthe\sparent. +D 2012-12-17T18:05:54.523 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -168,7 +168,7 @@ F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c 52adf60a59ee30717ffc24d5dffbf8305e2b0532 +F src/pragma.c 599af02bcc13c8f41a6670a19185119ffae18e6d F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -1025,10 +1025,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 3d65c70343196b8f69c5293e7703839846fade85 -R d2dffba8202bff8058b0bdfbf2948311 -T *branch * foreign-key-check -T *sym-foreign-key-check * -T -sym-trunk * +P 01c980e9bbdf82e0b37667b6403c19ed8c73ee62 +R abb2e93db46b538d88f02500430c7bfd U drh -Z c27ac5b11b52c35a742b54038f8a6374 +Z 4013a6f2f3fe7fcef30b92f6b8b3c386 diff --git a/manifest.uuid b/manifest.uuid index 9173b209b7..f929e33227 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -01c980e9bbdf82e0b37667b6403c19ed8c73ee62 \ No newline at end of file +603b695bf094b9da905f877a13dcb134463db92a \ No newline at end of file diff --git a/src/pragma.c b/src/pragma.c index 86f521a000..fbd556526a 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1174,14 +1174,19 @@ void sqlite3Pragma( assert( x==0 ); addrOk = sqlite3VdbeMakeLabel(v); if( pIdx==0 ){ - sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, pFK->aCol[0].iFrom, - regRow); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regRow); + int iKey = pFK->aCol[0].iFrom; + if( iKey>=0 && iKey!=pTab->iPKey ){ + sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow); + sqlite3ColumnDefault(v, pTab, iKey, regRow); + sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); + sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow, + sqlite3VdbeCurrentAddr(v)+3); + }else{ + sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow); + } sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk); - x = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeJumpHere(v, x-2); - sqlite3VdbeJumpHere(v, x-3); + sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); }else{ for(j=0; jnCol; j++){ sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, pFK->aCol[j].iFrom, From 613028b34e97c841a15e23074c7ccaaef0d4e435 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 17 Dec 2012 18:43:02 +0000 Subject: [PATCH 03/15] "PRAGMA foreign_key_check" with no argument checks the foreign keys on all tables. FossilOrigin-Name: 57d47423a65d183cb1efcb95b28cb143e788b953 --- manifest | 12 ++--- manifest.uuid | 2 +- src/pragma.c | 146 ++++++++++++++++++++++++++------------------------ 3 files changed, 84 insertions(+), 76 deletions(-) diff --git a/manifest b/manifest index 239a8f55d8..40430b8c8a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sissue\swith\schild\skeys\sthat\sare\snot\sINTEGER\sPRIMARY\sKEY\nreferencing\sINTEGER\sPRIMARY\sKEY\sin\sthe\sparent. -D 2012-12-17T18:05:54.523 +C "PRAGMA\sforeign_key_check"\swith\sno\sargument\schecks\sthe\sforeign\skeys\son\sall\ntables. +D 2012-12-17T18:43:02.925 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -168,7 +168,7 @@ F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c 599af02bcc13c8f41a6670a19185119ffae18e6d +F src/pragma.c b98dfe89b961d41be6d502d23adf18ed02dbb410 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -1025,7 +1025,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 01c980e9bbdf82e0b37667b6403c19ed8c73ee62 -R abb2e93db46b538d88f02500430c7bfd +P 603b695bf094b9da905f877a13dcb134463db92a +R 838d64421c46ea50daf2c57db5adcb2d U drh -Z 4013a6f2f3fe7fcef30b92f6b8b3c386 +Z f327b298aa59d7b1bf00c17e68686c2c diff --git a/manifest.uuid b/manifest.uuid index f929e33227..a607adedca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -603b695bf094b9da905f877a13dcb134463db92a \ No newline at end of file +57d47423a65d183cb1efcb95b28cb143e788b953 \ No newline at end of file diff --git a/src/pragma.c b/src/pragma.c index fbd556526a..d65f4cd101 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1115,33 +1115,51 @@ void sqlite3Pragma( #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ #ifndef SQLITE_OMIT_FOREIGN_KEY - if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 && zRight ){ - FKey *pFK; - Table *pTab; - Table *pParent; - Index *pIdx; - int i, j; - int x; - int *aiCols; + if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 ){ + FKey *pFK; /* A foreign key constraint */ + Table *pTab; /* Child table contain "REFERENCES" keyword */ + Table *pParent; /* Parent table that child points to */ + Index *pIdx; /* Index in the parent table */ + int i; /* Loop counter: Foreign key number for pTab */ + int j; /* Loop counter: Field of the foreign key */ + HashElem *k; /* Loop counter: Next table in schema */ + int x; /* result variable */ + int regResult; /* 3 registers to hold a result row */ + int regKey; /* Register to hold key for checking the FK */ + int regRow; /* Registers to hold a row from pTab */ + int addrTop; /* Top of a loop checking foreign keys */ + int addrOk; /* Jump here if the key is OK */ if( sqlite3ReadSchema(pParse) ) goto pragma_out; - pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); - if( pTab && pTab->pFKey ){ - int regResult; - int regRow; - int regKey; - v = sqlite3GetVdbe(pParse); - sqlite3VdbeSetNumCols(v, 2); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "fkid", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC); - sqlite3CodeVerifySchema(pParse, iDb); + regResult = pParse->nMem+1; + pParse->nMem += 3; + regKey = ++pParse->nMem; + regRow = ++pParse->nMem; + v = sqlite3GetVdbe(pParse); + sqlite3VdbeSetNumCols(v, 3); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "fkid", SQLITE_STATIC); + sqlite3CodeVerifySchema(pParse, iDb); + k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); + while( k ){ + if( zRight ){ + pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); + k = 0; + }else{ + pTab = (Table*)sqliteHashData(k); + k = sqliteHashNext(k); + } + if( pTab->pFKey==0 ) continue; + if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); + sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName, + P4_TRANSIENT); for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); if( pParent==0 ) break; pIdx = 0; - aiCols = 0; - x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); + x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); if( x==0 ){ if( pIdx==0 ){ sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); @@ -1151,62 +1169,52 @@ void sqlite3Pragma( sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); } }else{ + k = 0; break; } - sqlite3DbFree(db, aiCols); } - pParse->nTab = i; - if( pFK==0 ){ - int addrTop; - int addrOk; - addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); - regResult = pParse->nMem+1; - pParse->nMem += 2; - regRow = pParse->nMem+1; - pParse->nMem += pTab->nCol; - regKey = ++pParse->nMem; - for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ - pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); - assert( pParent!=0 ); - pIdx = 0; - aiCols = 0; - x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); - assert( x==0 ); - addrOk = sqlite3VdbeMakeLabel(v); - if( pIdx==0 ){ - int iKey = pFK->aCol[0].iFrom; - if( iKey>=0 && iKey!=pTab->iPKey ){ - sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow); - sqlite3ColumnDefault(v, pTab, iKey, regRow); - sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); - sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow, - sqlite3VdbeCurrentAddr(v)+3); - }else{ - sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow); - } - sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk); - sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); + if( pFK ) break; + if( pParse->nTabnTab = i; + addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); + for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); + assert( pParent!=0 ); + pIdx = 0; + x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); + assert( x==0 ); + addrOk = sqlite3VdbeMakeLabel(v); + if( pIdx==0 ){ + int iKey = pFK->aCol[0].iFrom; + if( iKey>=0 && iKey!=pTab->iPKey ){ + sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow); + sqlite3ColumnDefault(v, pTab, iKey, regRow); + sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); + sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow, + sqlite3VdbeCurrentAddr(v)+3); }else{ - for(j=0; jnCol; j++){ - sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, pFK->aCol[j].iFrom, - regRow+j); - sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); - } - sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey); - sqlite3VdbeChangeP4(v, -1, - sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); - sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); + sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow); } - sqlite3DbFree(db, aiCols); - sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult); - sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1); - sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 2); - sqlite3VdbeResolveLabel(v, addrOk); + sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk); + sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); + }else{ + for(j=0; jnCol; j++){ + sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, pFK->aCol[j].iFrom, + regRow+j); + sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); + } + sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey); + sqlite3VdbeChangeP4(v, -1, + sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); + sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); } - sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); - sqlite3VdbeJumpHere(v, addrTop); + sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1); + sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+2); + sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 3); + sqlite3VdbeResolveLabel(v, addrOk); } + sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); + sqlite3VdbeJumpHere(v, addrTop); } }else #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ From 9148defa61d769a88bf61e0fcf41451ebc2ecf34 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 17 Dec 2012 20:40:39 +0000 Subject: [PATCH 04/15] Enhance the error message for "foreign key mismatch" to include the names of the child and parent tables. Begin adding test cases for PRAGMA foreign_key_check. Make sure PRAGMA foreign_key_check gets all necessary table locks. FossilOrigin-Name: 0f9963526c299757ff95c153fb05a67cddb6af28 --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/fkey.c | 4 +++- src/pragma.c | 4 +++- test/e_fkey.test | 43 ++++++++++++++++++++++--------------------- test/fkey2.test | 29 ++++++++++++++++++++++++----- 6 files changed, 62 insertions(+), 38 deletions(-) diff --git a/manifest b/manifest index 40430b8c8a..7dfdfb2ad3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C "PRAGMA\sforeign_key_check"\swith\sno\sargument\schecks\sthe\sforeign\skeys\son\sall\ntables. -D 2012-12-17T18:43:02.925 +C Enhance\sthe\serror\smessage\sfor\s"foreign\skey\smismatch"\sto\sinclude\sthe\snames\sof\nthe\schild\sand\sparent\stables.\s\sBegin\sadding\stest\scases\sfor\s\nPRAGMA\sforeign_key_check.\s\sMake\ssure\sPRAGMA\sforeign_key_check\sgets\sall\nnecessary\stable\slocks. +D 2012-12-17T20:40:39.698 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -132,7 +132,7 @@ F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c 9b8d308979114991e5dc7cee958316e07186941d F src/expr.c 0e41d66d868b37dbc0e041c342e0036fad27e705 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c 21fb59849f5ce409d82736a34c1c21612d029e59 +F src/fkey.c 5b7a12e2f8620e855b0478a9a6798df9967bb277 F src/func.c 8147799b048065a1590805be464d05b4913e652c F src/global.c e59ecd2c553ad0d4bfbc84ca71231336f8993a7a F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 @@ -168,7 +168,7 @@ F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c b98dfe89b961d41be6d502d23adf18ed02dbb410 +F src/pragma.c 5cc87af77dd57e037258410e0ee5a9dab51a8a5a F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -383,7 +383,7 @@ F test/e_delete.test 89aa84d3d1bd284a0689ede04bce10226a5aeaa5 F test/e_droptrigger.test afd5c4d27dec607f5997a66bf7e2498a082cb235 F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e F test/e_expr.test 5489424d3d9a452ac3701cdf4b680ae31a157894 -F test/e_fkey.test a79ab1d3213c7ac64621eec28f8e8bb219775445 +F test/e_fkey.test 89a2ff734a33693b997534eff90724573948be7b F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 F test/e_insert.test d5331cc95e101af2508159fc98b6801631659ffe F test/e_reindex.test dfedfc32c5a282b0596c6537cbcd4217fbb1a216 @@ -409,7 +409,7 @@ F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e F test/filectrl.test 14fa712e42c4cb791e09dfd58a6a03efb47ef13a F test/filefmt.test ffa17b5aebc3eb4b1e3be1ccb5ee906ffbd97f6e F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da -F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f +F test/fkey2.test 5aa44e7153928a1f002803f94aaab4c76a7ceac2 F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e F test/fkey4.test c6c8f9f9be885f95c85c7bceb26f243ad906fd49 F test/fkey_malloc.test c3a12acd053c976de09036498eef09b83afa4a80 @@ -1025,7 +1025,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 603b695bf094b9da905f877a13dcb134463db92a -R 838d64421c46ea50daf2c57db5adcb2d +P 57d47423a65d183cb1efcb95b28cb143e788b953 +R 48149676f68d877f18e0c8a269ce004d U drh -Z f327b298aa59d7b1bf00c17e68686c2c +Z 4108a840f9ef999ff91109d05e04ab7d diff --git a/manifest.uuid b/manifest.uuid index a607adedca..02f0c8f087 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -57d47423a65d183cb1efcb95b28cb143e788b953 \ No newline at end of file +0f9963526c299757ff95c153fb05a67cddb6af28 \ No newline at end of file diff --git a/src/fkey.c b/src/fkey.c index c9b7c51af9..421311dc35 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -275,7 +275,9 @@ int sqlite3FkLocateIndex( if( !pIdx ){ if( !pParse->disableTriggers ){ - sqlite3ErrorMsg(pParse, "foreign key mismatch"); + sqlite3ErrorMsg(pParse, + "foreign key mismatch - \"%w\" referencing \"%w\"", + pFKey->pFrom->zName, pFKey->zTo); } sqlite3DbFree(pParse->db, aiCol); return 1; diff --git a/src/pragma.c b/src/pragma.c index d65f4cd101..60e91e6733 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1150,7 +1150,8 @@ void sqlite3Pragma( pTab = (Table*)sqliteHashData(k); k = sqliteHashNext(k); } - if( pTab->pFKey==0 ) continue; + if( pTab==0 || pTab->pFKey==0 ) continue; + sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName, @@ -1159,6 +1160,7 @@ void sqlite3Pragma( pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); if( pParent==0 ) break; pIdx = 0; + sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName); x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); if( x==0 ){ if( pIdx==0 ){ diff --git a/test/e_fkey.test b/test/e_fkey.test index 69e0868642..01bf534527 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -627,7 +627,8 @@ proc test_efkey_57 {tn isError sql} { execsql $sql do_test e_fkey-18.$tn { catchsql { INSERT INTO t2 VALUES(NULL) } - } [lindex {{0 {}} {1 {foreign key mismatch}}} $isError] + } [lindex {{0 {}} {/1 {foreign key mismatch - ".*" referencing ".*"}/}} \ + $isError] } test_efkey_57 2 0 { CREATE TABLE t1(x PRIMARY KEY) } test_efkey_57 3 0 { CREATE TABLE t1(x UNIQUE) } @@ -698,16 +699,16 @@ do_test e_fkey-19.2 { } {} do_test e_fkey-19.2 { catchsql { INSERT INTO child4 VALUES('xxx', 5) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child4" referencing "parent"}} do_test e_fkey-19.3 { catchsql { INSERT INTO child5 VALUES('xxx', 6) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child5" referencing "parent"}} do_test e_fkey-19.4 { catchsql { INSERT INTO child6 VALUES(2, 3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child6" referencing "parent"}} do_test e_fkey-19.5 { catchsql { INSERT INTO child7 VALUES(3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child7" referencing "parent"}} #------------------------------------------------------------------------- # Test errors in the database schema that are detected while preparing @@ -765,12 +766,12 @@ do_test e_fkey-20.1 { foreach {tn tbl ptbl err} { 2 c1 {} "no such table: main.nosuchtable" - 3 c2 p2 "foreign key mismatch" - 4 c3 p3 "foreign key mismatch" - 5 c4 p4 "foreign key mismatch" - 6 c5 p5 "foreign key mismatch" - 7 c6 p6 "foreign key mismatch" - 8 c7 p7 "foreign key mismatch" + 3 c2 p2 "foreign key mismatch - \"c2\" referencing \"p2\"" + 4 c3 p3 "foreign key mismatch - \"c3\" referencing \"p3\"" + 5 c4 p4 "foreign key mismatch - \"c4\" referencing \"p4\"" + 6 c5 p5 "foreign key mismatch - \"c5\" referencing \"p5\"" + 7 c6 p6 "foreign key mismatch - \"c6\" referencing \"p6\"" + 8 c7 p7 "foreign key mismatch - \"c7\" referencing \"p7\"" } { do_test e_fkey-20.$tn.1 { catchsql "INSERT INTO $tbl VALUES('a', 'b')" @@ -820,22 +821,22 @@ do_test e_fkey-21.2 { } {} do_test e_fkey-21.3 { catchsql { INSERT INTO child9 VALUES('I') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.4 { catchsql { INSERT INTO child9 VALUES('II') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.5 { catchsql { INSERT INTO child9 VALUES(NULL) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.6 { catchsql { INSERT INTO child10 VALUES('I', 'II', 'III') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} do_test e_fkey-21.7 { catchsql { INSERT INTO child10 VALUES(1, 2, 3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} do_test e_fkey-21.8 { catchsql { INSERT INTO child10 VALUES(NULL, NULL, NULL) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} #------------------------------------------------------------------------- # Test errors that are reported when creating the child table. @@ -1151,7 +1152,7 @@ do_test e_fkey-28.8 { CREATE TABLE c(a, b, FOREIGN KEY(a,b) REFERENCES p); } catchsql {DELETE FROM p} -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c" referencing "p"}} do_test e_fkey-28.9 { drop_all_tables execsql { @@ -1159,7 +1160,7 @@ do_test e_fkey-28.9 { CREATE TABLE c(a REFERENCES p); } catchsql {DELETE FROM p} -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c" referencing "p"}} #------------------------------------------------------------------------- @@ -2729,11 +2730,11 @@ do_test e_fkey-60.3 { do_test e_fkey-60.4 { execsql { CREATE TABLE nosuchtable(x PRIMARY KEY) } catchsql { DELETE FROM p } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c2" referencing "p"}} do_test e_fkey-60.5 { execsql { DROP TABLE c1 } catchsql { DELETE FROM p } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c2" referencing "p"}} do_test e_fkey-60.6 { execsql { DROP TABLE c2 } execsql { DELETE FROM p } diff --git a/test/fkey2.test b/test/fkey2.test index f0cc4d244c..a723598788 100644 --- a/test/fkey2.test +++ b/test/fkey2.test @@ -139,14 +139,21 @@ set FkeySimpleTests { 4.17 "UPDATE t7 SET a = 10" {0 {}} 5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}} - 5.2 "INSERT INTO t10 VALUES(1, 3)" {1 {foreign key mismatch}} + 5.2 "INSERT INTO t10 VALUES(1, 3)" + {1 {foreign key mismatch - "t10" referencing "t9"}} } do_test fkey2-1.1.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { - do_test fkey2-1.1.$tn { catchsql $zSql } $res + do_test fkey2-1.1.$tn.1 { catchsql $zSql } $res + do_test fkey2-1.1.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.1.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.1.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.1.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.1.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.1.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } drop_all_tables @@ -155,6 +162,12 @@ do_test fkey2-1.2.0 { } {} foreach {tn zSql res} $FkeySimpleTests { do_test fkey2-1.2.$tn { catchsql $zSql } $res + do_test fkey2-1.2.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.2.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.2.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.2.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.2.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.2.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } drop_all_tables @@ -165,6 +178,12 @@ do_test fkey2-1.3.0 { foreach {tn zSql res} $FkeySimpleTests { if {$res == "0 {}"} { set res {0 1} } do_test fkey2-1.3.$tn { catchsql $zSql } $res + do_test fkey2-1.3.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.3.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.3.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.3.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.3.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.3.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } execsql { PRAGMA count_changes = 0 } drop_all_tables @@ -681,7 +700,7 @@ foreach zSql [list { do_test fkey2-10.1.[incr tn] { execsql $zSql catchsql { INSERT INTO c DEFAULT VALUES } - } {1 {foreign key mismatch}} + } {/1 {foreign key mismatch - "c" referencing "."}/} } # "rowid" cannot be used as part of a child or parent key definition @@ -709,7 +728,7 @@ do_test fkey2-10.2.1 { INSERT INTO t1(rowid, a, b) VALUES(1, 1, 1); INSERT INTO t2 VALUES(1, 1); } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "t2" referencing "t1"}} do_test fkey2-10.2.2 { drop_all_tables catchsql { @@ -1223,7 +1242,7 @@ do_test fkey-2.14.3.8 { CREATE TABLE cc(a, b, FOREIGN KEY(a, b) REFERENCES pp(x, z)); } catchsql { INSERT INTO cc VALUES(1, 2) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "cc" referencing "pp"}} do_test fkey-2.14.3.9 { execsql { DROP TABLE cc } } {} From 4b4b473a1e71fc5cf0660f9aafcce71c28e26464 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 17 Dec 2012 20:57:15 +0000 Subject: [PATCH 05/15] Add to the foreign_key_check pragma an extra output column "parent" that contains the name of the parent table for the constraint that failed. FossilOrigin-Name: 97f7f7377259ecf562019f62ebed0897cfae236e --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/pragma.c | 13 ++++++++----- test/fkey_malloc.test | 3 +-- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index 7dfdfb2ad3..afed8ed585 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\serror\smessage\sfor\s"foreign\skey\smismatch"\sto\sinclude\sthe\snames\sof\nthe\schild\sand\sparent\stables.\s\sBegin\sadding\stest\scases\sfor\s\nPRAGMA\sforeign_key_check.\s\sMake\ssure\sPRAGMA\sforeign_key_check\sgets\sall\nnecessary\stable\slocks. -D 2012-12-17T20:40:39.698 +C Add\sto\sthe\sforeign_key_check\spragma\san\sextra\soutput\scolumn\s"parent"\sthat\ncontains\sthe\sname\sof\sthe\sparent\stable\sfor\sthe\sconstraint\sthat\sfailed. +D 2012-12-17T20:57:15.968 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -168,7 +168,7 @@ F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c 5cc87af77dd57e037258410e0ee5a9dab51a8a5a +F src/pragma.c c6e9ed9de9a4019cf2a049c78833b58c9a535fae F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -412,7 +412,7 @@ F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da F test/fkey2.test 5aa44e7153928a1f002803f94aaab4c76a7ceac2 F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e F test/fkey4.test c6c8f9f9be885f95c85c7bceb26f243ad906fd49 -F test/fkey_malloc.test c3a12acd053c976de09036498eef09b83afa4a80 +F test/fkey_malloc.test bb74c9cb8f8fceed03b58f8a7ef2df98520bbd51 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7 @@ -1025,7 +1025,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 57d47423a65d183cb1efcb95b28cb143e788b953 -R 48149676f68d877f18e0c8a269ce004d +P 0f9963526c299757ff95c153fb05a67cddb6af28 +R 09c7edec1884f69df7657ebe62d33418 U drh -Z 4108a840f9ef999ff91109d05e04ab7d +Z bca5ca1c6b7b99510bae2f63ea5f8e69 diff --git a/manifest.uuid b/manifest.uuid index 02f0c8f087..8fe083b23b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0f9963526c299757ff95c153fb05a67cddb6af28 \ No newline at end of file +97f7f7377259ecf562019f62ebed0897cfae236e \ No newline at end of file diff --git a/src/pragma.c b/src/pragma.c index 60e91e6733..a34ececddb 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1132,14 +1132,15 @@ void sqlite3Pragma( if( sqlite3ReadSchema(pParse) ) goto pragma_out; regResult = pParse->nMem+1; - pParse->nMem += 3; + pParse->nMem += 4; regKey = ++pParse->nMem; regRow = ++pParse->nMem; v = sqlite3GetVdbe(pParse); - sqlite3VdbeSetNumCols(v, 3); + sqlite3VdbeSetNumCols(v, 4); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC); - sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "fkid", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "parent", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "fkid", SQLITE_STATIC); sqlite3CodeVerifySchema(pParse, iDb); k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); while( k ){ @@ -1211,8 +1212,10 @@ void sqlite3Pragma( sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); } sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1); - sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+2); - sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 3); + sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0, + pFK->zTo, P4_TRANSIENT); + sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+3); + sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4); sqlite3VdbeResolveLabel(v, addrOk); } sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); diff --git a/test/fkey_malloc.test b/test/fkey_malloc.test index 4a36f5f321..b4b5b4eed7 100644 --- a/test/fkey_malloc.test +++ b/test/fkey_malloc.test @@ -29,6 +29,7 @@ do_malloc_test fkey_malloc-1 -sqlprep { INSERT INTO t2 VALUES('aaa'); UPDATE t1 SET a = 'bbb'; DELETE FROM t1; + PRAGMA foreign_key_check; } do_malloc_test fkey_malloc-2 -sqlprep { @@ -128,5 +129,3 @@ do_malloc_test fkey_malloc-7 -sqlprep { } finish_test - - From 7d22a4d7100216658b0340f9d5cbbd8ed6c7ec94 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 17 Dec 2012 22:32:14 +0000 Subject: [PATCH 06/15] Added test cases for PRAGMA foreign_key_check. Fixed a bug that appears when the column order of the child and parent differ. FossilOrigin-Name: 25411f83f99803e39feee368703fd20b4ade8eeb --- manifest | 13 +- manifest.uuid | 2 +- src/pragma.c | 9 +- test/fkey5.test | 310 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 324 insertions(+), 10 deletions(-) create mode 100644 test/fkey5.test diff --git a/manifest b/manifest index afed8ed585..c6b7e5c563 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sto\sthe\sforeign_key_check\spragma\san\sextra\soutput\scolumn\s"parent"\sthat\ncontains\sthe\sname\sof\sthe\sparent\stable\sfor\sthe\sconstraint\sthat\sfailed. -D 2012-12-17T20:57:15.968 +C Added\stest\scases\sfor\sPRAGMA\sforeign_key_check.\s\sFixed\sa\sbug\sthat\sappears\nwhen\sthe\scolumn\sorder\sof\sthe\schild\sand\sparent\sdiffer. +D 2012-12-17T22:32:14.381 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -168,7 +168,7 @@ F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c c6e9ed9de9a4019cf2a049c78833b58c9a535fae +F src/pragma.c 312d9ea0d9b98583514f6f8e92cb7e8f526ff4c6 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -412,6 +412,7 @@ F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da F test/fkey2.test 5aa44e7153928a1f002803f94aaab4c76a7ceac2 F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e F test/fkey4.test c6c8f9f9be885f95c85c7bceb26f243ad906fd49 +F test/fkey5.test 0bf64f2d19ad80433ca0b24edbf604a18b353d5f F test/fkey_malloc.test bb74c9cb8f8fceed03b58f8a7ef2df98520bbd51 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c @@ -1025,7 +1026,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 0f9963526c299757ff95c153fb05a67cddb6af28 -R 09c7edec1884f69df7657ebe62d33418 +P 97f7f7377259ecf562019f62ebed0897cfae236e +R e825c339f2d84a956eae42ef385eea53 U drh -Z bca5ca1c6b7b99510bae2f63ea5f8e69 +Z 0751ac95be4abd3e28a98e71458d0c00 diff --git a/manifest.uuid b/manifest.uuid index 8fe083b23b..57652e0971 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -97f7f7377259ecf562019f62ebed0897cfae236e \ No newline at end of file +25411f83f99803e39feee368703fd20b4ade8eeb \ No newline at end of file diff --git a/src/pragma.c b/src/pragma.c index a34ececddb..9a20607039 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1129,6 +1129,7 @@ void sqlite3Pragma( int regRow; /* Registers to hold a row from pTab */ int addrTop; /* Top of a loop checking foreign keys */ int addrOk; /* Jump here if the key is OK */ + int *aiCols; /* child to parent column mapping */ if( sqlite3ReadSchema(pParse) ) goto pragma_out; regResult = pParse->nMem+1; @@ -1183,7 +1184,8 @@ void sqlite3Pragma( pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); assert( pParent!=0 ); pIdx = 0; - x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); + aiCols = 0; + x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); assert( x==0 ); addrOk = sqlite3VdbeMakeLabel(v); if( pIdx==0 ){ @@ -1202,8 +1204,8 @@ void sqlite3Pragma( sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); }else{ for(j=0; jnCol; j++){ - sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, pFK->aCol[j].iFrom, - regRow+j); + sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, + aiCols ? aiCols[j] : pFK->aCol[0].iFrom, regRow+j); sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); } sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey); @@ -1217,6 +1219,7 @@ void sqlite3Pragma( sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+3); sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4); sqlite3VdbeResolveLabel(v, addrOk); + sqlite3DbFree(db, aiCols); } sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); sqlite3VdbeJumpHere(v, addrTop); diff --git a/test/fkey5.test b/test/fkey5.test new file mode 100644 index 0000000000..40a1a5e961 --- /dev/null +++ b/test/fkey5.test @@ -0,0 +1,310 @@ +# 2012 December 17 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file tests the PRAGMA foreign_key_check command. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable {!foreignkey} { + finish_test + return +} + +do_test fkey5-1.1 { + db eval { + CREATE TABLE p1(a INTEGER PRIMARY KEY); INSERT INTO p1 VALUES(88),(89); + CREATE TABLE p2(a INT PRIMARY KEY); INSERT INTO p2 VALUES(77),(78); + CREATE TABLE p3(a TEXT PRIMARY KEY); + INSERT INTO p3 VALUES(66),(67),('alpha'),('BRAVO'); + CREATE TABLE p4(a TEXT PRIMARY KEY COLLATE nocase); + INSERT INTO p4 VALUES('alpha'),('BRAVO'),('55'),('Delta'),('ECHO'); + CREATE TABLE p5(a INTEGER PRIMARY KEY, b, c, UNIQUE(b,c)); + INSERT INTO p5 VALUES(1,'Alpha','abc'),(2,'beta','def'); + CREATE TABLE p6(a INTEGER PRIMARY KEY, b TEXT COLLATE nocase, + c TEXT COLLATE rtrim, UNIQUE(b,c)); + INSERT INTO p6 VALUES(1,'Alpha','abc '),(2,'bETA','def '); + + CREATE TABLE c1(x INTEGER PRIMARY KEY references p1); + CREATE TABLE c2(x INTEGER PRIMARY KEY references p2); + CREATE TABLE c3(x INTEGER PRIMARY KEY references p3); + CREATE TABLE c4(x INTEGER PRIMARY KEY references p4); + CREATE TABLE c5(x INT references p1); + CREATE TABLE c6(x INT references p2); + CREATE TABLE c7(x INT references p3); + CREATE TABLE c8(x INT references p4); + CREATE TABLE c9(x TEXT UNIQUE references p1); + CREATE TABLE c10(x TEXT UNIQUE references p2); + CREATE TABLE c11(x TEXT UNIQUE references p3); + CREATE TABLE c12(x TEXT UNIQUE references p4); + CREATE TABLE c13(x TEXT COLLATE nocase references p3); + CREATE TABLE c14(x TEXT COLLATE nocase references p4); + CREATE TABLE c15(x, y, FOREIGN KEY(x,y) REFERENCES p5(b,c)); + CREATE TABLE c16(x, y, FOREIGN KEY(x,y) REFERENCES p5(c,b)); + CREATE TABLE c17(x, y, FOREIGN KEY(x,y) REFERENCES p6(b,c)); + CREATE TABLE c18(x, y, FOREIGN KEY(x,y) REFERENCES p6(c,b)); + CREATE TABLE c19(x TEXT COLLATE nocase, y TEXT COLLATE rtrim, + FOREIGN KEY(x,y) REFERENCES p5(b,c)); + CREATE TABLE c20(x TEXT COLLATE nocase, y TEXT COLLATE rtrim, + FOREIGN KEY(x,y) REFERENCES p5(c,b)); + CREATE TABLE c21(x TEXT COLLATE nocase, y TEXT COLLATE rtrim, + FOREIGN KEY(x,y) REFERENCES p6(b,c)); + CREATE TABLE c22(x TEXT COLLATE nocase, y TEXT COLLATE rtrim, + FOREIGN KEY(x,y) REFERENCES p6(c,b)); + + PRAGMA foreign_key_check; + } +} {} +do_test fkey5-1.2 { + db eval { + INSERT INTO c1 VALUES(90),(87),(88); + PRAGMA foreign_key_check; + } +} {c1 87 p1 0 c1 90 p1 0} +do_test fkey5-1.3 { + db eval { + PRAGMA foreign_key_check(c1); + } +} {c1 87 p1 0 c1 90 p1 0} +do_test fkey5-1.4 { + db eval { + PRAGMA foreign_key_check(c2); + } +} {} + +do_test fkey5-2.0 { + db eval { + INSERT INTO c5 SELECT x FROM c1; + DELETE FROM c1; + PRAGMA foreign_key_check; + } +} {c5 1 p1 0 c5 3 p1 0} +do_test fkey5-2.1 { + db eval { + PRAGMA foreign_key_check(c5); + } +} {c5 1 p1 0 c5 3 p1 0} +do_test fkey5-2.2 { + db eval { + PRAGMA foreign_key_check(c1); + } +} {} + +do_test fkey5-3.0 { + db eval { + INSERT INTO c9 SELECT x FROM c5; + DELETE FROM c5; + PRAGMA foreign_key_check; + } +} {c9 1 p1 0 c9 3 p1 0} +do_test fkey5-3.1 { + db eval { + PRAGMA foreign_key_check(c9); + } +} {c9 1 p1 0 c9 3 p1 0} +do_test fkey5-3.2 { + db eval { + PRAGMA foreign_key_check(c5); + } +} {} + +do_test fkey5-4.0 { + db eval { + DELETE FROM c9; + INSERT INTO c2 VALUES(79),(77),(76); + PRAGMA foreign_key_check; + } +} {c2 76 p2 0 c2 79 p2 0} +do_test fkey5-4.1 { + db eval { + PRAGMA foreign_key_check(c2); + } +} {c2 76 p2 0 c2 79 p2 0} +do_test fkey5-4.2 { + db eval { + INSERT INTO c6 SELECT x FROM c2; + DELETE FROM c2; + PRAGMA foreign_key_check; + } +} {c6 1 p2 0 c6 3 p2 0} +do_test fkey5-4.3 { + db eval { + PRAGMA foreign_key_check(c6); + } +} {c6 1 p2 0 c6 3 p2 0} +do_test fkey5-4.4 { + db eval { + INSERT INTO c10 SELECT x FROM c6; + DELETE FROM c6; + PRAGMA foreign_key_check; + } +} {c10 1 p2 0 c10 3 p2 0} +do_test fkey5-4.5 { + db eval { + PRAGMA foreign_key_check(c10); + } +} {c10 1 p2 0 c10 3 p2 0} + +do_test fkey5-5.0 { + db eval { + DELETE FROM c10; + INSERT INTO c3 VALUES(68),(67),(65); + PRAGMA foreign_key_check; + } +} {c3 65 p3 0 c3 68 p3 0} +do_test fkey5-5.1 { + db eval { + PRAGMA foreign_key_check(c3); + } +} {c3 65 p3 0 c3 68 p3 0} +do_test fkey5-5.2 { + db eval { + INSERT INTO c7 SELECT x FROM c3; + INSERT INTO c7 VALUES('Alpha'),('alpha'),('foxtrot'); + DELETE FROM c3; + PRAGMA foreign_key_check; + } +} {c7 1 p3 0 c7 3 p3 0 c7 4 p3 0 c7 6 p3 0} +do_test fkey5-5.3 { + db eval { + PRAGMA foreign_key_check(c7); + } +} {c7 1 p3 0 c7 3 p3 0 c7 4 p3 0 c7 6 p3 0} +do_test fkey5-5.4 { + db eval { + INSERT INTO c11 SELECT x FROM c7; + DELETE FROM c7; + PRAGMA foreign_key_check; + } +} {c11 1 p3 0 c11 3 p3 0 c11 4 p3 0 c11 6 p3 0} +do_test fkey5-5.5 { + db eval { + PRAGMA foreign_key_check(c11); + } +} {c11 1 p3 0 c11 3 p3 0 c11 4 p3 0 c11 6 p3 0} + +do_test fkey5-6.0 { + db eval { + DELETE FROM c11; + INSERT INTO c4 VALUES(54),(55),(56); + PRAGMA foreign_key_check; + } +} {c4 54 p4 0 c4 56 p4 0} +do_test fkey5-6.1 { + db eval { + PRAGMA foreign_key_check(c4); + } +} {c4 54 p4 0 c4 56 p4 0} +do_test fkey5-6.2 { + db eval { + INSERT INTO c8 SELECT x FROM c4; + INSERT INTO c8 VALUES('Alpha'),('ALPHA'),('foxtrot'); + DELETE FROM c4; + PRAGMA foreign_key_check; + } +} {c8 1 p4 0 c8 3 p4 0 c8 6 p4 0} +do_test fkey5-6.3 { + db eval { + PRAGMA foreign_key_check(c8); + } +} {c8 1 p4 0 c8 3 p4 0 c8 6 p4 0} +do_test fkey5-6.4 { + db eval { + INSERT INTO c12 SELECT x FROM c8; + DELETE FROM c8; + PRAGMA foreign_key_check; + } +} {c12 1 p4 0 c12 3 p4 0 c12 6 p4 0} +do_test fkey5-6.5 { + db eval { + PRAGMA foreign_key_check(c12); + } +} {c12 1 p4 0 c12 3 p4 0 c12 6 p4 0} + +do_test fkey5-7.1 { + db eval { + INSERT OR IGNORE INTO c13 SELECT * FROM c12; + INSERT OR IGNORE INTO C14 SELECT * FROM c12; + DELETE FROM c12; + PRAGMA foreign_key_check; + } +} {c14 1 p4 0 c14 3 p4 0 c14 6 p4 0 c13 1 p3 0 c13 2 p3 0 c13 3 p3 0 c13 4 p3 0 c13 5 p3 0 c13 6 p3 0} +do_test fkey5-7.2 { + db eval { + PRAGMA foreign_key_check(c14); + } +} {c14 1 p4 0 c14 3 p4 0 c14 6 p4 0} +do_test fkey5-7.3 { + db eval { + PRAGMA foreign_key_check(c13); + } +} {c13 1 p3 0 c13 2 p3 0 c13 3 p3 0 c13 4 p3 0 c13 5 p3 0 c13 6 p3 0} + +do_test fkey5-8.0 { + db eval { + DELETE FROM c13; + DELETE FROM c14; + INSERT INTO c19 VALUES('alpha','abc'); + PRAGMA foreign_key_check(c19); + } +} {c19 1 p5 0} +do_test fkey5-8.1 { + db eval { + DELETE FROM c19; + INSERT INTO c19 VALUES('Alpha','abc'); + PRAGMA foreign_key_check(c19); + } +} {} +do_test fkey5-8.2 { + db eval { + INSERT INTO c20 VALUES('Alpha','abc'); + PRAGMA foreign_key_check(c20); + } +} {c20 1 p5 0} +do_test fkey5-8.3 { + db eval { + DELETE FROM c20; + INSERT INTO c20 VALUES('abc','Alpha'); + PRAGMA foreign_key_check(c20); + } +} {} +do_test fkey5-8.4 { + db eval { + INSERT INTO c21 VALUES('alpha','abc '); + PRAGMA foreign_key_check(c21); + } +} {} +do_test fkey5-8.5 { + db eval { + DELETE FROM c21; + INSERT INTO c19 VALUES('Alpha','abc'); + PRAGMA foreign_key_check(c21); + } +} {} +do_test fkey5-8.6 { + db eval { + INSERT INTO c22 VALUES('Alpha','abc'); + PRAGMA foreign_key_check(c22); + } +} {c22 1 p6 0} +do_test fkey5-8.7 { + db eval { + DELETE FROM c22; + INSERT INTO c22 VALUES('abc ','ALPHA'); + PRAGMA foreign_key_check(c22); + } +} {} + + + +finish_test From 7ee751d241e7b2b86cbd81826e12aa729b688c28 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 19 Dec 2012 15:53:51 +0000 Subject: [PATCH 07/15] Proposed fix for ticket [a7b7803e8d1e8699cd8a]. FossilOrigin-Name: 3d0e00c731c39af91bad74c6d74853375bac356e --- manifest | 16 ++++++++++------ manifest.uuid | 2 +- src/where.c | 5 +++-- test/tkt-a7b7803e.test | 29 +++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 test/tkt-a7b7803e.test diff --git a/manifest b/manifest index 9c799f1bef..5d64c0aa4e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Optimize\sthe\sdegenerate\scase\sof\sa\sFROM\sclause\stable\sname\senclosed\sall\sby\nitself\sinside\sparentheses.\s\sGenerate\scode\sas\sif\sthe\sparentheses\sdid\snot\nexist,\srather\sthan\sthe\sold\sbehavior\sof\smanifesting\sthe\sparenthesized\stable\ninto\sa\stransient\stable.\s\sAlso,\stag\severy\sFROM-clause\sSELECT\ssubquery\sthat\sis\ngenerated\sby\sa\sparenthesized\sFROM-clause\sexpression\susing\sthe\sSF_NestedFrom\nflag.\s\sThe\snew\sSF_NestedFrom\sflag\sis\snot\syet\sused\sfor\sanything. -D 2012-12-18T19:36:11.944 +C Proposed\sfix\sfor\sticket\s[a7b7803e8d1e8699cd8a]. +D 2012-12-19T15:53:51.227 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -250,7 +250,7 @@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83 F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2 F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b -F src/where.c 7bdfde434b14ff88cc52ae41ac235ee1acb6b1e1 +F src/where.c b971ee2d1a4f5db1b4cfd5cb85e69f34e61781d0 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 @@ -784,6 +784,7 @@ F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5 F test/tkt-94c04eaadb.test fa9c71192f7e2ea2d51bf078bc34e8da6088bf71 F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67 +F test/tkt-a7b7803e.test 0baef2d1e7566e20a35bf6821d76a307ed78e3aa F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78 F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3 @@ -1026,7 +1027,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 9b67c633d932f3e566f521ee6a9cf3be193436fa -R d9a19320be87db9daf2926b8e71a7df8 +P 7fecced466d86a66b0b751c5b5608141e134fe2d +R 8b56234d9cc98530620effaeba3830d6 +T *branch * tkt-a7b7803e +T *sym-tkt-a7b7803e * +T -sym-trunk * U drh -Z bc761a01222fad0c80ad013442fb2ec8 +Z 4056ade20a6836865fb5397034c830dc diff --git a/manifest.uuid b/manifest.uuid index f26e1b6ae9..ae348b1652 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7fecced466d86a66b0b751c5b5608141e134fe2d \ No newline at end of file +3d0e00c731c39af91bad74c6d74853375bac356e \ No newline at end of file diff --git a/src/where.c b/src/where.c index 1be1b20a49..3b64b411d9 100644 --- a/src/where.c +++ b/src/where.c @@ -403,7 +403,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]); } pTerm = &pWC->a[idx = pWC->nTerm++]; - pTerm->pExpr = p; + pTerm->pExpr = sqlite3ExprSkipCollate(p); pTerm->wtFlags = wtFlags; pTerm->pWC = pWC; pTerm->iParent = -1; @@ -1188,7 +1188,8 @@ static void exprAnalyze( } pTerm = &pWC->a[idxTerm]; pMaskSet = pWC->pMaskSet; - pExpr = sqlite3ExprSkipCollate(pTerm->pExpr); + pExpr = pTerm->pExpr; + assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE ); prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft); op = pExpr->op; if( op==TK_IN ){ diff --git a/test/tkt-a7b7803e.test b/test/tkt-a7b7803e.test new file mode 100644 index 0000000000..0f1ce45f37 --- /dev/null +++ b/test/tkt-a7b7803e.test @@ -0,0 +1,29 @@ +# 2012 December 19 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. Specifically, +# it tests that ticket [a7b7803e8d1e8699cd8a460a38133b98892d2e17] has +# been fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/malloc_common.tcl + +do_test tkt-a7b7803e.1 { + db eval { + CREATE TABLE t1(a); + SELECT (t1.a==0) AS x + FROM t1 + WHERE a=0 OR x; + } +} {} +finish_test From 11c6387d38f1ca4bacc523d7937ee085a7eed12c Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 19 Dec 2012 16:25:55 +0000 Subject: [PATCH 08/15] Additional test cases involving the use of a result column specified by its "AS" name as a logical term of the WHERE clause. Ticket [a7b7803e8d1e8699cd]. FossilOrigin-Name: d7cc364e87870746c231b97b648658bf343945fa --- manifest | 15 ++++++-------- manifest.uuid | 2 +- test/tkt-a7b7803e.test | 46 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 5d64c0aa4e..5881ad31e0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Proposed\sfix\sfor\sticket\s[a7b7803e8d1e8699cd8a]. -D 2012-12-19T15:53:51.227 +C Additional\stest\scases\sinvolving\sthe\suse\sof\sa\sresult\scolumn\sspecified\sby\sits\n"AS"\sname\sas\sa\slogical\sterm\sof\sthe\sWHERE\sclause.\nTicket\s[a7b7803e8d1e8699cd]. +D 2012-12-19T16:25:55.128 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -784,7 +784,7 @@ F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5 F test/tkt-94c04eaadb.test fa9c71192f7e2ea2d51bf078bc34e8da6088bf71 F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67 -F test/tkt-a7b7803e.test 0baef2d1e7566e20a35bf6821d76a307ed78e3aa +F test/tkt-a7b7803e.test 86a86ba1912ac82b04137483a79e3d16b0d90583 F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78 F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3 @@ -1027,10 +1027,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 7fecced466d86a66b0b751c5b5608141e134fe2d -R 8b56234d9cc98530620effaeba3830d6 -T *branch * tkt-a7b7803e -T *sym-tkt-a7b7803e * -T -sym-trunk * +P 3d0e00c731c39af91bad74c6d74853375bac356e +R 0f7d6d3c7840770b9dfff3dff8a63da9 U drh -Z 4056ade20a6836865fb5397034c830dc +Z 77f4cb693913cee8634bb5c81a279a20 diff --git a/manifest.uuid b/manifest.uuid index ae348b1652..b12ef36898 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3d0e00c731c39af91bad74c6d74853375bac356e \ No newline at end of file +d7cc364e87870746c231b97b648658bf343945fa \ No newline at end of file diff --git a/test/tkt-a7b7803e.test b/test/tkt-a7b7803e.test index 0f1ce45f37..493d81db9e 100644 --- a/test/tkt-a7b7803e.test +++ b/test/tkt-a7b7803e.test @@ -20,10 +20,50 @@ source $testdir/malloc_common.tcl do_test tkt-a7b7803e.1 { db eval { - CREATE TABLE t1(a); - SELECT (t1.a==0) AS x + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(0,'first'),(99,'fuzzy'); + SELECT (t1.a==0) AS x, b FROM t1 WHERE a=0 OR x; } -} {} +} {1 first} +do_test tkt-a7b7803e.2 { + db eval { + SELECT a, (t1.b='fuzzy') AS x + FROM t1 + WHERE x + } +} {99 1} +do_test tkt-a7b7803e.3 { + db eval { + SELECT (a=99) AS x, (t1.b='fuzzy') AS y, * + FROM t1 + WHERE x AND y + } +} {1 1 99 fuzzy} +do_test tkt-a7b7803e.4 { + db eval { + SELECT (a=99) AS x, (t1.b='first') AS y, * + FROM t1 + WHERE x OR y + ORDER BY a + } +} {0 1 0 first 1 0 99 fuzzy} +do_test tkt-a7b7803e.5 { + db eval { + SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b + FROM t1 M, t1 N + WHERE x OR y + ORDER BY M.a, N.a + } +} {0 first 1 first 1 fuzzy 1 first 1 fuzzy 0 fuzzy} +do_test tkt-a7b7803e.6 { + db eval { + SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b + FROM t1 M, t1 N + WHERE x AND y + ORDER BY M.a, N.a + } +} {1 fuzzy 1 first} + finish_test From 0e8578fc9e3f5421f3262adbea6c3a17ed4d4903 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 19 Dec 2012 16:32:33 +0000 Subject: [PATCH 09/15] A couple more test cases. FossilOrigin-Name: 0ae142178d5ff874a4df58d699adad3a41e3d8d2 --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/tkt-a7b7803e.test | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 5881ad31e0..7afb73b883 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Additional\stest\scases\sinvolving\sthe\suse\sof\sa\sresult\scolumn\sspecified\sby\sits\n"AS"\sname\sas\sa\slogical\sterm\sof\sthe\sWHERE\sclause.\nTicket\s[a7b7803e8d1e8699cd]. -D 2012-12-19T16:25:55.128 +C A\scouple\smore\stest\scases. +D 2012-12-19T16:32:33.604 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -784,7 +784,7 @@ F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5 F test/tkt-94c04eaadb.test fa9c71192f7e2ea2d51bf078bc34e8da6088bf71 F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67 -F test/tkt-a7b7803e.test 86a86ba1912ac82b04137483a79e3d16b0d90583 +F test/tkt-a7b7803e.test 159ef554234fa1f9fb318c751b284bd1cf858da4 F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78 F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3 @@ -1027,7 +1027,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 3d0e00c731c39af91bad74c6d74853375bac356e -R 0f7d6d3c7840770b9dfff3dff8a63da9 +P d7cc364e87870746c231b97b648658bf343945fa +R 50795a0c6ec500ee7d72d138c83ca2b4 U drh -Z 77f4cb693913cee8634bb5c81a279a20 +Z d303ec7669d7ac26fa13d48f326287ae diff --git a/manifest.uuid b/manifest.uuid index b12ef36898..ebb04e9879 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d7cc364e87870746c231b97b648658bf343945fa \ No newline at end of file +0ae142178d5ff874a4df58d699adad3a41e3d8d2 \ No newline at end of file diff --git a/test/tkt-a7b7803e.test b/test/tkt-a7b7803e.test index 493d81db9e..b617cf6df2 100644 --- a/test/tkt-a7b7803e.test +++ b/test/tkt-a7b7803e.test @@ -65,5 +65,20 @@ do_test tkt-a7b7803e.6 { ORDER BY M.a, N.a } } {1 fuzzy 1 first} +do_test tkt-a7b7803e.7 { + db eval { + SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b + FROM t1 M JOIN t1 N ON x AND y + ORDER BY M.a, N.a + } +} {1 fuzzy 1 first} +do_test tkt-a7b7803e.8 { + db eval { + SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b + FROM t1 M JOIN t1 N ON x + ORDER BY M.a, N.a + } +} {1 fuzzy 1 first 1 fuzzy 0 fuzzy} + finish_test From 83e0dba3118798936d45bbf4d00064ac0cefe2aa Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 20 Dec 2012 00:32:49 +0000 Subject: [PATCH 10/15] Remove an unreachable branch operation from the foreign_key_check pragma. FossilOrigin-Name: b5a8f3160b5dc1397312add3c5410e7e0a12b75e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/pragma.c | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index c6b7e5c563..3c7204c5d2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Added\stest\scases\sfor\sPRAGMA\sforeign_key_check.\s\sFixed\sa\sbug\sthat\sappears\nwhen\sthe\scolumn\sorder\sof\sthe\schild\sand\sparent\sdiffer. -D 2012-12-17T22:32:14.381 +C Remove\san\sunreachable\sbranch\soperation\sfrom\sthe\sforeign_key_check\spragma. +D 2012-12-20T00:32:49.517 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -168,7 +168,7 @@ F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c 312d9ea0d9b98583514f6f8e92cb7e8f526ff4c6 +F src/pragma.c 1ff115f620d9496fe5018306b644ab9ef7e26a3c F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -1026,7 +1026,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 97f7f7377259ecf562019f62ebed0897cfae236e -R e825c339f2d84a956eae42ef385eea53 +P 25411f83f99803e39feee368703fd20b4ade8eeb +R c79c49ee3d52fd45a5706b357c7b932d U drh -Z 0751ac95be4abd3e28a98e71458d0c00 +Z 478f8348ae468b598dcecb8fdb7894c8 diff --git a/manifest.uuid b/manifest.uuid index 57652e0971..ed34ad3e01 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25411f83f99803e39feee368703fd20b4ade8eeb \ No newline at end of file +b5a8f3160b5dc1397312add3c5410e7e0a12b75e \ No newline at end of file diff --git a/src/pragma.c b/src/pragma.c index 9a20607039..0f92769a9f 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1190,7 +1190,8 @@ void sqlite3Pragma( addrOk = sqlite3VdbeMakeLabel(v); if( pIdx==0 ){ int iKey = pFK->aCol[0].iFrom; - if( iKey>=0 && iKey!=pTab->iPKey ){ + assert( iKey>=0 && iKeynCol ); + if( iKey!=pTab->iPKey ){ sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow); sqlite3ColumnDefault(v, pTab, iKey, regRow); sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); From 5cc3bea44ad2dc8008330934d7f8e65d4059b1fb Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 21 Dec 2012 16:15:35 +0000 Subject: [PATCH 11/15] Ensure the database size field in the db header of a backup database is set correctly. Fix for [0cfd98ee201]. FossilOrigin-Name: ff6857b6ed6a46671006b75157d8cf853a816ef9 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/backup.c | 14 +++++++++++--- test/filefmt.test | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 060a723fae..56b649c34b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sPRAGMA\sforeign_key_check.\s\sName\sthe\schild\sand\sparent\stables\sas\spart\sof\na\s"foreign\skey\smismatch"\serror. -D 2012-12-20T01:15:20.959 +C Ensure\sthe\sdatabase\ssize\sfield\sin\sthe\sdb\sheader\sof\sa\sbackup\sdatabase\sis\sset\scorrectly.\sFix\sfor\s[0cfd98ee201]. +D 2012-12-21T16:15:35.911 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -118,7 +118,7 @@ F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168 F src/analyze.c 7553068d21e32a57fc33ab6b2393fc8c1ba41410 F src/attach.c ea5247f240e2c08afd608e9beb380814b86655e1 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 -F src/backup.c cab40f2c1fe79d6eb93d3b4086c78c41ad2fa5d0 +F src/backup.c 32e35a3a4ea55b45c0e5f74eeb793aec71491517 F src/bitvec.c 26675fe8e431dc555e6f2d0e11e651d172234aa1 F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btree.c 7a80e4a67f32a2494c383a28a495bf3bd71cc230 @@ -407,7 +407,7 @@ F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30 F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e F test/filectrl.test 14fa712e42c4cb791e09dfd58a6a03efb47ef13a -F test/filefmt.test ffa17b5aebc3eb4b1e3be1ccb5ee906ffbd97f6e +F test/filefmt.test dbee33e57818249cdffbbb7b13464635217beff1 F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da F test/fkey2.test 5aa44e7153928a1f002803f94aaab4c76a7ceac2 F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e @@ -1028,7 +1028,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P b3c9e8f81275fe07272c01f66b16c16417f034af b5a8f3160b5dc1397312add3c5410e7e0a12b75e -R c25a7b70b2b47eedc79e89bea857f6d5 -U drh -Z 54888f90388c5aea5593479ed7ab2502 +P e408dc9080594dc464b8763dece6b365772c6105 +R 029b6fc924e25c670b20c53f3ff270ca +U dan +Z feee379cb7d3362de52284de8aba8c99 diff --git a/manifest.uuid b/manifest.uuid index 8c078e7bb1..75e67ccb96 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e408dc9080594dc464b8763dece6b365772c6105 \ No newline at end of file +ff6857b6ed6a46671006b75157d8cf853a816ef9 \ No newline at end of file diff --git a/src/backup.c b/src/backup.c index b234716d61..d3614b88c3 100644 --- a/src/backup.c +++ b/src/backup.c @@ -212,7 +212,12 @@ static int isFatalError(int rc){ ** page iSrcPg from the source database. Copy this data into the ** destination database. */ -static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ +static int backupOnePage( + sqlite3_backup *p, /* Backup handle */ + Pgno iSrcPg, /* Source database page to backup */ + const u8 *zSrcData, /* Source database page data */ + int bUpdate /* True for an update, false otherwise */ +){ Pager * const pDestPager = sqlite3BtreePager(p->pDest); const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc); int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); @@ -285,6 +290,9 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ */ memcpy(zOut, zIn, nCopy); ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0; + if( iOff==0 && bUpdate==0 ){ + sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc)); + } } sqlite3PagerUnref(pDestPg); } @@ -391,7 +399,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ DbPage *pSrcPg; /* Source page object */ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); if( rc==SQLITE_OK ){ - rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg)); + rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0); sqlite3PagerUnref(pSrcPg); } } @@ -639,7 +647,7 @@ void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){ int rc; assert( p->pDestDb ); sqlite3_mutex_enter(p->pDestDb->mutex); - rc = backupOnePage(p, iPage, aData); + rc = backupOnePage(p, iPage, aData, 1); sqlite3_mutex_leave(p->pDestDb->mutex); assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); if( rc!=SQLITE_OK ){ diff --git a/test/filefmt.test b/test/filefmt.test index 1165cd62d5..bc1af18e8b 100644 --- a/test/filefmt.test +++ b/test/filefmt.test @@ -213,4 +213,39 @@ do_execsql_test filefmt-3.3 { PRAGMA integrity_check; } {ok} +reset_db +do_execsql_test filefmt-4.1 { + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(x, y); + CREATE TABLE t2(x, y); + + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; +} + +do_test filefmt-4.2 { + sql36231 { INSERT INTO t2 SELECT * FROM t1 } +} {} + +do_test filefmt-4.3 { + forcedelete bak.db + db backup bak.db +} {} + +do_test filefmt-4.4 { + sqlite3 db2 bak.db + db2 eval { PRAGMA integrity_check } +} {ok} +db2 close + finish_test + From 14172743a22fce42e77e4ce69dee89511709ebba Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 31 Dec 2012 19:18:38 +0000 Subject: [PATCH 12/15] Add the test_regexp.c module containing a cross-platform implementation of the REGEXP operator. FossilOrigin-Name: 46c8c01b751c1ea7fc02cc35e3b5bb99dbe46c4b --- Makefile.in | 1 + Makefile.msc | 1 + main.mk | 1 + manifest | 26 +- manifest.uuid | 2 +- src/shell.c | 6 + src/tclsqlite.c | 2 + src/test_regexp.c | 721 ++++++++++++++++++++++++++++++++++++++++++++ test/regexp1.test | 84 ++++++ tool/build-shell.sh | 2 + 10 files changed, 833 insertions(+), 13 deletions(-) create mode 100644 src/test_regexp.c create mode 100644 test/regexp1.test diff --git a/Makefile.in b/Makefile.in index 8d9d3c9694..98a3edb091 100644 --- a/Makefile.in +++ b/Makefile.in @@ -370,6 +370,7 @@ TESTSRC = \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_quota.c \ + $(TOP)/src/test_regexp.c \ $(TOP)/src/test_rtree.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ diff --git a/Makefile.msc b/Makefile.msc index d12e5d0ec1..631e9edcbf 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -691,6 +691,7 @@ TESTSRC = \ $(TOP)\src\test_osinst.c \ $(TOP)\src\test_pcache.c \ $(TOP)\src\test_quota.c \ + $(TOP)\src\test_regexp.c \ $(TOP)\src\test_rtree.c \ $(TOP)\src\test_schema.c \ $(TOP)\src\test_server.c \ diff --git a/main.mk b/main.mk index cbae400a50..fdabcb4636 100644 --- a/main.mk +++ b/main.mk @@ -253,6 +253,7 @@ TESTSRC = \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_quota.c \ + $(TOP)/src/test_regexp.c \ $(TOP)/src/test_rtree.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ diff --git a/manifest b/manifest index 56b649c34b..69c3503ae5 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Ensure\sthe\sdatabase\ssize\sfield\sin\sthe\sdb\sheader\sof\sa\sbackup\sdatabase\sis\sset\scorrectly.\sFix\sfor\s[0cfd98ee201]. -D 2012-12-21T16:15:35.911 +C Add\sthe\stest_regexp.c\smodule\scontaining\sa\scross-platform\simplementation\s\nof\sthe\sREGEXP\soperator. +D 2012-12-31T19:18:38.256 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f -F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 +F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 -F Makefile.msc 5a3b6f34d263b01f8b798c291fac1529fd650308 +F Makefile.msc 2b8371775ea8df029d1acf0c3d4c3782d3bd5711 F Makefile.vxworks b18ad88e9a8c6a001f5cf4a389116a4f1a7ab45f F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 F VERSION 6d4f66eaebabc42ef8c2a4d2d0caf4ce7ee81137 @@ -103,7 +103,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 -F main.mk a0d170ae1a8a8683688e281194e09d47a68eaa3f +F main.mk 718265bbf49a846c6898b4da09593eef4068fa39 F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac @@ -175,7 +175,7 @@ F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 52331299f4095397d6d00715b70cd153baa11931 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c 5eab6941c0ac97355817f846b77cd20bfdf5a82e -F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019 +F src/shell.c e6525781d27a84f1b74586831b6ad8472a8c8dc6 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 @@ -183,7 +183,7 @@ F src/sqliteInt.h 2e5d50f26abf7cbc6162117735379d412f4091da F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e -F src/tclsqlite.c 515abd8e33e82aa330eeb54675185a7e1e5b6778 +F src/tclsqlite.c 5203bb7b71a302bea8896f176cd178e38d7803a4 F src/test1.c f62769c989146149590662ab02de4a813813a9c5 F src/test2.c 4178056dd1e7d70f954ad8a1e3edb71a2a784daf F src/test3.c 3c3c2407fa6ec7a19e24ae23f7cb439d0275a60d @@ -217,6 +217,7 @@ F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 F src/test_quota.c 0e0e2e3bf6766b101ecccd8c042b66e44e9be8f5 F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb +F src/test_regexp.c c24ae2a0de64eb9dfa1dd77b77448b1d794cd395 F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f @@ -666,6 +667,7 @@ F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6 F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459 F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d +F test/regexp1.test 38da302b75504dd8b960c8f06968ddf8039777ad F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce @@ -985,7 +987,7 @@ F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688 F test/zerodamage.test e7f77fded01dfcdf92ac2c5400f1e35d7a21463c F tool/build-all-msvc.bat 74fb6e5cca66ebdb6c9bbafb2f8b802f08146d38 x -F tool/build-shell.sh b64a481901fc9ffe5ca8812a2a9255b6cfb77381 +F tool/build-shell.sh 562df23cfdd25822b909b382afd5f99d968437fe F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2 @@ -1028,7 +1030,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P e408dc9080594dc464b8763dece6b365772c6105 -R 029b6fc924e25c670b20c53f3ff270ca -U dan -Z feee379cb7d3362de52284de8aba8c99 +P ff6857b6ed6a46671006b75157d8cf853a816ef9 +R 2e582c43743655b03da399d168172dad +U drh +Z c7b82d9d758f8f0ccceb37ea12601a6d diff --git a/manifest.uuid b/manifest.uuid index 75e67ccb96..74ce482207 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ff6857b6ed6a46671006b75157d8cf853a816ef9 \ No newline at end of file +46c8c01b751c1ea7fc02cc35e3b5bb99dbe46c4b \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index 7dd741b2d7..26de51c4f6 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1479,6 +1479,12 @@ static void open_db(struct callback_data *p){ } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); +#endif +#ifdef SQLITE_ENABLE_REGEXP + { + extern sqlite3_add_regexp_func(sqlite3*); + sqlite3_add_regexp_func(db); + } #endif } } diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 57dab85d4b..267f759701 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -3684,6 +3684,7 @@ static void init_all(Tcl_Interp *interp){ extern int SqlitetestSyscall_Init(Tcl_Interp*); extern int Sqlitetestfuzzer_Init(Tcl_Interp*); extern int Sqlitetestwholenumber_Init(Tcl_Interp*); + extern int Sqlitetestregexp_Init(Tcl_Interp*); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) extern int Sqlitetestfts3_Init(Tcl_Interp *interp); @@ -3727,6 +3728,7 @@ static void init_all(Tcl_Interp *interp){ SqlitetestSyscall_Init(interp); Sqlitetestfuzzer_Init(interp); Sqlitetestwholenumber_Init(interp); + Sqlitetestregexp_Init(interp); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); diff --git a/src/test_regexp.c b/src/test_regexp.c new file mode 100644 index 0000000000..353955cee5 --- /dev/null +++ b/src/test_regexp.c @@ -0,0 +1,721 @@ +/* +** 2012-11-13 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** The code in this file implements a compact but reasonably +** efficient regular-expression matcher for posix extended regular +** expressions against UTF8 text. The following syntax is supported: +** +** X* zero or more occurrences of X +** X+ one or more occurrences of X +** X? zero or one occurrences of X +** X{p,q} between p and q occurrences of X +** (X) match X +** X|Y X or Y +** ^X X occurring at the beginning of the string +** X$ X occurring at the end of the string +** . Match any single character +** \c Character c where c is one of \{}()[]|*+?. +** \c C-language escapes for c in afnrtv. ex: \t or \n +** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX +** \xXXX Where XXX is any number of hex digits, unicode value XXX +** [abc] Any single character from the set abc +** [^abc] Any single character not in the set abc +** [a-z] Any single character in the range a-z +** [^a-z] Any single character not in the range a-z +** \b Word boundary +** \w Word character. [A-Za-z0-9_] +** \W Non-word character +** \d Digit +** \D Non-digit +** \s Whitespace character +** \S Non-whitespace character +** +** A nondeterministic finite automaton (NFA) is used for matching, so the +** performance is bounded by O(N*M) where N is the size of the regular +** expression and M is the size of the input string. The matcher never +** exhibits exponential behavior. Note that the X{p,q} operator expands +** to p copies of X following by q-p copies of X? and that the size of the +** regular expression in the O(N*M) performance bound is computed after +** this expansion. +*/ +#include +#include +#include "sqlite3.h" + +/* The end-of-input character */ +#define RE_EOF 0 /* End of input */ + +/* The NFA is implemented as sequence of opcodes taken from the following +** set. Each opcode has a single integer argument. +*/ +#define RE_OP_MATCH 1 /* Match the one character in the argument */ +#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */ +#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */ +#define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */ +#define RE_OP_GOTO 5 /* Jump to opcode at iArg */ +#define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */ +#define RE_OP_CC_INC 7 /* Beginning of a [...] character class */ +#define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */ +#define RE_OP_CC_VALUE 9 /* Single value in a character class */ +#define RE_OP_CC_RANGE 10 /* Range of values in a character class */ +#define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */ +#define RE_OP_NOTWORD 12 /* Not a perl word character */ +#define RE_OP_DIGIT 13 /* digit: [0-9] */ +#define RE_OP_NOTDIGIT 14 /* Not a digit */ +#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */ +#define RE_OP_NOTSPACE 16 /* Not a digit */ +#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */ + +/* Each opcode is a "state" in the NFA */ +typedef unsigned short ReStateNumber; + +/* Because this is an NFA and not a DFA, multiple states can be active at +** once. An instance of the following object records all active states in +** the NFA. The implementation is optimized for the common case where the +** number of actives states is small. +*/ +typedef struct ReStateSet { + unsigned nState; /* Number of current states */ + ReStateNumber *aState; /* Current states */ +} ReStateSet; + +/* A compiled NFA (or an NFA that is in the process of being compiled) is +** an instance of the following object. +*/ +typedef struct ReCompiled { + const unsigned char *zIn; /* Regular expression text */ + const char *zErr; /* Error message to return */ + char *aOp; /* Operators for the virtual machine */ + int *aArg; /* Arguments to each operator */ + char zInit[12]; /* Initial text to match */ + int nInit; /* Number of characters in zInit */ + unsigned nState; /* Number of entries in aOp[] and aArg[] */ + unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */ +} ReCompiled; + +/* Add a state to the given state set if it is not already there */ +static void re_add_state(ReStateSet *pSet, int newState){ + unsigned i; + for(i=0; inState; i++) if( pSet->aState[i]==newState ) return; + pSet->aState[pSet->nState++] = newState; +} + +/* Extract the next unicode character from *pzIn and return it. Advance +** *pzIn to the first byte past the end of the character returned. To +** be clear: this routine converts utf8 to unicode. This routine is +** optimized for the common case where the next character is a single byte. +*/ +static unsigned re_next_char(const unsigned char **pzIn){ + unsigned c = **pzIn; + if( c>0 ) (*pzIn)++; + if( c>0x80 ){ + if( (c&0xe0)==0xc0 && ((*pzIn)[0]&0xc0)==0x80 ){ + c = (c&0x1f)<<6 | ((*pzIn)[0]&0x3f); + (*pzIn)++; + if( c<0x80 ) c = 0xfffd; + }else if( (c&0xf0)==0xe0 && ((*pzIn)[0]&0xc0)==0x80 + && ((*pzIn)[1]&0xc0)==0x80 ){ + c = (c&0x0f)<<12 | (((*pzIn)[0]&0x3f)<<6) | ((*pzIn)[1]&0x3f); + *pzIn += 2; + if( c<0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; + }else if( (c&0xf8)==0xf0 && ((*pzIn)[0]&0xc0)==0x80 + && ((*pzIn)[1]&0xc0)==0x80 && ((*pzIn)[2]&0xc0)==0x80 ){ + c = (c&0x07)<<18 | (((*pzIn)[0]&0x3f)<<12) | (((*pzIn)[1]&0x3f)<<6) + | ((*pzIn)[2]&0x3f); + *pzIn += 3; + if( c<0xffff ) c = 0xfffd; + }else{ + c = 0xfffd; + } + } + return c; +} + +/* Return true if c is a perl "word" character: [A-Za-z0-9_] */ +static int re_word_char(int c){ + return (c>='0' && c<='9') || (c>='a' && c<='z') + || (c>='A' && c<='Z') || c=='_'; +} + +/* Return true if c is a "digit" character: [0-9] */ +static int re_digit_char(int c){ + return (c>='0' && c<='9'); +} + +/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ +static int re_space_char(int c){ + return c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f'; +} + +/* Run a compiled regular expression on the zero-terminated input +** string zIn[]. Return true on a match and false if there is no match. +*/ +static int re_exec(ReCompiled *pRe, const unsigned char *zIn){ + ReStateSet aStateSet[2], *pThis, *pNext; + ReStateNumber aSpace[100]; + ReStateNumber *pToFree; + unsigned int i = 0; + unsigned int iSwap = 0; + int c = RE_EOF+1; + int cPrev = 0; + int rc = 0; + + if( pRe->nInit ){ + unsigned char x = pRe->zInit[0]; + while( zIn[0] && (zIn[0]!=x || memcmp(zIn, pRe->zInit, pRe->nInit)!=0) ){ + zIn++; + } + if( zIn[0]==0 ) return 0; + } + if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ + pToFree = 0; + aStateSet[0].aState = aSpace; + }else{ + pToFree = malloc( sizeof(ReStateNumber)*2*pRe->nState ); + if( pToFree==0 ) return -1; + aStateSet[0].aState = pToFree; + } + aStateSet[1].aState = &aStateSet[0].aState[pRe->nState]; + pNext = &aStateSet[1]; + pNext->nState = 0; + re_add_state(pNext, 0); + while( c!=RE_EOF && pNext->nState>0 ){ + cPrev = c; + c = re_next_char(&zIn); + pThis = pNext; + pNext = &aStateSet[iSwap]; + iSwap = 1 - iSwap; + pNext->nState = 0; + for(i=0; inState; i++){ + int x = pThis->aState[i]; + switch( pRe->aOp[x] ){ + case RE_OP_MATCH: { + if( pRe->aArg[x]==c ) re_add_state(pNext, x+1); + break; + } + case RE_OP_ANY: { + re_add_state(pNext, x+1); + break; + } + case RE_OP_WORD: { + if( re_word_char(c) ) re_add_state(pNext, x+1); + break; + } + case RE_OP_NOTWORD: { + if( !re_word_char(c) ) re_add_state(pNext, x+1); + break; + } + case RE_OP_DIGIT: { + if( re_digit_char(c) ) re_add_state(pNext, x+1); + break; + } + case RE_OP_NOTDIGIT: { + if( !re_digit_char(c) ) re_add_state(pNext, x+1); + break; + } + case RE_OP_SPACE: { + if( re_space_char(c) ) re_add_state(pNext, x+1); + break; + } + case RE_OP_NOTSPACE: { + if( !re_space_char(c) ) re_add_state(pNext, x+1); + break; + } + case RE_OP_BOUNDARY: { + if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1); + break; + } + case RE_OP_ANYSTAR: { + re_add_state(pNext, x); + re_add_state(pThis, x+1); + break; + } + case RE_OP_FORK: { + re_add_state(pThis, x+pRe->aArg[x]); + re_add_state(pThis, x+1); + break; + } + case RE_OP_GOTO: { + re_add_state(pThis, x+pRe->aArg[x]); + break; + } + case RE_OP_ACCEPT: { + rc = 1; + goto re_exec_end; + } + case RE_OP_CC_INC: + case RE_OP_CC_EXC: { + int j = 1; + int n = pRe->aArg[x]; + int hit = 0; + for(j=1; j>0 && jaOp[x+j]==RE_OP_CC_VALUE ){ + if( pRe->aArg[x+j]==c ){ + hit = 1; + j = -1; + } + }else{ + if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){ + hit = 1; + j = -1; + }else{ + j++; + } + } + } + if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; + if( hit ) re_add_state(pNext, x+n); + break; + } + } + } + } + for(i=0; inState; i++){ + if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } + } +re_exec_end: + free(pToFree); + return rc; +} + +/* Resize the opcode and argument arrays for an RE under construction. +*/ +static int re_resize(ReCompiled *p, int N){ + char *aOp; + int *aArg; + aOp = realloc(p->aOp, N*sizeof(p->aOp[0])); + if( aOp==0 ) return 1; + p->aOp = aOp; + aArg = realloc(p->aArg, N*sizeof(p->aArg[0])); + if( aArg==0 ) return 1; + p->aArg = aArg; + p->nAlloc = N; + return 0; +} + +/* Insert a new opcode and argument into an RE under construction. The +** insertion point is just prior to existing opcode iBefore. +*/ +static int re_insert(ReCompiled *p, int iBefore, int op, int arg){ + int i; + if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0; + for(i=p->nState; i>iBefore; i--){ + p->aOp[i] = p->aOp[i-1]; + p->aArg[i] = p->aArg[i-1]; + } + p->nState++; + p->aOp[iBefore] = op; + p->aArg[iBefore] = arg; + return iBefore; +} + +/* Append a new opcode and argument to the end of the RE under construction. +*/ +static int re_append(ReCompiled *p, int op, int arg){ + return re_insert(p, p->nState, op, arg); +} + +/* Make a copy of N opcodes starting at iStart onto the end of the RE +** under construction. +*/ +static void re_copy(ReCompiled *p, int iStart, int N){ + if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return; + memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0])); + memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0])); + p->nState += N; +} + +/* Return true if c is a hexadecimal digit character: [0-9a-fA-F] +** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If +** c is not a hex digit *pV is unchanged. +*/ +static int re_hex(int c, int *pV){ + if( c>='0' && c<='9' ){ + c -= '0'; + }else if( c>='a' && c<='f' ){ + c -= 'a' + 10; + }else if( c>='A' && c<='F' ){ + c -= 'A' + 10; + }else{ + return 0; + } + *pV = (*pV)*16 + c; + return 1; +} + +/* A backslash character has been seen, read the next character and +** return its intepretation. +*/ +static unsigned re_esc_char(ReCompiled *p){ + static const char zEsc[] = "afnrtv\\()*.+?[$^{|"; + static const char zTrans[] = "\a\f\n\r\t\v"; + int i, v = 0; + char c = p->zIn[0]; + if( c=='u' ){ + v = 0; + if( re_hex(p->zIn[1],&v) + && re_hex(p->zIn[2],&v) + && re_hex(p->zIn[3],&v) + && re_hex(p->zIn[4],&v) + ){ + p->zIn += 5; + return v; + } + } + if( c=='x' ){ + v = 0; + for(i=1; re_hex(p->zIn[i], &v); i++){} + if( i>1 ){ + p->zIn += i; + return v; + } + } + for(i=0; zEsc[i] && zEsc[i]!=c; i++){} + if( zEsc[i] ){ + if( c<6 ) c = zTrans[i]; + p->zIn++; + }else{ + p->zErr = "unknown \\ escape"; + } + return c; +} + +/* Forward declaration */ +static const char *re_subcompile_string(ReCompiled*); + +/* Compile RE text into a sequence of opcodes. Continue up to the +** first unmatched ")" character, then return. If an error is found, +** return a pointer to the error message string. +*/ +static const char *re_subcompile_re(ReCompiled *p){ + const char *zErr; + int iStart, iEnd, iGoto; + iStart = p->nState; + zErr = re_subcompile_string(p); + if( zErr ) return zErr; + while( p->zIn[0]=='|' ){ + iEnd = p->nState; + re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart); + iGoto = re_append(p, RE_OP_GOTO, 0); + p->zIn++; + zErr = re_subcompile_string(p); + if( zErr ) return zErr; + p->aArg[iGoto] = p->nState - iGoto; + } + return 0; +} + +/* Compile an element of regular expression text (anything that can be +** an operand to the "|" operator). Return NULL on success or a pointer +** to the error message if there is a problem. +*/ +static const char *re_subcompile_string(ReCompiled *p){ + int iPrev = -1; + int iStart; + unsigned c; + const char *zErr; + while( (c = re_next_char(&p->zIn))!=0 ){ + iStart = p->nState; + switch( c ){ + case '|': + case '$': + case ')': { + p->zIn--; + return 0; + } + case '(': { + zErr = re_subcompile_re(p); + if( zErr ) return zErr; + if( p->zIn[0]!=')' ) return "unmatched '('"; + p->zIn++; + break; + } + case '.': { + if( p->zIn[0]=='*' ){ + re_append(p, RE_OP_ANYSTAR, 0); + p->zIn++; + }else{ + re_append(p, RE_OP_ANY, 0); + } + break; + } + case '*': { + if( iPrev<0 ) return "'*' without operand"; + re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1); + re_append(p, RE_OP_FORK, iPrev - p->nState + 1); + break; + } + case '+': { + if( iPrev<0 ) return "'+' without operand"; + re_append(p, RE_OP_FORK, iPrev - p->nState); + break; + } + case '?': { + if( iPrev<0 ) return "'?' without operand"; + re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1); + break; + } + case '{': { + int m = 0, n = 0; + int sz, j; + if( iPrev<0 ) return "'{m,n}' without operand"; + while( (c=p->zIn[0])>='0' && c<='9' ){ m = m*10 + c - '0'; p->zIn++; } + n = m; + if( c==',' ){ + p->zIn++; + n = 0; + while( (c=p->zIn[0])>='0' && c<='9' ){ n = n*10 + c - '0'; p->zIn++; } + } + if( c!='}' ) return "unmatched '{'"; + if( n>0 && nzIn++; + sz = p->nState - iPrev; + if( m==0 ){ + if( n==0 ) return "both m and n are zero in '{m,n}'"; + re_insert(p, iPrev, RE_OP_FORK, sz+1); + n--; + }else{ + for(j=1; j0 ){ + re_append(p, RE_OP_FORK, -sz); + } + break; + } + case '[': { + int iFirst = p->nState; + if( p->zIn[0]=='^' ){ + re_append(p, RE_OP_CC_EXC, 0); + p->zIn++; + }else{ + re_append(p, RE_OP_CC_INC, 0); + } + while( (c = re_next_char(&p->zIn))!=0 ){ + if( c=='[' && p->zIn[0]==':' ){ + return "POSIX character classes not supported"; + } + if( c=='\\' ) c = re_esc_char(p); + if( p->zIn[0]=='-' && p->zIn[1] ){ + re_append(p, RE_OP_CC_RANGE, c); + p->zIn++; + c = re_next_char(&p->zIn); + if( c=='\\' ) c = re_esc_char(p); + re_append(p, RE_OP_CC_RANGE, c); + }else{ + re_append(p, RE_OP_CC_VALUE, c); + } + if( p->zIn[0]==']' ){ p->zIn++; break; } + } + if( c==0 ) return "unclosed '['"; + p->aArg[iFirst] = p->nState - iFirst; + break; + } + case '\\': { + int specialOp = 0; + switch( p->zIn[0] ){ + case 'b': specialOp = RE_OP_BOUNDARY; break; + case 'd': specialOp = RE_OP_DIGIT; break; + case 'D': specialOp = RE_OP_NOTDIGIT; break; + case 's': specialOp = RE_OP_SPACE; break; + case 'S': specialOp = RE_OP_NOTSPACE; break; + case 'w': specialOp = RE_OP_WORD; break; + case 'W': specialOp = RE_OP_NOTWORD; break; + } + if( specialOp ){ + p->zIn++; + re_append(p, specialOp, 0); + }else{ + c = re_esc_char(p); + re_append(p, RE_OP_MATCH, c); + } + break; + } + default: { + re_append(p, RE_OP_MATCH, c); + break; + } + } + iPrev = iStart; + } + return 0; +} + +/* Free and reclaim all the memory used by a previously compiled +** regular expression. Applications should invoke this routine once +** for every call to re_compile() to avoid memory leaks. +*/ +static void re_free(ReCompiled *pRe){ + if( pRe ){ + free(pRe->aOp); + free(pRe->aArg); + } +} + +/* +** Compile a textual regular expression in zIn[] into a compiled regular +** expression suitable for us by re_exec() and return a pointer to the +** compiled regular expression in *ppRe. Return NULL on success or an +** error message if something goes wrong. +*/ +static const char *re_compile(ReCompiled **ppRe, const char *zIn){ + ReCompiled *pRe; + const char *zErr; + int i, j; + + *ppRe = 0; + pRe = malloc( sizeof(*pRe) ); + if( pRe==0 ){ + return "out of memory"; + } + memset(pRe, 0, sizeof(*pRe)); + if( re_resize(pRe, 30) ){ + re_free(pRe); + return "out of memory"; + } + if( zIn[0]=='^' ){ + zIn++; + }else{ + re_append(pRe, RE_OP_ANYSTAR, 0); + } + pRe->zIn = (unsigned char*)zIn; + zErr = re_subcompile_re(pRe); + if( zErr ){ + re_free(pRe); + return zErr; + } + if( pRe->zIn[0]=='$' && pRe->zIn[1]==0 ){ + re_append(pRe, RE_OP_MATCH, RE_EOF); + re_append(pRe, RE_OP_ACCEPT, 0); + *ppRe = pRe; + }else if( pRe->zIn[0]==0 ){ + re_append(pRe, RE_OP_ACCEPT, 0); + *ppRe = pRe; + }else{ + re_free(pRe); + return "unrecognized character"; + } + if( pRe->aOp[0]==RE_OP_ANYSTAR ){ + for(j=0, i=1; jzInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ + unsigned x = pRe->aArg[i]; + if( x<=127 ){ + pRe->zInit[j++] = x; + }else if( x<=0xfff ){ + pRe->zInit[j++] = 0xc0 | (x>>6); + pRe->zInit[j++] = 0x80 | (x&0x3f); + }else if( x<=0xffff ){ + pRe->zInit[j++] = 0xd0 | (x>>12); + pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); + pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); + }else{ + break; + } + } + pRe->nInit = j; + } + return pRe->zErr; +} + +/* +** Implementation of the regexp() SQL function. This function implements +** the build-in REGEXP operator. The first argument to the function is the +** pattern and the second argument is the string. So, the SQL statements: +** +** A REGEXP B +** +** is implemented as regexp(B,A). +*/ +static void re_sql_func( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + ReCompiled *pRe; /* Compiled regular expression */ + const char *zPattern; /* The regular expression */ + const unsigned char *zStr;/* String being searched */ + const char *zErr; /* Compile error message */ + + pRe = sqlite3_get_auxdata(context, 0); + if( pRe==0 ){ + zPattern = (const char*)sqlite3_value_text(argv[0]); + if( zPattern==0 ) return; + zErr = re_compile(&pRe, zPattern); + if( zErr ){ + sqlite3_result_error(context, zErr, -1); + return; + } + if( pRe==0 ){ + sqlite3_result_error_nomem(context); + return; + } + sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); + } + zStr = (const unsigned char*)sqlite3_value_text(argv[1]); + if( zStr!=0 ){ + sqlite3_result_int(context, re_exec(pRe, zStr)); + } +} + +/* +** Invoke this routine in order to install the REGEXP function in an +** SQLite database connection. +** +** Use: +** +** sqlite3_auto_extension(sqlite3_add_regexp_func); +** +** to cause this extension to be automatically loaded into each new +** database connection. +*/ +int sqlite3_add_regexp_func(sqlite3 *db){ + return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0, + re_sql_func, 0, 0); +} + + +/***************************** Test Code ***********************************/ +#ifdef SQLITE_TEST +#include +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); + +/* Implementation of the TCL command: +** +** sqlite3_add_regexp_func $DB +*/ +static int tclSqlite3AddRegexpFunc( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + sqlite3_add_regexp_func(db); + return TCL_OK; +} + +/* Register the sqlite3_add_regexp_func TCL command with the TCL interpreter. +*/ +int Sqlitetestregexp_Init(Tcl_Interp *interp){ + Tcl_CreateObjCommand(interp, "sqlite3_add_regexp_func", + tclSqlite3AddRegexpFunc, 0, 0); + return TCL_OK; +} +#endif /* SQLITE_TEST */ +/**************************** End Of Test Code *******************************/ diff --git a/test/regexp1.test b/test/regexp1.test new file mode 100644 index 0000000000..db019a542d --- /dev/null +++ b/test/regexp1.test @@ -0,0 +1,84 @@ +# 2012 December 31 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements test for the REGEXP operator in test_regexp.c. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test regexp1-1.1 { + sqlite3_add_regexp_func db + db eval { + CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT); + INSERT INTO t1 VALUES(1, 'For since by man came death,'); + INSERT INTO t1 VALUES(2, 'by man came also the resurrection of the dead.'); + INSERT INTO t1 VALUES(3, 'For as in Adam all die,'); + INSERT INTO t1 VALUES(4, 'even so in Christ shall all be made alive.'); + + SELECT x FROM t1 WHERE y REGEXP '^For ' ORDER BY x; + } +} {1 3} + +do_execsql_test regexp1-1.2 { + SELECT x FROM t1 WHERE y REGEXP 'by|in' ORDER BY x; +} {1 2 3 4} +do_execsql_test regexp1-1.3 { + SELECT x FROM t1 WHERE y REGEXP 'by|Christ' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.4 { + SELECT x FROM t1 WHERE y REGEXP 'shal+ al+' ORDER BY x; +} {4} +do_execsql_test regexp1-1.5 { + SELECT x FROM t1 WHERE y REGEXP 'shall x*y*z*all' ORDER BY x; +} {4} +do_execsql_test regexp1-1.6 { + SELECT x FROM t1 WHERE y REGEXP 'shallx?y? ?z?all' ORDER BY x; +} {4} +do_execsql_test regexp1-1.7 { + SELECT x FROM t1 WHERE y REGEXP 'r{2}' ORDER BY x; +} {2} +do_execsql_test regexp1-1.8 { + SELECT x FROM t1 WHERE y REGEXP 'r{3}' ORDER BY x; +} {} +do_execsql_test regexp1-1.9 { + SELECT x FROM t1 WHERE y REGEXP 'r{1}' ORDER BY x; +} {1 2 3 4} +do_execsql_test regexp1-1.10 { + SELECT x FROM t1 WHERE y REGEXP 'ur{2,10}e' ORDER BY x; +} {2} +do_execsql_test regexp1-1.11 { + SELECT x FROM t1 WHERE y REGEXP '[Aa]dam' ORDER BY x; +} {3} +do_execsql_test regexp1-1.12 { + SELECT x FROM t1 WHERE y REGEXP '[^Aa]dam' ORDER BY x; +} {} +do_execsql_test regexp1-1.13 { + SELECT x FROM t1 WHERE y REGEXP '[^b-zB-Z]dam' ORDER BY x; +} {3} +do_execsql_test regexp1-1.14 { + SELECT x FROM t1 WHERE y REGEXP 'alive' ORDER BY x; +} {4} +do_execsql_test regexp1-1.15 { + SELECT x FROM t1 WHERE y REGEXP '^alive' ORDER BY x; +} {} +do_execsql_test regexp1-1.16 { + SELECT x FROM t1 WHERE y REGEXP 'alive$' ORDER BY x; +} {} +do_execsql_test regexp1-1.17 { + SELECT x FROM t1 WHERE y REGEXP 'alive.$' ORDER BY x; +} {4} +do_execsql_test regexp1-1.18 { + SELECT x FROM t1 WHERE y REGEXP 'alive\.$' ORDER BY x; +} {4} + + +finish_test diff --git a/tool/build-shell.sh b/tool/build-shell.sh index 8e62a71746..665480540c 100644 --- a/tool/build-shell.sh +++ b/tool/build-shell.sh @@ -15,7 +15,9 @@ gcc -o sqlite3 -g -Os -I. \ -DSQLITE_ENABLE_STAT3 \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_RTREE \ + -DSQLITE_ENABLE_REGEXP \ -DHAVE_READLINE \ -DHAVE_USLEEP=1 \ ../sqlite/src/shell.c ../sqlite/src/test_vfstrace.c \ + ../sqlite/src/test_regexp.c \ sqlite3.c -ldl -lreadline -lncurses From 25846af3be9e648b0ecf2cd6e4dca0dc802781df Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 31 Dec 2012 20:16:35 +0000 Subject: [PATCH 13/15] More test cases for the REGEXP operator. Fix minor bugs uncovered by these test cases. FossilOrigin-Name: a611c75061c8e821cb266fcb09759100d4a646b0 --- manifest | 16 +++--- manifest.uuid | 2 +- src/shell.c | 2 +- src/test_regexp.c | 10 ++-- test/regexp1.test | 127 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 142 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index 69c3503ae5..6dbf08a9e1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\stest_regexp.c\smodule\scontaining\sa\scross-platform\simplementation\s\nof\sthe\sREGEXP\soperator. -D 2012-12-31T19:18:38.256 +C More\stest\scases\sfor\sthe\sREGEXP\soperator.\s\sFix\sminor\sbugs\suncovered\sby\sthese\ntest\scases. +D 2012-12-31T20:16:35.189 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -175,7 +175,7 @@ F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 52331299f4095397d6d00715b70cd153baa11931 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c 5eab6941c0ac97355817f846b77cd20bfdf5a82e -F src/shell.c e6525781d27a84f1b74586831b6ad8472a8c8dc6 +F src/shell.c 11c9611580bb2ffce3a232f31f7f8cc310df0843 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 @@ -217,7 +217,7 @@ F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 F src/test_quota.c 0e0e2e3bf6766b101ecccd8c042b66e44e9be8f5 F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb -F src/test_regexp.c c24ae2a0de64eb9dfa1dd77b77448b1d794cd395 +F src/test_regexp.c d8cb342e08a346a6daeda29e434e2d9a49f557ec F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f @@ -667,7 +667,7 @@ F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6 F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459 F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d -F test/regexp1.test 38da302b75504dd8b960c8f06968ddf8039777ad +F test/regexp1.test bbcb74e1bbdc20a7c0b9b2360deda14c4df1b46a F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce @@ -1030,7 +1030,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P ff6857b6ed6a46671006b75157d8cf853a816ef9 -R 2e582c43743655b03da399d168172dad +P 46c8c01b751c1ea7fc02cc35e3b5bb99dbe46c4b +R 6e56302eb044cc0fb37d40997bea4520 U drh -Z c7b82d9d758f8f0ccceb37ea12601a6d +Z d62b4f1d3e09d5165b99ff6799092c00 diff --git a/manifest.uuid b/manifest.uuid index 74ce482207..90255ae0e2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -46c8c01b751c1ea7fc02cc35e3b5bb99dbe46c4b \ No newline at end of file +a611c75061c8e821cb266fcb09759100d4a646b0 \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index 26de51c4f6..4c50feb466 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1482,7 +1482,7 @@ static void open_db(struct callback_data *p){ #endif #ifdef SQLITE_ENABLE_REGEXP { - extern sqlite3_add_regexp_func(sqlite3*); + extern int sqlite3_add_regexp_func(sqlite3*); sqlite3_add_regexp_func(db); } #endif diff --git a/src/test_regexp.c b/src/test_regexp.c index 353955cee5..0d6ffa2c7e 100644 --- a/src/test_regexp.c +++ b/src/test_regexp.c @@ -342,13 +342,13 @@ static int re_hex(int c, int *pV){ if( c>='0' && c<='9' ){ c -= '0'; }else if( c>='a' && c<='f' ){ - c -= 'a' + 10; + c -= 'a' - 10; }else if( c>='A' && c<='F' ){ - c -= 'A' + 10; + c -= 'A' - 10; }else{ return 0; } - *pV = (*pV)*16 + c; + *pV = (*pV)*16 + (c & 0xff); return 1; } @@ -356,7 +356,7 @@ static int re_hex(int c, int *pV){ ** return its intepretation. */ static unsigned re_esc_char(ReCompiled *p){ - static const char zEsc[] = "afnrtv\\()*.+?[$^{|"; + static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; static const char zTrans[] = "\a\f\n\r\t\v"; int i, v = 0; char c = p->zIn[0]; @@ -381,7 +381,7 @@ static unsigned re_esc_char(ReCompiled *p){ } for(i=0; zEsc[i] && zEsc[i]!=c; i++){} if( zEsc[i] ){ - if( c<6 ) c = zTrans[i]; + if( i<6 ) c = zTrans[i]; p->zIn++; }else{ p->zErr = "unknown \\ escape"; diff --git a/test/regexp1.test b/test/regexp1.test index db019a542d..52877f7734 100644 --- a/test/regexp1.test +++ b/test/regexp1.test @@ -79,6 +79,133 @@ do_execsql_test regexp1-1.17 { do_execsql_test regexp1-1.18 { SELECT x FROM t1 WHERE y REGEXP 'alive\.$' ORDER BY x; } {4} +do_execsql_test regexp1-1.19 { + SELECT x FROM t1 WHERE y REGEXP 'ma[nd]' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.20 { + SELECT x FROM t1 WHERE y REGEXP '\bma[nd]' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.21 { + SELECT x FROM t1 WHERE y REGEXP 'ma[nd]\b' ORDER BY x; +} {1 2} +do_execsql_test regexp1-1.22 { + SELECT x FROM t1 WHERE y REGEXP 'ma\w' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.23 { + SELECT x FROM t1 WHERE y REGEXP 'ma\W' ORDER BY x; +} {} +do_execsql_test regexp1-1.24 { + SELECT x FROM t1 WHERE y REGEXP '\sma\w' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.25 { + SELECT x FROM t1 WHERE y REGEXP '\Sma\w' ORDER BY x; +} {} +do_execsql_test regexp1-1.26 { + SELECT x FROM t1 WHERE y REGEXP 'alive\S$' ORDER BY x; +} {4} +do_execsql_test regexp1-1.27 { + SELECT x FROM t1 WHERE y REGEXP + '\b(unto|us|son|given|his|name|called|' || + 'wonderful|councelor|mighty|god|everlasting|father|' || + 'prince|peace|alive)\b'; +} {4} +do_execsql_test regexp1-2.1 { + SELECT 'aaaabbbbcccc' REGEXP 'ab*c', + 'aaaacccc' REGEXP 'ab*c'; +} {1 1} +do_execsql_test regexp1-2.2 { + SELECT 'aaaabbbbcccc' REGEXP 'ab+c', + 'aaaacccc' REGEXP 'ab+c'; +} {1 0} +do_execsql_test regexp1-2.3 { + SELECT 'aaaabbbbcccc' REGEXP 'ab?c', + 'aaaacccc' REGEXP 'ab?c'; +} {0 1} +do_execsql_test regexp1-2.4 { + SELECT 'aaaabbbbbbcccc' REGEXP 'ab{3,5}c', + 'aaaabbbbbcccc' REGEXP 'ab{3,5}c', + 'aaaabbbbcccc' REGEXP 'ab{3,5}c', + 'aaaabbbcccc' REGEXP 'ab{3,5}c', + 'aaaabbcccc' REGEXP 'ab{3,5}c', + 'aaaabcccc' REGEXP 'ab{3,5}c' +} {0 1 1 1 0 0} +do_execsql_test regexp1-2.5 { + SELECT 'aaaabbbbcccc' REGEXP 'a(a|b|c)+c', + 'aaaabbbbcccc' REGEXP '^a(a|b|c){11}c$', + 'aaaabbbbcccc' REGEXP '^a(a|b|c){10}c$', + 'aaaabbbbcccc' REGEXP '^a(a|b|c){9}c$' +} {1 0 1 0} +do_execsql_test regexp1-2.6 { + SELECT 'aaaabbbbcccc' REGEXP '^a(a|bb|c)+c$', + 'aaaabbbbcccc' REGEXP '^a(a|bbb|c)+c$', + 'aaaabbbbcccc' REGEXP '^a(a|bbbb|c)+c$' +} {1 0 1} +do_execsql_test regexp1-2.7 { + SELECT 'aaaabbbbcccc' REGEXP '^a([ac]+|bb){3}c$', + 'aaaabbbbcccc' REGEXP '^a([ac]+|bb){4}c$', + 'aaaabbbbcccc' REGEXP '^a([ac]+|bb){5}c$' +} {0 1 1} + +do_execsql_test regexp1-2.8 { + SELECT 'abc*def+ghi.jkl[mno]pqr' REGEXP 'c.d', + 'abc*def+ghi.jkl[mno]pqr' REGEXP 'c\*d', + 'abc*def+ghi.jkl[mno]pqr' REGEXP 'f\+g', + 'abc*def+ghi.jkl[mno]pqr' REGEXP 'i\.j', + 'abc*def+ghi.jkl[mno]pqr' REGEXP 'l\[mno\]p' +} {1 1 1 1 1} + +do_test regexp1-2.9 { + set v1 "abc\ndef" + db eval {SELECT $v1 REGEXP '^abc\ndef$'} +} {1} +do_test regexp1-2.10 { + set v1 "abc\adef" + db eval {SELECT $v1 REGEXP '^abc\adef$'} +} {1} +do_test regexp1-2.11 { + set v1 "abc\tdef" + db eval {SELECT $v1 REGEXP '^abc\tdef$'} +} {1} +do_test regexp1-2.12 { + set v1 "abc\rdef" + db eval {SELECT $v1 REGEXP '^abc\rdef$'} +} {1} +do_test regexp1-2.13 { + set v1 "abc\fdef" + db eval {SELECT $v1 REGEXP '^abc\fdef$'} +} {1} +do_test regexp1-2.14 { + set v1 "abc\vdef" + db eval {SELECT $v1 REGEXP '^abc\vdef$'} +} {1} +do_execsql_test regexp1-2.15 { + SELECT 'abc\def' REGEXP '^abc\\def', + 'abc(def' REGEXP '^abc\(def', + 'abc)def' REGEXP '^abc\)def', + 'abc*def' REGEXP '^abc\*def', + 'abc.def' REGEXP '^abc\.def', + 'abc+def' REGEXP '^abc\+def', + 'abc?def' REGEXP '^abc\?def', + 'abc[def' REGEXP '^abc\[def', + 'abc$def' REGEXP '^abc\$', + '^def' REGEXP '\^def', + 'abc{4}x' REGEXP '^abc\{4\}x$', + 'abc|def' REGEXP '^abc\|def$' +} {1 1 1 1 1 1 1 1 1 1 1 1} + +do_execsql_test regexp1-2.20 { + SELECT 'abc$¢€xyz' REGEXP '^abc\u0024\u00a2\u20acxyz$', + 'abc$¢€xyz' REGEXP '^abc\u0024\u00A2\u20ACxyz$', + 'abc$¢€xyz' REGEXP '^abc\x24\xa2\x20acxyz$' +} {1 1 1} +do_execsql_test regexp1-2.21 { + SELECT 'abc$¢€xyz' REGEXP '^abc[\u0024][\u00a2][\u20ac]xyz$', + 'abc$¢€xyz' REGEXP '^abc[\u0024\u00A2\u20AC]{3}xyz$', + 'abc$¢€xyz' REGEXP '^abc[\x24][\xa2\x20ac]+xyz$' +} {1 1 1} +do_execsql_test regexp1-2.22 { + SELECT 'abc$¢€xyz' REGEXP '^abc[^\u0025-X][^ -\u007f][^\u20ab]xyz$' +} {1} finish_test From 384b7fe221bf460a55ae48e623de4ce15433e1f6 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 1 Jan 2013 13:55:31 +0000 Subject: [PATCH 14/15] Enhance the table_info pragma so that the pk column shows the order of the columns in a multi-column primary key. FossilOrigin-Name: 3076a89015071e9b40e728bd55160e3a6ed98820 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/pragma.c | 14 +++++++++++--- test/pragma.test | 14 ++++++++++---- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index 6dbf08a9e1..eafc99d925 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\stest\scases\sfor\sthe\sREGEXP\soperator.\s\sFix\sminor\sbugs\suncovered\sby\sthese\ntest\scases. -D 2012-12-31T20:16:35.189 +C Enhance\sthe\stable_info\spragma\sso\sthat\sthe\spk\scolumn\sshows\sthe\sorder\sof\sthe\ncolumns\sin\sa\smulti-column\sprimary\skey. +D 2013-01-01T13:55:31.529 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -168,7 +168,7 @@ F src/parse.y 5d5e12772845805fdfeb889163516b84fbb9ae95 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c 1ff115f620d9496fe5018306b644ab9ef7e26a3c +F src/pragma.c 8907c559d3127729d3bcedb1fe5c59fc196d3a17 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 @@ -654,7 +654,7 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0 F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16 F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 F test/permutations.test 360b92859c0af814b3fe10b68746936389606501 -F test/pragma.test a62f73293b0f0d79b0c87f8dd32d46fe53b0bd17 +F test/pragma.test 5ce333cae37d86cfe9b3add840906e375e2272b0 F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 @@ -1030,7 +1030,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 46c8c01b751c1ea7fc02cc35e3b5bb99dbe46c4b -R 6e56302eb044cc0fb37d40997bea4520 +P a611c75061c8e821cb266fcb09759100d4a646b0 +R e68c4dd2b29a4a981e4da04205873c7d U drh -Z d62b4f1d3e09d5165b99ff6799092c00 +Z 9a369736ae7dc5da841cecf2065e7d10 diff --git a/manifest.uuid b/manifest.uuid index 90255ae0e2..59765437da 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a611c75061c8e821cb266fcb09759100d4a646b0 \ No newline at end of file +3076a89015071e9b40e728bd55160e3a6ed98820 \ No newline at end of file diff --git a/src/pragma.c b/src/pragma.c index 0f92769a9f..7e8b59b8e4 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -948,9 +948,11 @@ void sqlite3Pragma( if( sqlite3ReadSchema(pParse) ) goto pragma_out; pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ - int i; + int i, k; int nHidden = 0; Column *pCol; + Index *pPk; + for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} sqlite3VdbeSetNumCols(v, 6); pParse->nMem = 6; sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); @@ -975,8 +977,14 @@ void sqlite3Pragma( }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, 5); } - sqlite3VdbeAddOp2(v, OP_Integer, - (pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6); + if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ + k = 0; + }else if( pPk==0 ){ + k = 1; + }else{ + for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} + } + sqlite3VdbeAddOp2(v, OP_Integer, k, 6); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); } } diff --git a/test/pragma.test b/test/pragma.test index 3c8d23a1fc..db6271c033 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -534,12 +534,20 @@ do_test pragma-6.2.2 { b DEFAULT (5+3), c TEXT, d INTEGER DEFAULT NULL, - e TEXT DEFAULT '' + e TEXT DEFAULT '', + UNIQUE(b,c,d), + PRIMARY KEY(e,b,c) ); PRAGMA table_info(t5); } -} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 0 2 c TEXT 0 <> 0 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 0} +} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 2 2 c TEXT 0 <> 3 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 1} db nullvalue {} +do_test pragma-6.2.3 { + execsql { + CREATE TABLE t2_3(a,b INTEGER PRIMARY KEY,c); + pragma table_info(t2_3) + } +} {0 a {} 0 {} 0 1 b INTEGER 0 {} 1 2 c {} 0 {} 0} ifcapable {foreignkey} { do_test pragma-6.3.1 { execsql { @@ -1619,5 +1627,3 @@ do_test 22.4.3 { } {ok} finish_test - - From b376daeb6758159e5b9835d729f0b22011e8e68a Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 1 Jan 2013 14:01:28 +0000 Subject: [PATCH 15/15] Reduce the size of the Index object (by 8 bytes on x64). FossilOrigin-Name: 5a2ac944839ec0c5b9147a035c6cbf0935f3d202 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/sqliteInt.h | 28 ++++++++++++++-------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/manifest b/manifest index eafc99d925..3b88cb1f55 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\stable_info\spragma\sso\sthat\sthe\spk\scolumn\sshows\sthe\sorder\sof\sthe\ncolumns\sin\sa\smulti-column\sprimary\skey. -D 2013-01-01T13:55:31.529 +C Reduce\sthe\ssize\sof\sthe\sIndex\sobject\s(by\s8\sbytes\son\sx64). +D 2013-01-01T14:01:28.252 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -179,7 +179,7 @@ F src/shell.c 11c9611580bb2ffce3a232f31f7f8cc310df0843 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 -F src/sqliteInt.h 2e5d50f26abf7cbc6162117735379d412f4091da +F src/sqliteInt.h 2091fb64a2274434a791951cb97ef8b1bd44c29b F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -1030,7 +1030,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P a611c75061c8e821cb266fcb09759100d4a646b0 -R e68c4dd2b29a4a981e4da04205873c7d +P 3076a89015071e9b40e728bd55160e3a6ed98820 +R ec755f0b3e93517441aaf44db561d61d U drh -Z 9a369736ae7dc5da841cecf2065e7d10 +Z 0e046cc23277e52323ecef741dd3466e diff --git a/manifest.uuid b/manifest.uuid index 59765437da..522d4edbae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3076a89015071e9b40e728bd55160e3a6ed98820 \ No newline at end of file +5a2ac944839ec0c5b9147a035c6cbf0935f3d202 \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 11dd8b4511..7922bf0d4f 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1484,20 +1484,20 @@ struct UnpackedRecord { ** element. */ struct Index { - char *zName; /* Name of this index */ - int *aiColumn; /* Which columns are used by this index. 1st is 0 */ - tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ - Table *pTable; /* The SQL table being indexed */ - char *zColAff; /* String defining the affinity of each column */ - Index *pNext; /* The next index associated with the same table */ - Schema *pSchema; /* Schema containing this index */ - u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ - char **azColl; /* Array of collation sequence names for index */ - int nColumn; /* Number of columns in the table used by this index */ - int tnum; /* Page containing root of this index in database file */ - u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ - u8 bUnordered; /* Use this index for == or IN queries only */ + char *zName; /* Name of this index */ + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ + tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ + Table *pTable; /* The SQL table being indexed */ + char *zColAff; /* String defining the affinity of each column */ + Index *pNext; /* The next index associated with the same table */ + Schema *pSchema; /* Schema containing this index */ + u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ + char **azColl; /* Array of collation sequence names for index */ + int tnum; /* DB Page containing root of this index */ + u16 nColumn; /* Number of columns in table used by this index */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ + unsigned bUnordered:1; /* Use this index for == or IN queries only */ #ifdef SQLITE_ENABLE_STAT3 int nSample; /* Number of elements in aSample[] */ tRowcnt avgEq; /* Average nEq value for key values not in aSample */