From 281bbe2a95d7d312ecbbdd8cbe3dbfc311ea466c Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 16 Oct 2012 23:17:14 +0000 Subject: [PATCH 01/38] Enable optimization of IN operators on constraints to virtual tables. FossilOrigin-Name: aa650746b19e4a6a373f7e47effff3ab2f48e78c --- manifest | 19 +++++++++++-------- manifest.uuid | 2 +- src/where.c | 27 +++++++++++++++++++-------- test/vtab1.test | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 75 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index be0f0a1ebc..f9f1a4b2c9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\scomments\sand\senhance\sreadability\sof\sthe\smkvsix\stool. -D 2012-10-15T20:28:22.029 +C Enable\soptimization\sof\sIN\soperators\son\sconstraints\sto\svirtual\stables. +D 2012-10-16T23:17:14.207 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5f4f26109f9d80829122e0e09f9cda008fa065fb F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -249,7 +249,7 @@ F src/vtab.c 9c64ae18af78c740610df841c6f49fc2d240a279 F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2 F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b -F src/where.c 3e6c1f9efe4c6a029b0a750e0f6a63964f43bcce +F src/where.c 62f667db8cdbb81028bf1c55ba4b0e845b79622c F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00 @@ -916,7 +916,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b F test/view.test b182a67ec43f490b156b5a710827a341be83dd17 -F test/vtab1.test 10fb9e656fe4b318cd82ff1616a340acc01aac4b +F test/vtab1.test 524beb672c42463fe3d37351b74253c0b835498d F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1 F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 @@ -1021,7 +1021,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9 -P 629a42d47a0d8f73de900f469845ce800bdb8959 -R c7830c7c0cb344c8cb6a175c95e5eb7f -U mistachkin -Z f2086ddd83e8b281e8d53ba90c91d2db +P 2c3af657fee6153842d660a6ce29aa7d791ebd38 +R 5cbef12c646a3899761eba454f8c4105 +T *branch * vtab-IN-opt +T *sym-vtab-IN-opt * +T -sym-trunk * +U drh +Z 07568d163c180c755b6278e856d28eda diff --git a/manifest.uuid b/manifest.uuid index 915ba49368..1c366b32f0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2c3af657fee6153842d660a6ce29aa7d791ebd38 \ No newline at end of file +aa650746b19e4a6a373f7e47effff3ab2f48e78c \ No newline at end of file diff --git a/src/where.c b/src/where.c index b7663386ac..586d069b3f 100644 --- a/src/where.c +++ b/src/where.c @@ -253,7 +253,7 @@ struct WhereCost { #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */ -#define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ +#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* xEXPR or x>=EXPR constraint */ #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and xeOperator&(pTerm->eOperator-1))==0 ); testcase( pTerm->eOperator==WO_IN ); testcase( pTerm->eOperator==WO_ISNULL ); - if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue; + if( pTerm->eOperator & (WO_ISNULL) ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; nTerm++; } @@ -2086,15 +2086,18 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ pUsage; for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ + u8 op; if( pTerm->leftCursor != pSrc->iCursor ) continue; assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); testcase( pTerm->eOperator==WO_IN ); testcase( pTerm->eOperator==WO_ISNULL ); - if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue; + if( pTerm->eOperator & (WO_ISNULL) ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; pIdxCons[j].iColumn = pTerm->u.leftColumn; pIdxCons[j].iTermOffset = i; - pIdxCons[j].op = (u8)pTerm->eOperator; + op = (u8)pTerm->eOperator; + if( op==WO_IN ) op = WO_EQ; + pIdxCons[j].op = op; /* The direct assignment in the previous line is possible only because ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The ** following asserts verify this fact. */ @@ -2104,7 +2107,7 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH ); - assert( pTerm->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) ); + assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) ); j++; } for(i=0; iplan.u.pVtabIdx; int nConstraint = pVtabIdx->nConstraint; struct sqlite3_index_constraint_usage *aUsage = @@ -4039,11 +4043,18 @@ static Bitmask codeOneLoopStart( sqlite3ExprCachePush(pParse); iReg = sqlite3GetTempRange(pParse, nConstraint+2); + addrNotFound = pLevel->addrBrk; for(j=1; j<=nConstraint; j++){ for(k=0; ka[iTerm].pExpr->pRight, iReg+j+1); + WhereTerm *pTerm = &pWC->a[aConstraint[k].iTermOffset]; + int iTarget = iReg+j+1; + if( pTerm->eOperator & WO_IN ){ + codeEqualityTerm(pParse, pTerm, pLevel, iTarget); + addrNotFound = pLevel->addrNxt; + }else{ + sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget); + } break; } } @@ -4051,7 +4062,7 @@ static Bitmask codeOneLoopStart( } sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg); sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1); - sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr, + sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr, pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC); pVtabIdx->needToFreeIdxStr = 0; for(j=0; j Date: Mon, 10 Dec 2012 09:08:17 +0000 Subject: [PATCH 02/38] Remove a reference to JumpOnce from a comment in vdbe.c. No code changes. FossilOrigin-Name: ee662c039d67f118008485d95603c5a43fcac75f --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/vdbe.c | 2 -- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 9c89093a19..e6b6ae5b0a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Factor\ssome\swork\sout\sof\sthe\sindex\sloop\sof\sthe\sbestBtreeIndex()\sroutine\sfor\na\ssmall\sperformance\sincrease. -D 2012-12-08T22:14:29.324 +C Remove\sa\sreference\sto\sJumpOnce\sfrom\sa\scomment\sin\svdbe.c.\sNo\scode\schanges. +D 2012-12-10T09:08:17.304 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -237,7 +237,7 @@ F src/update.c 28d2d098b43a2c70dae399896ea8a02f622410ef F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f F src/util.c 0af2e515dc0dabacec931bca39525f6c3f1c5455 F src/vacuum.c 2727bdd08847fcb6b2d2da6d14f018910e8645d3 -F src/vdbe.c 89138e8e8b5468f349ace581db9c49801f4d6543 +F src/vdbe.c fb1d2b75d3674cbad68a8c810f0bd27e977369c4 F src/vdbe.h b52887278cb173e66188da84dfab216bea61119d F src/vdbeInt.h 79abf9b31be406d35ca77d6999cb2d42aaf91e78 F src/vdbeapi.c 4c2418161cf45392ba76a7ca92f9a5f06b96f89c @@ -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 8542e6180d4321d45b34f33e481658908ce1430d -R 307714a402bf6d86beef10997fbac787 -U drh -Z c87a5a2068dc4b6318f0ce8f3372ea0a +P 92c9ab56b1c67b9468bec57ab1d2c483a69a2810 +R 2fd74e1da240a2ddf2869176f4bfa3b0 +U dan +Z 3ebdd624f50ae598ba1c6acf15c0ccaa diff --git a/manifest.uuid b/manifest.uuid index 5047e5ef81..e6c611266d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -92c9ab56b1c67b9468bec57ab1d2c483a69a2810 \ No newline at end of file +ee662c039d67f118008485d95603c5a43fcac75f \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 454993f5f1..ab54fe470e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2076,8 +2076,6 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ ** ** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise, ** set the flag and fall through to the next instruction. -** -** See also: JumpOnce */ case OP_Once: { /* jump */ assert( pOp->p1nOnceFlag ); From b136e902adae1379d9a1d7e50be7f7334f42f893 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 10 Dec 2012 10:22:48 +0000 Subject: [PATCH 03/38] Modify releasetest.tcl so that it runs the "checksymbols" test on a build without SQLITE_DEBUG defined. If SQLITE_DEBUG is defined, the sqlite3WhereTrace variable causes the test to fail. FossilOrigin-Name: 75e545a9e2614fae7db86ecfb84e41ecbe4097ba --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/releasetest.tcl | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index e6b6ae5b0a..a0cf91d6b5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sa\sreference\sto\sJumpOnce\sfrom\sa\scomment\sin\svdbe.c.\sNo\scode\schanges. -D 2012-12-10T09:08:17.304 +C Modify\sreleasetest.tcl\sso\sthat\sit\sruns\sthe\s"checksymbols"\stest\son\sa\sbuild\swithout\sSQLITE_DEBUG\sdefined.\sIf\sSQLITE_DEBUG\sis\sdefined,\sthe\ssqlite3WhereTrace\svariable\scauses\sthe\stest\sto\sfail. +D 2012-12-10T10:22:48.067 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -666,7 +666,7 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a -F test/releasetest.tcl e48fd8e0e8abad89f30e08620790533ae4e02010 +F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce F test/rollback.test a1b4784b864331eae8b2a98c189efa2a8b11ff07 F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test f777404492adb0e00868fd706a3721328fd3af48 @@ -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 92c9ab56b1c67b9468bec57ab1d2c483a69a2810 -R 2fd74e1da240a2ddf2869176f4bfa3b0 +P ee662c039d67f118008485d95603c5a43fcac75f +R 99976229de7a1d73e2300bb3e2610e17 U dan -Z 3ebdd624f50ae598ba1c6acf15c0ccaa +Z 60b80bb1966596bbdb0785b6ee10792d diff --git a/manifest.uuid b/manifest.uuid index e6c611266d..fe96af2ada 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ee662c039d67f118008485d95603c5a43fcac75f \ No newline at end of file +75e545a9e2614fae7db86ecfb84e41ecbe4097ba \ No newline at end of file diff --git a/test/releasetest.tcl b/test/releasetest.tcl index 3b4662c510..b635061c55 100644 --- a/test/releasetest.tcl +++ b/test/releasetest.tcl @@ -81,6 +81,22 @@ array set ::Configs { -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 } + "Check-Symbols" { + -DSQLITE_MEMDEBUG=1 + -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 + -DSQLITE_ENABLE_FTS3=1 + -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_ENABLE_MEMSYS5=1 + -DSQLITE_ENABLE_MEMSYS3=1 + -DSQLITE_ENABLE_COLUMN_METADATA=1 + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 + -DSQLITE_SECURE_DELETE=1 + -DSQLITE_SOUNDEX=1 + -DSQLITE_ENABLE_ATOMIC_WRITE=1 + -DSQLITE_ENABLE_IOTRACE=1 + -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 + -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 + } "Debug-One" { -O2 -DSQLITE_DEBUG=1 @@ -164,7 +180,8 @@ array set ::Configs { array set ::Platforms { Linux-x86_64 { - "Debug-One" "checksymbols test" + "Check-Symbols" checksymbols + "Debug-One" test "Secure-Delete" test "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Update-Delete-Limit" test @@ -330,15 +347,17 @@ proc main {argv} { # If the configuration included the SQLITE_DEBUG option, then remove # it and run veryquick.test. If it did not include the SQLITE_DEBUG option # add it and run veryquick.test. - set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] - if {$debug_idx < 0} { - run_test_suite "${zConfig}_debug" test [ - concat $config_options -DSQLITE_DEBUG=1 - ] - } else { - run_test_suite "${zConfig}_ndebug" test [ - lreplace $config_options $debug_idx $debug_idx - ] + if {$target!="checksymbols"} { + set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] + if {$debug_idx < 0} { + run_test_suite "${zConfig}_debug" test [ + concat $config_options -DSQLITE_DEBUG=1 + ] + } else { + run_test_suite "${zConfig}_ndebug" test [ + lreplace $config_options $debug_idx $debug_idx + ] + } } } From be707b396a9c43480c32b6f455f02f9b2c8e6a03 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 10 Dec 2012 22:19:14 +0000 Subject: [PATCH 04/38] When an arithmetic operation with two integer operands must give a floating-point answer due to overflow, make sure the answer is not rounded back to integer by affinity. FossilOrigin-Name: bd7aeeb691fee69dd6a562138a7aba8e8e192272 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/vdbe.c | 5 ++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index a0cf91d6b5..0f5136bfc8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Modify\sreleasetest.tcl\sso\sthat\sit\sruns\sthe\s"checksymbols"\stest\son\sa\sbuild\swithout\sSQLITE_DEBUG\sdefined.\sIf\sSQLITE_DEBUG\sis\sdefined,\sthe\ssqlite3WhereTrace\svariable\scauses\sthe\stest\sto\sfail. -D 2012-12-10T10:22:48.067 +C When\san\sarithmetic\soperation\swith\stwo\sinteger\soperands\smust\sgive\sa\s\nfloating-point\sanswer\sdue\sto\soverflow,\smake\ssure\sthe\sanswer\sis\snot\nrounded\sback\sto\sinteger\sby\saffinity. +D 2012-12-10T22:19:14.915 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -237,7 +237,7 @@ F src/update.c 28d2d098b43a2c70dae399896ea8a02f622410ef F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f F src/util.c 0af2e515dc0dabacec931bca39525f6c3f1c5455 F src/vacuum.c 2727bdd08847fcb6b2d2da6d14f018910e8645d3 -F src/vdbe.c fb1d2b75d3674cbad68a8c810f0bd27e977369c4 +F src/vdbe.c f51eb3207594703d24e91335cb16906e894b48aa F src/vdbe.h b52887278cb173e66188da84dfab216bea61119d F src/vdbeInt.h 79abf9b31be406d35ca77d6999cb2d42aaf91e78 F src/vdbeapi.c 4c2418161cf45392ba76a7ca92f9a5f06b96f89c @@ -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 ee662c039d67f118008485d95603c5a43fcac75f -R 99976229de7a1d73e2300bb3e2610e17 -U dan -Z 60b80bb1966596bbdb0785b6ee10792d +P 75e545a9e2614fae7db86ecfb84e41ecbe4097ba +R 181353fe376d2fba8949034c976d8829 +U drh +Z d04d8a3f1a11b6d0d53258b48058f73d diff --git a/manifest.uuid b/manifest.uuid index fe96af2ada..2e1ff51490 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -75e545a9e2614fae7db86ecfb84e41ecbe4097ba \ No newline at end of file +bd7aeeb691fee69dd6a562138a7aba8e8e192272 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index ab54fe470e..a2ab31e879 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1270,6 +1270,7 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ + char bIntint; /* Started out as two integer operands */ int flags; /* Combined MEM_* flags from both inputs */ i64 iA; /* Integer value of left operand */ i64 iB; /* Integer value of right operand */ @@ -1286,6 +1287,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){ iA = pIn1->u.i; iB = pIn2->u.i; + bIntint = 1; switch( pOp->opcode ){ case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break; case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break; @@ -1306,6 +1308,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ pOut->u.i = iB; MemSetTypeFlag(pOut, MEM_Int); }else{ + bIntint = 0; fp_math: rA = sqlite3VdbeRealValue(pIn1); rB = sqlite3VdbeRealValue(pIn2); @@ -1337,7 +1340,7 @@ fp_math: } pOut->r = rB; MemSetTypeFlag(pOut, MEM_Real); - if( (flags & MEM_Real)==0 ){ + if( (flags & MEM_Real)==0 && !bIntint ){ sqlite3VdbeIntegerAffinity(pOut); } #endif From 39ac5de6c583a01c958cedd280b1766f161de677 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 11 Dec 2012 19:10:37 +0000 Subject: [PATCH 05/38] Fix a typo in a comment that is used to generate documentation. No changes to code. FossilOrigin-Name: e38adb60a7952ca9e28e496b4e4658c724a368d2 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/sqlite.h.in | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 0f5136bfc8..b68345ead9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\san\sarithmetic\soperation\swith\stwo\sinteger\soperands\smust\sgive\sa\s\nfloating-point\sanswer\sdue\sto\soverflow,\smake\ssure\sthe\sanswer\sis\snot\nrounded\sback\sto\sinteger\sby\saffinity. -D 2012-12-10T22:19:14.915 +C Fix\sa\stypo\sin\sa\scomment\sthat\sis\sused\sto\sgenerate\ndocumentation.\s\sNo\schanges\sto\scode. +D 2012-12-11T19:10:37.876 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -176,7 +176,7 @@ F src/resolve.c cdd546d62da7763119ea1fa455a898959e03457f F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c e6daa524bbdfa98f4abdb8cb281498f0047d3161 F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019 -F src/sqlite.h.in e5552743b5b74dc675ebbdedd849ba8283424fa9 +F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 F src/sqliteInt.h d1f0866c69d94fe018a32f78c31a043e3fc0d0de @@ -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 75e545a9e2614fae7db86ecfb84e41ecbe4097ba -R 181353fe376d2fba8949034c976d8829 +P bd7aeeb691fee69dd6a562138a7aba8e8e192272 +R ff669a65bfe277dd417809f40c6cce8f U drh -Z d04d8a3f1a11b6d0d53258b48058f73d +Z 76b4a41a9e5a46c1b5883e541d2a2817 diff --git a/manifest.uuid b/manifest.uuid index 2e1ff51490..295f8303f7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bd7aeeb691fee69dd6a562138a7aba8e8e192272 \ No newline at end of file +e38adb60a7952ca9e28e496b4e4658c724a368d2 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 0bf0be5fcf..48f7381212 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -1587,7 +1587,7 @@ struct sqlite3_mem_methods { ** [SQLITE_USE_URI] symbol defined. ** ** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]]
SQLITE_CONFIG_COVERING_INDEX_SCAN -**
This option taks a single integer argument which is interpreted as +**
This option takes a single integer argument which is interpreted as ** a boolean in order to enable or disable the use of covering indices for ** full table scans in the query optimizer. The default setting is determined ** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" From 6949784cc42933f41449434317aa15c9f4c3a15c Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 11 Dec 2012 19:40:03 +0000 Subject: [PATCH 06/38] Update requirements marks and test cases for multi-VALUE INSERT and to fix typos in requirements text. FossilOrigin-Name: 81d9ee0f0df7f283e69ba1c2845339ea3318cd66 --- manifest | 14 +++++++------- manifest.uuid | 2 +- test/e_insert.test | 25 +++++++++++++++---------- test/e_uri.test | 4 ++-- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index b68345ead9..01dd6cd16a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\stypo\sin\sa\scomment\sthat\sis\sused\sto\sgenerate\ndocumentation.\s\sNo\schanges\sto\scode. -D 2012-12-11T19:10:37.876 +C Update\srequirements\smarks\sand\stest\scases\sfor\smulti-VALUE\sINSERT\sand\sto\sfix\ntypos\sin\srequirements\stext. +D 2012-12-11T19:40:03.452 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -385,13 +385,13 @@ F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e F test/e_expr.test 5489424d3d9a452ac3701cdf4b680ae31a157894 F test/e_fkey.test a79ab1d3213c7ac64621eec28f8e8bb219775445 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 -F test/e_insert.test c6ac239a97cb16dfbd0c16496f8cd871b4068c0c +F test/e_insert.test d5331cc95e101af2508159fc98b6801631659ffe F test/e_reindex.test dfedfc32c5a282b0596c6537cbcd4217fbb1a216 F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6 F test/e_select.test 07e8d81268ba1ffcaf1dc4bec48956af150c42f9 F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92 F test/e_update.test 161d5dc6a3ed9dd08f5264d13e20735d7a89f00c -F test/e_uri.test 9e190ca799d9190eec6e43f2aadf1d10c06a57a3 +F test/e_uri.test bc240fbc6cbbbdff832ee05858432a25961ab36a F test/e_vacuum.test 331da289ae186656cf5f2eb27f577a89c0c221af F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc2.test 796c59832e2b9a52842f382ffda8f3e989db03ad @@ -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 bd7aeeb691fee69dd6a562138a7aba8e8e192272 -R ff669a65bfe277dd417809f40c6cce8f +P e38adb60a7952ca9e28e496b4e4658c724a368d2 +R 6da93546e03c634583a9df9f228276ed U drh -Z 76b4a41a9e5a46c1b5883e541d2a2817 +Z 5bbcc49314c9f500d1f73110b0fcc62a diff --git a/manifest.uuid b/manifest.uuid index 295f8303f7..89526eb807 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e38adb60a7952ca9e28e496b4e4658c724a368d2 \ No newline at end of file +81d9ee0f0df7f283e69ba1c2845339ea3318cd66 \ No newline at end of file diff --git a/test/e_insert.test b/test/e_insert.test index ac4361f8df..951ae2447b 100644 --- a/test/e_insert.test +++ b/test/e_insert.test @@ -141,8 +141,8 @@ do_insert_tests e_insert-0 { delete_all_data -# EVIDENCE-OF: R-20288-20462 The first form (with the "VALUES" keyword) -# creates a single new row in an existing table. +# EVIDENCE-OF: R-21490-41092 The first form (with the "VALUES" keyword) +# creates one or more new rows in an existing table. # do_insert_tests e_insert-1.1 { 0 "SELECT count(*) FROM a2" {0} @@ -152,11 +152,14 @@ do_insert_tests e_insert-1.1 { 2a "INSERT INTO a2(a, b) VALUES(1, 2)" {} 2b "SELECT count(*) FROM a2" {2} + + 3a "INSERT INTO a2(a) VALUES(3),(4)" {} + 3b "SELECT count(*) FROM a2" {4} } -# EVIDENCE-OF: R-36040-20870 If no column-list is specified then the -# number of values must be the same as the number of columns in the -# table. +# EVIDENCE-OF: R-53616-44976 If no column-list is specified then the +# number of values inserted into each row must be the same as the number +# of columns in the table. # # A test in the block above verifies that if the VALUES list has the # correct number of columns (for table a2, 3 columns) works. So these @@ -171,9 +174,10 @@ do_insert_tests e_insert-1.2 -error { 4 "INSERT INTO a2 VALUES(1,2,3,4,5)" {a2 3 5} } -# EVIDENCE-OF: R-04006-57648 In this case the result of evaluating the -# left-most expression in the VALUES list is inserted into the left-most -# column of the new row, and so on. +# EVIDENCE-OF: R-34231-22576 In this case the result of evaluating the +# left-most expression in each term of the VALUES list is inserted into +# the left-most column of the each new row, and forth for each +# subsequent expression. # delete_all_data do_insert_tests e_insert-1.3 { @@ -187,8 +191,9 @@ do_insert_tests e_insert-1.3 { 3b "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {2 x y} } -# EVIDENCE-OF: R-62524-00361 If a column-list is specified, then the -# number of values must match the number of specified columns. +# EVIDENCE-OF: R-44710-64652 If a column-list is specified, then the +# number of values in each term of the VALUS list must match the number +# of specified columns. # do_insert_tests e_insert-1.4 -error { %d values for %d columns diff --git a/test/e_uri.test b/test/e_uri.test index 8c9949ef33..f16c0cb12c 100644 --- a/test/e_uri.test +++ b/test/e_uri.test @@ -261,9 +261,9 @@ foreach {tn uri error} " } -# EVIDENCE-OF: R-09651-31805 If "ro" is specified, then the database is +# EVIDENCE-OF: R-43036-46756 If "ro" is specified, then the database is # opened for read-only access, just as if the SQLITE_OPEN_READONLY flag -# had been set in the third argument to sqlite3_prepare_v2(). +# had been set in the third argument to sqlite3_open_v2(). # # EVIDENCE-OF: R-40137-26050 If the mode option is set to "rw", then the # database is opened for read-write (but not create) access, as if From 2d6381865dbaa5d6790834853d548e0cb27ecb33 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 12 Dec 2012 13:36:53 +0000 Subject: [PATCH 07/38] Version 3.7.15 FossilOrigin-Name: cd0b37c52658bfdf992b1e3dc467bae1835a94ae --- manifest | 13 ++++++++----- manifest.uuid | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/manifest b/manifest index 01dd6cd16a..b0a4c45629 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\srequirements\smarks\sand\stest\scases\sfor\smulti-VALUE\sINSERT\sand\sto\sfix\ntypos\sin\srequirements\stext. -D 2012-12-11T19:40:03.452 +C Version\s3.7.15 +D 2012-12-12T13:36:53.477 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -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 e38adb60a7952ca9e28e496b4e4658c724a368d2 +P 81d9ee0f0df7f283e69ba1c2845339ea3318cd66 R 6da93546e03c634583a9df9f228276ed -U drh -Z 5bbcc49314c9f500d1f73110b0fcc62a +T +bgcolor * #d0c0ff +T +sym-release * +T +sym-version-3.7.15 * +U dan +Z c2b6a5ded805ac723be241fad17cc751 diff --git a/manifest.uuid b/manifest.uuid index 89526eb807..940b7402c1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -81d9ee0f0df7f283e69ba1c2845339ea3318cd66 \ No newline at end of file +cd0b37c52658bfdf992b1e3dc467bae1835a94ae \ No newline at end of file From 32055c249900d715f7de68db51d37ce3c6885d74 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 12 Dec 2012 14:30:03 +0000 Subject: [PATCH 08/38] Allow the error message from "PRAGMA integrity_check" to be longer than 20,000 bytes. FossilOrigin-Name: 120c82d56ecb574f46db01679317b8062d8779ed --- manifest | 17 +++++++---------- manifest.uuid | 2 +- src/btree.c | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index b0a4c45629..aef5a7ffec 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Version\s3.7.15 -D 2012-12-12T13:36:53.477 +C Allow\sthe\serror\smessage\sfrom\s"PRAGMA\sintegrity_check"\sto\sbe\slonger\sthan\n20,000\sbytes. +D 2012-12-12T14:30:03.521 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -121,7 +121,7 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c cab40f2c1fe79d6eb93d3b4086c78c41ad2fa5d0 F src/bitvec.c 26675fe8e431dc555e6f2d0e11e651d172234aa1 F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c eccee944cb2221e919d7a855e5928d8643194b14 +F src/btree.c 7a80e4a67f32a2494c383a28a495bf3bd71cc230 F src/btree.h 3ad7964d6c5b1c7bff569aab6adfa075f8bf06cd F src/btreeInt.h 4e5c2bd0f9b36b2a815a6d84f771a61a65830621 F src/build.c f4f86c07002c6f3ee96c1e34e0e993a962ef2c73 @@ -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 81d9ee0f0df7f283e69ba1c2845339ea3318cd66 -R 6da93546e03c634583a9df9f228276ed -T +bgcolor * #d0c0ff -T +sym-release * -T +sym-version-3.7.15 * -U dan -Z c2b6a5ded805ac723be241fad17cc751 +P cd0b37c52658bfdf992b1e3dc467bae1835a94ae +R c074324819c1cb0b1b063013401b3d51 +U drh +Z 0c15681c6bbc86d62fafd9ce06645760 diff --git a/manifest.uuid b/manifest.uuid index 940b7402c1..3f1ad31ac9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cd0b37c52658bfdf992b1e3dc467bae1835a94ae \ No newline at end of file +120c82d56ecb574f46db01679317b8062d8779ed \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 246843b79e..bb64e643b0 100644 --- a/src/btree.c +++ b/src/btree.c @@ -8026,7 +8026,7 @@ char *sqlite3BtreeIntegrityCheck( } i = PENDING_BYTE_PAGE(pBt); if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); - sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000); + sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); sCheck.errMsg.useMalloc = 2; /* Check the integrity of the freelist From 4ac391fc05b6c696b81eb3d387f496256751bcba Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 13 Dec 2012 16:37:10 +0000 Subject: [PATCH 09/38] Attempt to further generalize the min/max optimization so that, if an appropriate index exists, it can be used by any aggregate query that contains only a single aggregate of the form max(colname) or min(colname) and does not contain a GROUP BY clause. FossilOrigin-Name: 7280e14cd8f55896451847b432171e8750a07c81 --- manifest | 19 +++++----- manifest.uuid | 2 +- src/select.c | 69 ++++++++++++++++++++++--------------- test/minmax.test | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 36 deletions(-) diff --git a/manifest b/manifest index aef5a7ffec..a4ed9e2e1c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sthe\serror\smessage\sfrom\s"PRAGMA\sintegrity_check"\sto\sbe\slonger\sthan\n20,000\sbytes. -D 2012-12-12T14:30:03.521 +C Attempt\sto\sfurther\sgeneralize\sthe\smin/max\soptimization\sso\sthat,\sif\san\sappropriate\sindex\sexists,\sit\scan\sbe\sused\sby\sany\saggregate\squery\sthat\scontains\sonly\sa\ssingle\saggregate\sof\sthe\sform\smax(colname)\sor\smin(colname)\sand\sdoes\snot\scontain\sa\sGROUP\sBY\sclause. +D 2012-12-13T16:37:10.621 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -174,7 +174,7 @@ F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c cdd546d62da7763119ea1fa455a898959e03457f F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c e6daa524bbdfa98f4abdb8cb281498f0047d3161 +F src/select.c f6c73171209b4af4d6b4258b6740415790415e2e F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 @@ -613,7 +613,7 @@ F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6 F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 F test/memsubsys1.test a8f9e37567453a5d1d9d37ec102d4d88ab6be33f F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9 -F test/minmax.test c61518429e66e228efc79661fbd2dc3e4924ec44 +F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef @@ -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 cd0b37c52658bfdf992b1e3dc467bae1835a94ae -R c074324819c1cb0b1b063013401b3d51 -U drh -Z 0c15681c6bbc86d62fafd9ce06645760 +P 120c82d56ecb574f46db01679317b8062d8779ed +R 39203e605a8f25e92556c6b1c71c785c +T *branch * minmax-opt +T *sym-minmax-opt * +T -sym-trunk * +U dan +Z ab1716797ba85b3e90d83237db257cf3 diff --git a/manifest.uuid b/manifest.uuid index 3f1ad31ac9..47b9352d93 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -120c82d56ecb574f46db01679317b8062d8779ed \ No newline at end of file +7280e14cd8f55896451847b432171e8750a07c81 \ No newline at end of file diff --git a/src/select.c b/src/select.c index a90877dc61..2568cf7081 100644 --- a/src/select.c +++ b/src/select.c @@ -3160,34 +3160,43 @@ static int flattenSubquery( #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ /* -** Analyze the SELECT statement passed as an argument to see if it -** is a min() or max() query. Return WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX if -** it is, or 0 otherwise. At present, a query is considered to be -** a min()/max() query if: +** Based on the contents of the AggInfo structure indicated by the first +** argument, this function checks if the following are true: ** -** 1. There is a single object in the FROM clause. +** * the query contains just a single aggregate function, +** * the aggregate function is either min() or max(), and +** * the argument to the aggregate function is a column value. ** -** 2. There is a single expression in the result set, and it is -** either min(x) or max(x), where x is a column reference. +** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX +** is returned as appropriate. Also, *ppMinMax is set to point to the +** list of arguments passed to the aggregate before returning. +** +** Or, if the conditions above are not met, *ppMinMax is set to 0 and +** WHERE_ORDERBY_NORMAL is returned. */ -static u8 minMaxQuery(Select *p){ - Expr *pExpr; - ExprList *pEList = p->pEList; +static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){ + int eRet = WHERE_ORDERBY_NORMAL; /* Return value */ - if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL; - pExpr = pEList->a[0].pExpr; - if( pExpr->op!=TK_AGG_FUNCTION ) return 0; - if( NEVER(ExprHasProperty(pExpr, EP_xIsSelect)) ) return 0; - pEList = pExpr->x.pList; - if( pEList==0 || pEList->nExpr!=1 ) return 0; - if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - if( sqlite3StrICmp(pExpr->u.zToken,"min")==0 ){ - return WHERE_ORDERBY_MIN; - }else if( sqlite3StrICmp(pExpr->u.zToken,"max")==0 ){ - return WHERE_ORDERBY_MAX; + *ppMinMax = 0; + if( pAggInfo->nFunc==1 ){ + Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */ + ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */ + + assert( pExpr->op==TK_AGG_FUNCTION ); + if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){ + const char *zFunc = pExpr->u.zToken; + if( sqlite3StrICmp(zFunc, "min")==0 ){ + eRet = WHERE_ORDERBY_MIN; + *ppMinMax = pEList; + }else if( sqlite3StrICmp(zFunc, "max")==0 ){ + eRet = WHERE_ORDERBY_MAX; + *ppMinMax = pEList; + } + } } - return WHERE_ORDERBY_NORMAL; + + assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 ); + return eRet; } /* @@ -4527,11 +4536,17 @@ int sqlite3Select( ** Refer to code and comments in where.c for details. */ ExprList *pMinMax = 0; - u8 flag = minMaxQuery(p); + u8 flag = WHERE_ORDERBY_NORMAL; + + assert( p->pGroupBy==0 ); + assert( flag==0 ); + if( p->pHaving==0 ){ + flag = minMaxQuery(&sAggInfo, &pMinMax); + } + assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) ); + if( flag ){ - assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) ); - assert( p->pEList->a[0].pExpr->x.pList->nExpr==1 ); - pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0); + pMinMax = sqlite3ExprListDup(db, pMinMax, 0); pDel = pMinMax; if( pMinMax && !db->mallocFailed ){ pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0; diff --git a/test/minmax.test b/test/minmax.test index 3621375ee7..fb9bbb383a 100644 --- a/test/minmax.test +++ b/test/minmax.test @@ -17,6 +17,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set ::testprefix minmax do_test minmax-1.0 { execsql { @@ -536,7 +537,96 @@ do_test minmax-12.17 { } } {5} +#------------------------------------------------------------------------- +reset_db +proc do_test_13 {op name sql1 sql2 res} { + set ::sqlite_search_count 0 + uplevel [list do_execsql_test $name.1 $sql1 $res] + set a $::sqlite_search_count + + set ::sqlite_search_count 0 + uplevel [list do_execsql_test $name.2 $sql2 $res] + set b $::sqlite_search_count + + uplevel [list do_test $name.3 [list expr "$a $op $b"] 1] +} + +# Run a test named $name. Check that SQL statements $sql1 and $sql2 both +# return the same result, but that $sql2 increments the $sqlite_search_count +# variable more often (indicating that it is visiting more rows to determine +# the result). +# +proc do_test_13_opt {name sql1 sql2 res} { + uplevel [list do_test_13 < $name $sql1 $sql2 $res] +} + +# Like [do_test_13_noopt], except this time check that the $sqlite_search_count +# variable is incremented the same number of times by both SQL statements. +# +proc do_test_13_noopt {name sql1 sql2 res} { + uplevel [list do_test_13 == $name $sql1 $sql2 $res] +} + +do_execsql_test 13.1 { + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES('a', 1, 1); + INSERT INTO t1 VALUES('b', 6, 6); + INSERT INTO t1 VALUES('c', 5, 5); + INSERT INTO t1 VALUES('a', 4, 4); + INSERT INTO t1 VALUES('a', 5, 5); + INSERT INTO t1 VALUES('c', 6, 6); + INSERT INTO t1 VALUES('b', 4, 4); + INSERT INTO t1 VALUES('c', 7, 7); + INSERT INTO t1 VALUES('b', 2, 2); + INSERT INTO t1 VALUES('b', 3, 3); + INSERT INTO t1 VALUES('a', 3, 3); + INSERT INTO t1 VALUES('b', 5, 5); + INSERT INTO t1 VALUES('c', 4, 4); + INSERT INTO t1 VALUES('c', 3, 3); + INSERT INTO t1 VALUES('a', 2, 2); + SELECT * FROM t1 ORDER BY a, b, c; +} {a 1 1 a 2 2 a 3 3 a 4 4 a 5 5 + b 2 2 b 3 3 b 4 4 b 5 5 b 6 6 + c 3 3 c 4 4 c 5 5 c 6 6 c 7 7 +} +do_execsql_test 13.2 { CREATE INDEX i1 ON t1(a, b, c) } + +do_test_13_opt 13.3 { + SELECT min(b) FROM t1 WHERE a='b' +} { + SELECT min(c) FROM t1 WHERE a='b' +} {2} + +do_test_13_opt 13.4 { + SELECT a, min(b) FROM t1 WHERE a='b' +} { + SELECT a, min(c) FROM t1 WHERE a='b' +} {b 2} + +do_test_13_opt 13.4 { + SELECT a||c, max(b)+4 FROM t1 WHERE a='c' +} { + SELECT a||c, max(c)+4 FROM t1 WHERE a='c' +} {c7 11} + +do_test_13_noopt 13.5 { + SELECT a||c, max(b+1) FROM t1 WHERE a='c' +} { + SELECT a||c, max(c+1) FROM t1 WHERE a='c' +} {c7 8} + +do_test_13_noopt 13.6 { + SELECT count(b) FROM t1 WHERE a='c' +} { + SELECT count(c) FROM t1 WHERE a='c' +} {5} + +do_test_13_noopt 13.7 { + SELECT min(b), count(b) FROM t1 WHERE a='a'; +} { + SELECT min(c), count(c) FROM t1 WHERE a='a'; +} {1 5} finish_test From 92f76572e06b6f8e70781299806154aeb726fdd1 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 13 Dec 2012 18:51:53 +0000 Subject: [PATCH 10/38] Increase the version number to 3.7.16 in advance of adding new features for the next release. FossilOrigin-Name: 8bcf5f51d8a6ea47ba4eb8de001d7a30e0a5f2c3 --- VERSION | 2 +- configure | 47 +++++++++++++++++++++++------------------------ manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/VERSION b/VERSION index 03d7b8fb9b..36f601f10e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.7.15 +3.7.16 diff --git a/configure b/configure index badc9da34e..f8a60e1625 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.62 for sqlite 3.7.15. +# Generated by GNU Autoconf 2.62 for sqlite 3.7.16. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. @@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.7.15' -PACKAGE_STRING='sqlite 3.7.15' +PACKAGE_VERSION='3.7.16' +PACKAGE_STRING='sqlite 3.7.16' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. @@ -1484,7 +1484,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.7.15 to adapt to many kinds of systems. +\`configure' configures sqlite 3.7.16 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1549,7 +1549,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.7.15:";; + short | recursive ) echo "Configuration of sqlite 3.7.16:";; esac cat <<\_ACEOF @@ -1665,7 +1665,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.7.15 +sqlite configure 3.7.16 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1679,7 +1679,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.7.15, which was +It was created by sqlite $as_me 3.7.16, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ @@ -3733,13 +3733,13 @@ if test "${lt_cv_nm_interface+set}" = set; then else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:3737: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3736: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3740: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3739: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3743: output\"" >&5) + (eval echo "\"\$as_me:3742: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -4961,7 +4961,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 4965 "configure"' > conftest.$ac_ext + echo '#line 4964 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -6830,11 +6830,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6834: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6833: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6838: \$? = $ac_status" >&5 + echo "$as_me:6837: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7169,11 +7169,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7173: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7172: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7177: \$? = $ac_status" >&5 + echo "$as_me:7176: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7274,11 +7274,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7278: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7277: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7282: \$? = $ac_status" >&5 + echo "$as_me:7281: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -7329,11 +7329,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7333: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7332: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7337: \$? = $ac_status" >&5 + echo "$as_me:7336: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -10142,7 +10142,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10146 "configure" +#line 10145 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10238,7 +10238,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10242 "configure" +#line 10241 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12908,7 +12908,6 @@ $as_echo "file not found" >&6; } - fi fi if test "${use_tcl}" = "no" ; then @@ -14033,7 +14032,7 @@ exec 6>&1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.7.15, which was +This file was extended by sqlite $as_me 3.7.16, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14086,7 +14085,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.7.15 +sqlite config.status 3.7.16 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff --git a/manifest b/manifest index aef5a7ffec..625ee0ceaa 100644 --- a/manifest +++ b/manifest @@ -1,12 +1,12 @@ -C Allow\sthe\serror\smessage\sfrom\s"PRAGMA\sintegrity_check"\sto\sbe\slonger\sthan\n20,000\sbytes. -D 2012-12-12T14:30:03.521 +C Increase\sthe\sversion\snumber\sto\s3.7.16\sin\sadvance\sof\sadding\snew\sfeatures\nfor\sthe\snext\srelease. +D 2012-12-13T18:51:53.652 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.msc 5a3b6f34d263b01f8b798c291fac1529fd650308 F Makefile.vxworks b18ad88e9a8c6a001f5cf4a389116a4f1a7ab45f F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 -F VERSION edab4af5a4623f8198833ea481ce98ab53750a8d +F VERSION 6d4f66eaebabc42ef8c2a4d2d0caf4ce7ee81137 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 @@ -15,7 +15,7 @@ F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure 252992f6588107ebd5d17c956d90cf7484580c6f x +F configure 00d3a5fda47ee7dd3e68dc9dad7eeb439cd1ffea x F configure.ac 81c43d151d0b0e406be056394cc9ff4cb3fd0444 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1 @@ -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 cd0b37c52658bfdf992b1e3dc467bae1835a94ae -R c074324819c1cb0b1b063013401b3d51 +P 120c82d56ecb574f46db01679317b8062d8779ed +R b094590aecd4a0f996453171da31d150 U drh -Z 0c15681c6bbc86d62fafd9ce06645760 +Z 0c1a6c038401cf9c1a2250c525824afb diff --git a/manifest.uuid b/manifest.uuid index 3f1ad31ac9..086ee95efe 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -120c82d56ecb574f46db01679317b8062d8779ed \ No newline at end of file +8bcf5f51d8a6ea47ba4eb8de001d7a30e0a5f2c3 \ No newline at end of file From e40ed78c1c18326aead4ea1d12c2c3c2d8858093 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 14 Dec 2012 15:36:17 +0000 Subject: [PATCH 11/38] Fix the virtual table IN optimizer so that it work even if the virtual table implementation leaves the sqlite3_index_info.aConstraintUsage[].omit flag clear for an equality constraint that it intends to use. FossilOrigin-Name: d6e045f89c5a4b500b1da7ea1224b132909bafc6 --- manifest | 15 +++--- manifest.uuid | 2 +- src/where.c | 125 ++++++++++++++++++++++++++++++-------------------- 3 files changed, 82 insertions(+), 60 deletions(-) diff --git a/manifest b/manifest index f9f1a4b2c9..054bc8cc9a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enable\soptimization\sof\sIN\soperators\son\sconstraints\sto\svirtual\stables. -D 2012-10-16T23:17:14.207 +C Fix\sthe\svirtual\stable\sIN\soptimizer\sso\sthat\sit\swork\seven\sif\sthe\svirtual\stable\nimplementation\sleaves\sthe\ssqlite3_index_info.aConstraintUsage[].omit\sflag\nclear\sfor\san\sequality\sconstraint\sthat\sit\sintends\sto\suse. +D 2012-12-14T15:36:17.947 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5f4f26109f9d80829122e0e09f9cda008fa065fb F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -249,7 +249,7 @@ F src/vtab.c 9c64ae18af78c740610df841c6f49fc2d240a279 F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2 F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b -F src/where.c 62f667db8cdbb81028bf1c55ba4b0e845b79622c +F src/where.c 87c95ca9005909af852a3a1b62451a43cf76fb6a F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00 @@ -1021,10 +1021,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9 -P 2c3af657fee6153842d660a6ce29aa7d791ebd38 -R 5cbef12c646a3899761eba454f8c4105 -T *branch * vtab-IN-opt -T *sym-vtab-IN-opt * -T -sym-trunk * +P aa650746b19e4a6a373f7e47effff3ab2f48e78c +R 53d6793b7f79df12237947dc6769b11d U drh -Z 07568d163c180c755b6278e856d28eda +Z b558501f7c6a3f6651858b319af65966 diff --git a/manifest.uuid b/manifest.uuid index 1c366b32f0..22bae2629b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aa650746b19e4a6a373f7e47effff3ab2f48e78c \ No newline at end of file +d6e045f89c5a4b500b1da7ea1224b132909bafc6 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 586d069b3f..b07709581d 100644 --- a/src/where.c +++ b/src/where.c @@ -2193,6 +2193,7 @@ static void bestVirtualIndex(WhereBestIdx *p){ WhereTerm *pTerm; int i, j; int nOrderBy; + int bAllowIN; /* Allow IN optimizations */ double rCost; /* Make sure wsFlags is initialized to some sane value. Otherwise, if the @@ -2227,59 +2228,83 @@ static void bestVirtualIndex(WhereBestIdx *p){ assert( pTab->azModuleArg && pTab->azModuleArg[0] ); assert( sqlite3GetVTable(pParse->db, pTab) ); - /* Set the aConstraint[].usable fields and initialize all - ** output variables to zero. - ** - ** aConstraint[].usable is true for constraints where the right-hand - ** side contains only references to tables to the left of the current - ** table. In other words, if the constraint is of the form: - ** - ** column = expr - ** - ** and we are evaluating a join, then the constraint on column is - ** only valid if all tables referenced in expr occur to the left - ** of the table containing column. - ** - ** The aConstraints[] array contains entries for all constraints - ** on the current table. That way we only have to compute it once - ** even though we might try to pick the best index multiple times. - ** For each attempt at picking an index, the order of tables in the - ** join might be different so we have to recompute the usable flag - ** each time. + /* Try once or twice. On the first attempt, allow IN optimizations. + ** If an IN optimization is accepted, but does not set the + ** pInfo->aConstrainUsage.omit flag, then it will not work (because it + ** will allow duplicate rows in the result set) so try again with + ** IN optimizations disabled. */ - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - pUsage = pIdxInfo->aConstraintUsage; - for(i=0; inConstraint; i++, pIdxCons++){ - j = pIdxCons->iTermOffset; - pTerm = &pWC->a[j]; - pIdxCons->usable = (pTerm->prereqRight&p->notReady) ? 0 : 1; - } - memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); - if( pIdxInfo->needToFreeIdxStr ){ - sqlite3_free(pIdxInfo->idxStr); - } - pIdxInfo->idxStr = 0; - pIdxInfo->idxNum = 0; - pIdxInfo->needToFreeIdxStr = 0; - pIdxInfo->orderByConsumed = 0; - /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */ - pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2); - nOrderBy = pIdxInfo->nOrderBy; - if( !p->pOrderBy ){ - pIdxInfo->nOrderBy = 0; - } - - if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ - return; - } - - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for(i=0; inConstraint; i++){ - if( pUsage[i].argvIndex>0 ){ - p->cost.used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight; + for(bAllowIN=1; bAllowIN>=0; bAllowIN--){ + /* Set the aConstraint[].usable fields and initialize all + ** output variables to zero. + ** + ** aConstraint[].usable is true for constraints where the right-hand + ** side contains only references to tables to the left of the current + ** table. In other words, if the constraint is of the form: + ** + ** column = expr + ** + ** and we are evaluating a join, then the constraint on column is + ** only valid if all tables referenced in expr occur to the left + ** of the table containing column. + ** + ** The aConstraints[] array contains entries for all constraints + ** on the current table. That way we only have to compute it once + ** even though we might try to pick the best index multiple times. + ** For each attempt at picking an index, the order of tables in the + ** join might be different so we have to recompute the usable flag + ** each time. + */ + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + pUsage = pIdxInfo->aConstraintUsage; + for(i=0; inConstraint; i++, pIdxCons++){ + j = pIdxCons->iTermOffset; + pTerm = &pWC->a[j]; + if( (pTerm->prereqRight&p->notReady)==0 + && (bAllowIN || pTerm->eOperator!=WO_IN) + ){ + pIdxCons->usable = 1; + }else{ + pIdxCons->usable = 0; + } } + memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); + if( pIdxInfo->needToFreeIdxStr ){ + sqlite3_free(pIdxInfo->idxStr); + } + pIdxInfo->idxStr = 0; + pIdxInfo->idxNum = 0; + pIdxInfo->needToFreeIdxStr = 0; + pIdxInfo->orderByConsumed = 0; + /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */ + pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2); + nOrderBy = pIdxInfo->nOrderBy; + if( !p->pOrderBy ){ + pIdxInfo->nOrderBy = 0; + } + + if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ + return; + } + + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pIdxCons++){ + if( pUsage[i].argvIndex>0 ){ + j = pIdxCons->iTermOffset; + pTerm = &pWC->a[j]; + p->cost.used |= pTerm->prereqRight; + if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){ + /* Do not attempt to use an IN constraint if the virtual table + ** says that the equivalent EQ constraint cannot be safely omitted. + ** If we do attempt to use such a constraint, some rows might be + ** repeated in the output. */ + break; + } + } + } + if( i>=pIdxInfo->nConstraint ) break; } - + /* If there is an ORDER BY clause, and the selected virtual table index ** does not satisfy it, increase the cost of the scan accordingly. This ** matches the processing for non-virtual tables in bestBtreeIndex(). From d03025327e616e52a2063cadf57830f9f2175419 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 14 Dec 2012 17:48:08 +0000 Subject: [PATCH 12/38] Remove an unreachable branch. Improvements to comments. FossilOrigin-Name: d2fb7619b063b329b6d7ba9a16a7290e5d868f23 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/where.c | 14 +++++++++----- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 940b592a46..9f25e3efc6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sin\sall\sthe\strunk\schanges\sthat\shave\soccurred\ssince\sthis\sbranch\swas\nopened. -D 2012-12-14T15:54:05.684 +C Remove\san\sunreachable\sbranch.\s\sImprovements\sto\scomments. +D 2012-12-14T17:48:08.139 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 6bcd38a2bc9a2d2ed8dc9b622625efb8c53e8911 +F src/where.c 7bdfde434b14ff88cc52ae41ac235ee1acb6b1e1 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 @@ -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 d6e045f89c5a4b500b1da7ea1224b132909bafc6 52e755943f87354febe214e5dc3b423a1e38ba80 -R 31359f190d617098d7e9f461bfe956e6 +P 6d507e4de924e2f4556ea4a03ee0fa7d647bf8bb +R fea13d1ea2e8500bc200a6665b5fa212 U drh -Z 9b9491b018acf588c19d70e23c3069cb +Z b1649d740bfab2686a02d105140cda9c diff --git a/manifest.uuid b/manifest.uuid index f44adeed60..f6dc61ed96 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6d507e4de924e2f4556ea4a03ee0fa7d647bf8bb \ No newline at end of file +d2fb7619b063b329b6d7ba9a16a7290e5d868f23 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 2573faff28..1be1b20a49 100644 --- a/src/where.c +++ b/src/where.c @@ -2247,12 +2247,16 @@ static void bestVirtualIndex(WhereBestIdx *p){ assert( sqlite3GetVTable(pParse->db, pTab) ); /* Try once or twice. On the first attempt, allow IN optimizations. - ** If an IN optimization is accepted, but does not set the - ** pInfo->aConstrainUsage.omit flag, then it will not work (because it - ** will allow duplicate rows in the result set) so try again with - ** IN optimizations disabled. + ** If an IN optimization is accepted by the virtual table xBestIndex + ** method, but the pInfo->aConstrainUsage.omit flag is not set, then + ** the query will not work because it might allow duplicate rows in + ** output. In that case, run the xBestIndex method a second time + ** without the IN constraints. Usually this loop only runs once. + ** The loop will exit using a "break" statement. */ - for(bAllowIN=1; bAllowIN>=0; bAllowIN--){ + for(bAllowIN=1; 1; bAllowIN--){ + assert( bAllowIN==0 || bAllowIN==1 ); + /* Set the aConstraint[].usable fields and initialize all ** output variables to zero. ** From 6c5b915fcb78e067fc8a6db022d5fc77aafe2e60 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 17 Dec 2012 16:46:37 +0000 Subject: [PATCH 13/38] 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 14/38] 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 15/38] "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 16/38] 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 17/38] 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 18/38] 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 985cd59c488254e8ec1bc83a7537d030922e2d88 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 18 Dec 2012 11:59:39 +0000 Subject: [PATCH 19/38] On atomic-write capable systems, if copying the contents of an in-memory journal to disk fails, close the (on disk) journal file before returning the error to the caller. This causes the subsequent rollback operation to use the in-memory journal. Fix for [df678d738adb]. FossilOrigin-Name: 8183d8d7ae1ff4bad2fcc01adb923b966b347832 --- manifest | 15 +++++---- manifest.uuid | 2 +- src/journal.c | 8 +++++ test/ioerr6.test | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 test/ioerr6.test diff --git a/manifest b/manifest index bd226934d5..c09bd79c58 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 On\satomic-write\scapable\ssystems,\sif\scopying\sthe\scontents\sof\san\sin-memory\sjournal\sto\sdisk\sfails,\sclose\sthe\s(on\sdisk)\sjournal\sfile\sbefore\sreturning\sthe\serror\sto\sthe\scaller.\sThis\scauses\sthe\ssubsequent\srollback\soperation\sto\suse\sthe\sin-memory\sjournal.\sFix\sfor\s[df678d738adb]. +D 2012-12-18T11:59:39.046 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -139,7 +139,7 @@ F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 F src/insert.c dc197aa9293a26d300eb5378880e701f7b20fefa -F src/journal.c eb7b9f5e783266521bcd9b2b93d419a219411f71 +F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d @@ -555,6 +555,7 @@ F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4 +F test/ioerr6.test 13f0f9c31d706f0dd575995c369af07c0227e9a2 F test/join.test 8d63cc4d230a7affafa4b6ab0b97c49b8ccb365c F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 @@ -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 52e755943f87354febe214e5dc3b423a1e38ba80 d2fb7619b063b329b6d7ba9a16a7290e5d868f23 -R fea13d1ea2e8500bc200a6665b5fa212 -U drh -Z 55c2c2e92a55016f5394cb88b53de07c +P 3d65c70343196b8f69c5293e7703839846fade85 +R e1810ef3af67331248d21f17d27662cf +U dan +Z 4ee7a84627eefb575e0cd9aa392cf624 diff --git a/manifest.uuid b/manifest.uuid index e5bf82bc6d..78f7e7a93e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3d65c70343196b8f69c5293e7703839846fade85 \ No newline at end of file +8183d8d7ae1ff4bad2fcc01adb923b966b347832 \ No newline at end of file diff --git a/src/journal.c b/src/journal.c index 06605cc956..fed27be3e3 100644 --- a/src/journal.c +++ b/src/journal.c @@ -59,6 +59,14 @@ static int createFile(JournalFile *p){ assert(p->iSize<=p->nBuf); rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0); } + if( rc!=SQLITE_OK ){ + /* If an error occurred while writing to the file, close it before + ** returning. This way, SQLite uses the in-memory journal data to + ** roll back changes made to the internal page-cache before this + ** function was called. */ + sqlite3OsClose(pReal); + p->pReal = 0; + } } } return rc; diff --git a/test/ioerr6.test b/test/ioerr6.test new file mode 100644 index 0000000000..d2f10b142d --- /dev/null +++ b/test/ioerr6.test @@ -0,0 +1,83 @@ +# 2012 December 18 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +set ::testprefix ioerr6 + +ifcapable !atomicwrite { + puts "skipping tests - not compiled with SQLITE_ENABLE_ATOMIC_WRITE..." + finish_test + return +} +faultsim_save_and_close + +do_test 1.1 { + testvfs shmfault -default true + shmfault devchar atomic + sqlite3 db test.db + execsql { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(2, 4); + INSERT INTO t1 VALUES(3, 6); + INSERT INTO t1 VALUES(4, 8); + } + + # Cause the first call to xWrite() to fail with SQLITE_FULL. + shmfault full 2 1 + catchsql { INSERT INTO t1 VALUES(5, 10) } +} {1 {database or disk is full}} + +do_test 1.2 { + execsql { PRAGMA integrity_check } +} {ok} + +db close +shmfault delete + +do_faultsim_test 2 -faults full* -prep { + shmfault devchar atomic + faultsim_restore + sqlite3 db test.db +} -body { + db eval { + CREATE TABLE t1(x PRIMARY KEY); + INSERT INTO t1 VALUES('abc'); + } +} -test { + set res [db one { PRAGMA integrity_check }] + if {$res != "ok"} { + error "integrity check: $res" + } +} + +do_faultsim_test 2 -faults full* -prep { + shmfault devchar atomic + faultsim_restore + sqlite3 db test.db +} -body { + db eval { + CREATE TABLE t1(x); + CREATE TABLE t2(x); + } +} -test { + db eval { CREATE TABLE t3(x) } + if {[db one { PRAGMA integrity_check }] != "ok"} { + error "integrity check failed" + } +} + +finish_test + From e924041846ac140c7940e23aaa3d026bed2a2dcf Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 18 Dec 2012 13:12:03 +0000 Subject: [PATCH 20/38] Remove old commented-out code from parse.y. Fix some over-length source lines in parse.y. No logical changes. FossilOrigin-Name: 7e30c021abe5a559918efaa5fc0b21db8ca782ad --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/parse.y | 21 ++++++--------------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/manifest b/manifest index c09bd79c58..0a4ce285f8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C On\satomic-write\scapable\ssystems,\sif\scopying\sthe\scontents\sof\san\sin-memory\sjournal\sto\sdisk\sfails,\sclose\sthe\s(on\sdisk)\sjournal\sfile\sbefore\sreturning\sthe\serror\sto\sthe\scaller.\sThis\scauses\sthe\ssubsequent\srollback\soperation\sto\suse\sthe\sin-memory\sjournal.\sFix\sfor\s[df678d738adb]. -D 2012-12-18T11:59:39.046 +C Remove\sold\scommented-out\scode\sfrom\sparse.y.\s\sFix\ssome\sover-length\ssource\slines\nin\sparse.y.\s\sNo\slogical\schanges. +D 2012-12-18T13:12:03.056 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -164,7 +164,7 @@ F src/os_unix.c ad459bb62eb6f3f6aae26d97b1a28fbac7bf0260 F src/os_win.c ce1f5db8a7bb4d6f2092b1b2cb9631bec54a6320 F src/pager.c 4092c907222cfd451c74fe6bd2fd64b342f7190f F src/pager.h 1109a06578ec5574dc2c74cf8d9f69daf36fe3e0 -F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 +F src/parse.y 15fdc2c98cbde920c2f6a0fcfd31ace646c45947 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 @@ -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 3d65c70343196b8f69c5293e7703839846fade85 -R e1810ef3af67331248d21f17d27662cf -U dan -Z 4ee7a84627eefb575e0cd9aa392cf624 +P 8183d8d7ae1ff4bad2fcc01adb923b966b347832 +R daafcb2309a3c630a36e62d610f5b299 +U drh +Z 712edd7eaa373607183a91c32998bfdc diff --git a/manifest.uuid b/manifest.uuid index 78f7e7a93e..da17717c5a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8183d8d7ae1ff4bad2fcc01adb923b966b347832 \ No newline at end of file +7e30c021abe5a559918efaa5fc0b21db8ca782ad \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index 0bfe823d45..1d95007f04 100644 --- a/src/parse.y +++ b/src/parse.y @@ -499,7 +499,8 @@ stl_prefix(A) ::= seltablist(X) joinop(Y). { if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].jointype = (u8)Y; } stl_prefix(A) ::= . {A = 0;} -seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) using_opt(U). { +seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) + on_opt(N) using_opt(U). { A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U); sqlite3SrcListIndexedBy(pParse, A, &I); } @@ -519,18 +520,6 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) usi A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,pSubquery,N,U); } } - - // A seltablist_paren nonterminal represents anything in a FROM that - // is contained inside parentheses. This can be either a subquery or - // a grouping of table and subqueries. - // -// %type seltablist_paren {Select*} -// %destructor seltablist_paren {sqlite3SelectDelete(pParse->db, $$);} -// seltablist_paren(A) ::= select(S). {A = S;} -// seltablist_paren(A) ::= seltablist(F). { -// sqlite3SrcListShiftJoinType(F); -// A = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0); -// } %endif SQLITE_OMIT_SUBQUERY %type dbnm {Token} @@ -653,7 +642,8 @@ where_opt(A) ::= WHERE expr(X). {A = X.pExpr;} ////////////////////////// The UPDATE command //////////////////////////////// // %ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) orderby_opt(O) limit_opt(L). { +cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) + orderby_opt(O) limit_opt(L). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE"); @@ -661,7 +651,8 @@ cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) } %endif %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W). { +cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) + where_opt(W). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); sqlite3Update(pParse,X,Y,W,R); From 92689d28698dca60d04e62a5080afac923dfd4de Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 18 Dec 2012 16:07:08 +0000 Subject: [PATCH 21/38] Resolve names in FROM-clause subqueries prior to resolving names in the result set expressions of a SELECT statement. FossilOrigin-Name: 9b67c633d932f3e566f521ee6a9cf3be193436fa --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/resolve.c | 34 +++++++++++++++++----------------- test/auth2.test | 12 ++++++------ 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/manifest b/manifest index 0a4ce285f8..49f083d71f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sold\scommented-out\scode\sfrom\sparse.y.\s\sFix\ssome\sover-length\ssource\slines\nin\sparse.y.\s\sNo\slogical\schanges. -D 2012-12-18T13:12:03.056 +C Resolve\snames\sin\sFROM-clause\ssubqueries\sprior\sto\sresolving\snames\sin\sthe\nresult\sset\sexpressions\sof\sa\sSELECT\sstatement. +D 2012-12-18T16:07:08.090 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -172,7 +172,7 @@ F src/pragma.c 015723c48072781d2740e310ab04dc92956b76d1 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 -F src/resolve.c cdd546d62da7763119ea1fa455a898959e03457f +F src/resolve.c 52331299f4095397d6d00715b70cd153baa11931 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c f6c73171209b4af4d6b4258b6740415790415e2e F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019 @@ -280,7 +280,7 @@ F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0 F test/auth.test 304e82f31592820d3bde26ab6b75deaa123e1a6f -F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882 +F test/auth2.test a2a371aa6df15f8b0c8109b33d3d7f0f73e4c9aa F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf F test/autoindex1.test 058d0b331ae6840a61bbee910d8cbae27bfd5991 @@ -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 8183d8d7ae1ff4bad2fcc01adb923b966b347832 -R daafcb2309a3c630a36e62d610f5b299 +P 7e30c021abe5a559918efaa5fc0b21db8ca782ad +R cf6ce0613e2ebca30b9d8e27d1083ff8 U drh -Z 712edd7eaa373607183a91c32998bfdc +Z 59025edc3e6d7e0bdfe9bc1ec0c29fc3 diff --git a/manifest.uuid b/manifest.uuid index da17717c5a..d887428c90 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7e30c021abe5a559918efaa5fc0b21db8ca782ad \ No newline at end of file +9b67c633d932f3e566f521ee6a9cf3be193436fa \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index 51aab7792b..eb594d1598 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1033,23 +1033,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ return WRC_Abort; } - /* Set up the local name-context to pass to sqlite3ResolveExprNames() to - ** resolve the result-set expression list. - */ - sNC.ncFlags = NC_AllowAgg; - sNC.pSrcList = p->pSrc; - sNC.pNext = pOuterNC; - - /* Resolve names in the result set. */ - pEList = p->pEList; - assert( pEList!=0 ); - for(i=0; inExpr; i++){ - Expr *pX = pEList->a[i].pExpr; - if( sqlite3ResolveExprNames(&sNC, pX) ){ - return WRC_Abort; - } - } - /* Recursively resolve names in all subqueries */ for(i=0; ipSrc->nSrc; i++){ @@ -1077,6 +1060,23 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } + /* Set up the local name-context to pass to sqlite3ResolveExprNames() to + ** resolve the result-set expression list. + */ + sNC.ncFlags = NC_AllowAgg; + sNC.pSrcList = p->pSrc; + sNC.pNext = pOuterNC; + + /* Resolve names in the result set. */ + pEList = p->pEList; + assert( pEList!=0 ); + for(i=0; inExpr; i++){ + Expr *pX = pEList->a[i].pExpr; + if( sqlite3ResolveExprNames(&sNC, pX) ){ + return WRC_Abort; + } + } + /* If there are no aggregate functions in the result-set, and no GROUP BY ** expression, do not allow aggregates in any of the other expressions. */ diff --git a/test/auth2.test b/test/auth2.test index f5dba1482a..9343fd6f71 100644 --- a/test/auth2.test +++ b/test/auth2.test @@ -131,12 +131,12 @@ do_test auth2-2.3 { } set ::authargs } {SQLITE_SELECT {} {} {} {} -SQLITE_READ v2 a main {} -SQLITE_READ v2 b main {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 +SQLITE_READ v2 a main {} +SQLITE_READ v2 b main {} SQLITE_SELECT {} {} {} v2 } do_test auth2-2.4 { @@ -149,20 +149,20 @@ do_test auth2-2.4 { } set ::authargs } {SQLITE_SELECT {} {} {} {} -SQLITE_READ v2 b main {} -SQLITE_READ v2 a main {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 +SQLITE_READ v2 b main {} +SQLITE_READ v2 a main {} SQLITE_SELECT {} {} {} v2 SQLITE_SELECT {} {} {} {} -SQLITE_READ v2 b main {} -SQLITE_READ v2 a main {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 +SQLITE_READ v2 b main {} +SQLITE_READ v2 a main {} SQLITE_SELECT {} {} {} v2 } db2 close From 832ee3d4ccf2941b17e718743ae7283f2e97a571 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 18 Dec 2012 19:36:11 +0000 Subject: [PATCH 22/38] Optimize the degenerate case of a FROM clause table name enclosed all by itself inside parentheses. Generate code as if the parentheses did not exist, rather than the old behavior of manifesting the parenthesized table into a transient table. Also, tag every FROM-clause SELECT subquery that is generated by a parenthesized FROM-clause expression using the SF_NestedFrom flag. The new SF_NestedFrom flag is not yet used for anything. FossilOrigin-Name: 7fecced466d86a66b0b751c5b5608141e134fe2d --- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/parse.y | 16 +++++++++++++--- src/select.c | 4 ++-- src/sqliteInt.h | 3 ++- test/where8.test | 14 ++++++++++++++ test/where9.test | 18 +++++++++++++++++- 7 files changed, 59 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 49f083d71f..9c799f1bef 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Resolve\snames\sin\sFROM-clause\ssubqueries\sprior\sto\sresolving\snames\sin\sthe\nresult\sset\sexpressions\sof\sa\sSELECT\sstatement. -D 2012-12-18T16:07:08.090 +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 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -164,7 +164,7 @@ F src/os_unix.c ad459bb62eb6f3f6aae26d97b1a28fbac7bf0260 F src/os_win.c ce1f5db8a7bb4d6f2092b1b2cb9631bec54a6320 F src/pager.c 4092c907222cfd451c74fe6bd2fd64b342f7190f F src/pager.h 1109a06578ec5574dc2c74cf8d9f69daf36fe3e0 -F src/parse.y 15fdc2c98cbde920c2f6a0fcfd31ace646c45947 +F src/parse.y 5d5e12772845805fdfeb889163516b84fbb9ae95 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 @@ -174,12 +174,12 @@ F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 52331299f4095397d6d00715b70cd153baa11931 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c f6c73171209b4af4d6b4258b6740415790415e2e +F src/select.c 5eab6941c0ac97355817f846b77cd20bfdf5a82e 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 f7581eb79d822a6993d6aa31baf1e67e6ff17d19 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -969,9 +969,9 @@ F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b F test/where7.test 5c566388f0cc318b0032ce860f4ac5548e3c265a -F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f +F test/where8.test 02619a9bfc6df7b19979a02852bac09c3c99477a F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 -F test/where9.test bcab47eff78f1412a6aec1d6b8a3939d4a9db098 +F test/where9.test 0157862ccf0cfdf1a4622cdf697e5e2f09a8de44 F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5 @@ -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 7e30c021abe5a559918efaa5fc0b21db8ca782ad -R cf6ce0613e2ebca30b9d8e27d1083ff8 +P 9b67c633d932f3e566f521ee6a9cf3be193436fa +R d9a19320be87db9daf2926b8e71a7df8 U drh -Z 59025edc3e6d7e0bdfe9bc1ec0c29fc3 +Z bc761a01222fad0c80ad013442fb2ec8 diff --git a/manifest.uuid b/manifest.uuid index d887428c90..f26e1b6ae9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9b67c633d932f3e566f521ee6a9cf3be193436fa \ No newline at end of file +7fecced466d86a66b0b751c5b5608141e134fe2d \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index 1d95007f04..abc0e7dc08 100644 --- a/src/parse.y +++ b/src/parse.y @@ -435,8 +435,8 @@ oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) // The "distinct" nonterminal is true (1) if the DISTINCT keyword is // present and false (0) if it is not. // -%type distinct {int} -distinct(A) ::= DISTINCT. {A = 1;} +%type distinct {u16} +distinct(A) ::= DISTINCT. {A = SF_Distinct;} distinct(A) ::= ALL. {A = 0;} distinct(A) ::= . {A = 0;} @@ -513,10 +513,20 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) as(Z) on_opt(N) using_opt(U). { if( X==0 && Z.n==0 && N==0 && U==0 ){ A = F; + }else if( F->nSrc==1 ){ + A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,0,N,U); + if( A ){ + struct SrcList_item *pNew = &A->a[A->nSrc-1]; + struct SrcList_item *pOld = F->a; + pNew->zName = pOld->zName; + pNew->zDatabase = pOld->zDatabase; + pOld->zName = pOld->zDatabase = 0; + } + sqlite3SrcListDelete(pParse->db, F); }else{ Select *pSubquery; sqlite3SrcListShiftJoinType(F); - pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0); + pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,SF_NestedFrom,0,0); A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,pSubquery,N,U); } } diff --git a/src/select.c b/src/select.c index 2568cf7081..711669ae80 100644 --- a/src/select.c +++ b/src/select.c @@ -55,7 +55,7 @@ Select *sqlite3SelectNew( ExprList *pGroupBy, /* the GROUP BY clause */ Expr *pHaving, /* the HAVING clause */ ExprList *pOrderBy, /* the ORDER BY clause */ - int isDistinct, /* true if the DISTINCT keyword is present */ + u16 selFlags, /* Flag parameters, such as SF_Distinct */ Expr *pLimit, /* LIMIT value. NULL means not used */ Expr *pOffset /* OFFSET value. NULL means no offset */ ){ @@ -79,7 +79,7 @@ Select *sqlite3SelectNew( pNew->pGroupBy = pGroupBy; pNew->pHaving = pHaving; pNew->pOrderBy = pOrderBy; - pNew->selFlags = isDistinct ? SF_Distinct : 0; + pNew->selFlags = selFlags; pNew->op = TK_SELECT; pNew->pLimit = pLimit; pNew->pOffset = pOffset; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 2b58a808fb..8a04ab47b0 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2102,6 +2102,7 @@ struct Select { #define SF_UseSorter 0x0040 /* Sort using a sorter */ #define SF_Values 0x0080 /* Synthesized from VALUES clause */ #define SF_Materialize 0x0100 /* Force materialization of views */ +#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ /* @@ -2814,7 +2815,7 @@ Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, void sqlite3DropIndex(Parse*, SrcList*, int); int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, - Expr*,ExprList*,int,Expr*,Expr*); + Expr*,ExprList*,u16,Expr*,Expr*); void sqlite3SelectDelete(sqlite3*, Select*); Table *sqlite3SrcListLookup(Parse*, SrcList*); int sqlite3IsReadOnly(Parse*, Table*, int); diff --git a/test/where8.test b/test/where8.test index a7d5edb3fd..3bf1790132 100644 --- a/test/where8.test +++ b/test/where8.test @@ -290,6 +290,20 @@ do_test where8-3.15 { } } {I I I I I I I I I I II II II II II II II II II II III III III III III 9 1} + +do_test where8-3.21 { + execsql_status { + SELECT a, d FROM t1, (t2) WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +do_test where8-3.22 { + execsql_status { + SELECT a, d FROM ((((((t1))), (((t2)))))) + WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} + + #----------------------------------------------------------------------- # The following tests - where8-4.* - verify that adding or removing # indexes does not change the results returned by various queries. diff --git a/test/where9.test b/test/where9.test index d618208ad6..a5c1d3935a 100644 --- a/test/where9.test +++ b/test/where9.test @@ -232,7 +232,7 @@ do_test where9-1.3.3 { } {90 91 92 97 scan 98 sort 0} do_test where9-1.3.4 { count_steps { - SELECT a FROM t4 + SELECT a FROM (t4) WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) OR (b NOT NULL AND c IS NULL AND d NOT NULL) @@ -876,5 +876,21 @@ do_test where9-8.1 { ORDER BY +a; } } {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} +do_test where9-8.2 { + db eval { + SELECT * + FROM t81 LEFT JOIN (t82) ON y=b JOIN t83 + WHERE c==p OR d==p + ORDER BY +a; + } +} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} +do_test where9-8.3 { + db eval { + SELECT * + FROM (t81) LEFT JOIN (main.t82) ON y=b JOIN t83 + WHERE c==p OR d==p + ORDER BY +a; + } +} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} finish_test From 8f25d18b21c2f8076b02ced2b57ef3bc29b56427 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 19 Dec 2012 02:36:45 +0000 Subject: [PATCH 23/38] Better resolution of table and column names in joins where some of the terms of the FROM clause are parenthesized. FossilOrigin-Name: 7344e791b9456286ecdca6d45f2f5260fb3f10e2 --- manifest | 17 ++++++++------ manifest.uuid | 2 +- src/resolve.c | 61 ++++++++++++++++++++++++++++++++++----------------- src/select.c | 6 ++++- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/manifest b/manifest index 9c799f1bef..276e397897 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 Better\sresolution\sof\stable\sand\scolumn\snames\sin\sjoins\swhere\ssome\sof\sthe\nterms\sof\sthe\sFROM\sclause\sare\sparenthesized. +D 2012-12-19T02:36:45.854 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -172,9 +172,9 @@ F src/pragma.c 015723c48072781d2740e310ab04dc92956b76d1 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 -F src/resolve.c 52331299f4095397d6d00715b70cd153baa11931 +F src/resolve.c 3104a5e4c2cb6e3a813b7266cbd541dc91e26fad F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 5eab6941c0ac97355817f846b77cd20bfdf5a82e +F src/select.c 3cce5b09cae6eaf23276bb2e5551a9d22cd7ae3d F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 @@ -1026,7 +1026,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 edfa9398c5c900d8f6d5f20d3c0d802e +T *branch * name-resolution-fix +T *sym-name-resolution-fix * +T -sym-trunk * U drh -Z bc761a01222fad0c80ad013442fb2ec8 +Z 3b6c2dbf5da1f286c0c463d165d85b47 diff --git a/manifest.uuid b/manifest.uuid index f26e1b6ae9..ee37ee7967 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7fecced466d86a66b0b751c5b5608141e134fe2d \ No newline at end of file +7344e791b9456286ecdca6d45f2f5260fb3f10e2 \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index eb594d1598..317ca3d81b 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -206,6 +206,20 @@ static int lookupName( pExpr->pTab = 0; ExprSetIrreducible(pExpr); + /* Translate the schema name in zDb into a pointer to the corresponding + ** schema. If not found, pSchema will remain NULL and nothing will match + ** resulting in an appropriate error message toward the end of this routine + */ + if( zDb ){ + for(i=0; inDb; i++){ + assert( db->aDb[i].zName ); + if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){ + pSchema = db->aDb[i].pSchema; + break; + } + } + } + /* Start at the inner-most context and move outward until a match is found */ while( pNC && cnt==0 ){ ExprList *pEList; @@ -214,31 +228,36 @@ static int lookupName( if( pSrcList ){ for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ Table *pTab; - int iDb; Column *pCol; pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( pTab->nCol>0 ); + if( zDb && pTab->pSchema!=pSchema ){ + continue; + } + if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ + ExprList *pEList = pItem->pSelect->pEList; + int hit = 0; + for(j=0; jnExpr; j++){ + if( zTab && sqlite3StrICmp(pEList->a[j].zSpan, zTab)!=0 ) continue; + if( sqlite3StrICmp(pEList->a[j].zName, zCol)==0 ){ + cnt++; + cntTab = 2; + pMatch = pItem; + pExpr->iColumn = j; + } + } + if( hit || zTab==0 ) continue; + } if( zTab ){ - if( pItem->zAlias ){ - char *zTabName = pItem->zAlias; - if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; - }else{ - char *zTabName = pTab->zName; - if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){ - continue; - } - if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){ - continue; - } + const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; + assert( zTabName!=0 ); + if( sqlite3StrICmp(zTabName, zTab)!=0 ){ + continue; } } if( 0==(cntTab++) ){ - pExpr->iTable = pItem->iCursor; - pExpr->pTab = pTab; - pSchema = pTab->pSchema; pMatch = pItem; } for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ @@ -252,17 +271,19 @@ static int lookupName( if( nameInUsingClause(pItem->pUsing, zCol) ) continue; } cnt++; - pExpr->iTable = pItem->iCursor; - pExpr->pTab = pTab; pMatch = pItem; - pSchema = pTab->pSchema; /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; break; } } } - } + if( pMatch ){ + pExpr->iTable = pMatch->iCursor; + pExpr->pTab = pMatch->pTab; + pSchema = pExpr->pTab->pSchema; + } + } /* if( pSrcList ) */ #ifndef SQLITE_OMIT_TRIGGER /* If we have not already resolved the name, then maybe diff --git a/src/select.c b/src/select.c index 711669ae80..2f20e7aa50 100644 --- a/src/select.c +++ b/src/select.c @@ -3392,7 +3392,8 @@ static int selectExpander(Walker *pWalker, Select *p){ ExprList *pNew = 0; int flags = pParse->db->flags; int longNames = (flags & SQLITE_FullColNames)!=0 - && (flags & SQLITE_ShortColNames)==0; + && (flags & SQLITE_ShortColNames)==0 + && (p->selFlags & SF_NestedFrom)==0; for(k=0; knExpr; k++){ Expr *pE = a[k].pExpr; @@ -3479,6 +3480,9 @@ static int selectExpander(Walker *pWalker, Select *p){ sColname.z = zColname; sColname.n = sqlite3Strlen30(zColname); sqlite3ExprListSetName(pParse, pNew, &sColname, 0); + if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ + pNew->a[pNew->nExpr-1].zSpan = sqlite3DbStrDup(db, zTabName); + } sqlite3DbFree(db, zToFree); } } From 0dde4739f32df29f390f961e7b63d60a1594f807 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 19 Dec 2012 13:41:03 +0000 Subject: [PATCH 24/38] Add commentary to the ExprList object to explain how zSpan is overloaded. Add test cases for the new name resolution functionality. FossilOrigin-Name: 3e7d84db7861911c9b2c7dcdabe0b213bf483d79 --- manifest | 16 +++++----- manifest.uuid | 2 +- src/sqliteInt.h | 22 ++++++++----- test/selectD.test | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 test/selectD.test diff --git a/manifest b/manifest index 276e397897..f327e2ee10 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Better\sresolution\sof\stable\sand\scolumn\snames\sin\sjoins\swhere\ssome\sof\sthe\nterms\sof\sthe\sFROM\sclause\sare\sparenthesized. -D 2012-12-19T02:36:45.854 +C Add\scommentary\sto\sthe\sExprList\sobject\sto\sexplain\show\szSpan\sis\soverloaded.\nAdd\stest\scases\sfor\sthe\snew\sname\sresolution\sfunctionality. +D 2012-12-19T13:41:03.688 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -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 f7581eb79d822a6993d6aa31baf1e67e6ff17d19 +F src/sqliteInt.h c708c868f10c491bd32a7e1210f4a314448fa728 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -698,6 +698,7 @@ F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532 F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25 F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977 +F test/selectD.test 243db87cd12f62aca4fe3f8b9465dfeab30b4d7a F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118 F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746 F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879 @@ -1026,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 edfa9398c5c900d8f6d5f20d3c0d802e -T *branch * name-resolution-fix -T *sym-name-resolution-fix * -T -sym-trunk * +P 7344e791b9456286ecdca6d45f2f5260fb3f10e2 +R be36637b6c87220d87a455d3dd649bed U drh -Z 3b6c2dbf5da1f286c0c463d165d85b47 +Z 4a19eec8d8a61f22fdf7238cb2d39c20 diff --git a/manifest.uuid b/manifest.uuid index ee37ee7967..47a5e32c0e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7344e791b9456286ecdca6d45f2f5260fb3f10e2 \ No newline at end of file +3e7d84db7861911c9b2c7dcdabe0b213bf483d79 \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 8a04ab47b0..6c10c38625 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1771,18 +1771,26 @@ struct Expr { ** list of "ID = expr" items in an UPDATE. A list of expressions can ** also be used as the argument to a function, in which case the a.zName ** field is not used. +** +** By default the Expr.zSpan field holds a human-readable description of +** the expression that is used in the generation of error messages and +** column labels. In this case, Expr.zSpan is typically the text of a +** column expression as it exists in a SELECT statement. However, if +** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name +** of the table to which the column of a FROM-clause subquery refers. */ struct ExprList { int nExpr; /* Number of expressions on the list */ int iECursor; /* VDBE Cursor associated with this ExprList */ struct ExprList_item { /* For each expression in the list */ - Expr *pExpr; /* The list of expressions */ - char *zName; /* Token associated with this expression */ - char *zSpan; /* Original text of the expression */ - u8 sortOrder; /* 1 for DESC or 0 for ASC */ - u8 done; /* A flag to indicate when processing is finished */ - u16 iOrderByCol; /* For ORDER BY, column number in result set */ - u16 iAlias; /* Index into Parse.aAlias[] for zName */ + Expr *pExpr; /* The list of expressions */ + char *zName; /* Token associated with this expression */ + char *zSpan; /* Original text of the expression */ + u8 sortOrder; /* 1 for DESC or 0 for ASC */ + unsigned done :1; /* A flag to indicate when processing is finished */ + unsigned bSpanIsTab :1; /* zSpan holds table name, not the span */ + u16 iOrderByCol; /* For ORDER BY, column number in result set */ + u16 iAlias; /* Index into Parse.aAlias[] for zName */ } *a; /* Alloc a power of two greater or equal to nExpr */ }; diff --git a/test/selectD.test b/test/selectD.test new file mode 100644 index 0000000000..8c86226434 --- /dev/null +++ b/test/selectD.test @@ -0,0 +1,78 @@ +# 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 name resolution in SELECT +# statements that have parenthesized FROM clauses. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test selectD-1.1 { + db eval { + CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(111,'x1'); + CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(222,'x2'); + CREATE TABLE t3(a,b); INSERT INTO t3 VALUES(333,'x3'); + CREATE TABLE t4(a,b); INSERT INTO t4 VALUES(444,'x4'); + + SELECT * + FROM (t1), (t2), (t3), (t4) + WHERE t4.a=t3.a+111 + AND t3.a=t2.a+111 + AND t2.a=t1.a+111; + } +} {111 x1 222 x2 333 x3 444 x4} +do_test selectD-1.2 { + db eval { + SELECT * + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } +} {111 x1 222 x2 333 x3 444 x4} +do_test selectD-1.3 { + db eval { + UPDATE t2 SET a=111; + UPDATE t3 SET a=111; + UPDATE t4 SET a=111; + SELECT * + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 USING(a)) USING (a)) USING (a); + } +} {111 x1 x2 x3 x4} +do_test selectD-1.4 { + db eval { + UPDATE t2 SET a=111; + UPDATE t3 SET a=111; + UPDATE t4 SET a=111; + SELECT * + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 USING(a)) + USING (a)) + USING (a); + } +} {111 x1 x2 x3 x4} +do_test selectD-1.5 { + db eval { + UPDATE t3 SET a=222; + UPDATE t4 SET a=222; + SELECT * + FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) + ON t1.a=t3.a-111; + } +} {111 x1 x2 222 x3 x4} +do_test selectD-1.6 { + db eval { + UPDATE t4 SET a=333; + SELECT * + FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) + ON t1.a=t3.a-111; + } +} {111 x1 x2 222 x3 {}} + +finish_test From 7ee751d241e7b2b86cbd81826e12aa729b688c28 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 19 Dec 2012 15:53:51 +0000 Subject: [PATCH 25/38] 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 26/38] 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 27/38] 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 28/38] 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 29/38] 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 30/38] 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 31/38] 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 32/38] 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 33/38] 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 */ From fb7773276b9fd7dbe4205ca68d40a4d174da5588 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 2 Jan 2013 14:57:32 +0000 Subject: [PATCH 34/38] When resolving result-set name collisions, make them x:1, x:2, x:3, etc. instead of x:1, x:1:1, x:1;1;1. FossilOrigin-Name: ef01e30456670e6b1bc67fe41ec27e52c182efaf --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/select.c | 3 +++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 7be42d45bf..4270211ad5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\sthe\slatest\strunk\schanges\sinto\sthe\sname-resolution\senhancement\sbranch. -D 2013-01-02T12:29:05.642 +C When\sresolving\sresult-set\sname\scollisions,\smake\sthem\sx:1,\sx:2,\sx:3,\setc.\ninstead\sof\sx:1,\sx:1:1,\sx:1;1;1. +D 2013-01-02T14:57:32.750 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -174,7 +174,7 @@ F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 3104a5e4c2cb6e3a813b7266cbd541dc91e26fad F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 3cce5b09cae6eaf23276bb2e5551a9d22cd7ae3d +F src/select.c bdede5fc109ab090dd6ce5edf6090434dc41bc0e F src/shell.c 11c9611580bb2ffce3a232f31f7f8cc310df0843 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 @@ -1031,7 +1031,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 3e7d84db7861911c9b2c7dcdabe0b213bf483d79 5a2ac944839ec0c5b9147a035c6cbf0935f3d202 -R b00123b22cae8fcb124aaeac1cbbab83 +P a5f4d2b641f7fafb6f1a312efeffb10f213d2d0a +R c34797fdba8f90f34f8f47a91737b456 U drh -Z 0dd559531357e580f4a230ac4f1af1d6 +Z 110af146cb022182bd89fc0a02fd1d9b diff --git a/manifest.uuid b/manifest.uuid index d30551697b..24c1cff51c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a5f4d2b641f7fafb6f1a312efeffb10f213d2d0a \ No newline at end of file +ef01e30456670e6b1bc67fe41ec27e52c182efaf \ No newline at end of file diff --git a/src/select.c b/src/select.c index 2f20e7aa50..7c7974e256 100644 --- a/src/select.c +++ b/src/select.c @@ -1375,6 +1375,9 @@ static int selectColumnsFromExprList( for(j=cnt=0; j1 && sqlite3Isdigit(zName[k]); k--){} + if( zName[k]==':' ) nName = k; zName[nName] = 0; zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); sqlite3DbFree(db, zName); From 3e3f1a5b493300ec1e3569037d7cb5afbd1b9207 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 3 Jan 2013 00:45:56 +0000 Subject: [PATCH 35/38] Now supports result sets of the form "TABLE.*" with nested FROM clauses. FossilOrigin-Name: 4cf5ed7ea198abc32f8118e79490e77f847f08c1 --- manifest | 20 +++--- manifest.uuid | 2 +- src/expr.c | 6 ++ src/resolve.c | 32 +++++++++- src/select.c | 44 +++++++++---- src/sqliteInt.h | 6 +- test/selectD.test | 157 +++++++++++++++++++++++++++++----------------- 7 files changed, 183 insertions(+), 84 deletions(-) diff --git a/manifest b/manifest index 4270211ad5..6f14f4982c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\sresolving\sresult-set\sname\scollisions,\smake\sthem\sx:1,\sx:2,\sx:3,\setc.\ninstead\sof\sx:1,\sx:1:1,\sx:1;1;1. -D 2013-01-02T14:57:32.750 +C Now\ssupports\sresult\ssets\sof\sthe\sform\s"TABLE.*"\swith\snested\sFROM\sclauses. +D 2013-01-03T00:45:56.842 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -130,7 +130,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 72a70dcfda75d3a1f81041ce4573e7afddcd8e4e F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c 9b8d308979114991e5dc7cee958316e07186941d -F src/expr.c 0e41d66d868b37dbc0e041c342e0036fad27e705 +F src/expr.c 4dff0b04eaaf133789279c6b8cd69175dfbb1691 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 5b7a12e2f8620e855b0478a9a6798df9967bb277 F src/func.c 8147799b048065a1590805be464d05b4913e652c @@ -172,14 +172,14 @@ F src/pragma.c 8907c559d3127729d3bcedb1fe5c59fc196d3a17 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 -F src/resolve.c 3104a5e4c2cb6e3a813b7266cbd541dc91e26fad +F src/resolve.c e00f17ac321b48f022e0154e5c2fb90456b6d3a6 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c bdede5fc109ab090dd6ce5edf6090434dc41bc0e +F src/select.c 9a76144e137fbe26bad3c5ef06e0acd953de9f59 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 2ce2d742e32be86bf1fc8e29c91ef91c29c32e60 +F src/sqliteInt.h e998703742455b2241731424c6ec142fd8d0258f F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -701,7 +701,7 @@ F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532 F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25 F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977 -F test/selectD.test 243db87cd12f62aca4fe3f8b9465dfeab30b4d7a +F test/selectD.test 5e05091a755b12e0afd350137c49b25f7d9bc61b F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118 F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746 F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879 @@ -1031,7 +1031,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 a5f4d2b641f7fafb6f1a312efeffb10f213d2d0a -R c34797fdba8f90f34f8f47a91737b456 +P ef01e30456670e6b1bc67fe41ec27e52c182efaf +R 0fd360c4969bf18ffc741f29481411d6 U drh -Z 110af146cb022182bd89fc0a02fd1d9b +Z a4217b32c27253cef4a6339df637b923 diff --git a/manifest.uuid b/manifest.uuid index 24c1cff51c..1c88f392e4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ef01e30456670e6b1bc67fe41ec27e52c182efaf \ No newline at end of file +4cf5ed7ea198abc32f8118e79490e77f847f08c1 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 9ca34ec7b7..5ba2616919 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3281,6 +3281,12 @@ void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){ sqlite3ExplainPush(pOut); sqlite3ExplainExpr(pOut, pList->a[i].pExpr); sqlite3ExplainPop(pOut); + if( pList->a[i].zName ){ + sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName); + } + if( pList->a[i].bSpanIsTab ){ + sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan); + } if( inExpr-1 ){ sqlite3ExplainNL(pOut); } diff --git a/src/resolve.c b/src/resolve.c index 317ca3d81b..e1bcbcc5c1 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -150,6 +150,35 @@ static int nameInUsingClause(IdList *pUsing, const char *zCol){ return 0; } +/* +** Subqueries stores the original database, table and column names for their +** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". +** Check to see if the zSpan given to this routine matches the zDb, zTab, +** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will +** match anything. +*/ +int sqlite3MatchSpanName( + const char *zSpan, + const char *zCol, + const char *zTab, + const char *zDb +){ + int n; + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} + if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){ + return 0; + } + zSpan += n+1; + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} + if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){ + return 0; + } + zSpan += n+1; + if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ + return 0; + } + return 1; +} /* ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up @@ -240,8 +269,7 @@ static int lookupName( ExprList *pEList = pItem->pSelect->pEList; int hit = 0; for(j=0; jnExpr; j++){ - if( zTab && sqlite3StrICmp(pEList->a[j].zSpan, zTab)!=0 ) continue; - if( sqlite3StrICmp(pEList->a[j].zName, zCol)==0 ){ + if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){ cnt++; cntTab = 2; pMatch = pItem; diff --git a/src/select.c b/src/select.c index 7c7974e256..502c524fd6 100644 --- a/src/select.c +++ b/src/select.c @@ -3294,6 +3294,7 @@ static int selectExpander(Walker *pWalker, Select *p){ ExprList *pEList; struct SrcList_item *pFrom; sqlite3 *db = pParse->db; + Expr *pE, *pRight, *pExpr; if( db->mallocFailed ){ return WRC_Abort; @@ -3379,7 +3380,7 @@ static int selectExpander(Walker *pWalker, Select *p){ ** that need expanding. */ for(k=0; knExpr; k++){ - Expr *pE = pEList->a[k].pExpr; + pE = pEList->a[k].pExpr; if( pE->op==TK_ALL ) break; assert( pE->op!=TK_DOT || pE->pRight!=0 ); assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); @@ -3399,9 +3400,10 @@ static int selectExpander(Walker *pWalker, Select *p){ && (p->selFlags & SF_NestedFrom)==0; for(k=0; knExpr; k++){ - Expr *pE = a[k].pExpr; - assert( pE->op!=TK_DOT || pE->pRight!=0 ); - if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){ + pE = a[k].pExpr; + pRight = pE->pRight; + assert( pE->op!=TK_DOT || pRight!=0 ); + if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){ /* This particular expression does not need to be expanded. */ pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); @@ -3416,32 +3418,42 @@ static int selectExpander(Walker *pWalker, Select *p){ /* This expression is a "*" or a "TABLE.*" and needs to be ** expanded. */ int tableSeen = 0; /* Set to 1 when TABLE matches */ - char *zTName; /* text of name of TABLE */ + char *zTName = 0; /* text of name of TABLE */ if( pE->op==TK_DOT ){ assert( pE->pLeft!=0 ); assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); zTName = pE->pLeft->u.zToken; - }else{ - zTName = 0; } for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; + Select *pSub = pFrom->pSelect; char *zTabName = pFrom->zAlias; + const char *zSchemaName = 0; if( zTabName==0 ){ zTabName = pTab->zName; } if( db->mallocFailed ) break; - if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ - continue; + if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ + int iDb; + pSub = 0; + if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ + continue; + } + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + zSchemaName = iDb>=0 ? db->aDb[i].zName : "*"; } - tableSeen = 1; for(j=0; jnCol; j++){ - Expr *pExpr, *pRight; char *zName = pTab->aCol[j].zName; char *zColname; /* The computed column name */ char *zToFree; /* Malloced string that needs to be freed */ Token sColname; /* Computed column name as a token */ + if( zTName && pSub + && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0 + ){ + continue; + } + /* If a column is marked as 'hidden' (currently only possible ** for virtual tables), do not include it in the expanded ** result-set list. @@ -3450,6 +3462,7 @@ static int selectExpander(Walker *pWalker, Select *p){ assert(IsVirtual(pTab)); continue; } + tableSeen = 1; if( i>0 && zTName==0 ){ if( (pFrom->jointype & JT_NATURAL)!=0 @@ -3484,7 +3497,14 @@ static int selectExpander(Walker *pWalker, Select *p){ sColname.n = sqlite3Strlen30(zColname); sqlite3ExprListSetName(pParse, pNew, &sColname, 0); if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ - pNew->a[pNew->nExpr-1].zSpan = sqlite3DbStrDup(db, zTabName); + struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; + if( pSub ){ + pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan); + }else{ + pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s", + zSchemaName, zTabName, zColname); + } + pX->bSpanIsTab = 1; } sqlite3DbFree(db, zToFree); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 2a28594fe5..74fa0b31ca 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1777,7 +1777,8 @@ struct Expr { ** column labels. In this case, Expr.zSpan is typically the text of a ** column expression as it exists in a SELECT statement. However, if ** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name -** of the table to which the column of a FROM-clause subquery refers. +** of the result column in the form: DATABASE.TABLE.COLUMN. This later +** form is used for name resolution with nested FROM clauses. */ struct ExprList { int nExpr; /* Number of expressions on the list */ @@ -1788,7 +1789,7 @@ struct ExprList { char *zSpan; /* Original text of the expression */ u8 sortOrder; /* 1 for DESC or 0 for ASC */ unsigned done :1; /* A flag to indicate when processing is finished */ - unsigned bSpanIsTab :1; /* zSpan holds table name, not the span */ + unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ u16 iOrderByCol; /* For ORDER BY, column number in result set */ u16 iAlias; /* Index into Parse.aAlias[] for zName */ } *a; /* Alloc a power of two greater or equal to nExpr */ @@ -3080,6 +3081,7 @@ void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); int sqlite3CodeSubselect(Parse *, Expr *, int, int); void sqlite3SelectPrep(Parse*, Select*, NameContext*); +int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); int sqlite3ResolveExprNames(NameContext*, Expr*); void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); diff --git a/test/selectD.test b/test/selectD.test index 8c86226434..6c01b2d5cb 100644 --- a/test/selectD.test +++ b/test/selectD.test @@ -15,64 +15,107 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -do_test selectD-1.1 { - db eval { - CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(111,'x1'); - CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(222,'x2'); - CREATE TABLE t3(a,b); INSERT INTO t3 VALUES(333,'x3'); - CREATE TABLE t4(a,b); INSERT INTO t4 VALUES(444,'x4'); - SELECT * - FROM (t1), (t2), (t3), (t4) - WHERE t4.a=t3.a+111 - AND t3.a=t2.a+111 - AND t2.a=t1.a+111; +for {set i 1} {$i<=2} {incr i} { + db close + forcedelete test$i.db + sqlite3 db test$i.db + if {$i==2} { + optimization_control db query-flattener off } -} {111 x1 222 x2 333 x3 444 x4} -do_test selectD-1.2 { - db eval { - SELECT * - FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) - ON t3.a=t2.a+111) - ON t2.a=t1.a+111; - } -} {111 x1 222 x2 333 x3 444 x4} -do_test selectD-1.3 { - db eval { - UPDATE t2 SET a=111; - UPDATE t3 SET a=111; - UPDATE t4 SET a=111; - SELECT * - FROM t1 JOIN (t2 JOIN (t3 JOIN t4 USING(a)) USING (a)) USING (a); - } -} {111 x1 x2 x3 x4} -do_test selectD-1.4 { - db eval { - UPDATE t2 SET a=111; - UPDATE t3 SET a=111; - UPDATE t4 SET a=111; - SELECT * - FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 USING(a)) - USING (a)) - USING (a); - } -} {111 x1 x2 x3 x4} -do_test selectD-1.5 { - db eval { - UPDATE t3 SET a=222; - UPDATE t4 SET a=222; - SELECT * - FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) - ON t1.a=t3.a-111; - } -} {111 x1 x2 222 x3 x4} -do_test selectD-1.6 { - db eval { - UPDATE t4 SET a=333; - SELECT * - FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) - ON t1.a=t3.a-111; - } -} {111 x1 x2 222 x3 {}} + do_test selectD-$i.0 { + db eval { + CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(111,'x1'); + CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(222,'x2'); + CREATE TEMP TABLE t3(a,b); INSERT INTO t3 VALUES(333,'x3'); + CREATE TABLE t4(a,b); INSERT INTO t4 VALUES(444,'x4'); + } + } {} + do_test selectD-$i.1 { + db eval { + SELECT * + FROM (t1), (t2), (t3), (t4) + WHERE t4.a=t3.a+111 + AND t3.a=t2.a+111 + AND t2.a=t1.a+111; + } + } {111 x1 222 x2 333 x3 444 x4} + do_test selectD-$i.2.1 { + db eval { + SELECT * + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } + } {111 x1 222 x2 333 x3 444 x4} + do_test selectD-$i.2.2 { + db eval { + SELECT t3.a + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } + } {333} + do_test selectD-$i.2.3 { + db eval { + SELECT t3.* + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } + } {333 x3} + do_test selectD-$i.2.3 { + db eval { + SELECT t3.*, t2.* + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } + } {333 x3 222 x2} + do_test selectD-$i.3 { + db eval { + UPDATE t2 SET a=111; + UPDATE t3 SET a=111; + UPDATE t4 SET a=111; + SELECT * + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 USING(a)) USING (a)) USING (a); + } + } {111 x1 x2 x3 x4} + do_test selectD-$i.4 { + db eval { + UPDATE t2 SET a=111; + UPDATE t3 SET a=111; + UPDATE t4 SET a=111; + SELECT * + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 USING(a)) + USING (a)) + USING (a); + } + } {111 x1 x2 x3 x4} + do_test selectD-$i.5 { + db eval { + UPDATE t3 SET a=222; + UPDATE t4 SET a=222; + SELECT * + FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) + ON t1.a=t3.a-111; + } + } {111 x1 x2 222 x3 x4} + do_test selectD-$i.6 { + db eval { + UPDATE t4 SET a=333; + SELECT * + FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) + ON t1.a=t3.a-111; + } + } {111 x1 x2 222 x3 {}} + do_test selectD-$i.7 { + db eval { + SELECT t1.*, t2.*, t3.*, t4.b + FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) + ON t1.a=t3.a-111; + } + } {111 x1 111 x2 222 x3 {}} +} finish_test From c75e09c761e5f47eb74243c3dea2b975bd23e8cc Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 3 Jan 2013 16:54:20 +0000 Subject: [PATCH 36/38] Remove an incorrect assert() statement (ticket [beba9cae6345a]). Fix other minor problems in the name resolution logic. FossilOrigin-Name: afe96a118c8a9627819ba5960aa83a607e734087 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/resolve.c | 6 +++--- src/select.c | 13 +++++++++---- test/selectD.test | 36 +++++++++++++++++++++++++++++++++++- 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 6f14f4982c..4ad96b9f41 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Now\ssupports\sresult\ssets\sof\sthe\sform\s"TABLE.*"\swith\snested\sFROM\sclauses. -D 2013-01-03T00:45:56.842 +C Remove\san\sincorrect\sassert()\sstatement\s(ticket\s[beba9cae6345a]).\s\sFix\sother\nminor\sproblems\sin\sthe\sname\sresolution\slogic. +D 2013-01-03T16:54:20.331 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -172,9 +172,9 @@ F src/pragma.c 8907c559d3127729d3bcedb1fe5c59fc196d3a17 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 -F src/resolve.c e00f17ac321b48f022e0154e5c2fb90456b6d3a6 +F src/resolve.c 2b3ae4d8578384caf2f19b42c5ccc0512ead84c3 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 9a76144e137fbe26bad3c5ef06e0acd953de9f59 +F src/select.c 962a028bbb5d0bce997a0802ee806fe72403df4d F src/shell.c 11c9611580bb2ffce3a232f31f7f8cc310df0843 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 @@ -701,7 +701,7 @@ F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532 F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25 F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977 -F test/selectD.test 5e05091a755b12e0afd350137c49b25f7d9bc61b +F test/selectD.test 03f7c1ea8d5ab3c637cbc30fcbbbac96b988c162 F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118 F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746 F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879 @@ -1031,7 +1031,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 ef01e30456670e6b1bc67fe41ec27e52c182efaf -R 0fd360c4969bf18ffc741f29481411d6 +P 4cf5ed7ea198abc32f8118e79490e77f847f08c1 +R 7012a50322397b26dcf2d91c29b5b20b U drh -Z a4217b32c27253cef4a6339df637b923 +Z 261d0c48c10993cca01d26b0f623d70a diff --git a/manifest.uuid b/manifest.uuid index 1c88f392e4..9e014e0a54 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4cf5ed7ea198abc32f8118e79490e77f847f08c1 \ No newline at end of file +afe96a118c8a9627819ba5960aa83a607e734087 \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index e1bcbcc5c1..b9f5123efa 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -262,9 +262,6 @@ static int lookupName( pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); assert( pTab->nCol>0 ); - if( zDb && pTab->pSchema!=pSchema ){ - continue; - } if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ ExprList *pEList = pItem->pSelect->pEList; int hit = 0; @@ -278,6 +275,9 @@ static int lookupName( } if( hit || zTab==0 ) continue; } + if( zDb && pTab->pSchema!=pSchema ){ + continue; + } if( zTab ){ const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; assert( zTabName!=0 ); diff --git a/src/select.c b/src/select.c index 502c524fd6..a0a4c10dac 100644 --- a/src/select.c +++ b/src/select.c @@ -1336,8 +1336,6 @@ static int selectColumnsFromExprList( /* Get an appropriate name for the column */ p = sqlite3ExprSkipCollate(pEList->a[i].pExpr); - assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue) - || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 ); if( (zName = pEList->a[i].zName)!=0 ){ /* If the column contains an "AS " phrase, use as the name */ zName = sqlite3DbStrDup(db, zName); @@ -3429,18 +3427,18 @@ static int selectExpander(Walker *pWalker, Select *p){ Select *pSub = pFrom->pSelect; char *zTabName = pFrom->zAlias; const char *zSchemaName = 0; + int iDb; if( zTabName==0 ){ zTabName = pTab->zName; } if( db->mallocFailed ) break; if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ - int iDb; pSub = 0; if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ continue; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - zSchemaName = iDb>=0 ? db->aDb[i].zName : "*"; + zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*"; } for(j=0; jnCol; j++){ char *zName = pTab->aCol[j].zName; @@ -3448,6 +3446,7 @@ static int selectExpander(Walker *pWalker, Select *p){ char *zToFree; /* Malloced string that needs to be freed */ Token sColname; /* Computed column name as a token */ + assert( zName ); if( zTName && pSub && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0 ){ @@ -3485,6 +3484,10 @@ static int selectExpander(Walker *pWalker, Select *p){ Expr *pLeft; pLeft = sqlite3Expr(db, TK_ID, zTabName); pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); + if( zSchemaName && iDb>0 ){ + pLeft = sqlite3Expr(db, TK_ID, zSchemaName); + pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0); + } if( longNames ){ zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); zToFree = zColname; @@ -3500,9 +3503,11 @@ static int selectExpander(Walker *pWalker, Select *p){ struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; if( pSub ){ pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan); + testcase( pX->zSpan==0 ); }else{ pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s", zSchemaName, zTabName, zColname); + testcase( pX->zSpan==0 ); } pX->bSpanIsTab = 1; } diff --git a/test/selectD.test b/test/selectD.test index 6c01b2d5cb..aa8c328ee9 100644 --- a/test/selectD.test +++ b/test/selectD.test @@ -25,10 +25,12 @@ for {set i 1} {$i<=2} {incr i} { } do_test selectD-$i.0 { db eval { + ATTACH ':memory:' AS aux1; CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(111,'x1'); CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(222,'x2'); CREATE TEMP TABLE t3(a,b); INSERT INTO t3 VALUES(333,'x3'); - CREATE TABLE t4(a,b); INSERT INTO t4 VALUES(444,'x4'); + CREATE TABLE main.t4(a,b); INSERT INTO main.t4 VALUES(444,'x4'); + CREATE TABLE aux1.t4(a,b); INSERT INTO aux1.t4 VALUES(555,'x5'); } } {} do_test selectD-$i.1 { @@ -72,6 +74,38 @@ for {set i 1} {$i<=2} {incr i} { ON t2.a=t1.a+111; } } {333 x3 222 x2} + do_test selectD-$i.2.4 { + db eval { + SELECT * + FROM t1 JOIN (t2 JOIN (main.t4 JOIN aux1.t4 ON aux1.t4.a=main.t4.a+111) + ON main.t4.a=t2.a+222) + ON t2.a=t1.a+111; + } + } {111 x1 222 x2 444 x4 555 x5} + do_test selectD-$i.2.5 { + db eval { + SELECT * + FROM t1 JOIN (t2 JOIN (main.t4 AS x JOIN aux1.t4 ON aux1.t4.a=x.a+111) + ON x.a=t2.a+222) + ON t2.a=t1.a+111; + } + } {111 x1 222 x2 444 x4 555 x5} + do_test selectD-$i.2.6 { + catchsql { + SELECT * + FROM t1 JOIN (t2 JOIN (main.t4 JOIN aux.t4 ON aux.t4.a=main.t4.a+111) + ON main.t4.a=t2.a+222) + ON t2.a=t1.a+111; + } + } {1 {no such table: aux.t4}} + do_test selectD-$i.2.7 { + db eval { + SELECT x.a, y.b + FROM t1 JOIN (t2 JOIN (main.t4 x JOIN aux1.t4 y ON y.a=x.a+111) + ON x.a=t2.a+222) + ON t2.a=t1.a+111; + } + } {444 x5} do_test selectD-$i.3 { db eval { UPDATE t2 SET a=111; From 38b384a0328c2dcbdc2d46732e08b68d6cec632f Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 3 Jan 2013 17:34:28 +0000 Subject: [PATCH 37/38] Further corner-case fixes to the name resolution logic. FossilOrigin-Name: 20730bad7082b559a65e8cfedb31b6afca50df58 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/resolve.c | 1 + src/select.c | 12 +++++++++--- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 4ad96b9f41..dc05246193 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\san\sincorrect\sassert()\sstatement\s(ticket\s[beba9cae6345a]).\s\sFix\sother\nminor\sproblems\sin\sthe\sname\sresolution\slogic. -D 2013-01-03T16:54:20.331 +C Further\scorner-case\sfixes\sto\sthe\sname\sresolution\slogic. +D 2013-01-03T17:34:28.998 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -172,9 +172,9 @@ F src/pragma.c 8907c559d3127729d3bcedb1fe5c59fc196d3a17 F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 -F src/resolve.c 2b3ae4d8578384caf2f19b42c5ccc0512ead84c3 +F src/resolve.c 521bdc0f6c7cf8246c0b9167d726d84005097c30 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 962a028bbb5d0bce997a0802ee806fe72403df4d +F src/select.c 395e458a6dc611cbe1179f424753f0c344957607 F src/shell.c 11c9611580bb2ffce3a232f31f7f8cc310df0843 F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 @@ -1031,7 +1031,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 4cf5ed7ea198abc32f8118e79490e77f847f08c1 -R 7012a50322397b26dcf2d91c29b5b20b +P afe96a118c8a9627819ba5960aa83a607e734087 +R f686e430b5e8ddbc351355c5b12308be U drh -Z 261d0c48c10993cca01d26b0f623d70a +Z 596a0ebdf23f364031e40c92982646b7 diff --git a/manifest.uuid b/manifest.uuid index 9e014e0a54..d88ae4ef31 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -afe96a118c8a9627819ba5960aa83a607e734087 \ No newline at end of file +20730bad7082b559a65e8cfedb31b6afca50df58 \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index b9f5123efa..944fb5cad7 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -271,6 +271,7 @@ static int lookupName( cntTab = 2; pMatch = pItem; pExpr->iColumn = j; + hit = 1; } } if( hit || zTab==0 ) continue; diff --git a/src/select.c b/src/select.c index a0a4c10dac..3168894802 100644 --- a/src/select.c +++ b/src/select.c @@ -3394,8 +3394,14 @@ static int selectExpander(Walker *pWalker, Select *p){ ExprList *pNew = 0; int flags = pParse->db->flags; int longNames = (flags & SQLITE_FullColNames)!=0 - && (flags & SQLITE_ShortColNames)==0 - && (p->selFlags & SF_NestedFrom)==0; + && (flags & SQLITE_ShortColNames)==0; + + /* When processing FROM-clause subqueries, it is always the case + ** that full_column_names=OFF and short_column_names=ON. The + ** sqlite3ResultSetOfSelect() routine makes it so. */ + assert( (p->selFlags & SF_NestedFrom)==0 + || ((flags & SQLITE_FullColNames)==0 && + (flags & SQLITE_ShortColNames)!=0) ); for(k=0; knExpr; k++){ pE = a[k].pExpr; @@ -3484,7 +3490,7 @@ static int selectExpander(Walker *pWalker, Select *p){ Expr *pLeft; pLeft = sqlite3Expr(db, TK_ID, zTabName); pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); - if( zSchemaName && iDb>0 ){ + if( zSchemaName ){ pLeft = sqlite3Expr(db, TK_ID, zSchemaName); pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0); } From 26de1e99568c0f04a7956f2503bd9fcf007d85e6 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 3 Jan 2013 19:34:46 +0000 Subject: [PATCH 38/38] Bring back test_regexp changes that were made when porting to Fossil. Also fix some bugs that were introduced by the Fossil port. FossilOrigin-Name: 45c158b1a015e0295244982e7a61ecc55cca8436 --- manifest | 12 +-- manifest.uuid | 2 +- src/test_regexp.c | 182 +++++++++++++++++++++++++++------------------- 3 files changed, 116 insertions(+), 80 deletions(-) diff --git a/manifest b/manifest index 9778454af4..964e3c070c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\scolumn\sname\sresolution\sin\squeries\swith\sparenthesized\sFROM\nclauses.\s\sAlso\sincludes\sa\sfix\sfor\sticket\s[beba9cae6345a3]. -D 2013-01-03T18:07:37.816 +C Bring\sback\stest_regexp\schanges\sthat\swere\smade\swhen\sporting\sto\sFossil.\s\sAlso\nfix\ssome\sbugs\sthat\swere\sintroduced\sby\sthe\sFossil\sport. +D 2013-01-03T19:34:46.935 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -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 d8cb342e08a346a6daeda29e434e2d9a49f557ec +F src/test_regexp.c 8d91d00e45e899eb13575bec4870ce415900bec6 F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f @@ -1031,7 +1031,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 5a2ac944839ec0c5b9147a035c6cbf0935f3d202 20730bad7082b559a65e8cfedb31b6afca50df58 -R f686e430b5e8ddbc351355c5b12308be +P 99127a669c49f82918853091c9c5b7db5ad73cba +R c3dfd34890a9fadadecfeeae8889e37f U drh -Z 393fc5c6f8c30a4998c90107d978b3f0 +Z 1e14e89eb51110980c945c9622f22c94 diff --git a/manifest.uuid b/manifest.uuid index bde0494343..83cfaa7c74 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -99127a669c49f82918853091c9c5b7db5ad73cba \ No newline at end of file +45c158b1a015e0295244982e7a61ecc55cca8436 \ No newline at end of file diff --git a/src/test_regexp.c b/src/test_regexp.c index 0d6ffa2c7e..87fb3db5b3 100644 --- a/src/test_regexp.c +++ b/src/test_regexp.c @@ -88,19 +88,30 @@ typedef struct ReStateSet { ReStateNumber *aState; /* Current states */ } ReStateSet; +/* An input string read one character at a time. +*/ +typedef struct ReInput ReInput; +struct ReInput { + const unsigned char *z; /* All text */ + int i; /* Next byte to read */ + int mx; /* EOF when i>=mx */ +}; + /* 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 */ +typedef struct ReCompiled ReCompiled; +struct ReCompiled { + ReInput sIn; /* Regular expression text */ const char *zErr; /* Error message to return */ char *aOp; /* Operators for the virtual machine */ int *aArg; /* Arguments to each operator */ + unsigned (*xNextChar)(ReInput*); /* Next character function */ 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){ @@ -114,24 +125,24 @@ static void re_add_state(ReStateSet *pSet, int newState){ ** 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)++; +static unsigned re_next_char(ReInput *p){ + unsigned c; + if( p->i>=p->mx ) return 0; + c = p->z[p->i++]; if( c>0x80 ){ - if( (c&0xe0)==0xc0 && ((*pzIn)[0]&0xc0)==0x80 ){ - c = (c&0x1f)<<6 | ((*pzIn)[0]&0x3f); - (*pzIn)++; + if( (c&0xe0)==0xc0 && p->imx && (p->z[p->i]&0xc0)==0x80 ){ + c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f); 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; + }else if( (c&0xf0)==0xe0 && p->i+1mx && (p->z[p->i]&0xc0)==0x80 + && (p->z[p->i+1]&0xc0)==0x80 ){ + c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); + p->i += 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; + }else if( (c&0xf8)==0xf0 && p->i+3mx && (p->z[p->i]&0xc0)==0x80 + && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ + c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) + | (p->z[p->i+2]&0x3f); + p->i += 3; if( c<0xffff ) c = 0xfffd; }else{ c = 0xfffd; @@ -139,6 +150,11 @@ static unsigned re_next_char(const unsigned char **pzIn){ } return c; } +static unsigned re_next_char_nocase(ReInput *p){ + unsigned c = re_next_char(p); + if( c>='A' && c<='Z' ) c += 'a' - 'A'; + return c; +} /* Return true if c is a perl "word" character: [A-Za-z0-9_] */ static int re_word_char(int c){ @@ -159,7 +175,7 @@ static int re_space_char(int c){ /* 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){ +int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ ReStateSet aStateSet[2], *pThis, *pNext; ReStateNumber aSpace[100]; ReStateNumber *pToFree; @@ -168,19 +184,25 @@ static int re_exec(ReCompiled *pRe, const unsigned char *zIn){ int c = RE_EOF+1; int cPrev = 0; int rc = 0; - + ReInput in; + + in.z = zIn; + in.i = 0; + in.mx = nIn>=0 ? nIn : strlen((char*)zIn); if( pRe->nInit ){ unsigned char x = pRe->zInit[0]; - while( zIn[0] && (zIn[0]!=x || memcmp(zIn, pRe->zInit, pRe->nInit)!=0) ){ - zIn++; + while( in.i+pRe->nInit<=in.mx + && (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0) + ){ + in.i++; } - if( zIn[0]==0 ) return 0; + if( in.i+pRe->nInit>in.mx ) 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 ); + pToFree = sqlite3_malloc( sizeof(ReStateNumber)*2*pRe->nState ); if( pToFree==0 ) return -1; aStateSet[0].aState = pToFree; } @@ -190,7 +212,7 @@ static int re_exec(ReCompiled *pRe, const unsigned char *zIn){ re_add_state(pNext, 0); while( c!=RE_EOF && pNext->nState>0 ){ cPrev = c; - c = re_next_char(&zIn); + c = pRe->xNextChar(&in); pThis = pNext; pNext = &aStateSet[iSwap]; iSwap = 1 - iSwap; @@ -250,7 +272,7 @@ static int re_exec(ReCompiled *pRe, const unsigned char *zIn){ } case RE_OP_ACCEPT: { rc = 1; - goto re_exec_end; + goto re_match_end; } case RE_OP_CC_INC: case RE_OP_CC_EXC: { @@ -282,8 +304,8 @@ static int re_exec(ReCompiled *pRe, const unsigned char *zIn){ for(i=0; inState; i++){ if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } } -re_exec_end: - free(pToFree); +re_match_end: + sqlite3_free(pToFree); return rc; } @@ -292,10 +314,10 @@ re_exec_end: static int re_resize(ReCompiled *p, int N){ char *aOp; int *aArg; - aOp = realloc(p->aOp, N*sizeof(p->aOp[0])); + aOp = sqlite3_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])); + aArg = sqlite3_realloc(p->aArg, N*sizeof(p->aArg[0])); if( aArg==0 ) return 1; p->aArg = aArg; p->nAlloc = N; @@ -359,30 +381,33 @@ 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' ){ + char c; + if( p->sIn.i>=p->sIn.mx ) return 0; + c = p->sIn.z[p->sIn.i]; + if( c=='u' && p->sIn.i+5sIn.mx ){ 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) + const unsigned char *zIn = p->sIn.z + p->sIn.i; + if( re_hex(zIn[1],&v) + && re_hex(zIn[2],&v) + && re_hex(zIn[3],&v) + && re_hex(zIn[4],&v) ){ - p->zIn += 5; + p->sIn.i += 5; return v; } } if( c=='x' ){ v = 0; - for(i=1; re_hex(p->zIn[i], &v); i++){} + for(i=1; p->sIn.isIn.mx && re_hex(p->sIn.z[p->sIn.i+i], &v); i++){} if( i>1 ){ - p->zIn += i; + p->sIn.i += i; return v; } } for(i=0; zEsc[i] && zEsc[i]!=c; i++){} if( zEsc[i] ){ if( i<6 ) c = zTrans[i]; - p->zIn++; + p->sIn.i++; }else{ p->zErr = "unknown \\ escape"; } @@ -392,6 +417,11 @@ static unsigned re_esc_char(ReCompiled *p){ /* Forward declaration */ static const char *re_subcompile_string(ReCompiled*); +/* Peek at the next byte of input */ +static unsigned char rePeek(ReCompiled *p){ + return p->sIn.isIn.mx ? p->sIn.z[p->sIn.i] : 0; +} + /* 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. @@ -402,11 +432,11 @@ static const char *re_subcompile_re(ReCompiled *p){ iStart = p->nState; zErr = re_subcompile_string(p); if( zErr ) return zErr; - while( p->zIn[0]=='|' ){ + while( rePeek(p)=='|' ){ iEnd = p->nState; re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart); iGoto = re_append(p, RE_OP_GOTO, 0); - p->zIn++; + p->sIn.i++; zErr = re_subcompile_string(p); if( zErr ) return zErr; p->aArg[iGoto] = p->nState - iGoto; @@ -423,26 +453,26 @@ static const char *re_subcompile_string(ReCompiled *p){ int iStart; unsigned c; const char *zErr; - while( (c = re_next_char(&p->zIn))!=0 ){ + while( (c = p->xNextChar(&p->sIn))!=0 ){ iStart = p->nState; switch( c ){ case '|': case '$': case ')': { - p->zIn--; + p->sIn.i--; return 0; } case '(': { zErr = re_subcompile_re(p); if( zErr ) return zErr; - if( p->zIn[0]!=')' ) return "unmatched '('"; - p->zIn++; + if( rePeek(p)!=')' ) return "unmatched '('"; + p->sIn.i++; break; } case '.': { - if( p->zIn[0]=='*' ){ + if( rePeek(p)=='*' ){ re_append(p, RE_OP_ANYSTAR, 0); - p->zIn++; + p->sIn.i++; }else{ re_append(p, RE_OP_ANY, 0); } @@ -468,16 +498,16 @@ static const char *re_subcompile_string(ReCompiled *p){ 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++; } + while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; } n = m; if( c==',' ){ - p->zIn++; + p->sIn.i++; n = 0; - while( (c=p->zIn[0])>='0' && c<='9' ){ n = n*10 + c - '0'; p->zIn++; } + while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; } } if( c!='}' ) return "unmatched '{'"; if( n>0 && nzIn++; + p->sIn.i++; sz = p->nState - iPrev; if( m==0 ){ if( n==0 ) return "both m and n are zero in '{m,n}'"; @@ -497,27 +527,27 @@ static const char *re_subcompile_string(ReCompiled *p){ } case '[': { int iFirst = p->nState; - if( p->zIn[0]=='^' ){ + if( rePeek(p)=='^' ){ re_append(p, RE_OP_CC_EXC, 0); - p->zIn++; + p->sIn.i++; }else{ re_append(p, RE_OP_CC_INC, 0); } - while( (c = re_next_char(&p->zIn))!=0 ){ - if( c=='[' && p->zIn[0]==':' ){ + while( (c = p->xNextChar(&p->sIn))!=0 ){ + if( c=='[' && rePeek(p)==':' ){ return "POSIX character classes not supported"; } if( c=='\\' ) c = re_esc_char(p); - if( p->zIn[0]=='-' && p->zIn[1] ){ + if( rePeek(p)=='-' ){ re_append(p, RE_OP_CC_RANGE, c); - p->zIn++; - c = re_next_char(&p->zIn); + p->sIn.i++; + c = p->xNextChar(&p->sIn); 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( rePeek(p)==']' ){ p->sIn.i++; break; } } if( c==0 ) return "unclosed '['"; p->aArg[iFirst] = p->nState - iFirst; @@ -525,7 +555,7 @@ static const char *re_subcompile_string(ReCompiled *p){ } case '\\': { int specialOp = 0; - switch( p->zIn[0] ){ + switch( rePeek(p) ){ case 'b': specialOp = RE_OP_BOUNDARY; break; case 'd': specialOp = RE_OP_DIGIT; break; case 'D': specialOp = RE_OP_NOTDIGIT; break; @@ -535,7 +565,7 @@ static const char *re_subcompile_string(ReCompiled *p){ case 'W': specialOp = RE_OP_NOTWORD; break; } if( specialOp ){ - p->zIn++; + p->sIn.i++; re_append(p, specialOp, 0); }else{ c = re_esc_char(p); @@ -557,30 +587,32 @@ static const char *re_subcompile_string(ReCompiled *p){ ** regular expression. Applications should invoke this routine once ** for every call to re_compile() to avoid memory leaks. */ -static void re_free(ReCompiled *pRe){ +void re_free(ReCompiled *pRe){ if( pRe ){ - free(pRe->aOp); - free(pRe->aArg); + sqlite3_free(pRe->aOp); + sqlite3_free(pRe->aArg); + sqlite3_free(pRe); } } /* ** Compile a textual regular expression in zIn[] into a compiled regular -** expression suitable for us by re_exec() and return a pointer to the +** expression suitable for us by re_match() 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){ +const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ ReCompiled *pRe; const char *zErr; int i, j; *ppRe = 0; - pRe = malloc( sizeof(*pRe) ); + pRe = sqlite3_malloc( sizeof(*pRe) ); if( pRe==0 ){ return "out of memory"; } memset(pRe, 0, sizeof(*pRe)); + pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char; if( re_resize(pRe, 30) ){ re_free(pRe); return "out of memory"; @@ -590,17 +622,19 @@ static const char *re_compile(ReCompiled **ppRe, const char *zIn){ }else{ re_append(pRe, RE_OP_ANYSTAR, 0); } - pRe->zIn = (unsigned char*)zIn; + pRe->sIn.z = (unsigned char*)zIn; + pRe->sIn.i = 0; + pRe->sIn.mx = strlen((char*)pRe->sIn.z); zErr = re_subcompile_re(pRe); if( zErr ){ re_free(pRe); return zErr; } - if( pRe->zIn[0]=='$' && pRe->zIn[1]==0 ){ + if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){ re_append(pRe, RE_OP_MATCH, RE_EOF); re_append(pRe, RE_OP_ACCEPT, 0); *ppRe = pRe; - }else if( pRe->zIn[0]==0 ){ + }else if( pRe->sIn.i>=pRe->sIn.mx ){ re_append(pRe, RE_OP_ACCEPT, 0); *ppRe = pRe; }else{ @@ -623,6 +657,7 @@ static const char *re_compile(ReCompiled **ppRe, const char *zIn){ break; } } + if( j>0 && pRe->zInit[j-1]==0 ) j--; pRe->nInit = j; } return pRe->zErr; @@ -651,8 +686,9 @@ static void re_sql_func( if( pRe==0 ){ zPattern = (const char*)sqlite3_value_text(argv[0]); if( zPattern==0 ) return; - zErr = re_compile(&pRe, zPattern); + zErr = re_compile(&pRe, zPattern, 0); if( zErr ){ + re_free(pRe); sqlite3_result_error(context, zErr, -1); return; } @@ -664,7 +700,7 @@ static void re_sql_func( } zStr = (const unsigned char*)sqlite3_value_text(argv[1]); if( zStr!=0 ){ - sqlite3_result_int(context, re_exec(pRe, zStr)); + sqlite3_result_int(context, re_match(pRe, zStr, -1)); } }