diff --git a/Makefile.in b/Makefile.in index 9b8073838f..0012103420 100644 --- a/Makefile.in +++ b/Makefile.in @@ -470,7 +470,8 @@ TESTSRC += \ $(TOP)/ext/misc/unionvtab.c \ $(TOP)/ext/misc/wholenumber.c \ $(TOP)/ext/misc/zipfile.c \ - $(TOP)/ext/userauth/userauth.c + $(TOP)/ext/userauth/userauth.c \ + $(TOP)/ext/rtree/test_rtreedoc.c # Source code to the library files needed by the test fixture # diff --git a/Makefile.msc b/Makefile.msc index 058ab825ac..5db2f199bd 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1587,6 +1587,7 @@ TESTEXT = \ $(TOP)\ext\misc\totype.c \ $(TOP)\ext\misc\unionvtab.c \ $(TOP)\ext\misc\wholenumber.c \ + $(TOP)\ext\rtree\test_rtreedoc.c \ fts5.c # If use of zlib is enabled, add the "zipfile.c" source file. diff --git a/ext/expert/expert1.test b/ext/expert/expert1.test index 3987d0c910..73541122d8 100644 --- a/ext/expert/expert1.test +++ b/ext/expert/expert1.test @@ -196,7 +196,7 @@ do_setup_rec_test $tn.9.1 { } { SELECT * FROM "t t" WHERE a=? } { - CREATE INDEX 't t_idx_00000061' ON 't t'(a); + CREATE INDEX "t t_idx_00000061" ON "t t"(a); SEARCH t t USING INDEX t t_idx_00000061 (a=?) } @@ -205,7 +205,7 @@ do_setup_rec_test $tn.9.2 { } { SELECT * FROM "t t" WHERE b BETWEEN ? AND ? } { - CREATE INDEX 't t_idx_00000062' ON 't t'(b); + CREATE INDEX "t t_idx_00000062" ON "t t"(b); SEARCH t t USING INDEX t t_idx_00000062 (b>? AND b0 && (azResults[0][0]!='0' || azResults[0][1]!=0) ){ + *((int *)pCount) += 1; + } + return 0; +} + static int idxCreateFromCons( sqlite3expert *p, IdxScan *pScan, @@ -940,17 +953,40 @@ static int idxCreateFromCons( if( rc==SQLITE_OK ){ /* Hash the list of columns to come up with a name for the index */ const char *zTable = pScan->pTab->zName; - char *zName; /* Index name */ - int i; - for(i=0; zCols[i]; i++){ - h += ((h<<3) + zCols[i]); - } - zName = sqlite3_mprintf("%s_idx_%08x", zTable, h); - if( zName==0 ){ + int quoteTable = idxIdentifierRequiresQuotes(zTable); + char *zName = 0; /* Index name */ + int collisions = 0; + do{ + int i; + char *zFind; + for(i=0; zCols[i]; i++){ + h += ((h<<3) + zCols[i]); + } + sqlite3_free(zName); + zName = sqlite3_mprintf("%s_idx_%08x", zTable, h); + if( zName==0 ) break; + /* Is is unique among table, view and index names? */ + zFmt = "SELECT count(*) FROM sqlite_schema WHERE name=%Q" + " AND type in ('index','table','view')"; + zFind = sqlite3_mprintf(zFmt, zName); + i = 0; + rc = sqlite3_exec(dbm, zFind, countNonzeros, &i, 0); + assert(rc==SQLITE_OK); + sqlite3_free(zFind); + if( i==0 ){ + collisions = 0; + break; + } + ++collisions; + }while( collisions<50 && zName!=0 ); + if( collisions ){ + /* This return means "Gave up trying to find a unique index name." */ + rc = SQLITE_BUSY_TIMEOUT; + }else if( zName==0 ){ rc = SQLITE_NOMEM; }else{ - if( idxIdentifierRequiresQuotes(zTable) ){ - zFmt = "CREATE INDEX '%q' ON %Q(%s)"; + if( quoteTable ){ + zFmt = "CREATE INDEX \"%w\" ON \"%w\"(%s)"; }else{ zFmt = "CREATE INDEX %s ON %s(%s)"; } @@ -959,7 +995,11 @@ static int idxCreateFromCons( rc = SQLITE_NOMEM; }else{ rc = sqlite3_exec(dbm, zIdx, 0, 0, p->pzErrmsg); - idxHashAdd(&rc, &p->hIdx, zName, zIdx); + if( rc!=SQLITE_OK ){ + rc = SQLITE_BUSY_TIMEOUT; + }else{ + idxHashAdd(&rc, &p->hIdx, zName, zIdx); + } } sqlite3_free(zName); sqlite3_free(zIdx); @@ -1883,6 +1923,10 @@ int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){ /* Create candidate indexes within the in-memory database file */ if( rc==SQLITE_OK ){ rc = idxCreateCandidates(p); + }else if ( rc==SQLITE_BUSY_TIMEOUT ){ + if( pzErr ) + *pzErr = sqlite3_mprintf("Cannot find a unique index name to propose."); + return rc; } /* Generate the stat1 data */ diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 613ceaf2b6..dcc391456a 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -697,6 +697,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ return pRet; } + /* ** Release a reference to data record returned by an earlier call to ** fts5DataRead(). @@ -837,6 +838,42 @@ int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){ return SQLITE_OK; } +/* +** Ensure that structure object (*pp) is writable. +** +** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If +** an error occurs, (*pRc) is set to an SQLite error code before returning. +*/ +static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ + Fts5Structure *p = *pp; + if( *pRc==SQLITE_OK && p->nRef>1 ){ + int nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel); + Fts5Structure *pNew; + pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); + if( pNew ){ + int i; + memcpy(pNew, p, nByte); + for(i=0; inLevel; i++) pNew->aLevel[i].aSeg = 0; + for(i=0; inLevel; i++){ + Fts5StructureLevel *pLvl = &pNew->aLevel[i]; + nByte = sizeof(Fts5StructureSegment) * pNew->aLevel[i].nSeg; + pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(pRc, nByte); + if( pLvl->aSeg==0 ){ + for(i=0; inLevel; i++){ + sqlite3_free(pNew->aLevel[i].aSeg); + } + sqlite3_free(pNew); + return; + } + memcpy(pLvl->aSeg, p->aLevel[i].aSeg, nByte); + } + p->nRef--; + pNew->nRef = 1; + } + *pp = pNew; + } +} + /* ** Deserialize and return the structure record currently stored in serialized ** form within buffer pData/nData. @@ -938,9 +975,11 @@ static int fts5StructureDecode( } /* -** +** Add a level to the Fts5Structure.aLevel[] array of structure object +** (*ppStruct). */ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ + fts5StructureMakeWritable(pRc, ppStruct); if( *pRc==SQLITE_OK ){ Fts5Structure *pStruct = *ppStruct; int nLevel = pStruct->nLevel; @@ -2117,7 +2156,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ if( pDlidx ){ int iSegid = pIter->pSeg->iSegid; pgnoLast = fts5DlidxIterPgno(pDlidx); - pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); + pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); }else{ Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ @@ -2144,7 +2183,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ ** forward to find the page containing the last rowid. */ for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){ i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); - Fts5Data *pNew = fts5DataRead(p, iAbs); + Fts5Data *pNew = fts5LeafRead(p, iAbs); if( pNew ){ int iRowid, bTermless; iRowid = fts5LeafFirstRowidOff(pNew); @@ -2175,6 +2214,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ pIter->pLeaf = pLast; pIter->iLeafPgno = pgnoLast; iOff = fts5LeafFirstRowidOff(pLast); + if( iOff>pLast->szLeaf ){ + p->rc = FTS5_CORRUPT; + return; + } iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid); pIter->iLeafOffset = iOff; @@ -2183,7 +2226,6 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ }else{ pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast); } - } fts5SegIterReverseInitPage(p, pIter); diff --git a/ext/fts5/test/fts5corrupt5.test b/ext/fts5/test/fts5corrupt5.test new file mode 100644 index 0000000000..4c00d913e0 --- /dev/null +++ b/ext/fts5/test/fts5corrupt5.test @@ -0,0 +1,458 @@ +# 2015 Apr 24 +# +# 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 tests that FTS5 handles corrupt databases (i.e. internal +# inconsistencies in the backing tables) correctly. In this case +# "correctly" means without crashing. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5corrupt3 + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} +sqlite3_fts5_may_be_corrupt 1 +database_may_be_corrupt + +#------------------------------------------------------------------------- +# dbsqlfuzz crash-0f47112aa7520cf08c6a835a88fdff8c2a32a188 +# +reset_db +do_test 1.0 { + sqlite3 db {} + db deserialize [decode_hexdb { +.open --hexdb +| size 24576 pagesize 4096 filename crash-0f47112aa7520c.db +| page 1 offset 0 +| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. +| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 00 .....@ ........ +| 96: 00 00 00 00 0d 00 00 00 06 0e 0f 00 0f aa 0f 53 ...............S +| 112: 0e e8 0e 8b 0e 33 0e 0f 00 00 00 00 00 00 00 00 .....3.......... +| 3584: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................ +| 3600: 06 06 17 11 11 01 31 74 61 62 6c 65 62 62 62 62 ......1tablebbbb +| 3616: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 62 62 .CREATE TABLE bb +| 3632: 28 61 29 56 05 06 17 1f 1f 01 7d 74 61 62 6c 65 (a)V.......table +| 3648: 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 6f 6e 66 t1_configt1_conf +| 3664: 69 67 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 ig.CREATE TABLE +| 3680: 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b 20 50 52 't1_config'(k PR +| 3696: 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 20 57 49 IMARY KEY, v) WI +| 3712: 54 48 4f 55 54 20 52 4f 57 49 44 5b 04 07 17 21 THOUT ROWID[...! +| 3728: 21 01 81 01 74 61 62 6c 65 74 31 5f 64 6f 63 73 !...tablet1_docs +| 3744: 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 04 43 52 izet1_docsize.CR +| 3760: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 64 EATE TABLE 't1_d +| 3776: 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 54 45 47 ocsize'(id INTEG +| 3792: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY, +| 3808: 73 7a 20 42 4c 4f 42 29 69 03 07 17 19 19 01 81 sz BLOB)i....... +| 3824: 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 31 5f 69 -tablet1_idxt1_i +| 3840: 64 78 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 dx.CREATE TABLE +| 3856: 27 74 31 5f 69 64 78 27 28 73 65 67 69 64 2c 20 't1_idx'(segid, +| 3872: 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 52 49 4d term, pgno, PRIM +| 3888: 41 52 59 20 4b 45 59 28 73 65 67 69 64 2c 20 74 ARY KEY(segid, t +| 3904: 65 72 6d 29 29 20 57 49 54 48 4f 55 54 20 52 4f erm)) WITHOUT RO +| 3920: 57 49 44 55 02 07 17 1b 1b 01 81 01 74 61 62 6c WIDU........tabl +| 3936: 65 74 31 5f 64 61 74 61 74 31 5f 64 61 74 61 02 et1_datat1_data. +| 3952: 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 CREATE TABLE 't1 +| 3968: 5f 64 61 74 61 27 28 69 64 20 49 4e 54 45 47 45 _data'(id INTEGE +| 3984: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 R PRIMARY KEY, b +| 4000: 6c 6f 63 6b 20 42 4c 4f 42 29 54 01 07 17 11 11 lock BLOB)T..... +| 4016: 08 81 15 74 61 62 6c 65 74 31 74 31 43 52 45 41 ...tablet1t1CREA +| 4032: 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 4c 45 TE VIRTUAL TABLE +| 4048: 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 28 61 t1 USING fts5(a +| 4064: 2c 62 2c 70 72 65 66 69 78 3d 22 31 2c 32 2c 33 ,b,prefix=.1,2,3 +| 4080: 2c 34 22 2c 20 63 6f 6e 74 65 6e 74 3d 22 22 29 ,4., content=..) +| page 2 offset 4096 +| 0: 0d 0b 6a 00 37 09 4c 02 0f e7 09 4c 0f c6 0f a4 ..j.7.L....L.... +| 16: 0f 88 0f 6d 0f 4b 0f 2c 0f 0e 0e ec 0e cd 0e ad ...m.K.,........ +| 32: 0e 8e 0e 6c 0e 4b 0e 29 0e 08 0d e6 0d c4 0d b5 ...l.K.)........ +| 48: 0d 97 0d 76 0d 54 0d 31 0d 15 0c f3 0c d3 0c b5 ...v.T.1........ +| 64: 0c 95 0c 73 0c 54 0c 32 0c 10 0b ee 0b cc 0b b0 ...s.T.2........ +| 80: 0b 8d 0b 7e 0b 48 0b 2e 0b 0b 0a ef 00 00 00 00 ...~.H.......... +| 2368: 00 00 00 00 00 00 00 00 00 00 00 00 15 0a 03 00 ................ +| 2384: 30 00 00 00 01 01 03 35 00 03 01 11 12 02 01 12 0......5........ +| 2400: 03 01 11 1c 8c 80 80 80 80 10 03 00 3e 00 00 00 ............>... +| 2416: 17 01 05 05 34 74 61 62 6c 03 02 03 01 04 77 68 ....4tabl.....wh +| 2432: 65 72 03 02 06 09 1b 8c 80 80 80 80 0f 03 00 3c er.............< +| 2448: 00 00 00 16 05 34 66 74 73 34 03 02 02 01 04 6e .....4fts4.....n +| 2464: 75 6d 62 03 06 01 04 09 1b 8c 80 80 80 80 0e 03 umb............. +| 2480: 00 3c 00 00 00 16 04 33 74 68 65 03 06 01 01 04 .<.....3the..... +| 2496: 01 03 77 68 65 03 02 04 04 0a 1b 8c 80 80 80 80 ..whe........... +| 2512: 0d 03 00 3c 00 00 00 16 04 33 6e 75 6d 03 06 01 ...<.....3num... +| 2528: 01 05 01 03 74 61 62 03 02 03 04 0a 19 8c 80 80 ....tab......... +| 2544: 80 80 0c 03 00 38 00 00 00 14 03 32 77 68 03 02 .....8.....2wh.. +| 2560: 04 00 04 33 66 74 73 03 02 02 04 07 18 8c 80 80 ...3fts......... +| 2576: 80 80 0b 03 00 36 00 00 00 13 03 32 74 61 03 02 .....6.....2ta.. +| 2592: 03 02 01 68 03 06 01 01 04 04 07 1b 8c 80 80 80 ...h............ +| 2608: 80 0a 03 00 3c 00 00 00 16 03 32 6e 75 03 06 01 ....<.....2nu... +| 2624: 01 05 01 02 6f 66 03 06 01 01 06 04 09 19 8c 80 ....of.......... +| 2640: 80 80 80 09 03 00 38 00 00 00 14 03 32 66 74 03 ......8.....2ft. +| 2656: 02 02 01 02 69 73 03 06 01 01 03 04 07 18 8c 80 ....is.......... +| 2672: 80 80 80 08 03 00 36 00 00 00 13 02 31 74 03 08 ......6.....1t.. +| 2688: 03 01 01 04 01 01 77 03 02 04 04 09 1a 8c 80 80 ......w......... +| 2704: 80 80 07 03 00 3a 00 00 00 15 02 31 6e 03 08 01 .....:.....1n... +| 2720: 01 02 05 01 00 6f 03 06 01 01 06 04 09 18 8c 80 .....o.......... +| 2736: 80 80 80 06 03 00 36 00 00 00 03 04 02 31 66 03 ......6......1f. +| 2752: 02 02 01 01 69 03 06 01 01 03 04 f6 1c 8c 80 80 ....i........... +| 2768: 80 80 05 03 00 3e 00 00 00 17 04 30 74 68 65 03 .....>.....0the. +| 2784: f6 01 01 04 01 05 77 68 65 72 65 03 02 04 0a 15 ......where..... +| 2800: 8c 80 80 80 80 04 03 00 30 00 00 00 11 01 01 06 ........0....... +| 2816: 06 30 74 61 62 6c 65 0f 42 03 07 1c 8c 81 80 80 .0table.B....... +| 2832: 80 03 03 00 3e 00 00 00 17 07 30 6e 75 6d 62 65 ....>.....0numbe +| 2848: 72 03 06 01 01 05 01 02 6f 66 03 06 04 0d 13 8c r.......of...... +| 2864: 80 80 80 80 02 03 00 2c 00 00 00 0f 01 01 03 02 .......,........ +| 2880: 30 6e 03 06 01 01 02 07 1b 8c 80 80 80 80 01 03 0n.............. +| 2896: 00 3c 00 00 00 16 08 30 66 74 73 34 61 75 78 03 .<.....0fts4aux. +| 2912: 02 02 01 02 69 73 03 06 04 0c 00 00 00 14 2a 00 ....is........*. +| 2928: 00 00 01 01 02 24 00 02 01 01 12 02 01 12 08 88 .....$.......... +| 2944: 80 80 80 80 12 03 00 16 00 00 00 05 02 1c 88 80 ................ +| 2960: 80 80 80 11 03 00 3e 00 00 00 17 05 34 72 6f 77 ......>.....4row +| 2976: 73 02 06 01 01 05 01 04 74 68 65 72 02 02 04 0b s.......ther.... +| 2992: 15 88 80 80 80 80 10 03 00 30 00 00 00 11 02 01 .........0...... +| 3008: 01 07 05 34 62 65 74 77 02 02 04 08 1b 88 80 80 ...4betw........ +| 3024: 80 80 0f 03 00 3c 00 00 00 16 04 04 33 72 6f 77 .....<......3row +| 3040: 02 06 01 01 05 01 03 74 68 65 02 08 05 0a 1b 88 .......the...... +| 3056: 80 80 80 80 0e 03 00 3c 00 00 00 16 01 01 02 04 .......<........ +| 3072: 33 61 72 65 02 02 03 01 03 62 65 74 02 02 07 08 3are.....bet.... +| 3088: 1b 88 80 80 80 80 0d 03 00 3c 00 00 00 16 13 32 .........<.....2 +| 3104: 74 68 02 08 02 01 01 07 00 04 33 61 6e 64 02 06 th........3and.. +| 3120: 04 0a 1b 88 80 80 80 80 0c 03 00 3c 00 00 00 16 ...........<.... +| 3136: 03 32 69 6e 02 06 01 01 06 01 02 72 6f 02 06 01 .2in.......ro... +| 3152: 01 05 04 09 18 88 80 80 80 80 0b 03 00 36 00 00 .............6.. +| 3168: 00 13 02 03 32 61 72 02 02 03 01 02 62 65 02 02 ....2ar.....be.. +| 3184: 04 05 07 1b 88 80 80 80 80 0a 03 00 3c 00 9e 00 ............<... +| 3200: 16 02 31 74 02 08 02 01 01 07 00 03 32 61 6e 02 ..1t........2an. +| 3216: 06 01 01 04 09 19 88 80 80 80 80 09 03 00 38 00 ..............8. +| 3232: 00 00 14 02 31 6e 02 06 01 01 03 01 01 72 02 06 ....1n.......r.. +| 3248: 01 01 05 04 08 17 88 80 80 80 80 08 03 00 34 00 ..............4. +| 3264: 00 00 12 02 31 62 02 02 04 01 01 69 02 06 01 01 ....1b.....i.... +| 3280: 06 04 06 19 88 80 80 80 80 07 03 00 38 00 00 00 ............8... +| 3296: 14 04 02 31 32 02 02 05 01 01 61 02 08 03 01 01 ...12.....a..... +| 3312: 02 05 06 1b 88 80 80 80 80 06 03 00 3c 00 00 00 ............<... +| 3328: 16 06 30 74 68 65 72 65 02 02 02 00 02 31 31 02 ..0there.....11. +| 3344: 06 01 01 04 0a 15 88 80 80 80 80 05 03 00 30 00 ..............0. +| 3360: 00 00 11 01 01 05 04 30 74 68 65 02 06 01 01 07 .......0the..... +| 3376: 07 1c 88 80 80 80 80 04 03 00 3e 00 00 00 17 01 ..........>..... +| 3392: 01 06 02 30 6e 02 06 01 01 03 01 04 72 6f 77 73 ...0n.......rows +| 3408: 02 06 07 08 1b 88 80 80 80 80 03 03 00 3c 00 00 .............<.. +| 3424: 00 16 08 30 62 65 74 77 65 65 6e 02 02 04 01 02 ...0between..... +| 3440: 69 6e 02 06 04 0c 1a 88 80 80 80 80 02 03 00 3a in.............: +| 3456: 00 00 00 15 04 30 61 6e 64 02 06 01 01 02 02 02 .....0and....... +| 3472: 72 65 02 02 03 04 0a 17 88 80 80 80 80 01 03 00 re.............. +| 3488: 34 00 00 00 12 02 30 31 02 06 01 01 04 01 01 32 4.....01.......2 +| 3504: 02 02 05 04 08 08 84 80 80 80 80 12 03 00 16 00 ................ +| 3520: 00 00 05 04 1b 84 80 80 80 80 11 03 00 3c 00 00 .............<.. +| 3536: 00 16 05 34 74 61 62 6c 01 06 01 01 05 02 03 65 ...4tabl.......e +| 3552: 72 6d 01 02 04 0b 1b 84 80 80 80 80 10 03 00 3c rm.............< +| 3568: 00 00 00 16 05 34 65 61 63 68 01 02 03 01 04 70 .....4each.....p +| 3584: 72 65 73 01 02 05 04 08 1a 84 80 80 80 80 0f 03 res............. +| 3600: 00 3a 00 00 00 15 04 33 74 65 72 01 02 04 02 02 .:.....3ter..... +| 3616: 68 65 01 06 01 01 03 04 08 1b 84 80 80 80 80 0e he.............. +| 3632: 03 00 3c 00 00 00 16 04 33 80 72 65 01 02 05 01 ..<.....3.re.... +| 3648: 03 74 61 62 01 06 01 01 05 04 08 1a 84 80 80 80 .tab............ +| 3664: 80 0d 03 00 3a 00 00 00 15 04 33 66 6f 72 01 02 ....:.....3for.. +| 3680: 02 02 02 74 73 01 06 01 01 04 04 08 1b 84 80 80 ...ts........... +| 3696: 80 80 0c 03 00 3c 00 00 00 17 03 32 74 68 01 06 .....<.....2th.. +| 3712: 01 01 03 00 04 33 65 61 63 01 02 03 04 09 18 84 .....3eac....... +| 3728: 80 80 80 80 0b 03 00 36 00 00 00 13 03 32 74 61 .......6.....2ta +| 3744: 01 06 01 01 05 02 01 65 01 02 04 04 09 19 84 80 .......e........ +| 3760: 80 80 80 0a 03 00 38 00 00 00 14 03 32 69 6e 01 ......8.....2in. +| 3776: 06 01 01 02 01 02 70 72 01 02 05 04 09 18 84 80 ......pr........ +| 3792: 80 80 80 09 03 00 36 00 00 00 13 03 32 66 6f 01 ......6.....2fo. +| 3808: 02 02 02 01 74 01 06 01 01 04 04 07 1b 84 80 80 ....t........... +| 3824: 80 80 08 03 00 3c 00 00 00 16 02 31 74 01 0a 04 .....<.....1t... +| 3840: 01 01 03 04 00 03 32 65 61 01 02 03 04 0a 17 84 ......2ea....... +| 3856: 80 80 80 80 07 03 00 34 00 00 00 12 02 31 69 01 .......4.....1i. +| 3872: 06 01 01 02 01 01 70 01 02 05 04 08 18 84 80 80 ......p......... +| 3888: 80 80 06 03 00 36 00 00 00 13 02 31 65 01 02 03 .....6.....1e... +| 3904: 01 01 66 01 08 02 01 01 04 04 06 1b 84 80 80 80 ..f............. +| 3920: 80 05 03 00 3c 00 00 00 16 05 30 74 65 72 6d 01 ....<.....0term. +| 3936: 02 04 02 02 68 65 01 06 01 01 03 04 09 14 84 80 ....he.......... +| 3952: 80 80 80 04 03 00 2e 00 00 00 10 06 30 64 61 62 ............0dab +| 3968: 6c 65 01 06 01 01 05 04 15 84 80 80 80 80 03 03 le.............. +| 3984: 00 30 00 00 00 11 02 08 30 70 72 65 73 65 6e 74 .0......0present +| 4000: 01 02 05 05 1b 84 80 80 80 80 02 03 00 3c 00 00 .............<.. +| 4016: 00 16 04 30 66 74 73 01 06 01 01 04 01 02 69 6e ...0fts.......in +| 4032: 01 06 01 01 04 0a 1a 84 80 80 80 80 01 03 00 3a ...............: +| 4048: 00 00 00 15 05 30 65 61 63 68 01 02 03 01 13 66 .....0each.....f +| 4064: 6f 72 01 02 02 04 09 06 01 03 00 12 03 0b 0f 00 or.............. +| 4080: 00 08 8c 80 80 80 80 11 03 00 16 00 00 00 05 04 ................ +| page 3 offset 8192 +| 0: 0a 00 00 00 32 0e 4f 00 0f fa 0f f1 0f e9 0f e1 ....2.O......... +| 16: 0f d8 0f d1 0f c9 0f c1 0f b9 0f b1 0f a9 0f a0 ................ +| 32: 0f 98 0f 90 0f 87 0f 80 0f 78 0f 71 0f 68 0f 5f .........x.q.h._ +| 48: 0f 56 0f 4d 0f 41 0f 38 0f 2f 0f 26 0f 1d 0f 13 .V.M.A.8./.&.... +| 64: 0f 0a 0f 01 0e f7 0e ee 0e e6 0e dd 0e d6 0e cd ................ +| 80: 0e c3 0e ba 0e 00 00 00 00 00 00 00 00 00 00 00 ................ +| 3648: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 ................ +| 3664: 04 01 10 01 03 34 74 20 07 04 01 0e 01 03 34 1e .....4t ......4. +| 3680: 09 04 01 12 01 03 33 74 68 1c 08 04 01 10 01 03 ......3th....... +| 3696: 33 6e 1a 08 04 01 10 01 03 32 77 18 08 04 01 10 3n.......2w..... +| 3712: 01 03 32 74 16 08 04 01 10 01 03 32 6e 14 07 04 ..2t.......2n... +| 3728: 01 0e 01 03 32 12 08 04 01 10 01 03 31 74 10 08 ....2.......1t.. +| 3744: 04 01 10 01 03 31 6e 0e 07 04 01 0e 01 03 31 0c .....1n.......1. +| 3760: 09 04 01 12 01 03 30 74 68 0a 08 04 01 10 01 03 ......0th....... +| 3776: 30 74 08 09 04 01 12 01 03 30 6e 75 06 08 04 01 0t.......0nu.... +| 3792: 10 01 03 30 6e 04 06 04 01 0c 01 03 02 08 04 01 ...0n........... +| 3808: 10 01 02 34 72 22 07 04 01 0e 01 02 34 20 08 04 ...4r.......4 .. +| 3824: 01 10 01 02 33 72 1e 09 04 01 12 01 02 33 61 72 ....3r.......3ar +| 3840: 1c 08 04 01 10 01 02 32 74 1a 08 04 01 10 01 02 .......2t....... +| 3856: 32 69 18 09 04 01 12 01 02 32 61 72 16 08 04 01 2i.......2ar.... +| 3872: 10 01 02 31 74 14 08 04 01 10 01 02 31 6e 12 08 ...1t.......1n.. +| 3888: 04 01 10 01 02 31 62 10 08 04 01 10 01 02 31 32 .....1b.......12 +| 3904: 0e 0b 04 01 16 01 02 30 74 68 65 72 0c 08 04 01 .......0ther.... +| 3920: 10 01 02 30 74 0a 08 04 01 10 01 02 30 6e 08 08 ...0t.......0n.. +| 3936: 14 01 10 01 02 30 62 06 08 04 01 10 01 02 30 61 .....0b.......0a +| 3952: 04 06 04 01 0c 01 02 02 07 04 09 10 01 34 74 22 .............4t. +| 3968: 06 04 09 0e 01 34 20 08 04 09 12 01 33 74 65 1e .....4 .....3te. +| 3984: 07 04 09 10 01 33 70 1c 07 04 09 10 01 33 66 1a .....3p......3f. +| 4000: 08 04 09 12 01 32 74 68 18 07 04 09 10 01 32 74 .....2th......2t +| 4016: 16 01 64 09 10 01 32 69 14 07 04 09 10 01 32 66 ..d...2i......2f +| 4032: 12 07 04 09 10 01 31 74 10 07 04 09 10 01 31 69 ......1t......1i +| 4048: 0e 06 04 09 0e 01 31 0c 08 04 09 12 01 30 74 65 ......1......0te +| 4064: 0a 06 04 09 10 01 30 74 08 07 04 09 10 01 30 70 ......0t......0p +| 4080: 06 08 04 09 12 00 00 00 00 00 00 00 00 00 00 00 ................ +| page 4 offset 12288 +| 4064: 00 00 00 00 00 00 00 00 00 00 00 05 03 03 00 10 ................ +| 4080: 03 05 05 02 03 00 10 04 06 05 01 03 00 10 04 04 ................ +| page 5 offset 16384 +| 0: 0a 00 00 00 02 0f eb 00 0f eb 0f f4 00 00 00 00 ................ +| 4064: 00 00 00 00 00 00 00 00 00 00 00 08 03 15 01 70 ...............p +| 4080: 67 73 7a 18 0b 03 1b 01 76 65 72 73 69 6f 6e 04 gsz.....version. +| page 6 offset 20480 +| 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +| 4080: 00 00 03 03 02 01 03 03 02 02 01 00 00 00 00 00 ................ +| end crash-0f47112aa7520c.db + }] +} {} + +do_catchsql_test 1.1 { + SELECT * FROM t1('R*') WHERE (a,b)<=(current_date,0) ORDER BY rowid DESC; +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +# +reset_db +do_test 2.0 { + sqlite3 db {} + db deserialize [decode_hexdb { + +.open --hexdb +| size 24576 pagesize 4096 filename sql047467.txt.db +| page 1 offset 0 +| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. +| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 00 .....@ ........ +| 96: 00 00 00 00 0d 00 00 00 06 0e 0f 00 0f aa 0f 53 ...............S +| 112: 0e e8 0e 8b 0e 33 0e 0f 01 00 00 00 00 00 00 00 .....3.......... +| 3584: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................ +| 3600: 06 06 17 11 11 01 31 74 61 62 6c 65 62 62 62 62 ......1tablebbbb +| 3616: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 62 62 .CREATE TABLE bb +| 3632: 28 61 29 56 05 06 17 1f 1f 01 7d 74 61 62 6c 65 (a)V.......table +| 3648: 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 6f 6e 66 t1_configt1_conf +| 3664: 69 67 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 ig.CREATE TABLE +| 3680: 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b 20 50 52 't1_config'(k PR +| 3696: 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 20 57 49 IMARY KEY, v) WI +| 3712: 54 48 4f 55 54 20 52 4f 57 49 44 5b 04 07 17 21 THOUT ROWID[...! +| 3728: 21 01 81 01 74 61 62 6c 65 74 31 5f 64 6f 63 73 !...tablet1_docs +| 3744: 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 04 43 52 izet1_docsize.CR +| 3760: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 64 EATE TABLE 't1_d +| 3776: 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 54 45 47 ocsize'(id INTEG +| 3792: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY, +| 3808: 73 7a 20 42 4c 4f 42 29 69 03 07 17 19 19 01 81 sz BLOB)i....... +| 3824: 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 31 5f 69 -tablet1_idxt1_i +| 3840: 64 78 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 dx.CREATE TABLE +| 3856: 27 74 31 5f 69 64 78 27 28 73 65 67 69 64 2c 20 't1_idx'(segid, +| 3872: 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 52 49 4d term, pgno, PRIM +| 3888: 41 52 59 20 4b 45 59 28 73 65 67 69 64 2c 20 74 ARY KEY(segid, t +| 3904: 65 72 6d 29 29 20 57 49 54 48 4f 55 54 20 52 4f erm)) WITHOUT RO +| 3920: 57 49 44 55 02 07 17 1b 1b 01 81 01 74 61 62 6c WIDU........tabl +| 3936: 65 74 31 5f 64 61 74 61 74 31 5f 64 61 74 61 02 et1_datat1_data. +| 3952: 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 CREATE TABLE 't1 +| 3968: 5f 64 61 74 61 27 28 69 64 20 49 4e 54 45 47 45 _data'(id INTEGE +| 3984: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 R PRIMARY KEY, b +| 4000: 6c 6f 63 6b 20 42 4c 4f 42 29 54 01 07 17 11 11 lock BLOB)T..... +| 4016: 08 81 15 74 61 62 6c 65 74 31 74 31 43 52 45 41 ...tablet1t1CREA +| 4032: 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 4c 45 TE VIRTUAL TABLE +| 4048: 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 28 61 t1 USING fts5(a +| 4064: 2c 62 2c 70 72 65 66 69 78 3d 22 31 2c 32 2c 33 ,b,prefix=.1,2,3 +| 4080: 2c 34 22 2c 20 63 6f 6e 74 65 6e 74 3d 22 22 29 ,4., content=..) +| page 2 offset 4096 +| 0: 0d 0b 6a 00 37 09 4c 02 0f e7 09 4c 0f c6 0f a4 ..j.7.L....L.... +| 16: 0f 88 0f 6d 0f 4b 0f 2c 0f 0e 0e ec 0e cd 0e ad ...m.K.,........ +| 32: 0e 8e 0e 6c 0e 4b 0e 29 0e 08 0d e6 0d c4 0d b5 ...l.K.)........ +| 48: 0d 97 0d 76 0d 54 0d 31 0d 15 0c f3 0c d3 0c b5 ...v.T.1........ +| 64: 0c 95 0c 73 0c 54 0c 32 0c 10 0b ee 0b cc 0b b0 ...s.T.2........ +| 80: 0b 8d 0b 7e 0b 48 0b 2e 0b 0b 0a ef 00 00 00 00 ...~.H.......... +| 2368: 00 00 00 00 00 00 00 00 00 00 00 00 15 0a 03 00 ................ +| 2384: 30 00 00 00 01 01 03 35 00 03 01 11 12 02 01 12 0......5........ +| 2400: 03 01 11 1c 8c 80 80 80 80 10 03 00 3e 00 00 00 ............>... +| 2416: 17 01 05 05 34 74 61 62 6c 03 02 03 01 04 77 68 ....4tabl.....wh +| 2432: 65 72 03 02 06 09 1b 8c 80 80 80 80 0f 03 00 3c er.............< +| 2448: 00 00 00 16 05 34 66 74 73 34 03 02 02 01 04 6e .....4fts4.....n +| 2464: 75 6d 62 03 06 01 04 09 1b 8c 80 80 80 80 0e 03 umb............. +| 2480: 00 3b ff f0 00 16 04 33 74 68 65 03 06 01 01 04 .;.....3the..... +| 2496: 01 03 77 68 65 03 02 04 04 0a 1b 8c 80 80 80 80 ..whe........... +| 2512: 0d 03 00 3c 00 00 00 16 04 33 6e 75 6d 03 06 01 ...<.....3num... +| 2528: 01 05 01 03 74 61 62 03 02 03 04 0a 19 8c 80 80 ....tab......... +| 2544: 80 80 0c 03 00 38 00 00 00 14 03 32 77 68 03 02 .....8.....2wh.. +| 2560: 04 00 04 33 66 74 73 03 02 02 04 07 18 8c 80 80 ...3fts......... +| 2576: 80 80 0b 03 00 36 00 00 00 13 03 32 74 61 03 02 .....6.....2ta.. +| 2592: 03 02 01 68 03 06 01 01 04 04 07 1b 8c 80 80 80 ...h............ +| 2608: 80 0a 03 00 3c 00 00 00 16 03 32 6e 75 03 06 01 ....<.....2nu... +| 2624: 01 05 01 02 6f 66 03 06 01 01 06 04 09 19 8c 80 ....of.......... +| 2640: 80 80 80 09 03 00 38 00 00 00 14 03 32 66 74 03 ......8.....2ft. +| 2656: 02 02 01 02 69 73 03 06 01 01 03 04 07 18 8c 80 ....is.......... +| 2672: 80 80 80 08 03 00 36 00 00 00 13 02 31 74 03 08 ......6.....1t.. +| 2688: 03 01 01 04 01 01 77 03 02 04 04 09 1a 8c 80 80 ......w......... +| 2704: 80 80 07 03 00 3a 00 00 00 15 02 31 6e 03 08 01 .....:.....1n... +| 2720: 01 02 05 01 00 6f 03 06 01 01 06 14 09 18 8c 80 .....o.......... +| 2736: 80 80 80 06 03 00 36 00 00 00 03 04 02 31 66 03 ......6......1f. +| 2752: 02 02 01 01 69 03 06 01 01 03 04 f6 1c 8c 80 80 ....i........... +| 2768: 80 80 05 03 00 3e 00 00 00 17 04 30 74 68 65 03 .....>.....0the. +| 2784: f6 01 01 04 01 05 77 68 65 72 65 03 02 04 0a 15 ......where..... +| 2800: 8c 80 80 80 80 04 03 00 30 00 00 00 11 01 01 06 ........0....... +| 2816: 06 30 74 61 62 6c 65 0f 42 03 07 1c 8c 81 80 80 .0table.B....... +| 2832: 80 03 03 00 3e 00 00 00 17 07 30 6e 75 6d 62 65 ....>.....0numbe +| 2848: 72 03 06 01 01 05 01 02 6f 66 03 06 04 0d 13 8c r.......of...... +| 2864: 80 80 80 80 02 03 00 2c 00 00 00 0f 01 01 03 02 .......,........ +| 2880: 30 6e 03 06 01 01 02 07 1b 8c 80 80 80 80 01 03 0n.............. +| 2896: 00 3c 00 00 00 16 08 30 66 74 73 34 61 75 78 03 .<.....0fts4aux. +| 2912: 02 02 01 02 69 73 03 06 04 0c 00 00 00 14 2a 00 ....is........*. +| 2928: 00 00 01 01 02 24 00 02 01 01 12 02 01 12 08 88 .....$.......... +| 2944: 80 80 80 80 12 03 00 16 00 00 00 05 02 1c 88 80 ................ +| 2960: 80 80 80 11 03 00 3e 00 00 00 17 05 34 72 6f 77 ......>.....4row +| 2976: 73 02 06 01 01 05 01 04 74 68 65 72 02 02 04 0b s.......ther.... +| 2992: 15 88 80 80 80 80 10 03 00 30 00 00 00 11 02 01 .........0...... +| 3008: 01 07 05 34 62 65 74 77 02 02 04 08 1b 88 80 80 ...4betw........ +| 3024: 80 80 0f 03 00 3c 00 00 00 16 04 04 33 72 6f 77 .....<......3row +| 3040: 02 06 01 01 05 01 03 74 68 65 02 08 05 0a 1b 88 .......the...... +| 3056: 80 80 80 80 0e 03 00 3c 00 00 00 16 01 01 02 04 .......<........ +| 3072: 33 61 72 65 02 02 03 01 03 62 65 74 02 02 07 08 3are.....bet.... +| 3088: 1b 88 80 80 80 80 0d 03 00 3c 00 00 00 16 13 32 .........<.....2 +| 3104: 74 68 02 08 02 01 01 07 00 04 33 61 6e 64 02 06 th........3and.. +| 3120: 04 0a 1b 88 80 80 80 80 0c 03 00 3c 00 00 00 16 ...........<.... +| 3136: 03 32 69 6e 02 06 01 01 06 01 02 72 6f 02 06 01 .2in.......ro... +| 3152: 01 05 04 09 18 88 80 80 80 80 0b 03 00 36 00 00 .............6.. +| 3168: 00 13 02 03 32 61 72 02 02 03 01 02 62 65 02 02 ....2ar.....be.. +| 3184: 04 05 07 1b 88 80 80 80 80 0a 03 00 3c 00 94 50 ............<..P +| 3200: 16 02 31 74 02 08 02 01 01 07 00 03 32 61 6e 02 ..1t........2an. +| 3216: 06 01 01 04 09 19 88 80 80 80 80 09 03 00 38 00 ..............8. +| 3232: 00 00 14 02 31 6e 02 06 01 01 03 01 01 72 02 06 ....1n.......r.. +| 3248: 01 01 05 04 08 17 88 80 80 80 80 08 03 00 34 00 ..............4. +| 3264: 00 00 12 02 31 62 02 02 04 01 01 69 02 06 01 01 ....1b.....i.... +| 3280: 06 04 06 19 88 80 80 80 80 07 03 00 38 00 00 00 ............8... +| 3296: 14 04 02 31 32 02 02 05 01 01 61 02 08 03 01 01 ...12.....a..... +| 3312: 02 05 06 1b 88 80 80 80 80 06 03 00 3c 00 00 00 ............<... +| 3328: 16 06 30 74 68 65 72 65 02 02 02 00 02 31 31 02 ..0there.....11. +| 3344: 06 01 01 04 0a 15 88 80 80 80 80 05 03 00 30 00 ..............0. +| 3360: 00 00 11 01 01 05 04 30 74 68 65 02 06 01 01 07 .......0the..... +| 3376: 07 1c 88 80 80 80 80 04 03 00 3e 00 00 00 17 01 ..........>..... +| 3392: 01 06 02 30 6e 02 06 01 01 03 01 04 72 6f 77 73 ...0n.......rows +| 3408: 02 06 07 08 1b 88 80 80 80 80 03 03 00 3c 00 00 .............<.. +| 3424: 00 16 08 30 62 65 74 77 65 65 6e 02 02 04 01 02 ...0between..... +| 3440: 69 6e 02 06 04 0c 1a 88 80 80 80 80 02 03 00 3a in.............: +| 3456: 00 00 00 15 04 30 61 6e 64 02 06 01 01 02 02 02 .....0and....... +| 3472: 72 65 02 02 03 04 0a 17 88 80 80 80 80 01 03 00 re.............. +| 3488: 34 00 00 00 12 02 30 31 02 06 01 01 04 01 01 32 4.....01.......2 +| 3504: 02 02 05 04 08 08 84 80 80 80 80 12 03 00 16 00 ................ +| 3520: 00 00 05 04 1b 84 80 80 80 80 11 03 00 3c 00 00 .............<.. +| 3536: 00 16 05 34 74 61 62 6c 01 06 01 01 05 02 03 65 ...4tabl.......e +| 3552: 72 6d 01 02 04 0b 1b 84 80 80 80 80 10 03 00 3c rm.............< +| 3568: 00 00 00 16 05 34 65 61 63 68 01 02 03 01 04 70 .....4each.....p +| 3584: 72 65 73 01 02 05 04 08 1a 84 80 80 80 80 0f 03 res............. +| 3600: 00 3a 00 00 00 15 04 33 74 65 72 01 02 04 02 02 .:.....3ter..... +| 3616: 68 65 01 06 01 01 03 04 08 1b 84 80 80 80 80 0e he.............. +| 3632: 03 00 3c 00 00 00 16 04 33 80 72 65 01 02 05 01 ..<.....3.re.... +| 3648: 03 74 61 62 01 06 01 01 05 04 08 1a 84 80 80 80 .tab............ +| 3664: 80 0d 03 00 3a 00 00 00 15 04 33 66 6f 72 01 02 ....:.....3for.. +| 3680: 02 02 02 74 73 01 06 01 01 04 04 08 1b 84 80 80 ...ts........... +| 3696: 80 80 0c 03 00 3c 00 00 00 17 03 32 74 68 01 06 .....<.....2th.. +| 3712: 01 01 03 00 04 33 65 61 63 01 02 03 04 09 18 84 .....3eac....... +| 3728: 80 80 80 80 0b 03 00 36 00 00 00 13 03 32 74 61 .......6.....2ta +| 3744: 01 06 01 01 05 02 01 65 01 02 04 04 09 19 84 80 .......e........ +| 3760: 80 80 80 0a 03 00 38 00 00 00 14 03 32 69 6e 01 ......8.....2in. +| 3776: 06 01 01 02 01 02 70 72 01 02 05 04 09 18 84 80 ......pr........ +| 3792: 80 80 80 09 03 00 36 00 00 00 13 03 32 66 6f 01 ......6.....2fo. +| 3808: 02 02 02 01 74 01 06 01 01 04 04 07 1b 84 80 80 ....t........... +| 3824: 80 80 08 03 00 3c 00 00 00 16 02 31 74 01 0a 04 .....<.....1t... +| 3840: 01 01 03 04 00 03 32 65 61 01 02 03 04 0a 17 84 ......2ea....... +| 3856: 80 80 80 80 07 03 00 34 00 00 00 12 02 31 69 01 .......4.....1i. +| 3872: 06 01 01 02 01 01 70 01 02 05 04 08 18 84 80 80 ......p......... +| 3888: 80 80 06 03 00 36 00 00 00 13 02 31 65 01 02 03 .....6.....1e... +| 3904: 01 01 66 01 08 02 01 01 04 04 06 1b 84 80 80 80 ..f............. +| 3920: 80 05 03 00 3c 00 00 00 16 05 30 74 65 72 6d 01 ....<.....0term. +| 3936: 02 04 02 02 68 65 01 06 01 01 03 04 09 14 84 80 ....he.......... +| 3952: 80 80 80 04 03 00 2e 00 00 00 10 06 30 64 61 62 ............0dab +| 3968: 6c 65 01 06 01 01 05 04 15 84 80 80 80 80 03 03 le.............. +| 3984: 00 30 00 00 00 11 02 08 30 70 72 65 73 65 6e 74 .0......0present +| 4000: 01 02 05 05 1b 84 80 80 80 80 02 03 00 3c 00 00 .............<.. +| 4016: 00 16 04 30 66 74 73 01 06 01 01 04 01 02 69 6e ...0fts.......in +| 4032: 01 06 01 01 04 0a 1a 84 80 80 80 80 01 03 00 3a ...............: +| 4048: 00 00 00 15 05 30 65 61 63 68 01 02 03 01 13 66 .....0each.....f +| 4064: 6f 72 01 02 02 04 09 06 01 03 00 12 03 0b 0f 00 or.............. +| 4080: 00 08 8c 80 80 80 80 11 03 00 16 00 00 00 05 04 ................ +| page 3 offset 8192 +| 0: 0a 00 00 00 32 0e 4f 00 0f fa 0f f1 0f e9 0f e1 ....2.O......... +| 16: 0f d8 0f d1 0f c9 0f c1 0f b9 0f b1 0f a9 0f a0 ................ +| 32: 0f 98 0f 90 0f 87 0f 80 0f 78 0f 71 0f 68 0f 5f .........x.q.h._ +| 48: 0f 56 0f 4d 0f 41 0f 38 0f 2f 0f 26 0f 1d 0f 13 .V.M.A.8./.&.... +| 64: 0f 0a 0f 01 0e f7 0e ee 0e e6 0e dd 0e d6 0e cd ................ +| 80: 0e c3 0e ba 0e 00 00 00 00 00 00 00 00 00 00 00 ................ +| 3648: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 ................ +| 3664: 04 01 10 01 03 34 74 20 07 04 01 0e 01 03 34 1e .....4t ......4. +| 3680: 09 04 01 12 01 03 33 74 68 1c 08 04 01 10 01 03 ......3th....... +| 3696: 33 6e 1a 08 04 01 10 01 03 32 77 18 08 04 01 10 3n.......2w..... +| 3712: 01 03 32 74 16 08 04 01 10 01 03 32 6e 14 07 04 ..2t.......2n... +| 3728: 01 0e 01 03 32 12 08 04 01 10 01 03 31 74 10 08 ....2.......1t.. +| 3744: 04 01 10 01 03 31 6e 0e 07 04 01 0e 01 03 31 0c .....1n.......1. +| 3760: 09 04 01 12 01 03 30 74 68 0a 08 04 01 10 01 03 ......0th....... +| 3776: 30 74 08 09 04 01 12 01 03 30 6e 75 06 08 04 01 0t.......0nu.... +| 3792: 10 01 03 30 6e 04 06 04 01 0c 01 03 02 08 04 01 ...0n........... +| 3808: 10 01 02 34 72 22 07 04 01 0e 01 02 34 20 08 04 ...4r.......4 .. +| 3824: 01 10 01 02 33 72 1e 09 04 01 12 01 02 33 61 72 ....3r.......3ar +| 3840: 1c 08 04 01 10 01 02 32 74 1a 08 04 01 10 01 02 .......2t....... +| 3856: 32 69 18 09 04 01 12 01 02 32 61 72 16 08 04 01 2i.......2ar.... +| 3872: 10 01 02 31 74 14 08 04 01 10 01 02 31 6e 12 08 ...1t.......1n.. +| 3888: 04 01 10 01 02 31 62 10 08 04 01 10 01 02 31 32 .....1b.......12 +| 3904: 0e 0b 04 01 16 01 02 30 74 68 65 72 0c 08 04 01 .......0ther.... +| 3920: 10 01 02 30 74 0a 08 04 01 10 01 02 30 6e 08 08 ...0t.......0n.. +| 3936: 14 01 10 01 02 30 62 06 08 04 01 10 01 02 30 61 .....0b.......0a +| 3952: 04 06 04 01 0c 01 02 02 07 04 09 10 01 34 74 22 .............4t. +| 3968: 06 04 09 0e 01 34 20 08 04 09 12 01 33 74 65 1e .....4 .....3te. +| 3984: 07 04 09 10 01 33 70 1c 07 04 09 10 01 33 66 1a .....3p......3f. +| 4000: 08 04 09 12 01 32 74 68 18 07 04 09 10 01 32 74 .....2th......2t +| 4016: 16 01 64 09 10 01 32 69 14 07 04 09 10 01 32 66 ..d...2i......2f +| 4032: 12 07 04 09 10 01 31 74 10 07 04 09 10 01 31 69 ......1t......1i +| 4048: 0e 06 04 09 0e 01 31 0c 08 04 09 12 01 30 74 65 ......1......0te +| 4064: 0a 06 04 09 10 01 30 74 08 07 04 09 10 01 30 70 ......0t......0p +| 4080: 06 08 04 09 12 00 00 00 00 00 00 00 00 00 00 00 ................ +| page 4 offset 12288 +| 4064: 00 00 00 00 00 00 00 00 00 00 00 05 03 03 00 10 ................ +| 4080: 03 05 05 02 03 00 10 04 06 05 01 03 00 10 04 04 ................ +| page 5 offset 16384 +| 0: 0a 00 00 00 02 0f eb 00 0f eb 0f f4 00 00 00 00 ................ +| 4064: 00 00 00 00 00 00 00 00 00 00 00 08 03 15 01 70 ...............p +| 4080: 67 73 7a 18 0b 03 1b 01 76 65 72 73 69 6f 6e 04 gsz.....version. +| page 6 offset 20480 +| 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +| 4080: 00 00 03 03 02 01 03 03 02 02 01 00 00 00 00 00 ................ +| end sql047467.txt.db +}]} {} + +do_catchsql_test 2.1 { +SELECT * FROM t1('R*R*R*R*') WHERE (a,b)<=(current_date,0) ORDER BY rowid DESC; +} {1 {database disk image is malformed}} + + +sqlite3_fts5_may_be_corrupt 0 +finish_test + diff --git a/ext/fts5/test/fts5vocab2.test b/ext/fts5/test/fts5vocab2.test index e736303cf4..6f7aad329c 100644 --- a/ext/fts5/test/fts5vocab2.test +++ b/ext/fts5/test/fts5vocab2.test @@ -255,6 +255,32 @@ do_test 5.1 { do_execsql_test 5.2 { SELECT * FROM t1 } {one two three four five} + +#------------------------------------------------------------------------- +# Check that the fts5 table cannot be written while there are vocab +# cursors open. +reset_db +do_execsql_test 5.0 { + CREATE VIRTUAL TABLE t1 USING fts5(a); + CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, instance); + WITH s(i) AS ( + VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<10000 + ) + INSERT INTO t1 SELECT + 'State Emergency Service (SES), Rural Fire Service (RFS) and Volunteers' + FROM s; +} + +do_catchsql_test 5.1 { + INSERT INTO t1 SELECT rowid FROM v1 +} {1 {query aborted}} + +do_catchsql_test 5.2 { + DELETE FROM t1 WHERE rowid>100; + INSERT INTO t1 SELECT randomblob(3000) FROM v1 +} {1 {query aborted}} + + finish_test diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c index d977d41ddc..c9988f6078 100644 --- a/ext/misc/fileio.c +++ b/ext/misc/fileio.c @@ -72,6 +72,11 @@ ** $path is a relative path, then $path is interpreted relative to $dir. ** And the paths returned in the "name" column of the table are also ** relative to directory $dir. +** +** Notes on building this extension for Windows: +** Unless linked statically with the SQLite library, a preprocessor +** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone +** DLL form of this extension for WIN32. See its use below for details. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 @@ -225,6 +230,22 @@ static sqlite3_uint64 fileTimeToUnixTime( return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000; } + +#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32)) +# /* To allow a standalone DLL, use this next replacement function: */ +# undef sqlite3_win32_utf8_to_unicode +# define sqlite3_win32_utf8_to_unicode utf8_to_utf16 +# +LPWSTR utf8_to_utf16(const char *z){ + int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0); + LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR)); + if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) ) + return rv; + sqlite3_free(rv); + return 0; +} +#endif + /* ** This function attempts to normalize the time values found in the stat() ** buffer to UTC. This is necessary on Win32, where the runtime library @@ -998,3 +1019,11 @@ int sqlite3_fileio_init( } return rc; } + +#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32)) +/* To allow a standalone DLL, make test_windirent.c use the same + * redefined SQLite API calls as the above extension code does. + * Just pull in this .c to accomplish this. As a beneficial side + * effect, this extension becomes a single translation unit. */ +# include "test_windirent.c" +#endif diff --git a/ext/misc/ieee754.c b/ext/misc/ieee754.c index 6cdd79a4d4..66d946f3d5 100644 --- a/ext/misc/ieee754.c +++ b/ext/misc/ieee754.c @@ -194,7 +194,11 @@ static void ieee754func( e += 1075; if( e<=0 ){ /* Subnormal */ - m >>= 1-e; + if( 1-e >= 64 ){ + m = 0; + }else{ + m >>= 1-e; + } e = 0; }else if( e>0x7ff ){ e = 0x7ff; diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 077d02d912..6fe3ab9676 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -605,7 +605,7 @@ static void jsonReturn( sqlite3_result_int64(pCtx, i); int_done: break; - int_as_real: i=0; /* no break */ deliberate_fall_through + int_as_real: ; /* no break */ deliberate_fall_through } case JSON_REAL: { double r; diff --git a/ext/misc/series.c b/ext/misc/series.c index 08e1829b8c..e8d8c10aec 100644 --- a/ext/misc/series.c +++ b/ext/misc/series.c @@ -448,7 +448,7 @@ int sqlite3_series_init( int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); #ifndef SQLITE_OMIT_VIRTUALTABLE - if( sqlite3_libversion_number()<3008012 ){ + if( sqlite3_libversion_number()<3008012 && pzErrMsg!=0 ){ *pzErrMsg = sqlite3_mprintf( "generate_series() requires SQLite 3.8.12 or later"); return SQLITE_ERROR; diff --git a/ext/misc/zipfile.c b/ext/misc/zipfile.c index b727f9f2be..78c669631c 100644 --- a/ext/misc/zipfile.c +++ b/ext/misc/zipfile.c @@ -561,6 +561,7 @@ static u16 zipfileGetU16(const u8 *aBuf){ ** Read and return a 32-bit little-endian unsigned integer from buffer aBuf. */ static u32 zipfileGetU32(const u8 *aBuf){ + if( aBuf==0 ) return 0; return ((u32)(aBuf[3]) << 24) + ((u32)(aBuf[2]) << 16) + ((u32)(aBuf[1]) << 8) @@ -863,7 +864,7 @@ static int zipfileGetEntry( aRead = (u8*)&aBlob[pNew->cds.iOffset]; } - rc = zipfileReadLFH(aRead, &lfh); + if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh); if( rc==SQLITE_OK ){ pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; pNew->iDataOff += lfh.nFile + lfh.nExtra; @@ -1139,13 +1140,13 @@ static int zipfileReadEOCD( int nRead; /* Bytes to read from file */ int rc = SQLITE_OK; + memset(pEOCD, 0, sizeof(ZipfileEOCD)); if( aBlob==0 ){ i64 iOff; /* Offset to read from */ i64 szFile; /* Total size of file in bytes */ fseek(pFile, 0, SEEK_END); szFile = (i64)ftell(pFile); if( szFile==0 ){ - memset(pEOCD, 0, sizeof(ZipfileEOCD)); return SQLITE_OK; } nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE)); diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 2151d5af04..47905c3d8c 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -64,7 +64,11 @@ #endif int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */ -#ifndef SQLITE_AMALGAMATION +/* +** If building separately, we will need some setup that is normally +** found in sqliteInt.h +*/ +#if !defined(SQLITE_AMALGAMATION) #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; @@ -77,7 +81,17 @@ typedef unsigned int u32; #if defined(NDEBUG) && defined(SQLITE_DEBUG) # undef NDEBUG #endif +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define ALWAYS(X) (1) +# define NEVER(X) (0) +#elif !defined(NDEBUG) +# define ALWAYS(X) ((X)?1:(assert(0),0)) +# define NEVER(X) ((X)?(assert(0),1):0) +#else +# define ALWAYS(X) (X) +# define NEVER(X) (X) #endif +#endif /* !defined(SQLITE_AMALGAMATION) */ #include #include @@ -135,7 +149,9 @@ struct Rtree { u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ u8 nAux; /* # of auxiliary columns in %_rowid */ +#ifdef SQLITE_ENABLE_GEOPOLY u8 nAuxNotNull; /* Number of initial not-null aux columns */ +#endif #ifdef SQLITE_DEBUG u8 bCorrupt; /* Shadow table corruption detected */ #endif @@ -666,18 +682,6 @@ static void nodeBlobReset(Rtree *pRtree){ } } -/* -** Check to see if pNode is the same as pParent or any of the parents -** of pParent. -*/ -static int nodeInParentChain(const RtreeNode *pNode, const RtreeNode *pParent){ - do{ - if( pNode==pParent ) return 1; - pParent = pParent->pParent; - }while( pParent ); - return 0; -} - /* ** Obtain a reference to an r-tree node. */ @@ -694,14 +698,7 @@ static int nodeAcquire( ** increase its reference count and return it. */ if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ - if( pParent && !pNode->pParent ){ - if( nodeInParentChain(pNode, pParent) ){ - RTREE_IS_CORRUPT(pRtree); - return SQLITE_CORRUPT_VTAB; - } - pParent->nRef++; - pNode->pParent = pParent; - }else if( pParent && pNode->pParent && pParent!=pNode->pParent ){ + if( pParent && pParent!=pNode->pParent ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -759,7 +756,7 @@ static int nodeAcquire( ** are the leaves, and so on. If the depth as specified on the root node ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. */ - if( pNode && rc==SQLITE_OK && iNode==1 ){ + if( rc==SQLITE_OK && pNode && iNode==1 ){ pRtree->iDepth = readInt16(pNode->zData); if( pRtree->iDepth>RTREE_MAX_DEPTH ){ rc = SQLITE_CORRUPT_VTAB; @@ -1365,11 +1362,12 @@ static int nodeRowidIndex( */ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){ RtreeNode *pParent = pNode->pParent; - if( pParent ){ + if( ALWAYS(pParent) ){ return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); + }else{ + *piIndex = -1; + return SQLITE_OK; } - *piIndex = -1; - return SQLITE_OK; } /* @@ -1492,7 +1490,8 @@ static RtreeSearchPoint *rtreeSearchPointNew( pNew = rtreeEnqueue(pCur, rScore, iLevel); if( pNew==0 ) return 0; ii = (int)(pNew - pCur->aPoint) + 1; - if( iiaNode[ii]==0 ); pCur->aNode[ii] = pCur->aNode[0]; }else{ @@ -1553,7 +1552,7 @@ static void rtreeSearchPointPop(RtreeCursor *p){ if( p->bPoint ){ p->anQueue[p->sPoint.iLevel]--; p->bPoint = 0; - }else if( p->nPoint ){ + }else if( ALWAYS(p->nPoint) ){ p->anQueue[p->aPoint[0].iLevel]--; n = --p->nPoint; p->aPoint[0] = p->aPoint[n]; @@ -1694,7 +1693,7 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); - if( rc==SQLITE_OK && p ){ + if( rc==SQLITE_OK && ALWAYS(p) ){ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); } return rc; @@ -1712,7 +1711,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; - if( p==0 ) return SQLITE_OK; + if( NEVER(p==0) ) return SQLITE_OK; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ @@ -1911,8 +1910,11 @@ static int rtreeFilter( } if( rc==SQLITE_OK ){ RtreeSearchPoint *pNew; + assert( pCsr->bPoint==0 ); /* Due to the resetCursor() call above */ pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1)); - if( pNew==0 ) return SQLITE_NOMEM; + if( NEVER(pNew==0) ){ /* Because pCsr->bPoint was FALSE */ + return SQLITE_NOMEM; + } pNew->id = 1; pNew->iCell = 0; pNew->eWithin = PARTLY_WITHIN; @@ -1989,7 +1991,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; if( bMatch==0 && p->usable - && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ + && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ /* We have an equality constraint on the rowid. Use strategy 1. */ int jj; @@ -2242,12 +2244,19 @@ static int AdjustTree( ){ RtreeNode *p = pNode; int cnt = 0; + int rc; while( p->pParent ){ RtreeNode *pParent = p->pParent; RtreeCell cell; int iCell; - if( (++cnt)>1000 || nodeParentIndex(pRtree, p, &iCell) ){ + cnt++; + if( NEVER(cnt>100) ){ + RTREE_IS_CORRUPT(pRtree); + return SQLITE_CORRUPT_VTAB; + } + rc = nodeParentIndex(pRtree, p, &iCell); + if( NEVER(rc!=SQLITE_OK) ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -2536,6 +2545,10 @@ static int updateMapping( xSetMapping = ((iHeight==0)?rowidWrite:parentWrite); if( iHeight>0 ){ RtreeNode *pChild = nodeHashLookup(pRtree, iRowid); + RtreeNode *p; + for(p=pNode; p; p=p->pParent){ + if( p==pChild ) return SQLITE_CORRUPT_VTAB; + } if( pChild ){ nodeRelease(pRtree, pChild->pParent); nodeReference(pNode); @@ -2631,11 +2644,12 @@ static int SplitNode( RtreeNode *pParent = pLeft->pParent; int iCell; rc = nodeParentIndex(pRtree, pLeft, &iCell); - if( rc==SQLITE_OK ){ + if( ALWAYS(rc==SQLITE_OK) ){ nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); rc = AdjustTree(pRtree, pParent, &leftbbox); + assert( rc==SQLITE_OK ); } - if( rc!=SQLITE_OK ){ + if( NEVER(rc!=SQLITE_OK) ){ goto splitnode_out; } } @@ -2710,7 +2724,7 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ */ iNode = sqlite3_column_int64(pRtree->pReadParent, 0); for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); - if( !pTest ){ + if( pTest==0 ){ rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); } } @@ -2741,6 +2755,7 @@ static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ pParent = pNode->pParent; pNode->pParent = 0; rc = deleteCell(pRtree, pParent, iCell, iHeight+1); + testcase( rc!=SQLITE_OK ); } rc2 = nodeRelease(pRtree, pParent); if( rc==SQLITE_OK ){ @@ -2963,7 +2978,7 @@ static int rtreeInsertCell( } }else{ rc = AdjustTree(pRtree, pNode, pCell); - if( rc==SQLITE_OK ){ + if( ALWAYS(rc==SQLITE_OK) ){ if( iHeight==0 ){ rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); }else{ @@ -3069,7 +3084,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ int rc2; RtreeNode *pChild = 0; i64 iChild = nodeGetRowid(pRtree, pRoot, 0); - rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); + rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); /* tag-20210916a */ if( rc==SQLITE_OK ){ rc = removeNode(pRtree, pChild, pRtree->iDepth-1); } @@ -3404,7 +3419,7 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ char *zSql; sqlite3_stmt *p; int rc; - i64 nRow = 0; + i64 nRow = RTREE_MIN_ROWEST; rc = sqlite3_table_column_metadata( db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0 @@ -3421,20 +3436,10 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ if( rc==SQLITE_OK ){ if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0); rc = sqlite3_finalize(p); - }else if( rc!=SQLITE_NOMEM ){ - rc = SQLITE_OK; - } - - if( rc==SQLITE_OK ){ - if( nRow==0 ){ - pRtree->nRowEst = RTREE_DEFAULT_ROWEST; - }else{ - pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); - } } sqlite3_free(zSql); } - + pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); return rc; } @@ -3584,9 +3589,12 @@ static int rtreeSqlInit( sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix); for(ii=0; iinAux; ii++){ if( ii ) sqlite3_str_append(p, ",", 1); +#ifdef SQLITE_ENABLE_GEOPOLY if( iinAuxNotNull ){ sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii); - }else{ + }else +#endif + { sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2); } } @@ -4265,8 +4273,10 @@ static int rtreeCheckTable( if( pStmt ){ nAux = sqlite3_column_count(pStmt) - 2; sqlite3_finalize(pStmt); + }else + if( check.rc!=SQLITE_NOMEM ){ + check.rc = SQLITE_OK; } - check.rc = SQLITE_OK; } /* Find number of dimensions in the rtree table. */ @@ -4523,7 +4533,10 @@ int sqlite3_rtree_query_callback( /* Allocate and populate the context object. */ pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); - if( !pGeomCtx ) return SQLITE_NOMEM; + if( !pGeomCtx ){ + if( xDestructor ) xDestructor(pContext); + return SQLITE_NOMEM; + } pGeomCtx->xGeom = 0; pGeomCtx->xQueryFunc = xQueryFunc; pGeomCtx->xDestructor = xDestructor; diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test index c37a2a77d5..0423bf557c 100644 --- a/ext/rtree/rtree1.test +++ b/ext/rtree/rtree1.test @@ -57,9 +57,12 @@ ifcapable !rtree { do_test rtree-1.1.1 { execsql { CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2) } } {} -do_test rtree-1.1.2 { +do_test rtree-1.1.2a { execsql { SELECT name FROM sqlite_master ORDER BY name } } {t1 t1_node t1_parent t1_rowid} +do_execsql_test rtree-1.1.2b { + SELECT name FROM pragma_table_list WHERE type='shadow' ORDER BY name; +} {t1_node t1_parent t1_rowid} do_test rtree-1.1.3 { execsql { DROP TABLE t1; diff --git a/ext/rtree/rtreeA.test b/ext/rtree/rtreeA.test index 8344863df0..921ba0b510 100644 --- a/ext/rtree/rtreeA.test +++ b/ext/rtree/rtreeA.test @@ -145,7 +145,7 @@ populate_t1 do_test rtreeA-2.2.0 { truncate_node 1 200 } {} do_corruption_tests rtreeA-2.2 { 1 "SELECT * FROM t1" - 2 "SELECT * FROM t1 WHERE rowid=5" + 2 "SELECT * FROM t1 WHERE +rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } @@ -160,7 +160,7 @@ do_test rtreeA-3.1.0.1 { set_tree_depth t1 } {1} do_test rtreeA-3.1.0.2 { set_tree_depth t1 3 } {3} do_corruption_tests rtreeA-3.1 { 1 "SELECT * FROM t1" - 2 "SELECT * FROM t1 WHERE rowid=5" + 2 "SELECT * FROM t1 WHERE +rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" } @@ -171,7 +171,7 @@ do_execsql_test rtreeA-3.1.0.3 { do_test rtreeA-3.2.0 { set_tree_depth t1 1000 } {1000} do_corruption_tests rtreeA-3.2 { 1 "SELECT * FROM t1" - 2 "SELECT * FROM t1 WHERE rowid=5" + 2 "SELECT * FROM t1 WHERE +rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" } @@ -183,7 +183,7 @@ do_test rtreeA-3.3.0 { } {65535} do_corruption_tests rtreeA-3.3 { 1 "SELECT * FROM t1" - 2 "SELECT * FROM t1 WHERE rowid=5" + 2 "SELECT * FROM t1 WHERE +rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" } @@ -203,7 +203,7 @@ do_test rtreeA-4.1.0 { } {4000} do_corruption_tests rtreeA-4.1 { 1 "SELECT * FROM t1" - 2 "SELECT * FROM t1 WHERE rowid=5" + 2 "SELECT * FROM t1 WHERE +rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } @@ -216,7 +216,7 @@ create_t1 populate_t1 do_execsql_test rtreeA-5.1.0 { DELETE FROM t1_parent } {} do_corruption_tests rtreeA-5.1 { - 1 "DELETE FROM t1 WHERE rowid = 5" + 1 "DELETE FROM t1 WHERE +rowid = 5" 2 "DELETE FROM t1" } diff --git a/ext/rtree/rtreedoc.test b/ext/rtree/rtreedoc.test new file mode 100644 index 0000000000..d2b37428a9 --- /dev/null +++ b/ext/rtree/rtreedoc.test @@ -0,0 +1,1582 @@ +# 2021 September 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 focus of this file is testing the r-tree extension. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] rtree_util.tcl] +source $testdir/tester.tcl +set testprefix rtreedoc + +ifcapable !rtree { + finish_test + return +} + +# This command returns the number of columns in table $tbl within the +# database opened by database handle $db +proc column_count {db tbl} { + set nCol 0 + $db eval "PRAGMA table_info = $tbl" { incr nCol } + return $nCol +} + +proc column_name_list {db tbl} { + set lCol [list] + $db eval "PRAGMA table_info = $tbl" { + lappend lCol $name + } + return $lCol +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 3 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-1 + +# EVIDENCE-OF: R-15060-13876 A 1-dimensional R*Tree thus has 3 columns. +do_execsql_test 1.1.1 { CREATE VIRTUAL TABLE rt1 USING rtree(id, x1,x2) } +do_test 1.1.2 { column_count db rt1 } 3 + +# EVIDENCE-OF: R-19353-19546 A 2-dimensional R*Tree has 5 columns. +do_execsql_test 1.2.1 { CREATE VIRTUAL TABLE rt2 USING rtree(id,x1,x2, y1,y2) } +do_test 1.2.2 { column_count db rt2 } 5 + +# EVIDENCE-OF: R-13615-19528 A 3-dimensional R*Tree has 7 columns. +do_execsql_test 1.3.1 { + CREATE VIRTUAL TABLE rt3 USING rtree(id, x1,x2, y1,y2, z1,z2) +} +do_test 1.3.2 { column_count db rt3 } 7 + +# EVIDENCE-OF: R-53479-41922 A 4-dimensional R*Tree has 9 columns. +do_execsql_test 1.4.1 { + CREATE VIRTUAL TABLE rt4 USING rtree(id, x1,x2, y1,y2, z1,z2, v1,v2) +} +do_test 1.4.2 { column_count db rt4 } 9 + +# EVIDENCE-OF: R-13981-28768 And a 5-dimensional R*Tree has 11 columns. +do_execsql_test 1.5.1 { + CREATE VIRTUAL TABLE rt5 USING rtree(id, x1,x2, y1,y2, z1,z2, v1,v2, w1,w2) +} +do_test 1.5.2 { column_count db rt5 } 11 + + +# Attempt to create r-tree tables with 6 and 7 dimensions. +# +# EVIDENCE-OF: R-61533-25862 The SQLite R*Tree implementation does not +# support R*Trees wider than 5 dimensions. +do_catchsql_test 2.1.1 { + CREATE VIRTUAL TABLE rt6 USING rtree( + id, x1,x2, y1,y2, z1,z2, v1,v2, w1,w2, a1,a2 + ) +} {1 {Too many columns for an rtree table}} +do_catchsql_test 2.1.2 { + CREATE VIRTUAL TABLE rt6 USING rtree( + id, x1,x2, y1,y2, z1,z2, v1,v2, w1,w2, a1,a2, b1, b2 + ) +} {1 {Too many columns for an rtree table}} + +# Attempt to create r-tree tables with no columns, a single column, or +# an even number of columns. This and the tests above establish that: +# +# EVIDENCE-OF: R-16717-50504 Each R*Tree index is a virtual table with +# an odd number of columns between 3 and 11. +foreach {tn cols err} { + 1 "" "Too few columns for an rtree table" + 2 "x" "Too few columns for an rtree table" + 3 "x,y" "Too few columns for an rtree table" + 4 "a,b,c,d" "Wrong number of columns for an rtree table" + 5 "a,b,c,d,e,f" "Wrong number of columns for an rtree table" + 6 "a,b,c,d,e,f,g,h" "Wrong number of columns for an rtree table" + 7 "a,b,c,d,e,f,g,h,i,j" "Wrong number of columns for an rtree table" + 8 "a,b,c,d,e,f,g,h,i,j,k,l" "Too many columns for an rtree table" +} { + do_catchsql_test 3.$tn " + CREATE VIRTUAL TABLE xyz USING rtree($cols) + " [list 1 $err] +} + +# EVIDENCE-OF: R-17874-21123 The first column of an SQLite R*Tree is +# similar to an integer primary key column of a normal SQLite table. +# +# EVIDENCE-OF: R-46619-65417 The first column is always a 64-bit signed +# integer primary key. +# +# EVIDENCE-OF: R-46866-24036 It may only store a 64-bit signed integer +# value. +# +# EVIDENCE-OF: R-00250-64843 If an attempt is made to insert any other +# non-integer value into this column, the r-tree module silently +# converts it to an integer before writing it into the database. +# +do_execsql_test 4.0 { CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2) } +foreach {tn val res} { + 1 10 10 + 2 10.6 10 + 3 10.99 10 + 4 '123' 123 + 5 X'313233' 123 + 6 -10 -10 + 7 9223372036854775807 9223372036854775807 + 8 -9223372036854775808 -9223372036854775808 + 9 '9223372036854775807' 9223372036854775807 + 10 '-9223372036854775808' -9223372036854775808 + 11 'hello+world' 0 +} { + do_execsql_test 4.$tn.1 " + DELETE FROM rt; + INSERT INTO rt VALUES($val, 10, 20); + " + do_execsql_test 4.$tn.2 { + SELECT typeof(id), id FROM rt + } [list integer $res] +} + +# EVIDENCE-OF: R-15544-29079 Inserting a NULL value into this column +# causes SQLite to automatically generate a new unique primary key +# value. +do_execsql_test 5.1 { + DELETE FROM rt; + INSERT INTO rt VALUES(100, 1, 2); + INSERT INTO rt VALUES(NULL, 1, 2); +} +do_execsql_test 5.2 { SELECT id FROM rt } {100 101} +do_execsql_test 5.3 { + INSERT INTO rt VALUES(9223372036854775807, 1, 2); + INSERT INTO rt VALUES(NULL, 1, 2); +} +do_execsql_test 5.4 { + SELECT count(*) FROM rt; +} 4 +do_execsql_test 5.5 { + SELECT id IN(100, 101, 9223372036854775807) FROM rt ORDER BY 1; +} {0 1 1 1} + + +# EVIDENCE-OF: R-64317-38978 The other columns are pairs, one pair per +# dimension, containing the minimum and maximum values for that +# dimension, respectively. +# +# Show this by observing that attempts to insert rows with max>min fail. +# +do_execsql_test 6.1 { + CREATE VIRTUAL TABLE rtF USING rtree(id, x1,x2, y1,y2); + CREATE VIRTUAL TABLE rtI USING rtree_i32(id, x1,x2, y1,y2, z1,z2); +} +foreach {tn x1 x2 y1 y2 ok} { + 1 10.3 20.1 30.9 40.2 1 + 2 10.3 20.1 40.2 30.9 0 + 3 10.3 30.9 20.1 40.2 1 + 4 20.1 10.3 30.9 40.2 0 +} { + do_test 6.2.$tn { + catch { db eval { INSERT INTO rtF VALUES(NULL, $x1, $x2, $y1, $y2) } } + } [expr $ok==0] +} +foreach {tn x1 x2 y1 y2 z1 z2 ok} { + 1 10 20 30 40 50 60 1 + 2 10 20 30 40 60 50 0 + 3 10 20 30 50 40 60 1 + 4 10 20 40 30 50 60 0 + 5 10 30 20 40 50 60 1 + 6 20 10 30 40 50 60 0 +} { + do_test 6.3.$tn { + catch { db eval { INSERT INTO rtI VALUES(NULL,$x1,$x2,$y1,$y2,$z1,$z2) } } + } [expr $ok==0] +} + +# EVIDENCE-OF: R-08054-15429 The min/max-value pair columns are stored +# as 32-bit floating point values for "rtree" virtual tables or as +# 32-bit signed integers in "rtree_i32" virtual tables. +# +# Show this by showing that large values are rounded in ways consistent +# with those two 32-bit types. +do_execsql_test 7.1 { + DELETE FROM rtI; + INSERT INTO rtI VALUES( + 0, -2000000000, 2000000000, -5000000000, 5000000000, + -1000000000000, 10000000000000 + ); + SELECT * FROM rtI; +} { + 0 -2000000000 2000000000 -705032704 705032704 727379968 1316134912 +} +do_execsql_test 7.2 { + DELETE FROM rtF; + INSERT INTO rtF VALUES( + 0, -2000000000, 2000000000, + -1000000000000, 10000000000000 + ); + SELECT * FROM rtF; +} { + 0 -2000000000.0 2000000000.0 -1000000126976.0 10000000876544.0 +} + +# EVIDENCE-OF: R-47371-54529 Unlike regular SQLite tables which can +# store data in a variety of datatypes and formats, the R*Tree rigidly +# enforce these storage types. +# +# EVIDENCE-OF: R-39153-14977 If any other type of value is inserted into +# such a column, the r-tree module silently converts it to the required +# type before writing the new record to the database. +do_execsql_test 8.1 { + DELETE FROM rtI; + INSERT INTO rtI VALUES( + 1, 'hello world', X'616263', NULL, 44.5, 1000, 9999.9999 + ); + SELECT * FROM rtI; +} { + 1 0 0 0 44 1000 9999 +} + +do_execsql_test 8.2 { + SELECT + typeof(x1), typeof(x2), typeof(y1), typeof(y2), typeof(z1), typeof(z2) + FROM rtI +} {integer integer integer integer integer integer} + +do_execsql_test 8.3 { + DELETE FROM rtF; + INSERT INTO rtF VALUES( + 1, 'hello world', X'616263', NULL, 44 + ); + SELECT * FROM rtF; +} { + 1 0.0 0.0 0.0 44.0 +} +do_execsql_test 8.4 { + SELECT + typeof(x1), typeof(x2), typeof(y1), typeof(y2) + FROM rtF +} {real real real real} + + + + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 3.1 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-2 +reset_db + +foreach {tn name clist} { + 1 t1 "id x1 x2" + 2 t2 "id x1 x2 y1 y2 z1 z2" +} { +# EVIDENCE-OF: R-15142-18077 A new R*Tree index is created as follows: +# CREATE VIRTUAL TABLE USING rtree(); + do_execsql_test 1.$tn.1 " + CREATE VIRTUAL TABLE $name USING rtree([join $clist ,]) + " + +# EVIDENCE-OF: R-51698-09302 The is the name your +# application chooses for the R*Tree index and is a +# comma separated list of between 3 and 11 columns. + do_test 1.$tn.2 { column_name_list db $name } [list {*}$clist] + +# EVIDENCE-OF: R-50130-53472 The virtual table creates +# three shadow tables to actually store its content. + do_execsql_test 1.$tn.3 { + SELECT count(*) FROM sqlite_schema + } [expr 1+3] + +# EVIDENCE-OF: R-45256-35998 The names of these shadow tables are: +# _node _rowid _parent + do_execsql_test 1.$tn.4 { + SELECT name FROM sqlite_schema WHERE rootpage>0 ORDER BY 1 + } [list ${name}_node ${name}_parent ${name}_rowid] + + do_execsql_test 1.$tn.5 "DROP TABLE $name" +} + +# EVIDENCE-OF: R-11241-54478 As an example, consider creating a +# two-dimensional R*Tree index for use in spatial queries: CREATE +# VIRTUAL TABLE demo_index USING rtree( id, -- Integer primary key minX, +# maxX, -- Minimum and maximum X coordinate minY, maxY -- Minimum and +# maximum Y coordinate ); +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE demo_index USING rtree( + id, -- Integer primary key + minX, maxX, -- Minimum and maximum X coordinate + minY, maxY -- Minimum and maximum Y coordinate + ); + INSERT INTO demo_index VALUES(1,2,3,4,5); + INSERT INTO demo_index VALUES(6,7,8,9,10); +} + +# EVIDENCE-OF: R-02287-33529 The shadow tables are ordinary SQLite data +# tables. +# +# Ordinary tables. With ordinary sqlite_schema entries. +do_execsql_test 2.1 { + SELECT type, name, sql FROM sqlite_schema WHERE sql NOT LIKE '%virtual%' +} { + table demo_index_rowid + {CREATE TABLE "demo_index_rowid"(rowid INTEGER PRIMARY KEY,nodeno)} + table demo_index_node + {CREATE TABLE "demo_index_node"(nodeno INTEGER PRIMARY KEY,data)} + table demo_index_parent + {CREATE TABLE "demo_index_parent"(nodeno INTEGER PRIMARY KEY,parentnode)} +} + +# EVIDENCE-OF: R-10863-13089 You can query them directly if you like, +# though this unlikely to reveal anything particularly useful. +# +# Querying: +do_execsql_test 2.2 { + SELECT count(*) FROM demo_index_node; + SELECT count(*) FROM demo_index_rowid; + SELECT count(*) FROM demo_index_parent; +} {1 2 0} + +# EVIDENCE-OF: R-05650-46070 And you can UPDATE, DELETE, INSERT or even +# DROP the shadow tables, though doing so will corrupt your R*Tree +# index. +do_execsql_test 2.3 { + DELETE FROM demo_index_rowid; + INSERT INTO demo_index_parent VALUES(2, 3); + UPDATE demo_index_node SET data = 'hello world' +} +do_catchsql_test 2.4 { + SELECT * FROM demo_index WHERE minX>10 AND maxX<30 +} {1 {database disk image is malformed}} +do_execsql_test 2.5 { + DROP TABLE demo_index_rowid +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 3.1.1 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-3 +reset_db + +# EVIDENCE-OF: R-44253-50720 In the argments to "rtree" in the CREATE +# VIRTUAL TABLE statement, the names of the columns are taken from the +# first token of each argument. All subsequent tokens within each +# argument are silently ignored. +# +foreach {tn cols lCol} { + 1 {(id TEXT, x1 TEXT, x2 TEXT, y1 TEXT, y2 TEXT)} {id x1 x2 y1 y2} + 2 {(id TEXT, x1 UNIQUE, x2 TEXT, y1 NOT NULL, y2 TEXT)} {id x1 x2 y1 y2} + 3 {(id, x1 DEFAULT 4, x2 TEXT, y1 NOT NULL, y2 TEXT)} {id x1 x2 y1 y2} +} { + do_execsql_test 1.$tn.1 " CREATE VIRTUAL TABLE abc USING rtree $cols " + do_test 1.$tn.2 { column_name_list db abc } $lCol + +# EVIDENCE-OF: R-52032-06717 This means, for example, that if you try to +# give a column a type affinity or add a constraint such as UNIQUE or +# NOT NULL or DEFAULT to a column, those extra tokens are accepted as +# valid, but they do not change the behavior of the rtree. + + # Show there are no UNIQUE constraints + do_execsql_test 1.$tn.3 { + INSERT INTO abc VALUES(1, 10.0, 20.0, 10.0, 20.0); + INSERT INTO abc VALUES(2, 10.0, 20.0, 10.0, 20.0); + } + + # Show the default values have not been modified + do_execsql_test 1.$tn.4 { + INSERT INTO abc DEFAULT VALUES; + SELECT * FROM abc WHERE rowid NOT IN (1,2) + } {3 0.0 0.0 0.0 0.0} + + # Show that there are no NOT NULL constraints + do_execsql_test 1.$tn.5 { + INSERT INTO abc VALUES(NULL, NULL, NULL, NULL, NULL); + SELECT * FROM abc WHERE rowid NOT IN (1,2,3) + } {4 0.0 0.0 0.0 0.0} + +# EVIDENCE-OF: R-06893-30579 In an RTREE virtual table, the first column +# always has a type affinity of INTEGER and all other data columns have +# a type affinity of REAL. + do_execsql_test 1.$tn.5 { + INSERT INTO abc VALUES('5', '5', '5', '5', '5'); + SELECT * FROM abc WHERE rowid NOT IN (1,2,3,4) + } {5 5.0 5.0 5.0 5.0} + do_execsql_test 1.$tn.6 { + SELECT type FROM pragma_table_info('abc') ORDER BY cid + } {INT REAL REAL REAL REAL} + + do_execsql_test 1.$tn.7 " CREATE VIRTUAL TABLE abc2 USING rtree_i32 $cols " + +# EVIDENCE-OF: R-06224-52418 In an RTREE_I32 virtual table, all columns +# have type affinity of INTEGER. + do_execsql_test 1.$tn.8 { + INSERT INTO abc2 VALUES('6.0', '6.0', '6.0', '6.0', '6.0'); + SELECT * FROM abc2 + } {6 6 6 6 6} + do_execsql_test 1.$tn.9 { + SELECT type FROM pragma_table_info('abc2') ORDER BY cid + } {INT INT INT INT INT} + + + do_execsql_test 1.$tn.10 { + DROP TABLE abc; + DROP TABLE abc2; + } +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 3.2 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-4 +reset_db + +# EVIDENCE-OF: R-36195-31555 The usual INSERT, UPDATE, and DELETE +# commands work on an R*Tree index just like on regular tables. +# +# Create a regular table and an rtree table. Perform INSERT, UPDATE and +# DELETE operations, then observe that the contents of the two tables +# are identical. +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2); + CREATE TABLE t1(id INTEGER PRIMARY KEY, x1 REAL, x2 REAL); +} +foreach {tn sql} { + 1 "INSERT INTO %TBL% VALUES(5, 11,12)" + 2 "INSERT INTO %TBL% VALUES(11, -11,14.5)" + 3 "UPDATE %TBL% SET x1=-99 WHERE id=11" + 4 "DELETE FROM %TBL% WHERE x2=14.5" + 5 "DELETE FROM %TBL%" +} { + set sql1 [string map {%TBL% rt} $sql] + set sql2 [string map {%TBL% t1} $sql] + do_execsql_test 1.$tn.0 $sql1 + do_execsql_test 1.$tn.1 $sql2 + + set data1 [execsql {SELECT * FROM rt ORDER BY 1}] + set data2 [execsql {SELECT * FROM t1 ORDER BY 1}] + + set res [expr {$data1==$data2}] + do_test 1.$tn.2 {set res} 1 +} + +# EVIDENCE-OF: R-56987-45305 +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE demo_index USING rtree( + id, -- Integer primary key + minX, maxX, -- Minimum and maximum X coordinate + minY, maxY -- Minimum and maximum Y coordinate + ); + + INSERT INTO demo_index VALUES + (28215, -80.781227, -80.604706, 35.208813, 35.297367), + (28216, -80.957283, -80.840599, 35.235920, 35.367825), + (28217, -80.960869, -80.869431, 35.133682, 35.208233), + (28226, -80.878983, -80.778275, 35.060287, 35.154446), + (28227, -80.745544, -80.555382, 35.130215, 35.236916), + (28244, -80.844208, -80.841988, 35.223728, 35.225471), + (28262, -80.809074, -80.682938, 35.276207, 35.377747), + (28269, -80.851471, -80.735718, 35.272560, 35.407925), + (28270, -80.794983, -80.728966, 35.059872, 35.161823), + (28273, -80.994766, -80.875259, 35.074734, 35.172836), + (28277, -80.876793, -80.767586, 35.001709, 35.101063), + (28278, -81.058029, -80.956375, 35.044701, 35.223812), + (28280, -80.844208, -80.841972, 35.225468, 35.227203), + (28282, -80.846382, -80.844193, 35.223972, 35.225655); +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 3.3 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-5 + +do_execsql_test 1.0 { + INSERT INTO demo_index + SELECT NULL, minX, maxX, minY+0.2, maxY+0.2 FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX+0.2, maxX+0.2, minY, maxY FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX, maxX, minY+0.4, maxY+0.4 FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX+0.4, maxX+0.4, minY, maxY FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX, maxX, minY+0.8, maxY+0.8 FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX+0.8, maxX+0.8, minY, maxY FROM demo_index; + + SELECT count(*) FROM demo_index; +} {896} + +proc do_vmstep_test {tn sql expr} { + execsql $sql + set step [db status vmstep] + do_test $tn.$step "expr {[subst $expr]}" 1 +} + +# EVIDENCE-OF: R-45880-07724 Any valid query will work against an R*Tree +# index. +do_execsql_test 1.1.0 { + CREATE TABLE demo_tbl AS SELECT * FROM demo_index; +} +foreach {tn sql} { + 1 {SELECT * FROM %TBL% ORDER BY 1} + 2 {SELECT max(minX) FROM %TBL% ORDER BY 1} + 3 {SELECT max(minX) FROM %TBL% GROUP BY round(minY) ORDER BY 1} +} { + set sql1 [string map {%TBL% demo_index} $sql] + set sql2 [string map {%TBL% demo_tbl} $sql] + + do_execsql_test 1.1.$tn $sql1 [execsql $sql2] +} + +# EVIDENCE-OF: R-60814-18273 The R*Tree implementation just makes some +# kinds of queries especially efficient. +# +# The second query is more efficient than the first. +do_vmstep_test 1.2.1 {SELECT * FROM demo_index WHERE +rowid=28269} {$step>2000} +do_vmstep_test 1.2.2 {SELECT * FROM demo_index WHERE rowid=28269} {$step<100} + +# EVIDENCE-OF: R-37800-50174 Queries against the primary key are +# efficient: SELECT * FROM demo_index WHERE id=28269; +do_vmstep_test 2.2 { SELECT * FROM demo_index WHERE id=28269 } {$step < 100} + +# EVIDENCE-OF: R-35847-18866 The big reason for using an R*Tree is so +# that you can efficiently do range queries against the coordinate +# ranges. +# +# EVIDENCE-OF: R-49927-54202 +do_vmstep_test 2.3 { + SELECT id FROM demo_index + WHERE minX<=-80.77470 AND maxX>=-80.77470 + AND minY<=35.37785 AND maxY>=35.37785; +} {$step < 100} + +# EVIDENCE-OF: R-12823-37176 The query above will quickly locate all +# zipcodes that contain the SQLite main office in their bounding box, +# even if the R*Tree contains many entries. +# +do_execsql_test 2.4 { + SELECT id FROM demo_index + WHERE minX<=-80.77470 AND maxX>=-80.77470 + AND minY<=35.37785 AND maxY>=35.37785; +} { + 28322 28269 +} + +# EVIDENCE-OF: R-07351-00257 For example, to find all zipcode bounding +# boxes that overlap with the 28269 zipcode: SELECT A.id FROM demo_index +# AS A, demo_index AS B WHERE A.maxX>=B.minX AND A.minX<=B.maxX +# AND A.maxY>=B.minY AND A.minY<=B.maxY AND B.id=28269; +# +# Also check that it is efficient +# +# EVIDENCE-OF: R-39094-01937 This second query will find both 28269 +# entry (since every bounding box overlaps with itself) and also other +# zipcode that is close enough to 28269 that their bounding boxes +# overlap. +# +# 28269 is there in the result. +# +do_vmstep_test 2.5.1 { + SELECT A.id FROM demo_index AS A, demo_index AS B + WHERE A.maxX>=B.minX AND A.minX<=B.maxX + AND A.maxY>=B.minY AND A.minY<=B.maxY + AND B.id=28269 +} {$step < 100} +do_execsql_test 2.5.2 { + SELECT A.id FROM demo_index AS A, demo_index AS B + WHERE A.maxX>=B.minX AND A.minX<=B.maxX + AND A.maxY>=B.minY AND A.minY<=B.maxY + AND B.id=28269; +} { + 28293 28216 28322 28286 28269 + 28215 28336 28262 28291 28320 + 28313 28298 28287 +} + +# EVIDENCE-OF: R-02723-34107 Note that it is not necessary for all +# coordinates in an R*Tree index to be constrained in order for the +# index search to be efficient. +# +# EVIDENCE-OF: R-22490-27246 One might, for example, want to query all +# objects that overlap with the 35th parallel: SELECT id FROM demo_index +# WHERE maxY>=35.0 AND minY<=35.0; +do_vmstep_test 2.6.1 { + SELECT id FROM demo_index + WHERE maxY>=35.0 AND minY<=35.0; +} {$step < 100} +do_execsql_test 2.6.2 { + SELECT id FROM demo_index + WHERE maxY>=35.0 AND minY<=35.0; +} {} + + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 3.4 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-6 +reset_db + +# EVIDENCE-OF: R-08327-00674 By default, coordinates are stored in an +# R*Tree using 32-bit floating point values. +# +# EVIDENCE-OF: R-22000-53613 The default virtual table ("rtree") stores +# coordinates as single-precision (4-byte) floating point numbers. +# +# Show this by showing that rounding is consistent with 32-bit float +# rounding. +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE rt USING rtree(id, a,b); +} +do_execsql_test 1.1 { + INSERT INTO rt VALUES(14, -1000000000000, 1000000000000); + SELECT * FROM rt; +} {14 -1000000126976.0 1000000126976.0} + +# EVIDENCE-OF: R-39127-51288 When a coordinate cannot be exactly +# represented by a 32-bit floating point number, the lower-bound +# coordinates are rounded down and the upper-bound coordinates are +# rounded up. +foreach {tn val} { + 1 100000000000 + 2 200000000000 + 3 300000000000 + 4 400000000000 + + 5 -100000000000 + 6 -200000000000 + 7 -300000000000 + 8 -400000000000 +} { + set val [expr $val] + do_execsql_test 2.$tn.0 {DELETE FROM rt} + do_execsql_test 2.$tn.1 {INSERT INTO rt VALUES(23, $val, $val)} + do_execsql_test 2.$tn.2 { + SELECT $val>=a, $val<=b, a!=b FROM rt + } {1 1 1} +} + +do_execsql_test 3.0 { + DROP TABLE rt; + CREATE VIRTUAL TABLE rt USING rtree(id, x1,x2, y1,y2); +} + +# EVIDENCE-OF: R-45870-62834 Thus, bounding boxes might be slightly +# larger than specified, but will never be any smaller. +foreach {tn x1 x2 y1 y2} { + 1 100000000000 200000000000 300000000000 400000000000 +} { + set val [expr $val] + do_execsql_test 3.$tn.0 {DELETE FROM rt} + do_execsql_test 3.$tn.1 {INSERT INTO rt VALUES(23, $x1, $x2, $y1, $y2)} + do_execsql_test 3.$tn.2 { + SELECT (x2-x1)*(y2-y1) >= ($x2-$x1)*($y2-$y1) FROM rt + } {1} +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 3.5 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-7 +reset_db + +# EVIDENCE-OF: R-55979-39402 It is the nature of the Guttman R-Tree +# algorithm that any write might radically restructure the tree, and in +# the process change the scan order of the nodes. +# +# In the test below, the INSERT marked "THIS INSERT!!" does not affect +# the results of queries with an ORDER BY, but does affect the results +# of one without an ORDER BY. Therefore the INSERT changed the scan +# order. +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE rt USING rtree(id, minX, maxX); + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<51 + ) + INSERT INTO rt SELECT NULL, i%10, (i%10)+5 FROM s +} +do_execsql_test 1.1 { SELECT count(*) FROM rt_node } 1 +do_test 1.2 { + set res1 [db eval {SELECT * FROM rt WHERE maxX < 30}] + set res1o [db eval {SELECT * FROM rt WHERE maxX < 30 ORDER BY +id}] + + db eval { INSERT INTO rt VALUES(NULL, 50, 50) } ;# THIS INSERT!! + + set res2 [db eval {SELECT * FROM rt WHERE maxX < 30}] + set res2o [db eval {SELECT * FROM rt WHERE maxX < 30 ORDER BY +id}] + list [expr {$res1==$res2}] [expr {$res1o==$res2o}] +} {0 1} + +do_execsql_test 1.3 { SELECT count(*) FROM rt_node } 3 + +# EVIDENCE-OF: R-00683-48865 For this reason, it is not generally +# possible to modify the R-Tree in the middle of a query of the R-Tree. +# Attempts to do so will fail with a SQLITE_LOCKED "database table is +# locked" error. +# +# SQLITE_LOCKED==6 +# +do_test 1.4 { + set nCnt 3 + db eval { SELECT * FROM rt WHERE minX>0 AND maxX<12 } { + incr nCnt -1 + if {$nCnt==0} { + set rc [catch {db eval { + INSERT INTO rt VALUES(NULL, 51, 51); + }} msg] + set errorcode [db errorcode] + break + } + } + + list $errorcode $rc $msg +} {6 1 {database table is locked}} + +# EVIDENCE-OF: R-19740-29710 So, for example, suppose an application +# runs one query against an R-Tree like this: SELECT id FROM demo_index +# WHERE maxY>=35.0 AND minY<=35.0; Then for each "id" value +# returned, suppose the application creates an UPDATE statement like the +# following and binds the "id" value returned against the "?1" +# parameter: UPDATE demo_index SET maxY=maxY+0.5 WHERE id=?1; +# +# EVIDENCE-OF: R-52919-32711 Then the UPDATE might fail with an +# SQLITE_LOCKED error. +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE demo_index USING rtree( + id, -- Integer primary key + minX, maxX, -- Minimum and maximum X coordinate + minY, maxY -- Minimum and maximum Y coordinate + ); + INSERT INTO demo_index VALUES + (28215, -80.781227, -80.604706, 35.208813, 35.297367), + (28216, -80.957283, -80.840599, 35.235920, 35.367825), + (28217, -80.960869, -80.869431, 35.133682, 35.208233), + (28226, -80.878983, -80.778275, 35.060287, 35.154446); +} +do_test 2.1 { + db eval { SELECT id FROM demo_index WHERE maxY>=35.0 AND minY<=35.0 } { + set rc [catch { + db eval { UPDATE demo_index SET maxY=maxY+0.5 WHERE id=$id } + } msg] + set errorcode [db errorcode] + break + } + list $errorcode $rc $msg +} {6 1 {database table is locked}} + +# EVIDENCE-OF: R-32604-49843 Ordinary tables in SQLite are able to read +# and write at the same time. +# +do_execsql_test 3.0 { + CREATE TABLE x1(a INTEGER PRIMARY KEY, b, c); + INSERT INTO x1 VALUES(1, 1, 1); + INSERT INTO x1 VALUES(2, 2, 2); + INSERT INTO x1 VALUES(3, 3, 3); + INSERT INTO x1 VALUES(4, 4, 4); +} +do_test 3.1 { + set res [list] + db eval { SELECT * FROM x1 } { + lappend res $a $b $c + switch -- $a { + 1 { + db eval { INSERT INTO x1 VALUES(5, 5, 5) } + } + 2 { + db eval { UPDATE x1 SET c=20 WHERE a=2 } + } + 3 { + db eval { DELETE FROM x1 WHERE c IN (3,4) } + } + } + } + set res +} {1 1 1 2 2 2 3 3 3 5 5 5} +do_execsql_test 3.2 { + SELECT * FROM x1 +} {1 1 1 2 2 20 5 5 5} + +# EVIDENCE-OF: R-06177-00576 And R-Tree can appear to read and write at +# the same time in some circumstances, if it can figure out how to +# reliably run the query to completion before starting the update. +# +# In 8.2, it can, it 8.1, it cannot. +do_test 8.1 { + db eval { SELECT * FROM rt } { + set rc [catch { db eval { INSERT INTO rt VALUES(53,53,53) } } msg] + break; + } + list $rc $msg +} {1 {database table is locked}} +do_test 8.2 { + db eval { SELECT * FROM rt ORDER BY +id } { + set rc [catch { db eval { INSERT INTO rt VALUES(53,53,53) } } msg] + break + } + list $rc $msg +} {0 {}} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 4 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-8 +reset_db + +# EVIDENCE-OF: R-21062-30088 For the example above, one might create an +# auxiliary table as follows: CREATE TABLE demo_data( id INTEGER PRIMARY +# KEY, -- primary key objname TEXT, -- name of the object objtype TEXT, +# -- object type boundary BLOB -- detailed boundary of object ); +# +# One might. +# +do_execsql_test 1.0 { + CREATE TABLE demo_data( + id INTEGER PRIMARY KEY, -- primary key + objname TEXT, -- name of the object + objtype TEXT, -- object type + boundary BLOB -- detailed boundary of object + ); +} + +do_execsql_test 1.1 { + CREATE VIRTUAL TABLE demo_index USING rtree( + id, -- Integer primary key + minX, maxX, -- Minimum and maximum X coordinate + minY, maxY -- Minimum and maximum Y coordinate + ); + + INSERT INTO demo_index VALUES + (28215, -80.781227, -80.604706, 35.208813, 35.297367), + (28216, -80.957283, -80.840599, 35.235920, 35.367825), + (28217, -80.960869, -80.869431, 35.133682, 35.208233), + (28226, -80.878983, -80.778275, 35.060287, 35.154446), + (28227, -80.745544, -80.555382, 35.130215, 35.236916), + (28244, -80.844208, -80.841988, 35.223728, 35.225471), + (28262, -80.809074, -80.682938, 35.276207, 35.377747), + (28269, -80.851471, -80.735718, 35.272560, 35.407925), + (28270, -80.794983, -80.728966, 35.059872, 35.161823), + (28273, -80.994766, -80.875259, 35.074734, 35.172836), + (28277, -80.876793, -80.767586, 35.001709, 35.101063), + (28278, -81.058029, -80.956375, 35.044701, 35.223812), + (28280, -80.844208, -80.841972, 35.225468, 35.227203), + (28282, -80.846382, -80.844193, 35.223972, 35.225655); + + INSERT INTO demo_index + SELECT NULL, minX, maxX, minY+0.2, maxY+0.2 FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX+0.2, maxX+0.2, minY, maxY FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX, maxX, minY+0.4, maxY+0.4 FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX+0.4, maxX+0.4, minY, maxY FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX, maxX, minY+0.8, maxY+0.8 FROM demo_index; + INSERT INTO demo_index + SELECT NULL, minX+0.8, maxX+0.8, minY, maxY FROM demo_index; + + INSERT INTO demo_data(id) SELECT id FROM demo_index; + + SELECT count(*) FROM demo_index; +} {896} + +set ::contained_in 0 +proc contained_in {args} {incr ::contained_in ; return 0} +db func contained_in contained_in + +# EVIDENCE-OF: R-32671-43888 Then an efficient way to find the specific +# ZIP code for the main SQLite office would be to run a query like this: +# SELECT objname FROM demo_data, demo_index WHERE +# demo_data.id=demo_index.id AND contained_in(demo_data.boundary, +# 35.37785, -80.77470) AND minX<=-80.77470 AND maxX>=-80.77470 AND +# minY<=35.37785 AND maxY>=35.37785; +do_vmstep_test 1.2 { + SELECT objname FROM demo_data, demo_index + WHERE demo_data.id=demo_index.id + AND contained_in(demo_data.boundary, 35.37785, -80.77470) + AND minX<=-80.77470 AND maxX>=-80.77470 + AND minY<=35.37785 AND maxY>=35.37785; +} {$step<100} +set ::contained_in1 $::contained_in + +# EVIDENCE-OF: R-32761-23915 One would get the same answer without the +# use of the R*Tree index using the following simpler query: SELECT +# objname FROM demo_data WHERE contained_in(demo_data.boundary, +# 35.37785, -80.77470); +set ::contained_in 0 +do_vmstep_test 1.3 { + SELECT objname FROM demo_data + WHERE contained_in(demo_data.boundary, 35.37785, -80.77470); +} {$step>3200} + +# EVIDENCE-OF: R-40261-32799 The problem with this latter query is that +# it must apply the contained_in() function to all entries in the +# demo_data table. +# +# 896 of them, IIRC. +do_test 1.4 { + set ::contained_in +} 896 + +# EVIDENCE-OF: R-24212-52761 The use of the R*Tree in the penultimate +# query reduces the number of calls to contained_in() function to a +# small subset of the entire table. +# +# 2 is a small subset of 896. +# +# EVIDENCE-OF: R-39057-63901 The R*Tree index did not find the exact +# answer itself, it merely limited the search space. +# +# contained_in() filtered out those 2 rows. +do_test 1.5 { + set ::contained_in1 +} {2} + + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 4.1 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-9 +reset_db + +# EVIDENCE-OF: R-46566-43213 Beginning with SQLite version 3.24.0 +# (2018-06-04), r-tree tables can have auxiliary columns that store +# arbitrary data. Auxiliary columns can be used in place of secondary +# tables such as "demo_data". +# +# EVIDENCE-OF: R-41287-48160 Auxiliary columns are marked with a "+" +# symbol before the column name. +# +# This interface cannot conveniently be used to prove anything about +# versions of SQLite prior to 3.24.0. +# +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE rta USING rtree( + id, u1,u2, v1,v2, +aux + ); + + INSERT INTO rta(aux) VALUES(NULL); + INSERT INTO rta(aux) VALUES(45); + INSERT INTO rta(aux) VALUES(22.3); + INSERT INTO rta(aux) VALUES('hello'); + INSERT INTO rta(aux) VALUES(X'ABCD'); + + SELECT typeof(aux), quote(aux) FROM rta; +} { + null NULL + integer 45 + real 22.3 + text 'hello' + blob X'ABCD' +} + +# EVIDENCE-OF: R-30514-26093 Auxiliary columns must come after all of +# the coordinate boundary columns. +foreach {tn cols} { + 1 "id x1,x2, +extra, y1,y2" + 2 "extra, +id x1,x2, y1,y2" + 3 "id, x1,+x2, extra, y1,y2" +} { + do_catchsql_test 2.$tn " + CREATE VIRTUAL TABLE rrr USING rtree($cols) + " {1 {Auxiliary rtree columns must be last}} +} +do_catchsql_test 3.0 { + CREATE VIRTUAL TABLE rrr USING rtree(+id, extra, x1, x2); +} {1 {near "+": syntax error}} + +# EVIDENCE-OF: R-01280-03635 An RTREE table can have no more than 100 +# columns total. In other words, the count of columns including the +# integer primary key column, the coordinate boundary columns, and all +# auxiliary columns must be 100 or less. +do_catchsql_test 3.1 { + CREATE VIRTUAL TABLE r1 USING rtree(intid, u1,u2, + +c00, +c01, +c02, +c03, +c04, +c05, +c06, +c07, +c08, +c09, + +c10, +c11, +c12, +c13, +c14, +c15, +c16, +c17, +c18, +c19, + +c20, +c21, +c22, +c23, +c24, +c25, +c26, +c27, +c28, +c29, + +c30, +c31, +c32, +c33, +c34, +c35, +c36, +c37, +c38, +c39, + +c40, +c41, +c42, +c43, +c44, +c45, +c46, +c47, +c48, +c49, + +c50, +c51, +c52, +c53, +c54, +c55, +c56, +c57, +c58, +c59, + +c60, +c61, +c62, +c63, +c64, +c65, +c66, +c67, +c68, +c69, + +c70, +c71, +c72, +c73, +c74, +c75, +c76, +c77, +c78, +c79, + +c80, +c81, +c82, +c83, +c84, +c85, +c86, +c87, +c88, +c89, + +c90, +c91, +c92, +c93, +c94, +c95, +c96 + ); +} {0 {}} +do_catchsql_test 3.2 { + DROP TABLE r1; + CREATE VIRTUAL TABLE r1 USING rtree(intid, u1,u2, + +c00, +c01, +c02, +c03, +c04, +c05, +c06, +c07, +c08, +c09, + +c10, +c11, +c12, +c13, +c14, +c15, +c16, +c17, +c18, +c19, + +c20, +c21, +c22, +c23, +c24, +c25, +c26, +c27, +c28, +c29, + +c30, +c31, +c32, +c33, +c34, +c35, +c36, +c37, +c38, +c39, + +c40, +c41, +c42, +c43, +c44, +c45, +c46, +c47, +c48, +c49, + +c50, +c51, +c52, +c53, +c54, +c55, +c56, +c57, +c58, +c59, + +c60, +c61, +c62, +c63, +c64, +c65, +c66, +c67, +c68, +c69, + +c70, +c71, +c72, +c73, +c74, +c75, +c76, +c77, +c78, +c79, + +c80, +c81, +c82, +c83, +c84, +c85, +c86, +c87, +c88, +c89, + +c90, +c91, +c92, +c93, +c94, +c95, +c96, +c97 + ); +} {1 {Too many columns for an rtree table}} +do_catchsql_test 3.3 { + CREATE VIRTUAL TABLE r1 USING rtree(intid, u1,u2, v1,v2, + +c00, +c01, +c02, +c03, +c04, +c05, +c06, +c07, +c08, +c09, + +c10, +c11, +c12, +c13, +c14, +c15, +c16, +c17, +c18, +c19, + +c20, +c21, +c22, +c23, +c24, +c25, +c26, +c27, +c28, +c29, + +c30, +c31, +c32, +c33, +c34, +c35, +c36, +c37, +c38, +c39, + +c40, +c41, +c42, +c43, +c44, +c45, +c46, +c47, +c48, +c49, + +c50, +c51, +c52, +c53, +c54, +c55, +c56, +c57, +c58, +c59, + +c60, +c61, +c62, +c63, +c64, +c65, +c66, +c67, +c68, +c69, + +c70, +c71, +c72, +c73, +c74, +c75, +c76, +c77, +c78, +c79, + +c80, +c81, +c82, +c83, +c84, +c85, +c86, +c87, +c88, +c89, + +c90, +c91, +c92, +c93, +c94, + ); +} {0 {}} +do_catchsql_test 3.4 { + DROP TABLE r1; + CREATE VIRTUAL TABLE r1 USING rtree(intid, u1,u2, v1,v2, + +c00, +c01, +c02, +c03, +c04, +c05, +c06, +c07, +c08, +c09, + +c10, +c11, +c12, +c13, +c14, +c15, +c16, +c17, +c18, +c19, + +c20, +c21, +c22, +c23, +c24, +c25, +c26, +c27, +c28, +c29, + +c30, +c31, +c32, +c33, +c34, +c35, +c36, +c37, +c38, +c39, + +c40, +c41, +c42, +c43, +c44, +c45, +c46, +c47, +c48, +c49, + +c50, +c51, +c52, +c53, +c54, +c55, +c56, +c57, +c58, +c59, + +c60, +c61, +c62, +c63, +c64, +c65, +c66, +c67, +c68, +c69, + +c70, +c71, +c72, +c73, +c74, +c75, +c76, +c77, +c78, +c79, + +c80, +c81, +c82, +c83, +c84, +c85, +c86, +c87, +c88, +c89, + +c90, +c91, +c92, +c93, +c94, +c95, + ); +} {1 {Too many columns for an rtree table}} + +# EVIDENCE-OF: R-05552-15084 +do_execsql_test 4.0 { + CREATE VIRTUAL TABLE demo_index2 USING rtree( + id, -- Integer primary key + minX, maxX, -- Minimum and maximum X coordinate + minY, maxY, -- Minimum and maximum Y coordinate + +objname TEXT, -- name of the object + +objtype TEXT, -- object type + +boundary BLOB -- detailed boundary of object + ); +} +do_execsql_test 4.1 { + CREATE VIRTUAL TABLE demo_index USING rtree( + id, -- Integer primary key + minX, maxX, -- Minimum and maximum X coordinate + minY, maxY -- Minimum and maximum Y coordinate + ); + CREATE TABLE demo_data( + id INTEGER PRIMARY KEY, -- primary key + objname TEXT, -- name of the object + objtype TEXT, -- object type + boundary BLOB -- detailed boundary of object + ); + + INSERT INTO demo_index2(id) VALUES(1); + INSERT INTO demo_index(id) VALUES(1); + INSERT INTO demo_data(id) VALUES(1); +} +do_test 4.2 { + catch { array unset R } + db eval {SELECT * FROM demo_index2} R { set r1 [array names R] } + catch { array unset R } + db eval {SELECT * FROM demo_index NATURAL JOIN demo_data } R { + set r2 [array names R] + } + expr {$r1==$r2} +} {1} + +# EVIDENCE-OF: R-26099-32169 SELECT objname FROM demo_index2 WHERE +# contained_in(boundary, 35.37785, -80.77470) AND minX<=-80.77470 AND +# maxX>=-80.77470 AND minY<=35.37785 AND maxY>=35.37785; +do_execsql_test 4.3.1 { + DELETE FROM demo_index2; + INSERT INTO demo_index2(id,minX,maxX,minY,maxY) VALUES + (28215, -80.781227, -80.604706, 35.208813, 35.297367), + (28216, -80.957283, -80.840599, 35.235920, 35.367825), + (28217, -80.960869, -80.869431, 35.133682, 35.208233), + (28226, -80.878983, -80.778275, 35.060287, 35.154446), + (28227, -80.745544, -80.555382, 35.130215, 35.236916), + (28244, -80.844208, -80.841988, 35.223728, 35.225471), + (28262, -80.809074, -80.682938, 35.276207, 35.377747), + (28269, -80.851471, -80.735718, 35.272560, 35.407925), + (28270, -80.794983, -80.728966, 35.059872, 35.161823), + (28273, -80.994766, -80.875259, 35.074734, 35.172836), + (28277, -80.876793, -80.767586, 35.001709, 35.101063), + (28278, -81.058029, -80.956375, 35.044701, 35.223812), + (28280, -80.844208, -80.841972, 35.225468, 35.227203), + (28282, -80.846382, -80.844193, 35.223972, 35.225655); +} +set ::contained_in 0 +proc contained_in {args} { + incr ::contained_in + return 0 +} +db func contained_in contained_in +do_execsql_test 4.3.2 { + SELECT objname FROM demo_index2 + WHERE contained_in(boundary, 35.37785, -80.77470) + AND minX<=-80.77470 AND maxX>=-80.77470 + AND minY<=35.37785 AND maxY>=35.37785; +} +do_test 4.3.3 { + # Function invoked only once because r-tree filtering happened first. + set ::contained_in +} 1 +set ::contained_in 0 +do_execsql_test 4.3.4 { + SELECT objname FROM demo_index2 + WHERE contained_in(boundary, 35.37785, -80.77470) +} +do_test 4.3.3 { + # Function invoked 14 times because no r-tree filtering. Inefficient. + set ::contained_in +} 14 + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 4.1.1 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-9 +reset_db + +# EVIDENCE-OF: R-24021-02490 For auxiliary columns, only the name of the +# column matters. The type affinity is ignored. +# +# EVIDENCE-OF: R-39906-44154 Constraints such as NOT NULL, UNIQUE, +# REFERENCES, or CHECK are also ignored. +do_execsql_test 1.0 { PRAGMA foreign_keys = on } +foreach {tn auxcol nm} { + 1 "+extra INTEGER" extra + 2 "+extra TEXT" extra + 3 "+extra BLOB" extra + 4 "+extra REAL" extra + + 5 "+col NOT NULL" col + 6 "+col CHECK (col IS NOT NULL)" col + 7 "+col REFERENCES tbl(x)" col +} { + do_execsql_test 1.$tn.1 " + CREATE VIRTUAL TABLE rt USING rtree_i32(k, a,b, $auxcol) + " + + # Check that the aux column has no affinity. Or NOT NULL constraint. + # And that the aux column is the child key of an FK constraint. + # + do_execsql_test 1.$tn.2 " + INSERT INTO rt($nm) VALUES(NULL), (45), (-123.2), ('456'), (X'ABCD'); + SELECT typeof($nm), quote($nm) FROM rt; + " { + null NULL + integer 45 + real -123.2 + text '456' + blob X'ABCD' + } + + # Check that there is no UNIQUE constraint either. + # + do_execsql_test 1.$tn.3 " + INSERT INTO rt($nm) VALUES('xyz'), ('xyz'), ('xyz'); + " + + do_execsql_test 1.$tn.2 { + DROP TABLE rt + } +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 5 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-10 + +# EVIDENCE-OF: R-21011-43790 If integer coordinates are desired, declare +# the table using "rtree_i32" instead: CREATE VIRTUAL TABLE intrtree +# USING rtree_i32(id,x0,x1,y0,y1,z0,z1); +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE intrtree USING rtree_i32(id,x0,x1,y0,y1,z0,z1); + INSERT INTO intrtree DEFAULT VALUES; + SELECT typeof(x0) FROM intrtree; +} {integer} + +# EVIDENCE-OF: R-09193-49806 An rtree_i32 stores coordinates as 32-bit +# signed integers. +# +# Show that coordinates are cast in a way consistent with casting to +# a signed 32-bit integer. +do_execsql_test 1.1 { + DELETE FROM intrtree; + INSERT INTO intrtree VALUES(333, + 1<<44, (1<<44)+1, + 10000000000, 10000000001, + -10000000001, -10000000000 + ); + SELECT * FROM intrtree; +} { + 333 0 1 1410065408 1410065409 -1410065409 -1410065408 +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 7.1 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-11 +reset_db + +# This command assumes that the argument is a node blob for a 2 dimensional +# i32 r-tree table. It decodes and returns a list of cells from the node +# as a list. Each cell is itself a list of the following form: +# +# {$rowid $minX $maxX $minY $maxY} +# +# For internal (non-leaf) nodes, the rowid is replaced by the child node +# number. +# +proc rnode {aData} { + set nDim 2 + + set nData [string length $aData] + set nBytePerCell [expr (8 + 2*$nDim*4)] + binary scan [string range $aData 2 3] S nCell + + set res [list] + for {set i 0} {$i < $nCell} {incr i} { + set iOff [expr $i*$nBytePerCell+4] + set cell [string range $aData $iOff [expr $iOff+$nBytePerCell-1]] + binary scan $cell WIIII rowid x1 x2 y1 y2 + lappend res [list $rowid $x1 $x2 $y1 $y2] + } + + return $res +} + +# aData must be a node blob. This command returns true if the node contains +# rowid $rowid, or false otherwise. +# +proc rnode_contains {aData rowid} { + set L [rnode $aData] + foreach cell $L { + set r [lindex $cell 0] + if {$r==$rowid} { return 1 } + } + return 0 +} + +proc rnode_replace_cell {aData iCell cell} { + set aCell [binary format WIIII {*}$cell] + set nDim 2 + set nBytePerCell [expr (8 + 2*$nDim*4)] + set iOff [expr $iCell*$nBytePerCell+4] + + set aNew [binary format a*a*a* \ + [string range $aData 0 $iOff-1] \ + $aCell \ + [string range $aData $iOff+$nBytePerCell end] \ + ] + return $aNew +} + +db function rnode rnode +db function rnode_contains rnode_contains +db function rnode_replace_cell rnode_replace_cell + +foreach {tn nm} { + 1 x1 + 2 asdfghjkl + 3 hello_world +} { + do_execsql_test 1.$tn.1 " + CREATE VIRTUAL TABLE $nm USING rtree(a,b,c,d,e); + " + + # EVIDENCE-OF: R-33789-46762 The content of an R*Tree index is actually + # stored in three ordinary SQLite tables with names derived from the + # name of the R*Tree. + # + # EVIDENCE-OF: R-39849-06566 This is their schema: CREATE TABLE + # %_node(nodeno INTEGER PRIMARY KEY, data) CREATE TABLE %_parent(nodeno + # INTEGER PRIMARY KEY, parentnode) CREATE TABLE %_rowid(rowid INTEGER + # PRIMARY KEY, nodeno) + # + # EVIDENCE-OF: R-07489-10051 The "%" in the name of each shadow table is + # replaced by the name of the R*Tree virtual table. So, if the name of + # the R*Tree table is "xyz" then the three shadow tables would be + # "xyz_node", "xyz_parent", and "xyz_rowid". + do_execsql_test 1.$tn.2 { + SELECT sql FROM sqlite_schema WHERE name!=$nm ORDER BY 1 + } [string map [list % $nm] " + {CREATE TABLE \"%_node\"(nodeno INTEGER PRIMARY KEY,data)} + {CREATE TABLE \"%_parent\"(nodeno INTEGER PRIMARY KEY,parentnode)} + {CREATE TABLE \"%_rowid\"(rowid INTEGER PRIMARY KEY,nodeno)} + "] + + do_execsql_test 1.$tn "DROP TABLE $nm" +} + + +# EVIDENCE-OF: R-51070-59303 There is one entry in the %_node table for +# each R*Tree node. +# +# The following creates a 6 node r-tree structure. +# +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE r1 USING rtree_i32(i, x1,x2, y1,y2); + WITH t(i) AS ( + VALUES(1) UNION SELECT i+1 FROM t WHERE i<110 + ) + INSERT INTO r1 SELECT i, (i%10), (i%10)+2, (i%6), (i%7)+6 FROM t; +} +do_execsql_test 2.1 { + SELECT count(*) FROM r1_node; +} 6 + +# EVIDENCE-OF: R-27261-09153 All nodes other than the root have an entry +# in the %_parent shadow table that identifies the parent node. +# +# In this case nodes 2-6 are the children of node 1. +# +do_execsql_test 2.3 { + SELECT nodeno, parentnode FROM r1_parent +} {2 1 3 1 4 1 5 1 6 1} + +# EVIDENCE-OF: R-02358-35037 The %_rowid shadow table maps entry rowids +# to the node that contains that entry. +# +do_execsql_test 2.4 { + SELECT 'failed' FROM r1_rowid WHERE 0==rnode_contains( + (SELECT data FROM r1_node WHERE nodeno=r1_rowid.nodeno), rowid + ) +} +do_test 2.5 { + db eval { SELECT nodeno, data FROM r1_node WHERE nodeno!=1 } { + set L [rnode $data] + foreach cell $L { + set rowid [lindex $cell 0] + set rowid_nodeno 0 + db eval {SELECT nodeno AS rowid_nodeno FROM r1_rowid WHERE rowid=$rowid} { + break + } + if {$rowid_nodeno!=$nodeno} { error "data mismatch!" } + } + } +} {} + +# EVIDENCE-OF: R-65201-22208 Extra columns appended to the %_rowid table +# hold the content of auxiliary columns. +# +# EVIDENCE-OF: R-44161-28345 The names of these extra %_rowid columns +# are probably not the same as the actual auxiliary column names. +# +# In this case, the auxiliary columns are named "e1" and "e2". The +# extra %_rowid columns are named "a0" and "a1". +# +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE rtaux USING rtree(id, x1,x2, y1,y2, +e1, +e2); + SELECT sql FROM sqlite_schema WHERE name='rtaux_rowid'; +} { + {CREATE TABLE "rtaux_rowid"(rowid INTEGER PRIMARY KEY,nodeno,a0,a1)} +} +do_execsql_test 3.1 { + INSERT INTO rtaux(e1, e2) VALUES('hello', 'world'), (123, 456); +} +do_execsql_test 3.2 { + SELECT a0, a1 FROM rtaux_rowid; +} { + hello world 123 456 +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 7.2 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc-12 +reset_db +forcedelete test.db2 + +db function rnode rnode +db function rnode_contains rnode_contains +db function rnode_replace_cell rnode_replace_cell + +# EVIDENCE-OF: R-13571-45795 The scalar SQL function rtreecheck(R) or +# rtreecheck(S,R) runs an integrity check on the rtree table named R +# contained within database S. +# +# EVIDENCE-OF: R-36011-59963 The function returns a human-language +# description of any problems found, or the string 'ok' if everything is +# ok. +# +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE rt1 USING rtree(id, a, b); + WITH s(i) AS ( + VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<200 + ) + INSERT INTO rt1 SELECT i, i, i FROM s; + + ATTACH 'test.db2' AS 'aux'; + CREATE VIRTUAL TABLE aux.rt1 USING rtree(id, a, b); + INSERT INTO aux.rt1 SELECT * FROM rt1; +} + +do_execsql_test 1.1.1 { SELECT rtreecheck('rt1'); } {ok} +do_execsql_test 1.1.2 { SELECT rtreecheck('main', 'rt1'); } {ok} +do_execsql_test 1.1.3 { SELECT rtreecheck('aux', 'rt1'); } {ok} +do_catchsql_test 1.1.4 { + SELECT rtreecheck('nosuchdb', 'rt1'); +} {1 {SQL logic error}} + +# Corrupt the table in database 'main': +do_execsql_test 1.2.1 { UPDATE rt1_node SET nodeno=21 WHERE nodeno=3; } +do_execsql_test 1.2.1 { SELECT rtreecheck('rt1')=='ok'; } {0} +do_execsql_test 1.2.2 { SELECT rtreecheck('main', 'rt1')=='ok'; } {0} +do_execsql_test 1.2.3 { SELECT rtreecheck('aux', 'rt1')=='ok'; } {1} +do_execsql_test 1.2.4 { UPDATE rt1_node SET nodeno=3 WHERE nodeno=21; } + +# Corrupt the table in database 'aux': +do_execsql_test 1.2.1 { UPDATE aux.rt1_node SET nodeno=21 WHERE nodeno=3; } +do_execsql_test 1.2.1 { SELECT rtreecheck('rt1')=='ok'; } {1} +do_execsql_test 1.2.2 { SELECT rtreecheck('main', 'rt1')=='ok'; } {1} +do_execsql_test 1.2.3 { SELECT rtreecheck('aux', 'rt1')=='ok'; } {0} +do_execsql_test 1.2.4 { UPDATE rt1_node SET nodeno=3 WHERE nodeno=21; } + +# EVIDENCE-OF: R-45759-33459 Example: To verify that an R*Tree named +# "demo_index" is well-formed and internally consistent, run: SELECT +# rtreecheck('demo_index'); +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE demo_index USING rtree(id, x1,x2, y1,y2); + INSERT INTO demo_index SELECT id, a, b, a, b FROM rt1; +} +do_execsql_test 2.1 { SELECT rtreecheck('demo_index') } {ok} +do_execsql_test 2.2 { + UPDATE demo_index_rowid SET nodeno=44 WHERE rowid=44; + SELECT rtreecheck('demo_index'); +} {{Found (44 -> 44) in %_rowid table, expected (44 -> 4)}} + + +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE rt2 USING rtree_i32(id, a, b, c, d); + WITH s(i) AS ( + VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<200 + ) + INSERT INTO rt2 SELECT i, i, i+2, i, i+2 FROM s; +} + +# EVIDENCE-OF: R-02555-31045 for each dimension, (coord1 <= coord2). +# +execsql BEGIN +do_test 3.1 { + set cell [ + lindex [execsql {SELECT rnode(data) FROM rt2_node WHERE nodeno=3}] 0 3 + ] + set cell [list [lindex $cell 0] \ + [lindex $cell 2] [lindex $cell 1] \ + [lindex $cell 3] [lindex $cell 4] \ + ] + execsql { + UPDATE rt2_node SET data=rnode_replace_cell(data, 3, $cell) WHERE nodeno=3 + } + execsql { SELECT rtreecheck('rt2') } +} {{Dimension 0 of cell 3 on node 3 is corrupt}} +execsql ROLLBACK + +# EVIDENCE-OF: R-13844-15873 unless the cell is on the root node, that +# the cell is bounded by the parent cell on the parent node. +# +execsql BEGIN +do_test 3.2 { + set cell [ + lindex [execsql {SELECT rnode(data) FROM rt2_node WHERE nodeno=3}] 0 3 + ] + lset cell 3 450 + lset cell 4 451 + execsql { + UPDATE rt2_node SET data=rnode_replace_cell(data, 3, $cell) WHERE nodeno=3 + } + execsql { SELECT rtreecheck('rt2') } +} {{Dimension 1 of cell 3 on node 3 is corrupt relative to parent}} +execsql ROLLBACK + +# EVIDENCE-OF: R-02505-03621 for leaf nodes, that there is an entry in +# the %_rowid table corresponding to the cell's rowid value that points +# to the correct node. +# +execsql BEGIN +do_test 3.3 { + execsql { + UPDATE rt2_rowid SET rowid=452 WHERE rowid=100 + } + execsql { SELECT rtreecheck('rt2') } +} {{Mapping (100 -> 6) missing from %_rowid table}} +execsql ROLLBACK + +# EVIDENCE-OF: R-50927-02218 for cells on non-leaf nodes, that there is +# an entry in the %_parent table mapping from the cell's child node to +# the node that it resides on. +# +execsql BEGIN +do_test 3.4.1 { + execsql { + UPDATE rt2_parent SET parentnode=123 WHERE nodeno=3 + } + execsql { SELECT rtreecheck('rt2') } +} {{Found (3 -> 123) in %_parent table, expected (3 -> 1)}} +execsql ROLLBACK +execsql BEGIN +do_test 3.4.2 { + execsql { + UPDATE rt2_parent SET nodeno=123 WHERE nodeno=3 + } + execsql { SELECT rtreecheck('rt2') } +} {{Mapping (3 -> 1) missing from %_parent table}} +execsql ROLLBACK + +# EVIDENCE-OF: R-23235-09153 That there are the same number of entries +# in the %_rowid table as there are leaf cells in the r-tree structure, +# and that there is a leaf cell that corresponds to each entry in the +# %_rowid table. +execsql BEGIN +do_test 3.5 { + execsql { INSERT INTO rt2_rowid VALUES(1000, 1000) } + execsql { SELECT rtreecheck('rt2') } +} {{Wrong number of entries in %_rowid table - expected 200, actual 201}} +execsql ROLLBACK + +# EVIDENCE-OF: R-62800-43436 That there are the same number of entries +# in the %_parent table as there are non-leaf cells in the r-tree +# structure, and that there is a non-leaf cell that corresponds to each +# entry in the %_parent table. +execsql BEGIN +do_test 3.6 { + execsql { INSERT INTO rt2_parent VALUES(1000, 1000) } + execsql { SELECT rtreecheck('rt2') } +} {{Wrong number of entries in %_parent table - expected 9, actual 10}} +execsql ROLLBACK + + + +finish_test + diff --git a/ext/rtree/rtreedoc2.test b/ext/rtree/rtreedoc2.test new file mode 100644 index 0000000000..ca0c6b31bd --- /dev/null +++ b/ext/rtree/rtreedoc2.test @@ -0,0 +1,346 @@ +# 2021 September 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 focus of this file is testing the r-tree extension. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] rtree_util.tcl] +source $testdir/tester.tcl +set testprefix rtreedoc2 + +ifcapable !rtree { + finish_test + return +} + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 6 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc2-1 + +# EVIDENCE-OF: R-35254-48865 A call to one of the above APIs creates a +# new SQL function named by the second parameter (zQueryFunc or zGeom). +# +# [register_circle_geom db] registers new geometry callback "Qcircle" +# and legacy implementation "circle". Test that these do actually appear. +# +do_execsql_test 1.1.0 { + SELECT * FROM pragma_function_list WHERE name IN('circle', 'qcircle'); +} { +} +do_test 1.1 { + register_circle_geom db +} {SQLITE_OK} +do_execsql_test 1.1.2 { + SELECT * FROM pragma_function_list WHERE name = 'circle' AND enc='utf8'; +} { + circle 0 s utf8 -1 0 +} +do_execsql_test 1.1.3 { + SELECT * FROM pragma_function_list WHERE name = 'qcircle' AND enc='utf8'; +} { + qcircle 0 s utf8 -1 0 +} + +do_execsql_test 1.2.0 { SELECT circle(1, 2, 3); } {{}} +do_execsql_test 1.2.1 { SELECT qcircle(1, 2, 3); } {{}} + +# EVIDENCE-OF: R-61427-46983 +do_execsql_test 1.3.0 { + CREATE VIRTUAL TABLE demo_index USING rtree(id, x1,x2, y1,y2); + INSERT INTO demo_index VALUES(10, 45,45, 24,24); + INSERT INTO demo_index VALUES(20, 50,50, 28,28); + INSERT INTO demo_index VALUES(30, 43,43, 22,22); +} +do_execsql_test 1.3.1 { + SELECT id FROM demo_index WHERE id MATCH circle(45.3, 22.9, 5.0) +} {10 30} + +# EVIDENCE-OF: R-16907-50223 The SQL syntax for custom queries is the +# same regardless of which interface, sqlite3_rtree_geometry_callback() +# or sqlite3_rtree_query_callback(), is used to register the SQL +# function. +do_execsql_test 1.3.2 { + SELECT id FROM demo_index WHERE id MATCH qcircle(45.3, 22.9, 5.0, 1) +} {10 30} + + +# EVIDENCE-OF: R-59634-51678 When that SQL function appears on the +# right-hand side of the MATCH operator and the left-hand side of the +# MATCH operator is any column in the R*Tree virtual table, then the +# callback defined by the third argument (xQueryFunc or xGeom) is +# invoked to determine if a particular object or subtree overlaps the +# desired region. +proc box_geom {args} { + lappend ::box_geom [concat [lindex $args 0] [lrange $args 2 end-1]] + return "" +} +register_box_geom db box_geom +set box_geom [list] +do_execsql_test 1.3.2 { + SELECT id FROM demo_index WHERE id MATCH box(43,46, 21,25); +} {10 30} +do_test 1.3.3 { + set ::box_geom +} [list {*}{ + {box {43.0 46.0 21.0 25.0} {45.0 45.0 24.0 24.0}} + {box {43.0 46.0 21.0 25.0} {50.0 50.0 28.0 28.0}} + {box {43.0 46.0 21.0 25.0} {43.0 43.0 22.0 22.0}} +}] + +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Section 6 of documentation. +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +set testprefix rtreedoc2-2 + +# EVIDENCE-OF: R-02424-24769 The second argument is the number of +# coordinates in each r-tree entry, and is always the same for any given +# R*Tree. +# +# EVIDENCE-OF: R-40260-16838 The number of coordinates is 2 for a +# 1-dimensional R*Tree, 4 for a 2-dimensional R*Tree, 6 for a +# 3-dimensional R*Tree, and so forth. +# +# The second argument refered to above is the length of the list passed +# as the 3rd parameter to the Tcl script. +# +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE rt1 USING rtree(id, x1,x2); + CREATE VIRTUAL TABLE rt2 USING rtree(id, x1,x2, y1,y2); + CREATE VIRTUAL TABLE rt3 USING rtree(id, x1,x2, y1,y2, z1,z2); + + INSERT INTO rt1 DEFAULT VALUES; + INSERT INTO rt2 DEFAULT VALUES; + INSERT INTO rt3 DEFAULT VALUES; +} +foreach {tn tbl nCoord} { + 1 rt1 2 + 2 rt2 4 + 3 rt3 6 +} { + set ::box_geom [list] + do_catchsql_test 1.$tn.1 " + SELECT id FROM $tbl WHERE id MATCH box(); + " {1 {SQL logic error}} + + do_test 1.$tn.2 { + llength [lindex $::box_geom 0 2] + } $nCoord +} + +# EVIDENCE-OF: R-28051-48608 If xGeom returns anything other than +# SQLITE_OK, then the r-tree query will abort with an error. +proc box_geom {args} { + error "an error!" +} +do_catchsql_test 2.0 { + SELECT * FROM rt2 WHERE id MATCH box(22,23, 24,25); +} {1 {SQL logic error}} + +do_execsql_test 3.0 { + INSERT INTO rt1 VALUES(10, 10, 10); + INSERT INTO rt1 VALUES(11, 11, 11); + INSERT INTO rt1 VALUES(12, 12, 12); + INSERT INTO rt1 VALUES(13, 13, 13); + INSERT INTO rt1 VALUES(14, 14, 14); +} + +# EVIDENCE-OF: R-53759-57366 The exact same sqlite3_rtree_geometry +# structure is used for every callback for same MATCH operator in the +# same query. +proc box_geom {args} { + lappend ::ptr_list [lindex $args 4] + return 0 +} +set ::ptr_list [list] +do_execsql_test 3.1 { + SELECT * FROM rt1 WHERE id MATCH box(1,1); +} +do_test 3.2 { + set val [lindex $::ptr_list 0] + foreach p $::ptr_list { + if {$p!=$val} {error "pointer mismatch"} + } +} {} + +# EVIDENCE-OF: R-60247-35692 The contents of the sqlite3_rtree_geometry +# structure are initialized by SQLite but are not subsequently modified. +proc box_geom {args} { + lappend ::box_geom [concat [lindex $args 0] [lrange $args 2 end-1]] + if {[llength $::box_geom]==3} { + return "zero" + } + return "" +} +set ::box_geom [list] +do_catchsql_test 3.2 { + SELECT * FROM rt1 WHERE id MATCH box(1,1); +} {1 {SQL logic error}} +do_test 3.3 { + set ::box_geom +} [list {*}{ + {box {1.0 1.0} {0.0 0.0}} + {box {1.0 1.0} {10.0 10.0}} + {box {1.0 1.0} {11.0 11.0}} + {box 0.0 {12.0 12.0}} +}] + +# EVIDENCE-OF: R-31246-29731 The pContext member of the +# sqlite3_rtree_geometry structure is always set to a copy of the +# pContext argument passed to sqlite3_rtree_geometry_callback() when the +# callback is registered. +reset_db +do_execsql_test 4.0 { + CREATE VIRTUAL TABLE r1 USING rtree(id, minX,maxX, minY,maxY); + WITH s(i) AS ( + VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<120 + ) + INSERT INTO r1 SELECT i,i,i+1, 200,201 FROM s; +} +set ctx [register_box_geom db box_geom] +set ::box_geom [list] +proc box_geom {args} { + lappend ::box_geom [lindex $args 1] + return "" +} +do_execsql_test 4.1 { + SELECT count(*) FROM r1 WHERE id MATCH box(0,150,199,201) +} 120 +do_test 4.2 { + foreach g $::box_geom { + if {$g!=$ctx} {error "pointer mismatch"} + } +} {} + +# EVIDENCE-OF: R-09904-19077 The aParam[] array (size nParam) contains +# the parameter values passed to the SQL function on the right-hand side +# of the MATCH operator. +proc box_geom {args} { + set ::box_geom [lindex $args 2] +} +foreach {tn q vals} { + 1 "SELECT count(*) FROM r1 WHERE id MATCH box(1,2,3)" {1.0 2.0 3.0} + 2 "SELECT count(*) FROM r1 WHERE id MATCH box(10001)" {10001.0} + 3 "SELECT count(*) FROM r1 WHERE id MATCH box(-10001)" {-10001.0} +} { + do_catchsql_test 5.$tn.1 $q {1 {SQL logic error}} + do_test 5.$tn.2 { set ::box_geom } $vals +} + +do_execsql_test 5.0 { + CREATE VIRTUAL TABLE myrtree USING rtree(id, x1,x2); + INSERT INTO myrtree VALUES(1, 1, 1); + INSERT INTO myrtree VALUES(2, 2, 2); + INSERT INTO myrtree VALUES(3, 3, 3); +} + +# EVIDENCE-OF: R-44448-00687 The pUser and xDelUser members of the +# sqlite3_rtree_geometry structure are initially set to NULL. +set ::box_geom_calls 0 +proc box_geom {args} { + incr ::box_geom_calls + return user_is_zero +} +do_execsql_test 5.1.1 { + SELECT * FROM myrtree WHERE id MATCH box(4, 5); +} +do_test 5.1.2 { set ::box_geom_calls } 3 + + +# EVIDENCE-OF: R-55837-00155 The pUser variable may be set by the +# callback implementation to any arbitrary value that may be useful to +# subsequent invocations of the callback within the same query (for +# example, a pointer to a complicated data structure used to test for +# region intersection). +# +# EVIDENCE-OF: R-34745-08839 If the xDelUser variable is set to a +# non-NULL value, then after the query has finished running SQLite +# automatically invokes it with the value of the pUser variable as the +# only argument. +# +set ::box_geom_calls 0 +proc box_geom {args} { + incr ::box_geom_calls + switch -- $::box_geom_calls { + 1 { + return user_is_zero + } + 2 { + return [list user box_geom_finalizer] + } + } + return "" +} +proc box_geom_finalizer {} { + set ::box_geom_finalizer "::box_geom_calls is $::box_geom_calls" +} +do_execsql_test 5.1.1 { + SELECT * FROM myrtree WHERE id MATCH box(4, 5); +} +do_test 5.1.2 { set ::box_geom_calls } 3 +do_test 5.1.3 { + set ::box_geom_finalizer +} {::box_geom_calls is 3} + + +# EVIDENCE-OF: R-28176-28813 The xGeom callback always does a +# depth-first search of the r-tree. +# +# For a breadth first search, final test case would return "B L" only. +# +do_execsql_test 6.0 { + CREATE VIRTUAL TABLE xyz USING rtree(x, x1,x2, y1,y2); + WITH s(i) AS ( + VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<15 + ) + INSERT INTO xyz SELECT NULL, one.i,one.i+1, two.i,two.i+1 FROM s one, s two; +} +do_execsql_test 6.1 { + SELECT count(*) FROM xyz_node +} {10} +proc box_geom {args} { + set coords [lindex $args 3] + set area [expr { + ([lindex $coords 1]-[lindex $coords 0]) * + ([lindex $coords 3]-[lindex $coords 2]) + }] + if {$area==1} { + lappend ::box_geom_calls L + } else { + lappend ::box_geom_calls B + } +} +set ::box_geom_calls [list] +do_execsql_test 6.2 { + SELECT count(*) FROM xyz WHERE x MATCH box(0,20,0,20) +} 225 +do_test 6.3 { + set prev "" + set box_calls [list] + foreach c $::box_geom_calls { + if {$c!=$prev} { + lappend ::box_calls $c + set prev $c + } + } + set ::box_calls +} {B L B L B L B L B L B L B L B L B L} + + +finish_test + diff --git a/ext/rtree/rtreedoc3.test b/ext/rtree/rtreedoc3.test new file mode 100644 index 0000000000..0403409fae --- /dev/null +++ b/ext/rtree/rtreedoc3.test @@ -0,0 +1,292 @@ +# 2021 September 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 focus of this file is testing the r-tree extension. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] rtree_util.tcl] +source $testdir/tester.tcl +set testprefix rtreedoc3 + +ifcapable !rtree { + finish_test + return +} + + +# This command assumes that the argument is a node blob for a 2 dimensional +# i32 r-tree table. It decodes and returns a list of cells from the node +# as a list. Each cell is itself a list of the following form: +# +# {$rowid $minX $maxX $minY $maxY} +# +# For internal (non-leaf) nodes, the rowid is replaced by the child node +# number. +# +proc rnode_cells {aData} { + set nDim 2 + + set nData [string length $aData] + set nBytePerCell [expr (8 + 2*$nDim*4)] + binary scan [string range $aData 2 3] S nCell + + set res [list] + for {set i 0} {$i < $nCell} {incr i} { + set iOff [expr $i*$nBytePerCell+4] + set cell [string range $aData $iOff [expr $iOff+$nBytePerCell-1]] + binary scan $cell WIIII rowid x1 x2 y1 y2 + lappend res [list $rowid $x1 $x2 $y1 $y2] + } + + return $res +} + +# Interpret the first two bytes of the blob passed as the only parameter +# as a 16-bit big-endian integer and return the value. If this blob is +# the root node of an r-tree, this value is the height of the tree. +# +proc rnode_height {aData} { + binary scan [string range $aData 0 1] S nHeight + return $nHeight +} + +# Return a blob containing node iNode of r-tree "rt". +# +proc rt_node_get {iNode} { + db one { SELECT data FROM rt_node WHERE nodeno=$iNode } +} + + +#-------------------------------------------------------------- +# API: +# +# pq_init +# Initialize a new test. +# +# pq_test_callback +# Invoked each time the xQueryCallback function is called. This Tcl +# command checks that the arguments that SQLite passed to xQueryCallback +# are as expected. +# +# pq_test_row +# Invoked each time a row is returned. Checks that the row returned +# was predicted by the documentation. +# +# DATA STRUCTURE: +# The priority queue is stored as a Tcl list. The order of elements in +# the list is unimportant - it is just used as a set here. Each element +# in the priority queue is itself a list. The first element is the +# priority value for the entry (a real). Following this is a list of +# key-value pairs that make up the entries fields. +# +proc pq_init {} { + global Q + set Q(pri_queue) [list] + + set nHeight [rnode_height [rt_node_get 1]] + set nCell [llength [rnode_cells [rt_node_get 1]]] + + # EVIDENCE-OF: R-54708-13595 An R*Tree query is initialized by making + # the root node the only entry in a priority queue sorted by rScore. + lappend Q(pri_queue) [list 0.0 [list \ + iLevel [expr $nHeight+1] \ + iChild 1 \ + iCurrent 0 \ + ]] +} + +proc pq_extract {} { + global Q + if {[llength $Q(pri_queue)]==0} { + error "priority queue is empty!" + } + + # Find the priority queue entry with the lowest score. + # + # EVIDENCE-OF: R-47257-47871 Smaller scores are processed first. + set iBest 0 + set rBestScore [lindex $Q(pri_queue) 0 0] + for {set ii 1} {$ii < [llength $Q(pri_queue)]} {incr ii} { + set rScore [expr [lindex $Q(pri_queue) $ii 0]] + if {$rScore<$rBestScore} { + set rBestScore $rScore + set iBest $ii + } + } + + # Extract the entry with the lowest score from the queue and return it. + # + # EVIDENCE-OF: R-60002-49798 The query proceeds by extracting the entry + # from the priority queue that has the lowest score. + set ret [lindex $Q(pri_queue) $iBest] + set Q(pri_queue) [lreplace $Q(pri_queue) $iBest $iBest] + + return $ret +} + +proc pq_new_entry {rScore iLevel cell} { + global Q + + set rowid_name "iChild" + if {$iLevel==0} { set rowid_name "iRowid" } + + set kv [list] + lappend kv aCoord [lrange $cell 1 end] + lappend kv iLevel $iLevel + + if {$iLevel==0} { + lappend kv iRowid [lindex $cell 0] + } else { + lappend kv iChild [lindex $cell 0] + lappend kv iCurrent 0 + } + + lappend Q(pri_queue) [list $rScore $kv] +} + +proc pq_test_callback {L res} { + #pq_debug "pq_test_callback $L -> $res" + global Q + + array set G $L ;# "Got" - as in stuff passed to xQuery + + # EVIDENCE-OF: R-65127-42665 If the extracted priority queue entry is a + # node (a subtree), then the next child of that node is passed to the + # xQueryFunc callback. + # + # If it had been a leaf, the row should have been returned, instead of + # xQueryCallback being called on a child - as is happening here. + foreach {rParentScore parent} [pq_extract] {} + array set P $parent ;# "Parent" - as in parent of expected cell + if {$P(iLevel)==0} { error "query callback mismatch (1)" } + set child_node [rnode_cells [rt_node_get $P(iChild)]] + set expected_cell [lindex $child_node $P(iCurrent)] + set expected_coords [lrange $expected_cell 1 end] + if {[llength $expected_coords] != [llength $G(aCoord)]} { + puts [array get P] + puts "E: $expected_coords G: $G(aCoord)" + error "coordinate mismatch in query callback (1)" + } + foreach a [lrange $expected_cell 1 end] b $G(aCoord) { + if {$a!=$b} { error "coordinate mismatch in query callback (2)" } + } + + # Check level is as expected + # + if {$G(iLevel) != $P(iLevel)-1} { + error "iLevel mismatch in query callback (1)" + } + + # Unless the callback returned NOT_WITHIN, add the entry to the priority + # queue. + # + # EVIDENCE-OF: R-28754-35153 Those subelements for which the xQueryFunc + # callback sets eWithin to PARTLY_WITHIN or FULLY_WITHIN are added to + # the priority queue using the score supplied by the callback. + # + # EVIDENCE-OF: R-08681-45277 Subelements that return NOT_WITHIN are + # discarded. + set r [lindex $res 0] + set rScore [lindex $res 1] + if {$r!="fully" && $r!="partly" && $r!="not"} { + error "unknown result: $r - expected \"fully\", \"partly\" or \"not\"" + } + if {$r!="not"} { + pq_new_entry $rScore [expr $P(iLevel)-1] $expected_cell + } + + # EVIDENCE-OF: R-07194-63805 If the node has more children then it is + # returned to the priority queue. Otherwise it is discarded. + incr P(iCurrent) + if {$P(iCurrent)<[llength $child_node]} { + lappend Q(pri_queue) [list $rParentScore [array get P]] + } +} + +proc pq_test_result {id x1 x2 y1 y2} { + #pq_debug "pq_test_result $id $x1 $x2 $y1 $y2" + foreach {rScore next} [pq_extract] {} + + # The extracted entry must be a leaf (otherwise, xQueryCallback would + # have been called on the extracted entries children instead of just + # returning the data). + # + # EVIDENCE-OF: R-13214-54017 If that entry is a leaf (meaning that it is + # an actual R*Tree entry and not a subtree) then that entry is returned + # as one row of the query result. + array set N $next + if {$N(iLevel)!=0} { error "result row mismatch (1)" } + + if {$x1!=[lindex $N(aCoord) 0] || $x2!=[lindex $N(aCoord) 1] + || $y1!=[lindex $N(aCoord) 2] || $y2!=[lindex $N(aCoord) 3] + } { + if {$N(iLevel)!=0} { error "result row mismatch (2)" } + } + + if {$id!=$N(iRowid)} { error "result row mismatch (3)" } +} + +proc pq_done {} { + global Q + # EVIDENCE-OF: R-57438-45968 The query runs until the priority queue is + # empty. + if {[llength $Q(pri_queue)]>0} { + error "priority queue is not empty!" + } +} + +proc pq_debug {caption} { + global Q + + puts "**** $caption ****" + set i 0 + foreach q [lsort -real -index 0 $Q(pri_queue)] { + puts "PQ $i: $q" + incr i + } +} + +#-------------------------------------------------------------- + +proc box_query {a} { + set res [list fully [expr rand()]] + pq_test_callback $a $res + return $res +} + +register_box_query db box_query + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE rt USING rtree_i32(id, x1,x2, y1,y2); + WITH s(i) AS ( + SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<64 + ) + INSERT INTO rt SELECT NULL, a.i, a.i+1, b.i, b.i+1 FROM s a, s b; +} + +proc box_query {a} { + set res [list fully [expr rand()]] + pq_test_callback $a $res + return $res +} + +pq_init +db eval { SELECT id, x1,x2, y1,y2 FROM rt WHERE id MATCH qbox() } { + pq_test_result $id $x1 $x2 $y1 $y2 +} +pq_done + +finish_test + + diff --git a/ext/rtree/test_rtreedoc.c b/ext/rtree/test_rtreedoc.c new file mode 100644 index 0000000000..752a7ac124 --- /dev/null +++ b/ext/rtree/test_rtreedoc.c @@ -0,0 +1,349 @@ +/* +** 2010 August 28 +** +** 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. +** +************************************************************************* +** Code for testing all sorts of SQLite interfaces. This code +** is not included in the SQLite library. +*/ + +#include "sqlite3.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif + +/* Solely for the UNUSED_PARAMETER() macro. */ +#include "sqliteInt.h" + +#ifdef SQLITE_ENABLE_RTREE + +typedef struct BoxGeomCtx BoxGeomCtx; +struct BoxGeomCtx { + Tcl_Interp *interp; + Tcl_Obj *pScript; +}; + +typedef struct BoxQueryCtx BoxQueryCtx; +struct BoxQueryCtx { + Tcl_Interp *interp; + Tcl_Obj *pScript; +}; + +static void testDelUser(void *pCtx){ + BoxGeomCtx *p = (BoxGeomCtx*)pCtx; + Tcl_EvalObjEx(p->interp, p->pScript, 0); + Tcl_DecrRefCount(p->pScript); + sqlite3_free(p); +} + +static int invokeTclGeomCb( + const char *zName, + sqlite3_rtree_geometry *p, + int nCoord, + sqlite3_rtree_dbl *aCoord +){ + int rc = SQLITE_OK; + if( p->pContext ){ + char aPtr[64]; + BoxGeomCtx *pCtx = (BoxGeomCtx*)p->pContext; + Tcl_Interp *interp = pCtx->interp; + Tcl_Obj *pScript = 0; + Tcl_Obj *pParam = 0; + Tcl_Obj *pCoord = 0; + int ii; + Tcl_Obj *pRes; + + + pScript = Tcl_DuplicateObj(pCtx->pScript); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(zName,-1)); + + sqlite3_snprintf(sizeof(aPtr)-1, aPtr, "%p", (void*)p->pContext); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(aPtr,-1)); + + pParam = Tcl_NewObj(); + for(ii=0; iinParam; ii++){ + Tcl_ListObjAppendElement( + interp, pParam, Tcl_NewDoubleObj(p->aParam[ii]) + ); + } + Tcl_ListObjAppendElement(interp, pScript, pParam); + + pCoord = Tcl_NewObj(); + for(ii=0; ii0 ){ + const char *zCmd = Tcl_GetString(aObj[0]); + if( 0==sqlite3_stricmp(zCmd, "zero") ){ + p->aParam[0] = 0.0; + p->nParam = 1; + } + else if( 0==sqlite3_stricmp(zCmd, "user") ){ + if( p->pUser || p->xDelUser ){ + rc = SQLITE_ERROR; + }else{ + BoxGeomCtx *pCtx = sqlite3_malloc(sizeof(BoxGeomCtx)); + if( pCtx==0 ){ + rc = SQLITE_NOMEM; + }else{ + pCtx->interp = interp; + pCtx->pScript = Tcl_DuplicateObj(pRes); + Tcl_IncrRefCount(pCtx->pScript); + Tcl_ListObjReplace(interp, pCtx->pScript, 0, 1, 0, 0); + p->pUser = (void*)pCtx; + p->xDelUser = testDelUser; + } + } + } + else if( 0==sqlite3_stricmp(zCmd, "user_is_zero") ){ + if( p->pUser || p->xDelUser ) rc = SQLITE_ERROR; + } + } + } + } + return rc; +} + +/* +# EVIDENCE-OF: R-00693-36727 The legacy xGeom callback is invoked with +# four arguments. + +# EVIDENCE-OF: R-50437-53270 The first argument is a pointer to an +# sqlite3_rtree_geometry structure which provides information about how +# the SQL function was invoked. + +# EVIDENCE-OF: R-00090-24248 The third argument, aCoord[], is an array +# of nCoord coordinates that defines a bounding box to be tested. + +# EVIDENCE-OF: R-28207-40885 The last argument is a pointer into which +# the callback result should be written. + +*/ +static int box_geom( + sqlite3_rtree_geometry *p, /* R-50437-53270 */ + int nCoord, /* R-02424-24769 */ + sqlite3_rtree_dbl *aCoord, /* R-00090-24248 */ + int *pRes /* R-28207-40885 */ +){ + int ii; + + if( p->nParam!=nCoord ){ + invokeTclGeomCb("box", p, nCoord, aCoord); + return SQLITE_ERROR; + } + if( invokeTclGeomCb("box", p, nCoord, aCoord) ) return SQLITE_ERROR; + + for(ii=0; iip->aParam[ii+1] || aCoord[ii+1]aParam[ii] ){ + /* R-28207-40885 */ + *pRes = 0; + return SQLITE_OK; + } + } + + /* R-28207-40885 */ + *pRes = 1; + + return SQLITE_OK; +} + +static int SQLITE_TCLAPI register_box_geom( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); + extern const char *sqlite3ErrName(int); + sqlite3 *db; + BoxGeomCtx *pCtx; + char aPtr[64]; + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB SCRIPT"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + + pCtx = (BoxGeomCtx*)ckalloc(sizeof(BoxGeomCtx*)); + pCtx->interp = interp; + pCtx->pScript = Tcl_DuplicateObj(objv[2]); + Tcl_IncrRefCount(pCtx->pScript); + + sqlite3_rtree_geometry_callback(db, "box", box_geom, (void*)pCtx); + + sqlite3_snprintf(64, aPtr, "%p", (void*)pCtx); + Tcl_SetObjResult(interp, Tcl_NewStringObj(aPtr, -1)); + return TCL_OK; +} + +static int box_query(sqlite3_rtree_query_info *pInfo){ + const char *azParentWithin[] = {"not", "partly", "fully", 0}; + BoxQueryCtx *pCtx = (BoxQueryCtx*)pInfo->pContext; + Tcl_Interp *interp = pCtx->interp; + Tcl_Obj *pEval; + Tcl_Obj *pArg; + Tcl_Obj *pTmp = 0; + int rc; + int ii; + + pEval = Tcl_DuplicateObj(pCtx->pScript); + Tcl_IncrRefCount(pEval); + pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); + + /* aParam[] */ + pTmp = Tcl_NewObj(); + Tcl_IncrRefCount(pTmp); + for(ii=0; iinParam; ii++){ + Tcl_Obj *p = Tcl_NewDoubleObj(pInfo->aParam[ii]); + Tcl_ListObjAppendElement(interp, pTmp, p); + } + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("aParam", -1)); + Tcl_ListObjAppendElement(interp, pArg, pTmp); + Tcl_DecrRefCount(pTmp); + + /* aCoord[] */ + pTmp = Tcl_NewObj(); + Tcl_IncrRefCount(pTmp); + for(ii=0; iinCoord; ii++){ + Tcl_Obj *p = Tcl_NewDoubleObj(pInfo->aCoord[ii]); + Tcl_ListObjAppendElement(interp, pTmp, p); + } + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("aCoord", -1)); + Tcl_ListObjAppendElement(interp, pArg, pTmp); + Tcl_DecrRefCount(pTmp); + + /* anQueue[] */ + pTmp = Tcl_NewObj(); + Tcl_IncrRefCount(pTmp); + for(ii=0; ii<=pInfo->mxLevel; ii++){ + Tcl_Obj *p = Tcl_NewIntObj((int)pInfo->anQueue[ii]); + Tcl_ListObjAppendElement(interp, pTmp, p); + } + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("anQueue", -1)); + Tcl_ListObjAppendElement(interp, pArg, pTmp); + Tcl_DecrRefCount(pTmp); + + /* iLevel */ + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("iLevel", -1)); + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewIntObj(pInfo->iLevel)); + + /* mxLevel */ + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("mxLevel", -1)); + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewIntObj(pInfo->mxLevel)); + + /* iRowid */ + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("iRowid", -1)); + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewWideIntObj(pInfo->iRowid)); + + /* rParentScore */ + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("rParentScore", -1)); + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewDoubleObj(pInfo->rParentScore)); + + /* eParentWithin */ + assert( pInfo->eParentWithin==0 + || pInfo->eParentWithin==1 + || pInfo->eParentWithin==2 + ); + Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("eParentWithin", -1)); + Tcl_ListObjAppendElement(interp, pArg, + Tcl_NewStringObj(azParentWithin[pInfo->eParentWithin], -1) + ); + + Tcl_ListObjAppendElement(interp, pEval, pArg); + rc = Tcl_EvalObjEx(interp, pEval, 0) ? SQLITE_ERROR : SQLITE_OK; + + if( rc==SQLITE_OK ){ + double rScore = 0.0; + int nObj = 0; + int eP = 0; + Tcl_Obj **aObj = 0; + Tcl_Obj *pRes = Tcl_GetObjResult(interp); + + if( Tcl_ListObjGetElements(interp, pRes, &nObj, &aObj) + || nObj!=2 + || Tcl_GetDoubleFromObj(interp, aObj[1], &rScore) + || Tcl_GetIndexFromObj(interp, aObj[0], azParentWithin, "value", 0, &eP) + ){ + rc = SQLITE_ERROR; + }else{ + pInfo->rScore = rScore; + pInfo->eParentWithin = eP; + } + } + + Tcl_DecrRefCount(pArg); + Tcl_DecrRefCount(pEval); + return rc; +} + +static void box_query_destroy(void *p){ + BoxQueryCtx *pCtx = (BoxQueryCtx*)p; + Tcl_DecrRefCount(pCtx->pScript); + ckfree(pCtx); +} + +static int SQLITE_TCLAPI register_box_query( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); + extern const char *sqlite3ErrName(int); + sqlite3 *db; + BoxQueryCtx *pCtx; + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB SCRIPT"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + + pCtx = (BoxQueryCtx*)ckalloc(sizeof(BoxQueryCtx*)); + pCtx->interp = interp; + pCtx->pScript = Tcl_DuplicateObj(objv[2]); + Tcl_IncrRefCount(pCtx->pScript); + + sqlite3_rtree_query_callback( + db, "qbox", box_query, (void*)pCtx, box_query_destroy + ); + + Tcl_ResetResult(interp); + return TCL_OK; +} +#endif /* SQLITE_ENABLE_RTREE */ + + +int Sqlitetestrtreedoc_Init(Tcl_Interp *interp){ +#ifdef SQLITE_ENABLE_RTREE + Tcl_CreateObjCommand(interp, "register_box_geom", register_box_geom, 0, 0); + Tcl_CreateObjCommand(interp, "register_box_query", register_box_query, 0, 0); +#endif /* SQLITE_ENABLE_RTREE */ + return TCL_OK; +} + diff --git a/ext/session/sessionat.test b/ext/session/sessionat.test index 4a3f59a441..8141d92322 100644 --- a/ext/session/sessionat.test +++ b/ext/session/sessionat.test @@ -20,9 +20,14 @@ if {![info exists testdir]} { source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} - set testprefix sessionat +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + db close sqlite3_shutdown test_sqlite3_log log diff --git a/main.mk b/main.mk index 50ee9e2270..91c1fe7eb5 100644 --- a/main.mk +++ b/main.mk @@ -388,7 +388,8 @@ TESTSRC += \ $(TOP)/ext/misc/zipfile.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ - $(TOP)/ext/fts5/fts5_test_tok.c + $(TOP)/ext/fts5/fts5_test_tok.c \ + $(TOP)/ext/rtree/test_rtreedoc.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c diff --git a/manifest b/manifest index 0d262ad1a2..bfb38b6ed4 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ C Merge\srecent\strunk\senhancements\sinto\sthe\sreuse-schema\sbranch. -D 2021-08-09T18:17:26.144 +D 2021-10-04T11:49:35.957 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 27da61b991eee661f56262465d6feb3e4932904dda729cb9add349efff49c445 +F Makefile.in c5fd8f6f963de95afe68340d98708bc6043b71a8f0edf7d76a1ccbc4be3c6cc7 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 -F Makefile.msc c1241e5f498c1a7088758430c20e2f228ab25bc5ac4d7a96b367b119f3b99ced +F Makefile.msc 9a5844f43ca9dcc792d036c86b501d8aa9d8fe14ef48ce1ce355f5eacc55f887 F README.md 27fb76aa7eb57ed63a53bbba7292b6bf71f51125554f79f16b5d040edd1e6110 F VERSION c6595fef606851f2bc3ebed6a7386c73751835fc909feab7c093739fa4b3c1d1 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -51,8 +51,8 @@ F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 -F ext/expert/expert1.test 6703fd74711daf8230240680b0a348d0720e28819b602701adfbd32457fdcddd -F ext/expert/sqlite3expert.c fdcd3bd969351c4e860a1368a6ab64bc4c94d2d89396805b28853a514d06fd92 +F ext/expert/expert1.test 3c642a4e7bbb14f21ddab595436fb465a4733f47a0fe5b2855e1d5ff900ef08e +F ext/expert/sqlite3expert.c bee71c524ab33441f0eae46b39691b9d348d1960d9274e834520ea6c573b542e F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72 F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e @@ -120,7 +120,7 @@ F ext/fts5/fts5_buffer.c 89a51b37c4aa1c02c1ec24c18c55196c0693b29a752fedfd036938d F ext/fts5/fts5_config.c 8336d0ff6db0933f63cfec8ae0ab76e68393259cbccc0b46e1f79f7fa1842ff3 F ext/fts5/fts5_expr.c 6ea447b0cb1888110087a8c04133817b0ccf964fe22414371b0e32189a556533 F ext/fts5/fts5_hash.c 1aa93c9b5f461afba66701ee226297dc78402b3bdde81e90a10de5fe3df14959 -F ext/fts5/fts5_index.c 75249b8218dac9c9e7d62362e2be414c1399fdc380c838e5512e2042967b38c8 +F ext/fts5/fts5_index.c b5df80b2f27cd4d0858fa22679fbd144a3f4a812e0392866108dab95e6a78ff0 F ext/fts5/fts5_main.c 35ebbcae681a4a40027c47bc2e94d7e7c81e331dc406bb9b23c546454ee8f98a F ext/fts5/fts5_storage.c 58ba71e6cd3d43a5735815e7956ee167babb4d2cbfe206905174792af4d09d75 F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae @@ -163,6 +163,7 @@ F ext/fts5/test/fts5corrupt.test 77ae6f41a7eba10620efb921cf7dbe218b0ef232b04519d F ext/fts5/test/fts5corrupt2.test 7453752ba12ce91690c469a6449d412561cc604b1dec994e16ab132952e7805f F ext/fts5/test/fts5corrupt3.test 0e473620582a53ac61f468f364db8a151c1e18d2a879b16439d172c12c4c9828 F ext/fts5/test/fts5corrupt4.test f4c08e2182a48d8b70975fd869ee5391855c06d8a0ff87b6a2529e7c5a88a1d3 +F ext/fts5/test/fts5corrupt5.test a6368d90baa61e606ebd53024584de2a02f981757d0941154432a727f3972828 F ext/fts5/test/fts5delete.test 619295b20dbc1d840b403ee07c878f52378849c3c02e44f2ee143b3e978a0aa7 F ext/fts5/test/fts5detail.test 54015e9c43ec4ba542cfb93268abdf280e0300f350efd08ee411284b03595cc4 F ext/fts5/test/fts5determin.test 1b77879b2ae818b5b71c859e534ee334dac088b7cf3ff3bf76a2c82b1c788d11 @@ -230,7 +231,7 @@ F ext/fts5/test/fts5unindexed.test 9021af86a0fb9fc616f7a69a996db0116e7936d0db638 F ext/fts5/test/fts5update.test b8affd796e45c94a4d19ad5c26606ea06065a0f162a9562d9f005b5a80ccf0bc F ext/fts5/test/fts5version.test c8f2cc105f0abf0224965f93e584633dee3e06c91478bc67e468f7cfdf97fd6a F ext/fts5/test/fts5vocab.test 7ed80d9af1ddaaa1637da05e406327b5aac250848bc604c1c1cc667908b87760 -F ext/fts5/test/fts5vocab2.test c0a8397523561eb780b4f439e75d4969fade0ac40bc73e0c8fd2f3e065111161 +F ext/fts5/test/fts5vocab2.test 681980e92e031c9f3fe8d9c149189e876c108da2fb0fb3a25bd8a9b94bff8f68 F ext/fts5/tool/fts5speed.tcl b0056f91a55b2d1a3684ec05729de92b042e2f85 F ext/fts5/tool/fts5txt2db.tcl c0d43c8590656f8240e622b00957b3a0facc49482411a9fdc2870b45c0c82f9f F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093 @@ -302,11 +303,11 @@ F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f82 F ext/misc/decimal.c 09f967dcf4a1ee35a76309829308ec278d3648168733f4a1147820e11ebefd12 F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1 F ext/misc/explain.c 0086fab288d4352ea638cf40ac382aad3b0dc5e845a1ea829a694c015fd970fe -F ext/misc/fileio.c 9b69e25da3b51d4a1d905a464ccb96709792ad627a742ba09215bc0d1447e7bd +F ext/misc/fileio.c 57fefd0efc535e62bb8b07fa146875171481da81a759bbfbe2fc91bab90058e0 F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d -F ext/misc/ieee754.c cd6ab89f85fda8a020559b3f4d03001a8a62dd856beda5af3f558621d12be913 -F ext/misc/json1.c 76c5d9e0960fd15b4be79dacb76d872b4d0d983ce13e72ebfe9481d82cb9345d +F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511 +F ext/misc/json1.c 96a44b84f00d35f9450abae1f0167abe60de9379b2b27ea652c6cf14450b4739 F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b @@ -320,7 +321,7 @@ F ext/misc/regexp.c 5853b0e5ed40c47f7ded2b0bf2ff73796f7cb21543089c5f07308e003264 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946 -F ext/misc/series.c 233804fd4e07de94ecae42b487fb38bbd819b249114bb34bb46f227c8c7111df +F ext/misc/series.c f9896e76b029e3c6553c520552555e803e26e7dfe1890d5866243cf072d938d0 F ext/misc/sha1.c c8f2253c8792ffab9517695ea7d88c079f0395a5505eefef5c8198fe184ed5ac F ext/misc/shathree.c e984f31731de4cf302a0386be5fe664580f63d8204c47b9b41cc4b997745f9ec F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 @@ -338,7 +339,7 @@ F ext/misc/vfsstat.c 474d08efc697b8eba300082cb1eb74a5f0f3df31ed257db1cb07e72ab0e F ext/misc/vtablog.c 5538acd0c8ddaae372331bee11608d76973436b77d6a91e8635cfc9432fba5ae F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd F ext/misc/wholenumber.c a838d1bea913c514ff316c69695efbb49ea3b8cb37d22afc57f73b6b010b4546 -F ext/misc/zipfile.c b7261ef6dbc2d18924ff80c40fb5d56c9ccfee3f822a7d3d43b7c87af3d27218 +F ext/misc/zipfile.c 9078475440114135594322cdf137f5475acfca5ab22af21a3f3e65ba38d2de20 F ext/misc/zorder.c b0ff58fa643afa1d846786d51ea8d5c4b6b35aa0254ab5a82617db92f3adda64 F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 F ext/rbu/rbu1.test c62904bd9526dcdc3496a21199aaf14ae191bbadbf67f076bf16be6b3f2115c2 @@ -394,9 +395,9 @@ F ext/repair/test/checkindex01.test b530f141413b587c9eb78ff734de6bb79bc3515c3350 F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/geopoly.c 98d45533989e908bf65b43f36ff6eaad95a9ffe6f3b6b8658fbd47d45c58b10b -F ext/rtree/rtree.c f1ce6a86f7cbff634900653bec2dfeba732dd4450eec921c0ac3851d41f462b9 +F ext/rtree/rtree.c 03b238f2134bac3cffeffa03f70cac3b23c9329ab865a73bb0242bfd2f19daf8 F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412 -F ext/rtree/rtree1.test 00792b030a4e188ff1b22e8530e8aa0452bb5dd81c2b18cb004afc7dc63e040e +F ext/rtree/rtree1.test 35c3bc0def71317b7601ee0d1149e7df2cd8fc4f13ec89a64761ac3f46ca123f F ext/rtree/rtree2.test 9d9deddbb16fd0c30c36e6b4fdc3ee3132d765567f0f9432ee71e1303d32603d F ext/rtree/rtree3.test 4ee5d7df86040efe3d8d84f141f2962a7745452200a7cba1db06f86d97050499 F ext/rtree/rtree4.test 304de65d484540111b896827e4261815e5dca4ce28eeecd58be648cd73452c4b @@ -405,7 +406,7 @@ F ext/rtree/rtree6.test 9ce3691c1aac43070a9f194f0ebf54372db346c5a82241fd11b525ed F ext/rtree/rtree7.test c8fb2e555b128dd0f0bdb520c61380014f497f8a23c40f2e820acc9f9e4fdce5 F ext/rtree/rtree8.test 2d99006a1386663978c9e1df167554671e4f711c419175b39f332719deb1ce0e F ext/rtree/rtree9.test c646f12c8c1c68ef015c6c043d86a0c42488e2e68ed1bb1b0771a7ca246cbabf -F ext/rtree/rtreeA.test ed2f1be9c06dde0b1ab93a95dd9e87eeaa02db2d30bcb4b9179b69ee3dc3319b +F ext/rtree/rtreeA.test c0d8e91e25052d5f3fbda17632ca843b82ca13c4181fb6000a0d63bd2d7e70ce F ext/rtree/rtreeB.test 4cec297f8e5c588654bbf3c6ed0903f10612be8a2878055dd25faf8c71758bc9 F ext/rtree/rtreeC.test c4bfa9a61c6788c03e4a9ce40ab2cfc6100982559effd9842d1b658e1d47aa5f F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c4a1100dc @@ -419,8 +420,12 @@ F ext/rtree/rtree_util.tcl db734b4c5e75fed6acc56d9701f2235345acfdec750b5fc7b5879 F ext/rtree/rtreecheck.test d67d5b3e9e45bfa8cd90734e8e9302144ac415b8e9176c6f02d4f92892ee8a35 F ext/rtree/rtreecirc.test aec664eb21ae943aeb344191407afff5d392d3ae9d12b9a112ced0d9c5de298e F ext/rtree/rtreeconnect.test 225ad3fcb483d36cbee423a25052a6bbae762c9576ae9268332360c68c170d3d +F ext/rtree/rtreedoc.test 903c5229758bcef1d5590892bf973d9f91dcd1d96b1c09b6761e2c0e398e78e1 +F ext/rtree/rtreedoc2.test 194ebb7d561452dcdc10bf03f44e30c082c2f0c14efeb07f5e02c7daf8284d93 +F ext/rtree/rtreedoc3.test 555a878c4d79c4e37fa439a1c3b02ee65d3ebaf75d9e8d96a9c55d66db3efbf8 F ext/rtree/rtreefuzz001.test 0fc793f67897c250c5fde96cefee455a5e2fb92f4feeabde5b85ea02040790ee F ext/rtree/sqlite3rtree.h 03c8db3261e435fbddcfc961471795cbf12b24e03001d0015b2636b0f3881373 +F ext/rtree/test_rtreedoc.c e81d9bf69f7cbc8ba536458bbd8fc06a6f9ca93165f7d68832f588461e6a53cb F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/util/randomshape.tcl 54ee03d0d4a1c621806f7f44d5b78d2db8fac26e0e8687c36c4bd0203b27dbff F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 @@ -446,7 +451,7 @@ F ext/session/sessionG.test 3828b944cd1285f4379340fd36f8b64c464fc84df6ff3ccbc955 F ext/session/sessionH.test b17afdbd3b8f17e9bab91e235acf167cf35485db2ab2df0ea8893fbb914741a4 F ext/session/session_common.tcl f613174665456b2d916ae8df3e5735092a1c1712f36f46840172e9a01e8cc53e F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3 -F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28f0c1cc142c3ec +F ext/session/sessionat.test 52993535f1230a42f70886643574ba7ae60ef854f8add9c8e3fcc3eb5c564bd2 F ext/session/sessionbig.test 890ade19e3f80f3d3a3e83821ff79c5e2af906a67ecb5450879f0015cadf101e F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7 @@ -467,7 +472,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 0d98192758cdb5d3766f5ce5d0fddea9ca201bdbd990a9eb98ebe79baba89ef0 +F main.mk 2a87e919ae96ab47f20b0c4e6568a58071233fe7306afa56b925a356e5c26399 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -479,44 +484,44 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 618d0c615e44d3f57259530cf4737a9eeb6c381133a804e41e2b4e1706fe4a57 -F src/analyze.c da68cbd52e696dca799da27c30721b604a55e156fdeb232be7bc5ef2618de7c3 +F src/alter.c aa59bacdcd7716eae842999467542f3bf14e73a691e9462acdb3217151de96aa +F src/analyze.c 352b3db73a36d3ddd26c511e9d913787adaabb1aa068589601512cfd9b0f49c0 F src/attach.c 74fab1b71a5fef866eae143aa5fa8c66174a4aa5829189764948e621ad3adbd6 F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33 F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 -F src/btree.c e204a9c8fb4fe5dbb910a863ba487f4af9b5c501254ec4ccbfcdd6b1f65b7fb4 +F src/btree.c ced03fe226a3a42b77b504afcd0a9da14da39b42fac5935988243803cf03bc14 F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22 F src/btreeInt.h 7bc15a24a02662409ebcd6aeaa1065522d14b7fda71573a2b0568b458f514ae0 -F src/build.c 336ecd22dc408b6935d2362b40e21070ef31e088d29c97d68cc6f4df74878c1d +F src/build.c 034d67c88857fc3014c64e4cb95eaa26710e8bdab6ef15fc610bd776075a319b F src/callback.c 38b73a7a594b5b9fd322acf0a7a0e614bbc9a2edaecab437eb1d8b6d79afba3b F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 22b5ea591c18d677b521a99d375dfc626917bc23786c5f52aca113924b8edb9e -F src/date.c e0632f335952b32401482d099321bbf12716b29d6e72836b53ae49683ebae4bf +F src/date.c 467848d8187382764e37fd7678d6825cdfb90d9e564648527ed832e999ea7b85 F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a -F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c +F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d F src/delete.c 3ce6af6b64c8b476de51ccc32da0cb3142d42e65754e1d8118addf65b8bcba15 -F src/expr.c e98375fc63552cc8cdd36a41bdca3039cb603d9fe67abd9c9f40adae8405fbc5 +F src/expr.c ebc76aa98b9bcd556c2855942c18f4d2f52c6504c13f7285413026bdd1c7bbf5 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 1905af1821b88321e1bb9d6a69e704495b6844a9b6c29398d40117cc251e893c -F src/func.c c224240cbc97fa5e9c4fe9acb128716cb835ca045532bca6951b7c45b020c56c -F src/global.c 5eba017ebbd887e2365e6e6e815e1619e41406b8946d17594e94116174787df5 +F src/func.c 761a989e9b6a1f03cae05ef274f30881cb8f7ec816af2913b58dc18ac31ffd0e +F src/global.c 612ea60c9acbcb45754c2ed659b4a56936a06814718e969636fedc7e3b889808 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c 4ebff642574d3866316439b3dfce165f80e130e8969853c656d71b2afc5dd73c +F src/insert.c 16fa4b66709db6db5bb31475f2321d0a839269765aa2461a520c151c385e8b9d F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 0aa9e7f08e168e3874cb54984408e3976dafdf5616d511952c425b5ac088ea3e -F src/main.c 6e86cafdd62cb4b9a9c0166c0771907b77252bb7751b4e0bce2d14efffa6538f -F src/malloc.c cbc93cdd429c4594912017d92ab656e2579aca64dbd1c6888551275bed46f25b +F src/main.c 29777eb1c99214cf12047f139322ef4a491ddf29296236ba94a4bbb1f887077e +F src/malloc.c ef796bcc0e81d845d59a469f1cf235056caf9024172fd524e32136e65593647b F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de F src/mem2.c b93b8762ab999a29ae7751532dadf0a1ac78040308a5fb1d17fcc365171d67eb F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6 F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944 -F src/memdb.c 73622017aa03a3cabd1c4d6fca97eedada2155817dd0d74d6c1aeb42573b515d +F src/memdb.c cd8cf3ee965db4a4ab4b5423b49a4ef810490b8ba828911e523325f2cce3ed1a F src/memjournal.c a85f0dc5c02a42453d0bc3819ecfb5666cb6433e5deefcd93ccbe05c9f088b83 F src/msvc.h 3a15918220367a8876be3fa4f2abe423a861491e84b864fb2b7426bf022a28f8 F src/mutex.c 5e3409715552348732e97b9194abe92fdfcd934cfb681df4ba0ab87ac6c18d25 @@ -525,36 +530,36 @@ F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 F src/mutex_unix.c dd2b3f1cc1863079bc1349ac0fec395a500090c4fe4e11ab775310a49f2f956d F src/mutex_w32.c caa50e1c0258ac4443f52e00fe8aaea73b6d0728bd8856bedfff822cae418541 F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 -F src/os.c 59ed1f503347e8b5434c0ce7d7d0f02a3f24a72fea8b26d0bba2de8dfaef778b +F src/os.c 91fc69f95ef0528368174dca20f01e1d8f82934f719e1cb50fd0260b18c028a6 F src/os.h 26890f540b475598cd9881dcc68931377b8d429d3ea3e2eeb64470cde64199f8 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 F src/os_unix.c b11e4610769922253dec27d7af4a07ff84f65169d19bda5e9b12a152a706f7f5 F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c 95c255256b13827caf038c8f963d334784073f38ab6ef9d70371d9d04f3c43e0 +F src/pager.c dabb867aa4fabe01d05a5fb39c4d497b2d7bff591b738ffbe7f715ee28b67cdb F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f -F src/parse.y 0ba0baec5de6921ec8ba8bbcf1018969144ef29d26112e17539d8fbb1662e3eb -F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177 +F src/parse.y 82bdd593c50bddcc285a173e9788a20296103217f5f5290c9122abf8af71af62 +F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 -F src/pcache1.c 00541fef31d2798dc20308ee1fa46205b76ad1df2c871e9c9bfe9508e59ab54c -F src/pragma.c 0d91c5dd6c8f5088ec67962fc5730a0947d238bae5dd8b9b5290e136778ae9d7 -F src/pragma.h 6c85e80048ea4a4fa42e769cbfdd252046f83db2c6681cebace54d7bb7c43480 -F src/prepare.c 3668279bfdec5e58e54a284b068d36746efadd5b2ecbb7475f3a1d12a8297653 -F src/printf.c 78fabb49b9ac9a12dd1c89d744abdc9b67fd3205e62967e158f78b965a29ec4b +F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65 +F src/pragma.c a582bc58a7a05caedbbec3965212ad9343a8369bfdb3017f74672bbf35ee6b00 +F src/pragma.h 482ee258d264738041362e7272fd92a09ad53d926f10b9fa38624befabbec528 +F src/prepare.c 5251d1bab075470ff6bc2acca05da8e74a3c81b057c52d27adba18e7172b2dc3 +F src/printf.c 5901672228f305f7d493cbc4e7d76a61a5caecdbc1cd06b1f9ec42ea4265cf8d F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c -F src/resolve.c 42b94d37a54200707a95566eff4f7e8a380e32d080016b699f23bd79a73a5028 +F src/resolve.c b9e60afa56d0484ee573aba54d9e73603736236df33d2ae3421b4cd0367d907d F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c 63077c0243ded1432d97c90c1a4c3419b3a574b36634c674599a68bfe4c3bdc2 -F src/shell.c.in 90e7c6063d6e8abea262beda0e619b893bd3636a88e283e6d4227e58aa2985eb -F src/sqlite.h.in a10c0512fa8f3cea54a01391d0e559a21873d2a9f3c331198e4fb9e2c1b18727 +F src/select.c 916d18b586d8efad5a46040e48405f42e7f90fff33bca1cfab040169515ec525 +F src/shell.c.in b6f7cd92701d378382acc99bbd226e1e3d0cfbf494ab4c21f28d2a09ffcfddea +F src/sqlite.h.in 0b54872eca3326d165a807b8f964e1b3026e26f9d28ee7f2aeb852ea2252eea8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h e97f4e9b509408fea4c4e9bef5a41608dfac343b4d3c7a990dedde1e19af9510 -F src/sqliteInt.h e36399792ac0acd05c6480cb11b110bf050b913cae929cd8d05ce58e2f6ee285 +F src/sqliteInt.h 149e887c58f288e0bee1ee8da40dfec95f20ceea7ed5c9551fa1c9fa333f840a F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c d0956e57c71160155f620a3efeb1e5c05a3f8b9a897dd09c5263268e5d237579 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 -F src/tclsqlite.c 1ae70091c34402aec6f637ef5646789f68be1169fe4332d6844db0666773cee3 +F src/tclsqlite.c 78450c0a24bbd9379a8f4827d830e743d5f884389042d6644e4bcba17fdde952 F src/test1.c 63761c2be2607f1b425fde991beda48aed384f8d67f2b4ee549174c88b433009 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644 @@ -599,7 +604,7 @@ F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939 -F src/test_tclsh.c 6cbaaf30c4fed76c7b35baa42c521fd225c49563ff580f736d77def9e216b31b +F src/test_tclsh.c ec192bceacfe67d427e97d4985a298b0aaadb266dfbc77c54425537c5e263b65 F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc F src/test_thread.c 269ea9e1fa5828dba550eb26f619aa18aedbc29fd92f8a5f6b93521fbb74a61c F src/test_vdbecov.c f60c6f135ec42c0de013a1d5136777aa328a776d33277f92abac648930453d43 @@ -616,28 +621,28 @@ F src/trigger.c db412c9616de92de782540e84095262eec7eabc5627b3a5f3974ec30bcf58204 F src/update.c 69c4c10bc6873a80c0a77cb578f9fc60ee90003d03f9530bc3370fa24615772d F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c e9fd5c474691a7c913dfc971f01cf6d3a3d5954db04e0764a6426f845505e692 +F src/util.c 3f27a1eae01c8bbbb8cdef2f26bd8e6a2a7db08106ef7c3dcc990787a5da6e86 F src/vacuum.c 344acf0354037adb6d64451968a4ec0a6a8f81e753acdc5fe852cb4d4fcf4a6a -F src/vdbe.c 7c72ebe8ef2457c906e78bbd9ad5dec1f628f4be35610bd32c0748fa3568526e +F src/vdbe.c 571042c58e365eaf3de94d3db2599e25323a463d2632ecef4f33ae7c4de82e4d F src/vdbe.h bfde0b0f429a0ba4203e5319780a6a1c8b2a809c5cd6baa9ae22e257a657b8b3 F src/vdbeInt.h 38206c8dd6b60ff03d9fd4f626b1b4fd0eef7cdc44f2fc2c1973b0f932a3f26b F src/vdbeapi.c aa5aaf2c37676b83af5724c6cd8207a3064ed46a217fd180957f75ac84f7a2a5 F src/vdbeaux.c a133350fa97ca98ad4c1fb1e0cc11c677c09a74df0717384ce3ce2f9a1483baa F src/vdbeblob.c ae6c3cbc723d0a21d6a5c61c573b45080f795e15a524bc29953bc3114348fa7a -F src/vdbemem.c 53881aa0a7845922a075b3f375695588618098871a7a4120af4c297b80fa3e64 +F src/vdbemem.c 0e830c2aab24241eed85bd4c8a5bd8c9b959081316477440bfffb35089ba7d0b F src/vdbesort.c cd5130f683706c1a43e165a74187745fb3351cb56052cf9dc91de820634bbde2 -F src/vdbetrace.c 666c6fd9f1b62be6999e072a45b913e3c2c3518bc60dfd4d54fe304130acb724 +F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c -F src/vtab.c b2178e665a4e73818432cee82dd54ade4d765abfb3d998073572c82cff472e37 +F src/vtab.c 86fc8020a6cb7cd01396c01924a07dc5eb07e35f2d3574d3ab239ffb135284cc F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 2be08331d798237ad5d7ae0b252700ffb2b63189cb18d993496d009a93e2f81c F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c 7342becedf3f8a26f9817f08436bdf8b56ad69af83705f6b9320a0ad3092c2ac -F src/where.c 99b6e13664a7bd9a553c554978d0e253066995dade621f44cffa8928c8b493b5 +F src/where.c d9215db24449143da844ab86c727bbbef94ad45c53cc17bc0051386472db5789 F src/whereInt.h 9248161dd004f625ce5d3841ca9b99fed3fc8d61522cf76340fc5217dbe1375b -F src/wherecode.c 9b33f463a279feeee69622b747b0050f0b836eb8b5ac48599ba3a6bfbea798a7 -F src/whereexpr.c 3a9144a9d52e110efdc012a73b1574e7b2b4df4bf98949387cb620295eba0975 -F src/window.c 420167512050a0dfc0f0115b9f0c7d299da9759c9bb2ae83a61fb8d730a5707f +F src/wherecode.c 0208553a0602146b5640747c0e3f7a8c785108c2d06a160b69f23491e9dc781e +F src/whereexpr.c e5fdac355deef93a821f03b90770f92f2be833e92bbdeff8ac1b6c2ae1f74356 +F src/window.c f27e34e896f84e0bedec32b027d4531f224971ce3e16f8e1d97a968875ddcec1 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 F test/affinity3.test eecb0dabee4b7765a8465439d5e99429279ffba23ca74a7eae270a452799f9e7 @@ -652,21 +657,21 @@ F test/alter4.test 716caa071dd8a3c6d57225778d15d3c3cbf5e34b2e84ae44199aeb2bbf50a F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959 F test/alterauth2.test 381b1ab603c9ef96314a3158528ea17f7964449385a28eeaf8191120b2e24a8d F test/altercol.test b11fa1b131e80ab5b6ecfb3b725fb0419c14ca6efba5adb57aeabfc9baa0c8f3 -F test/altercorrupt.test 584d707a80e106952d6382790c8919bcf9f0db678ed3a1c09fd98b7f9d1d3a10 +F test/altercorrupt.test 2e1d705342cf9d7de884518ddbb053fd52d7e60d2b8869b7b63b2fda68435c12 F test/alterdropcol.test a653a3945f964d26845ec0cd0a8e74189f46de3119a984c5bc45457da392612e F test/alterdropcol2.test 527fce683b200d620f560f666c44ae33e22728e990a10a48a543280dfd4b4d41 F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811 F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9 F test/altermalloc2.test ca3ebc01670d9313953a2b7628d8cc00dc5ea9988f229b3cbbbe1cca506dae45 -F test/altermalloc3.test 059841a3de6b6780efd9f0b30bf1d9b4443c555f68d39975cbcac2583167b239 -F test/alterqf.test 67568ad152db8c1187b15633b801242cf960f1beafc51261a3d1725d910baeb2 +F test/altermalloc3.test 4660ac6240a8c82ba3947b927612dcc7c05a8eec3fe3c9f38e047ca69a789a33 +F test/alterqf.test 6b2482a957692606b23567ebd2cf80eb773e3c826086f5f151eee9c5a962623d F test/altertab.test a13e11cb1933575002367613b1094f0eeb31f493e4bd9ebeca73279fe00c85e7 F test/altertab2.test b0d62f323ca5dab42b0bc028c52e310ebdd13e655e8fac070fe622bad7852c2b -F test/altertab3.test 2b82fa2236a3a91553d53ae5555d8e723c7eec174c41f1fa62ff497355398479 +F test/altertab3.test 5929f522fd6fd708396ad9f317d4af9ff1a93e460df85bb1d54d4499eeb94960 F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test 547bb700f903107b38611b014ca645d6b5bb819f5210d7bf39c40802aafeb7d7 F test/analyze3.test 4440c4932247adb2b4e0c838f657c19dc7af4f56859255436dc4e855f39b9324 -F test/analyze4.test 293ec8ea21525f3435beaf9d6b1efb29e719415f03a0cd2a70589d59098e8464 +F test/analyze4.test 68bd069f3ac7ac1e652ddd9f04f57d5606ddb4208450f5297005db7aa0dd707d F test/analyze5.test fa5131952303ac4146aba101b116b9c8cb89e2637531c334a6df7f7d19dddc0d F test/analyze6.test 028f5bdfc9e5b5294768fa9a7185b8cd1d019aa7aab5b2f8ee42d7271d9a3b28 F test/analyze7.test 079d17c495e396bdbd6cc6a083112788a6fbfb3b95c42e760e4270a53c9ead8f @@ -694,7 +699,7 @@ F test/attach4.test 00e754484859998d124d144de6d114d920f2ed6ca2f961e6a7f4183c714f F test/attachmalloc.test 12c4f028e570acf9e0a4b0b7fe6f536e21f3d5ebddcece423603d0569beaf438 F test/auth.test 567d917e0baddb6d0026a251cff977a3ab2c805a3cef906ba8653aafe7ad7240 F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1 -F test/auth3.test db21405b95257c24d29273b6b31d0efc59e1d337e3d5804ba2d1fd4897b1ae49 +F test/auth3.test 76d20a7fa136d63bcfcf8bcb65c0b1455ed71078d81f22bcd0550d3eb18594ab F test/autoanalyze1.test b9cc3f32a990fa56669b668d237c6d53e983554ae80c0604992e18869a0b2dec F test/autoinc.test 997d6f185f138229dc4251583a1d04816423dddc2fc034871a01aeb1d728cb39 F test/autoindex1.test fe27af92eaf884bd9c38f94be3e8afa04ec494e5eefb189902026181a6175f5e @@ -778,7 +783,7 @@ F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6 F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151ecac0b95 F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1 F test/colname.test 87ad5458bb8709312dac0d6755fd30e8e4ca83298d0a9ef6e5c24277a3c3390e -F test/columncount.test d86fb6307261186370698962790ad2088ed419e4a4e823512b502f17d443b1b7 +F test/columncount.test 6fe99c2f35738b0129357a1cf3fa483f76140f4cd8a89014c88c33c876d2638f F test/conflict.test ac0667090f66130ac77d5fb764655558ca6600dd6d88f670ca9123b61c448337 F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1 F test/conflict3.test 81865d9599609aca394fb3b9cd5f561d4729ea5b176bece3644f6ecb540f88ac @@ -803,7 +808,7 @@ F test/corruptH.test 79801d97ec5c2f9f3c87739aa1ec2eb786f96454 F test/corruptI.test a17bbf54fdde78d43cf3cc34b0057719fd4a173a3d824285b67dc5257c064c7b F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4 F test/corruptK.test 5b4212fe346699831c5ad559a62c54e11c0611bdde1ea8423a091f9c01aa32af -F test/corruptL.test df132ba9ffd6fa15038380b4154998b9904ab8f1ea78400d7da53c920cb3b13d +F test/corruptL.test 7d3440831ca24ba64305583c4d4506d417d3f89f5775c0b7cc8102db078f8ff5 F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067 F test/corruptN.test c2a96ff81386027f7d7e95858783aa36f82ba1532106969575e3c8f90903a5bb F test/cost.test b11cdbf9f11ffe8ef99c9881bf390e61fe92baf2182bad1dbe6de59a7295c576 @@ -855,12 +860,12 @@ F test/e_blobbytes.test 439a945953b35cb6948a552edaec4dc31fd70a05 F test/e_blobclose.test 4b3c8c60c2171164d472059c73e9f3c1844bb66d F test/e_blobopen.test e95e1d40f995056f6f322cd5e1a1b83a27e1a145 F test/e_blobwrite.test f87ff598b67af5b3ec002a8d83e804dc8d23808e88cf0080c176612fc9ffce14 -F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579 +F test/e_changes.test 0f8c3e6aab7335cb772d5a3ea34ca4c82f98d0eb896e2eb3add971c16984b405 F test/e_createtable.test 7997c0106c181243e0ac7db7ba8b9ae7233d0bfb0188605650322a7a02ea326e F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e F test/e_droptrigger.test 235c610f8bf8ec44513e222b9085c7e49fad65ad0c1975ac2577109dd06fd8fa F test/e_dropview.test 74e405df7fa0f762e0c9445b166fe03955856532e2bb234c372f7c51228d75e7 -F test/e_expr.test 6ba7a51ece7b3e7fc145f14f924eed25ebb5a24e7b8596c78f3838d372cf0385 +F test/e_expr.test e164550b9f8fd9c130283d1eae692dff9e2ba67f4dbd35f7325021f5d4b8851c F test/e_fkey.test 351c7b989e5aefcc339ef5fc78dc4738442bd247a392cd67d81c2881000c369e F test/e_fts3.test 17ba7c373aba4d4f5696ba147ee23fd1a1ef70782af050e03e262ca187c5ee07 F test/e_insert.test f02f7f17852b2163732c6611d193f84fc67bc641fb4882c77a464076e5eba80e @@ -868,7 +873,7 @@ F test/e_reindex.test 2b0e29344497d9a8a999453a003cb476b6b1d2eef2d6c120f83c2d3a42 F test/e_resolve.test a61751c368b109db73df0f20fc75fb47e166b1d8 F test/e_select.test c5425a423da06d0494119db8361ebfc6de302929f7546ca596d56224137e0360 F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f -F test/e_totalchanges.test b12ee5809d3e63aeb83238dd501a7bca7fd72c10 +F test/e_totalchanges.test c927f7499dc3aa28b9b556b7d6d115a2f0fe41f012b128d16bf1f3b30e9b41e4 F test/e_update.test f46c2554d915c9197548681e8d8c33a267e84528 F test/e_uri.test 47eeb2960e74613f0f8722b2f13aef08fde69daa16e5380ac93df84dac8b1f72 F test/e_vacuum.test 0d8832a2ce92350db0d0cff47b419465fd9772562e1f77ff7d9478c07a4980d2 @@ -1037,7 +1042,7 @@ F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f75 F test/fts4unicode.test 82a9c16b68ba2f358a856226bb2ee02f81583797bc4744061c54401bf1a0f4c9 F test/fts4upfrom.test f25835162c989dffd5e2ef91ec24c4848cc9973093e2d492d1c7b32afac1b49d F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d -F test/func.test 77f6ea02c97d9ea64074461d347276a75df22d2cf51045a40f90857569e985f0 +F test/func.test 3a65ddb6c1998f71aa86492501a6be87904197e62bfb5b70b2493552b558abd1 F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func3.test 600a632c305a88f3946d38f9a51efe145c989b2e13bd2b2a488db47fe76bab6a F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31 @@ -1059,17 +1064,17 @@ F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e4 F test/fuzzdata5.db e35f64af17ec48926481cfaf3b3855e436bd40d1cfe2d59a9474cb4b748a52a5 F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7 F test/fuzzdata7.db 0166b56fd7a6b9636a1d60ef0a060f86ddaecf99400a666bb6e5bbd7199ad1f2 -F test/fuzzdata8.db 4581978d13472885cf33c0bbebb236a69f73f1b7316678d266422f4b96cfdf2c +F test/fuzzdata8.db 6e5fc4979d39b1d93371190202eb19d023c4671edaff205842024a632e3ecc58 F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8 F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14 F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc F test/gcfault.test dd28c228a38976d6336a3fc42d7e5f1ad060cb8c -F test/gencol1.test 6912c4280d0ad26d6e3d133a93c5abd6db0e00bc5c95d6159131a62ab4e6f586 +F test/gencol1.test cc0dbb0ee116e5602e18ea7d47f2a0f76b26e09a823b7c36ef254370c2b0f3c1 F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98 F test/having.test a89236dd8d55aa50c4805f82ac9daf64d477a44d712d8209c118978d0ca21ec9 F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751 F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711 -F test/hook.test 55b6d605d06dadbb04416eae8ad79889aea3521d119c87f04353c25f9c1450a4 +F test/hook.test bb9e03b93fa4bc3bf0f164e455ddaee3603dee0501cb61119baed557c8f936be F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8 F test/icu.test 716a6b89fbabe5cc63e0cd4c260befb08fd7b9d761f04d43669233292f0753b1 F test/ieee754.test b0945d12be7d255f3dfa18e2511b17ca37e0edd2b803231c52d05b86c04ab26e @@ -1101,7 +1106,7 @@ F test/index7.test b238344318e0b4e42126717f6554f0e7dfd0b39cecad4b736039b43e1e3b6 F test/index8.test caa097735c91dbc23d8a402f5e63a2a03c83840ba3928733ed7f9a03f8a912a3 F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721 F test/indexedby.test f21eca4f7a6ffe14c8500a7ad6cd53166666c99e5ccd311842a28bc94a195fe0 -F test/indexexpr1.test 7e0e7a33acb4d9b3524398e6ce90cc05c26603fabbaf3062083a036c8874bc12 +F test/indexexpr1.test 8f7241410e351679010f14cd7cd30357622d04a784508ff54ba5ce99f64a2228 F test/indexexpr2.test 2c7abe3c48f8aaa5a448615ab4d13df3662185d28419c00999670834a3f0b484 F test/indexfault.test 98d78a8ff1f5335628b62f886a1cb7c7dac1ef6d48fa39c51ec871c87dce9811 F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 @@ -1125,7 +1130,7 @@ F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4 F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b -F test/istrue.test 9619a2d77580f676048aaff7a16a0bcfea2b96c6c660dfaded2e53c873418899 +F test/istrue.test e7f285bb70282625c258e866ce6337d4c762922f5a300e1b50f958aef6e7d9c9 F test/join.test 25da4f53523a4aa17c893134b47fba6aa4799bb33350517b157785878290e238 F test/join2.test 9bdc615841b91c97a16d68bad9508aea11fa0c6b34e5689847bcc4dac70e4990 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 @@ -1198,7 +1203,7 @@ F test/memjournal.test 70f3a00c7f84ee2978ad14e831231caa1e7f23915a2c54b4f775a021d F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 F test/memsubsys1.test 9e7555a22173b8f1c96c281ce289b338fcba2abe8b157f8798ca195bbf1d347e F test/memsubsys2.test 3e4a8d0c05fd3e5fa92017c64666730a520c7e08 -F test/minmax.test 0015e5cd5e7af48bb3364f26d9f3a9cdbea2a442d4774281c39e2229591b7351 +F test/minmax.test fe638b55d77d2375531a8f549b338eafcd9adfbd2f72df37ed77d9b26ca0a71a F test/minmax2.test cf9311babb6f0518d04e42fd6a42c619531c4309a9dd790a2c4e9b3bc595e0de F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax4.test 272ca395257f05937dc96441c9dde4bc9fbf116a8d4fa02baeb0d13d50e36c87 @@ -1257,7 +1262,7 @@ F test/oserror.test 1fc9746b83d778e70d115049747ba19c7fba154afce7cc165b09feb6ca6a F test/ossfuzz.c 9636dad2092a05a32110df0ca06713038dd0c43dd89a77dabe4b8b0d71096715 F test/ossshell.c f125c5bd16e537a2549aa579b328dd1c59905e7ab1338dfc210e755bb7b69f17 F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f -F test/pager1.test a4e438c344663ad7f0bf6e880cacae7531bdf7d960db15a3db4751273ecee06d +F test/pager1.test 50df7826a03382ff508ed80a6b3c589c3e3ececc1d15239d67126be4b7c8b64b F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71 F test/pager3.test 4e9a83d6ca0838d7c602c9eb93d1357562d9059c1e02ffb138a8271020838370 F test/pager4.test a122e9e6925d5b23b31e3dfef8c6a44bbf19590e @@ -1270,9 +1275,9 @@ F test/parser1.test 6ccdf5e459a5dc4673d3273dc311a7e9742ca952dd0551a6a6320d27035c F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442 F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff -F test/permutations.test 63da39a4234eed2ccd10bf7872de58e24d53a50d11014dc8a8ab9f252368e880 +F test/permutations.test dfdb3356ceb66e515193b12e3fd46706d28274142dc46fd37da63fda9cd4a68a F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f -F test/pragma.test 30d5bbebd5e9cb5383155cf3f3c81297b98f6642d152e9d4100cf6888630da2c +F test/pragma.test cae534c12a033a5c319ccc94f50b32811acdef9f67bf19a82ff42697caccd69f F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f F test/pragma3.test 92a46bbea12322dd94a404f49edcfbfc913a2c98115f0d030a7459bb4712ef31 F test/pragma4.test ca5e4dfc46adfe490f75d73734f70349d95a199e6510973899e502eef2c8b1f8 @@ -1289,7 +1294,7 @@ F test/quick.test 1681febc928d686362d50057c642f77a02c62e57 F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26 F test/quota.test bfb269ce81ea52f593f9648316cd5013d766dd2a F test/quota2.test 7dc12e08b11cbc4c16c9ba2aa2e040ea8d8ab4b8 -F test/quote.test b8ddaba6b81dcf63bb31243219e28a2f96e04396adc50108cc7e5593019c3eb5 +F test/quote.test f33f95990e4032d1227b98c0ef314c0a077d162f3f2e61b3039ed69e6f8adbbf F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459 F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736 @@ -1299,7 +1304,7 @@ F test/regexp1.test 0c3ff80f66b0eff80e623eb5db7a3dad512095c573d78ac23009785f6d8f F test/regexp2.test 55ed41da802b0e284ac7e2fe944be3948f93ff25abbca0361a609acfed1368b5 F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d F test/releasetest.tcl 6f803ef0b896f8f3f4c26eb072c0399963a5987a509a64d45f5dfbc1ebae2951 x -F test/releasetest_data.tcl f88ed29aa18366ed3956ace36c96ec6868ef5b9ee04cc05d32f4d81031e19e28 +F test/releasetest_data.tcl 1673991f780277748a0c7524969ae9d6f7cfb21d8c790f6081ee5c0d96ea0f28 F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb F test/returning1.test f96c7245f6ac16038e802760cd90b93479369939a8a7a44e2329ee5aed28239c @@ -1341,7 +1346,7 @@ F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2 F test/scanstatus.test 9a0ed37ab6d57b50567282788fffdf832d9b16739ecc41bff9d77a8d767cf317 F test/schema.test 5dd11c96ba64744de955315d2e4f8992e447533690153b93377dffb2a5ef5431 F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5 -F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38 +F test/schema3.test 8ed4ae66e082cdd8b1b1f22d8549e1e7a0db4527a8e6ee8b6193053ee1e5c9ce F test/schema4.test 3b26c9fa916abb6dadf894137adcf41b7796f7b9 F test/schema5.test 29699b4421f183c8f0e88bd28ce7d75d13ea653e F test/schema6.test e4bd1f23d368695eb9e7b51ef6e02ca0642ea2ab4a52579959826b5e7dce1f9b @@ -1380,9 +1385,9 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69 F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 56a7358a2a05e850e9e4aa24629db9c8975e8038dbe8debd2d95be22a5f03612 -F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b -F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494 +F test/shell1.test d014dc15876233d06df1eaf5a22275653a1d391abf1ca28df5ef3ac688df72e9 +F test/shell2.test a03b835a9e7dcc1f79b471e4d62e0c85bdc4cb954a00820702603f1409553caf +F test/shell3.test 1586a163e7918775d3c25530bf84200453f30a77cc338490389114a67352f4d9 F test/shell4.test 3ed6c4b42fd695efcbc25d69ef759dbb15855ca8e52ba6c5ee076f8b435f48be F test/shell5.test 84a30b55722a95a5b72989e691c469a999ca7591e7aa00b7fabc783ea5c9a6fe F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3 @@ -1391,10 +1396,10 @@ F test/shell8.test 96be02ea0c21f05b24c1883d7b711a1fa8525a68ab7b636aacf6057876941 F test/shmlock.test 3dbf017d34ab0c60abe6a44e447d3552154bd0c87b41eaf5ceacd408dd13fda5 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5 -F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce +F test/shrink.test 9521e5e0d74c0b6192794f3de3a3e5e3190d465527ae365d96763ef753c7229c F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329 F test/skipscan1.test 1a9972e1dc15ca3887f306d3cd9a29679afb382eca0f3539f3b746f3c2ccaf68 -F test/skipscan2.test c588cb7ed947db724d300f2a0dc537dd2ad292b0f812641d8481bc0b95dd3f49 +F test/skipscan2.test b032ed3e0ba5caa4df6c43ef22c31566aac67783bc031869155989a7ccdb5bd5 F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5 F test/skipscan5.test 0672103fd2c8f96bd114133f356192b35ece45c794fe3677e1d9e5e3104a608e F test/skipscan6.test bddbb35dd335e2d21b7791a61e3b2e1f3255dc307ce80aa6fe19cc298e6feb13 @@ -1411,7 +1416,7 @@ F test/sort2.test cc23b7c19d684657559e8a55b02f7fcee03851d0 F test/sort3.test 1480ed7c4c157682542224e05e3b75faf4a149e5 F test/sort4.test 5c34d9623a4ae5921d956dfa2b70e77ed0fc6e5c F test/sort5.test 6b43ae0e2169b5ceed441844492e55ba7f1ae0790528395ddf7888ab3094525d -F test/sorterref.test a13ed207a0eea3c7898f308f979bfb518f68c598ec737d2c494dfd3deaa83506 +F test/sorterref.test 9a606c86a4c682db5eeaaefa0565b52102778db53e48ca7101cd4f9ebcc0ad94 F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66 F test/speed1.test f2974a91d79f58507ada01864c0e323093065452 F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb @@ -1431,9 +1436,11 @@ F test/sqllimits1.test 3f9030e5d35375ad3b912b4908094aa806335c8e9d804b8ffff70c5e9 F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a F test/startup.c 1beb5ca66fcc0fce95c3444db9d1674f90fc605499a574ae2434dcfc10d22805 F test/stat.test 15a3106eddedfc882f64bc09f237b4169be4b92dd57c93031b8ff8b13af3e7c5 -F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1 +F test/statfault.test 55f86055f9cd7b2d962a621b8a04215c1cebd4eaaecde92d279442327fe648a0 F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec75 F test/stmtvtab1.test 6873dfb24f8e79cbb5b799b95c2e4349060eb7a3b811982749a84b359468e2d5 +F test/strict1.test 2e590641b1f26f9ff0db5b785df07f932988af1ab6ccab792b792ebb8405d657 +F test/strict2.test e78cedd56eb1c3e0b09b16c594dbfcb7e95bc6d85f68f0fd6501c243be28e219 F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49 F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f F test/subquery2.test 90cf944b9de8204569cf656028391e4af1ccc8c0cc02d4ef38ee3be8de1ffb12 @@ -1455,7 +1462,7 @@ F test/tabfunc01.test d6821e7042e5653104dac0c63d75eff24a2415ab1889fc68b5db7fde59 F test/table.test eb3463b7add9f16a5bb836badf118cf391b809d09fdccd1f79684600d07ec132 F test/tableapi.test ecbcc29c4ab62c1912c3717c48ea5c5e59f7d64e4a91034e6148bd2b82f177f4 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 -F test/tclsqlite.test 03b70fcd669745fe315c900389c928cdab61ee182ad6c75bd21a54d023f57bcb +F test/tclsqlite.test 207d3661a36de1c4a11affc020f6626aed2ac2693c4bfa8ddbb54e3c36ff05d7 F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08 F test/tempdb2.test 353864e96fd3ae2f70773d0ffbf8b1fe48589b02c2ec05013b540879410c3440 F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900 @@ -1463,7 +1470,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc -F test/tester.tcl 0b7957eb669371250008a5e1fef5902257905fd6d560e8834be1ac4a0620063a +F test/tester.tcl 9426f7385393fbb6ba90e12ca51d0caf65e48b6a98c0dd169676d93b8c79d34c F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -1506,7 +1513,7 @@ F test/tkt-7a31705a7e6.test 9e9c057b6a9497c8f7ba7b16871029414ccf6550e7345d9085d6 F test/tkt-7bbfb7d442.test 7b2cd79c7a17ae6750e75ec1a7846712a69c9d18 F test/tkt-80ba201079.test 105a721e6aad0ae3c5946d7615d1e4d03f6145b8 F test/tkt-80e031a00f.test 9ee36348b761bf7c14261e002b75a4c0d5a04d4c -F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c +F test/tkt-8454a207b9.test aff2e76143cfa443ddce6f7d85968a2e9b57a3deb0b881b730120740555f9e2f F test/tkt-868145d012.test a5f941107ece6a64410ca4755c6329b7eb57a356 F test/tkt-8c63ff0ec.test 258b7fc8d7e4e1cb5362c7d65c143528b9c4cbed F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5 @@ -1531,7 +1538,7 @@ F test/tkt-d11f09d36e.test d999b548fef885d1d1afa49a0e8544ecf436869d F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09 F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30 F test/tkt-f3e5abed55.test d5a0126118142d13e27f6ce9f4c47096e9321c00 -F test/tkt-f67b41381a.test a23bc124c981662db712167bacd0ed8ad11abac9 +F test/tkt-f67b41381a.test 9120eab5e949969a29087e01bf57ac6a52b6c06c16be41091a74c2a863ffc660 F test/tkt-f777251dc7a.test d1a8fc3eefb7a9e64d19ff24d5c8c94c34a632fb F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7 F test/tkt-f973c7ac31.test 28ef85c7f015477916795246d8286aeda39d4ead @@ -1629,7 +1636,7 @@ F test/trace3.test ae2004df24b585fed9046cc0bae4601762bc6fc4aa321d475f1350bba5047 F test/trans.test 45f6f9ab6f66a7b5744f1caac06b558f95da62501916906cf55586a896f9f439 F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76 F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94 -F test/transitive1.test 06bcfeeb2ed719abf6ae582f9f65a6b07642dd1363fa648ae9a74a35e83a825c +F test/transitive1.test d75f4c919bdbee75cb3968fb614f74f4bbb2a71823c048b2042af682f08c56df F test/trigger1.test d30cd09ae8ac365a088f09daba583cc5c0b8fc7d4e1d70809d0b4be3bf6ae2ab F test/trigger2.test 6e35bd7321c49e63d540aee980eb95dec63e1d1caca175224101045bcc80871f F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945 @@ -1667,7 +1674,7 @@ F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2 F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97 F test/unordered.test 0edaf3411d300693bca595897c5201421c6c5ec787990a1dfe2f7f60ae93f1e2 -F test/update.test e906ca7cb1dc6f52af1ea243e08f727edfa79f924c2691f2f9e72481f847310d +F test/update.test ef3ebbafeb4be5c96db831f40796e2e77ee846da5ee8b61cfedb1ff1b9e0cc23 F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3 F test/upfrom1.tcl 8859d9d437f03b44174c4524a7a734a391fd4526fcff65be08285dafc9dc9041 F test/upfrom1.test 8cb06689e99cd707d884faa16da0e8eb26ff658bb01c47ddf72fadade666e6e1 @@ -1694,7 +1701,7 @@ F test/vacuum6.test d3173a54edc81d13d99e4cf4972232b3cbb52f1d56ed48c3a939ef4e751c F test/vacuummem.test 7b42abb3208bd82dd23a7536588396f295a314f2 F test/varint.test bbce22cda8fc4d135bcc2b589574be8410614e62 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 -F test/view.test ea88361d5e9bc8eabf9f573185a16aea73a885be9b6c6a95ae84908913416a80 +F test/view.test d654fbadae82f936c2a820bbc892592085467548ff59e88acef201416e9fe48a F test/view2.test db32c8138b5b556f610b35dfddd38c5a58a292f07fda5281eedb0851b2672679 F test/vtab1.test 772c94825d455dffc5da34dcf4b648d8a23887616185fa024a472bf745e56df8 F test/vtab2.test 14d4ab26cee13ba6cf5c5601b158e4f57552d3b055cdd9406cf7f711e9c84082 @@ -1714,6 +1721,7 @@ F test/vtabF.test 1918844c7c902f6a16c8dacf1ec8f84886d6e78b F test/vtabH.test 2efb5a24b0bb50796b21eca23032cfb77abfa4b0c03938e38ce5897abac404ca F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f F test/vtabJ.test a6aef49d558af90fae10565b29501f82a95781cb4f797f2d13e2d19f9b6bc77b +F test/vtabK.test d769aa2689999f510045c219ec45dc8db5266a9b3932bf47d49e1065e4261b69 F test/vtab_alter.test 736e66fb5ec7b4fee58229aa3ada2f27ec58bc58c00edae4836890c3784c6783 F test/vtab_err.test dcc8b7b9cb67522b3fe7a272c73856829dae4ab7fdb30399aea1b6981bda2b65 F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad @@ -1769,7 +1777,7 @@ F test/whereA.test 9d1077b117f1b68d5f739d94f36956c36cf995eb87bb19b77b2e81af020ed F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6 F test/whereD.test c1c335e914e28b122e000e9310f02d2be83e1c9dbca2e29f46bd732703944d1b -F test/whereE.test 0ac7e61c6225354a980666996539da154991b4325af943a25a9079079c82fb03 +F test/whereE.test 7a727b5d5b6bc8fa4cef5206e90cc0363e55ca7f0566f6fbad0206e43170f59e F test/whereF.test 926b65519608e3f2aa28720822b9154fb5c7b13519dd78194f434a511ab3dac5 F test/whereG.test b2a479f425f7d0a432df7e842e8484560908ef703fe0fd407888ff85e7097238 F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2 @@ -1786,7 +1794,7 @@ F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2 F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972 F test/win32longpath.test 4baffc3acb2e5188a5e3a895b2b543ed09e62f7c72d713c1feebf76222fe9976 F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc -F test/window1.test 9242d8083158d17b9b0cb8c7ecfc767987a1863c0076020b3ecb197df7d71886 +F test/window1.test 778ac2a5a037ab1378ffcc6f0444da8658630e576ab23cc0af3d649fd1332e55 F test/window2.tcl 492c125fa550cda1dd3555768a2303b3effbeceee215293adf8871efc25f1476 F test/window2.test e466a88bd626d66edc3d352d7d7e1d5531e0079b549ba44efb029d1fbff9fd3c F test/window3.tcl acea6e86a4324a210fd608d06741010ca83ded9fde438341cb978c49928faf03 @@ -1801,23 +1809,24 @@ F test/window8.tcl 5e02e41d9d9a80f597063aed1a381eb19d1d0ef677a4f0df352c5365cf23f F test/window8.test 4ab16817414af0c904abe2ebdf88eb6c2b00058b84f9748c6174ff11fc45f1ed F test/window9.test 349c71eab4288a1ffc19e2f65872ec2c37e6cf8a1dda2ad300364b7450ae4836 F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be -F test/windowB.test 6e601f8178ba8ba28b2f19e74fe613815084bb4a8d2ad942defc7d42e191e521 +F test/windowB.test b67bda5645f3226790e1a360c4225241840b84adb5aa2e69bfb0b27eef3b84d9 +F test/windowC.test ecf1831b995408b03f708386b37ece7a05108faf2288c0c55cff873c100e145f F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0 F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b -F test/windowfault.test 21919e601f20b976ea2a73aa401220c89ed0e8d203c4f69476ea55bce3726496 +F test/windowfault.test 15094c1529424e62f798bc679e3fe9dfab6e8ba2f7dfe8c923b6248c31660a7c F test/windowpushd.test d8895d08870b7226f7693665bd292eb177e62ca06799184957b3ca7dc03067df F test/with1.test 7bc5abfe4c80c0cef8a90f5a66d60b9982e8ccd7350c8eb70611323a3b8e07ba -F test/with2.test bbf82609bbacc0a7a01d822022aed7b2fa702436dd3d0ecf942023564d2bba13 +F test/with2.test a1df41b987198383b9b70bf5e5fda390582e46398653858dbc6ceb24253b28df F test/with3.test ad32d13ad50661e6fa305f62a0717649c348792e7b658bf2644976227a9e0373 F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205 F test/with5.test 6248213c41fab36290b5b73aa3f937309dfba337004d9d8434c3fabc8c7d4be8 F test/with6.test 661d7e416bef6c0a2556b2c9f0c8178a5b15932bed65246abed99723a8d4e7c0 F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64 -F test/without_rowid1.test 6abc5d497f634520944dac0a89a6c240a48d2ee0f8353356a750eb70dc1db41a +F test/without_rowid1.test df3de14f1cc422d2b0f9b79969b5ef8e51c86ed87834ab35fb5139403e7f5a03 F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99 F test/without_rowid3.test 39ab0dd773eaa62e59b17093f875327630f54c4145458f6d2b053d68d4b2f67b F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a -F test/without_rowid5.test 89b1c587bd92a0590e440da33e7666bf4891572a +F test/without_rowid5.test f058a97600c09c7c8754733b9d8adc428e055f815d8926d74b59b872e20d0e2b F test/without_rowid6.test efbd7add62c59bf5ca97bf8da674e734e6a70ef979234e816166824b4d258f68 F test/without_rowid7.test d7c59a93d726b55812d620f8f284e01904a5b85f9ee9eea8f2f68571a5e8c40e F test/wordcount.c d721a4b6fae93e6e33449700bce1686bc23257c27425bc3ef1599dc912adec66 @@ -1848,8 +1857,8 @@ F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5 F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f -F tool/lemon.c a5acddd3eec6a388872aae6efc7563336348a9c45e5563642f77e8e3a50e859d -F tool/lempar.c 1d3d075da18681c67ecc66c1f171e7094e18cd2cfba6a8a1bd4f3f639d6656e1 +F tool/lemon.c dabeb66806057235fdca34d77e077a554396d8b8c2e1982dfd292b9dba3be13c +F tool/lempar.c 7d4b1c863a6c7f75f0a04bfa7000b7388898c9ee9c906adc675bc40590ad0abe F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9 F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862 F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca @@ -1858,12 +1867,12 @@ F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a19 F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8 F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x F tool/mkctimec.tcl 5ef1891ed3d0e8143ff39bad7c01ed60c2817a2fb2d9a09487f7ccad2df621e4 -F tool/mkkeywordhash.c 08b6e4d7a482a7f37a9a0032e7ba968e26624a027b6b2e9ba589be6f5e3d8c2c +F tool/mkkeywordhash.c 7b1ffdb0731938cfb52dbf57461ff9132cb350612d944530e2f6031a7f16bf69 F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a895ab33 F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef F tool/mkopcodeh.tcl 130b88697da6ec5b89b41844d955d08fb62c2552e889dec8c7bcecb28d8f50bd F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa -F tool/mkpragmatab.tcl 720adb9f91c992a2783a6ae98454d61b124c5c8a26b383322fe8a8a0d99a76a8 +F tool/mkpragmatab.tcl 650e689c71d57580972b894ec98c5803d34c49d2db850473f4287229b3091b71 F tool/mkshellc.tcl df5d249617f9cc94d5c48eb0401673eb3f31f383ecbc54e8a13ca3dd97e89450 F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 @@ -1876,7 +1885,7 @@ F tool/offsets.c 8ed2b344d33f06e71366a9b93ccedaa38c096cc1dbd4c3c26ad08c611528584 F tool/omittest.tcl 3d222272b1d840b4e3d67bff0cb743ce3f633faddadb3702b2056b726775db8f F tool/opcodesum.tcl 740ed206ba8c5040018988129abbf3089a0ccf4a F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b -F tool/replace.tcl 60f91e8dd06ab81f74d213ecbd9c9945f32ac048 +F tool/replace.tcl 937c931ad560688e85bdd6258bdc754371bb1e2732e1fb28ef441e44c9228fce F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5 F tool/run-speed-test.sh f95d19fd669b68c4c38b6b475242841d47c66076 @@ -1895,7 +1904,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd F tool/split-sqlite3c.tcl 3efcd4240b738f6bb2b5af0aea7e1e0ef9bc1c61654f645076cec883030b710c -F tool/sqldiff.c a94207d8a8b8ae20973012756362a850ba1f95bb4ed02cf950fd469eb556717c +F tool/sqldiff.c 9c639de9fa541a947ea9776435c84adf00f43945e262556e15219ef09f0d912c F tool/sqlite3_analyzer.c.in 7eeaae8b0d7577662acaabbb11107af0659d1b41bc1dfdd4d91422de27127968 F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898 F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848 @@ -1930,7 +1939,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d71adc3fd85cfc5902b7146101d030ab2fbe932ed05848a407a7422ddeeb4c43 bf9d70fc2fde06a3f132270a26d4abe321687169066aff26ad9e92757c7f9ee4 -R 69b8fa43accc454cc462f6123d0cbffd +P c8d1f17fde280acd3e98772d947a5b1e698b08d25b23bcd0731ae2d18cfbddcf 8b24c177061c38361588f419eda9b7943b72a0c6b2855b6f39272451b8a1b813 +R a5e83cfff0ecd99be280c76ff7d7a24c U drh -Z 6f252d3e5e8964418a93d64c78ab4e77 +Z c3acbdaa9ebf3d49766fe9216fcd8c82 diff --git a/manifest.uuid b/manifest.uuid index e9a7e57fbd..4323d4e342 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c8d1f17fde280acd3e98772d947a5b1e698b08d25b23bcd0731ae2d18cfbddcf \ No newline at end of file +64234c5c9a5709e7c7c1b18bf32f4374692129b353c4698010abfc98bd00bcc4 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 61e26cb0bd..53e45ae2b3 100644 --- a/src/alter.c +++ b/src/alter.c @@ -647,8 +647,7 @@ void sqlite3AlterRenameColumn( "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' " - " AND (type != 'index' OR tbl_name = %Q)" - " AND sql NOT LIKE 'create virtual%%'", + " AND (type != 'index' OR tbl_name = %Q)", zDb, zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1, pTab->zName @@ -690,7 +689,7 @@ void sqlite3AlterRenameColumn( ** the parse tree. */ struct RenameToken { - void *p; /* Parse tree element created by token t */ + const void *p; /* Parse tree element created by token t */ Token t; /* The token that created parse tree element p */ RenameToken *pNext; /* Next is a list of all RenameToken objects */ }; @@ -732,9 +731,9 @@ struct RenameCtx { ** Technically, as x no longer points into a valid object or to the byte ** following a valid object, it may not be used in comparison operations. */ -static void renameTokenCheckAll(Parse *pParse, void *pPtr){ +static void renameTokenCheckAll(Parse *pParse, const void *pPtr){ if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){ - RenameToken *p; + const RenameToken *p; u8 i = 0; for(p=pParse->pRename; p; p=p->pNext){ if( p->p ){ @@ -760,7 +759,11 @@ static void renameTokenCheckAll(Parse *pParse, void *pPtr){ ** with tail recursion in tokenExpr() routine, for a small performance ** improvement. */ -void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ +const void *sqlite3RenameTokenMap( + Parse *pParse, + const void *pPtr, + const Token *pToken +){ RenameToken *pNew; assert( pPtr || pParse->db->mallocFailed ); renameTokenCheckAll(pParse, pPtr); @@ -782,7 +785,7 @@ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ ** with parse tree element pFrom. This function remaps the associated token ** to parse tree element pTo. */ -void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){ +void sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){ RenameToken *p; renameTokenCheckAll(pParse, pTo); for(p=pParse->pRename; p; p=p->pNext){ @@ -798,7 +801,8 @@ void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){ */ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ Parse *pParse = pWalker->pParse; - sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr); + sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr); + sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab); return WRC_Continue; } @@ -828,6 +832,7 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){ memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC); + if( sNC.pParse->db->mallocFailed ) return; sqlite3WalkSelect(pWalker, p); sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols); } @@ -842,12 +847,12 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){ */ static void unmapColumnIdlistNames( Parse *pParse, - IdList *pIdList + const IdList *pIdList ){ if( pIdList ){ int ii; for(ii=0; iinId; ii++){ - sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName); + sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); } } } @@ -859,9 +864,7 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; int i; if( pParse->nErr ) return WRC_Abort; - if( p->selFlags & (SF_View|SF_CopyCte) ){ - testcase( p->selFlags & SF_View ); - testcase( p->selFlags & SF_CopyCte ); + if( NEVER(p->selFlags & (SF_View|SF_CopyCte)) ){ return WRC_Prune; } if( ALWAYS(p->pEList) ){ @@ -876,7 +879,7 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ SrcList *pSrc = p->pSrc; for(i=0; inSrc; i++){ sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); - if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort; + sqlite3WalkExpr(pWalker, pSrc->a[i].pOn); unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); } } @@ -944,7 +947,7 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){ static RenameToken *renameTokenFind( Parse *pParse, struct RenameCtx *pCtx, - void *pPtr + const void *pPtr ){ RenameToken **pp; if( NEVER(pPtr==0) ){ @@ -1063,18 +1066,18 @@ static void renameColumnParseError( static void renameColumnElistNames( Parse *pParse, RenameCtx *pCtx, - ExprList *pEList, + const ExprList *pEList, const char *zOld ){ if( pEList ){ int i; for(i=0; inExpr; i++){ - char *zName = pEList->a[i].zEName; + const char *zName = pEList->a[i].zEName; if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) && ALWAYS(zName!=0) && 0==sqlite3_stricmp(zName, zOld) ){ - renameTokenFind(pParse, pCtx, (void*)zName); + renameTokenFind(pParse, pCtx, (const void*)zName); } } } @@ -1088,15 +1091,15 @@ static void renameColumnElistNames( static void renameColumnIdlistNames( Parse *pParse, RenameCtx *pCtx, - IdList *pIdList, + const IdList *pIdList, const char *zOld ){ if( pIdList ){ int i; for(i=0; inId; i++){ - char *zName = pIdList->a[i].zName; + const char *zName = pIdList->a[i].zName; if( 0==sqlite3_stricmp(zName, zOld) ){ - renameTokenFind(pParse, pCtx, (void*)zName); + renameTokenFind(pParse, pCtx, (const void*)zName); } } } @@ -1498,7 +1501,7 @@ static void renameColumnFunc( sqlite3WalkSelect(&sWalker, pSelect); } if( rc!=SQLITE_OK ) goto renameColumnFunc_done; - }else if( ALWAYS(IsOrdinaryTable(sParse.pNewTable)) ){ + }else if( IsOrdinaryTable(sParse.pNewTable) ){ /* A regular table */ int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); FKey *pFKey; @@ -1796,7 +1799,7 @@ static void renameTableFunc( static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){ - renameTokenFind(pWalker->pParse, pWalker->u.pRename, (void*)pExpr); + renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr); } return WRC_Continue; } @@ -2066,7 +2069,7 @@ drop_column_done: ** statement. Argument pSrc contains the possibly qualified name of the ** table being edited, and token pName the name of the column to drop. */ -void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Token *pName){ +void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){ sqlite3 *db = pParse->db; /* Database handle */ Table *pTab; /* Table to modify */ int iDb; /* Index of db containing pTab in aDb[] */ diff --git a/src/analyze.c b/src/analyze.c index d7c24b1282..304b3bb739 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -434,7 +434,6 @@ static void statInit( + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); } #endif - db = sqlite3_context_db_handle(context); p = sqlite3DbMallocZero(db, n); if( p==0 ){ sqlite3_result_error_nomem(context); @@ -853,28 +852,19 @@ static void statGet( ** ** I = (K+D-1)/D */ - char *z; - int i; + sqlite3_str sStat; /* Text of the constructed "stat" line */ + int i; /* Loop counter */ - char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 ); - if( zRet==0 ){ - sqlite3_result_error_nomem(context); - return; - } - - sqlite3_snprintf(24, zRet, "%llu", + sqlite3StrAccumInit(&sStat, 0, 0, 0, (p->nKeyCol+1)*100); + sqlite3_str_appendf(&sStat, "%llu", p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); - z = zRet + sqlite3Strlen30(zRet); for(i=0; inKeyCol; i++){ u64 nDistinct = p->current.anDLt[i] + 1; u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; - sqlite3_snprintf(24, z, " %llu", iVal); - z += sqlite3Strlen30(z); + sqlite3_str_appendf(&sStat, " %llu", iVal); assert( p->current.anEq[i] ); } - assert( z[0]=='\0' && z>zRet ); - - sqlite3_result_text(context, zRet, -1, sqlite3_free); + sqlite3ResultStrAccum(context, &sStat); } #ifdef SQLITE_ENABLE_STAT4 else if( eCall==STAT_GET_ROWID ){ @@ -893,6 +883,8 @@ static void statGet( } }else{ tRowcnt *aCnt = 0; + sqlite3_str sStat; + int i; assert( p->iGetnSample ); switch( eCall ){ @@ -904,23 +896,12 @@ static void statGet( break; } } - - { - char *zRet = sqlite3MallocZero(p->nCol * 25); - if( zRet==0 ){ - sqlite3_result_error_nomem(context); - }else{ - int i; - char *z = zRet; - for(i=0; inCol; i++){ - sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]); - z += sqlite3Strlen30(z); - } - assert( z[0]=='\0' && z>zRet ); - z[-1] = '\0'; - sqlite3_result_text(context, zRet, -1, sqlite3_free); - } + sqlite3StrAccumInit(&sStat, 0, 0, 0, p->nCol*100); + for(i=0; inCol; i++){ + sqlite3_str_appendf(&sStat, "%llu ", (u64)aCnt[i]); } + if( sStat.nChar ) sStat.nChar--; + sqlite3ResultStrAccum(context, &sStat); } #endif /* SQLITE_ENABLE_STAT4 */ #ifndef SQLITE_DEBUG @@ -1841,9 +1822,12 @@ static int loadStatTbl( */ static int loadStat4(sqlite3 *db, const char *zDb){ int rc = SQLITE_OK; /* Result codes from subroutines */ + const Table *pStat4; assert( db->lookaside.bDisable ); - if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){ + if( (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 + && IsOrdinaryTable(pStat4) + ){ rc = loadStatTbl(db, "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", @@ -1880,6 +1864,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ char *zSql; int rc = SQLITE_OK; Schema *pSchema = db->aDb[iDb].pSchema; + const Table *pStat1; assert( iDb>=0 && iDbnDb ); assert( db->aDb[iDb].pBt!=0 ); @@ -1902,7 +1887,9 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ /* Load new statistics out of the sqlite_stat1 table */ sInfo.db = db; sInfo.zDatabase = db->aDb[iDb].zDbSName; - if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){ + if( (pStat1 = sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)) + && IsOrdinaryTable(pStat1) + ){ zSql = sqlite3MPrintf(db, "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ diff --git a/src/btree.c b/src/btree.c index 57c556cc26..6db35cd315 100644 --- a/src/btree.c +++ b/src/btree.c @@ -3142,7 +3142,7 @@ static int lockBtree(BtShared *pBt){ goto page1_init_failed; } - /* If the write version is set to 2, this database should be accessed + /* If the read version is set to 2, this database should be accessed ** in WAL mode. If the log is not already open, open it now. Then ** return SQLITE_OK and return without populating BtShared.pPage1. ** The caller detects this and calls this function again. This is @@ -5480,9 +5480,7 @@ int sqlite3BtreeTableMoveto( if( pCur->info.nKey==intKey ){ return SQLITE_OK; } - }else if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - }else{ + }else if( rc!=SQLITE_DONE ){ return rc; } } @@ -7327,6 +7325,7 @@ static int editPage( pData = &aData[get2byteNotZero(&aData[hdr+5])]; if( pDatapPg->aDataEnd) ) goto editpage_fail; /* Add cells to the start of the page */ if( iNewpDbPage)!=1 ){ + if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ rc = SQLITE_CORRUPT_BKPT; }else{ if( iOffset+ovflPageSize<(u32)nTotal ){ @@ -9154,7 +9153,7 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){ } }while( rc==SQLITE_OK && nOut>0 ); - if( rc==SQLITE_OK && nRem>0 ){ + if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){ Pgno pgnoNew; MemPage *pNew = 0; rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); diff --git a/src/build.c b/src/build.c index 77b1104901..6757cfb95d 100644 --- a/src/build.c +++ b/src/build.c @@ -913,10 +913,10 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){ ** are not \000 terminated and are not persistent. The returned string ** is \000 terminated and is persistent. */ -char *sqlite3NameFromToken(sqlite3 *db, Token *pName){ +char *sqlite3NameFromToken(sqlite3 *db, const Token *pName){ char *zName; if( pName ){ - zName = sqlite3DbStrNDup(db, (char*)pName->z, pName->n); + zName = sqlite3DbStrNDup(db, (const char*)pName->z, pName->n); sqlite3Dequote(zName); }else{ zName = 0; @@ -1579,7 +1579,7 @@ void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ /* If there is no type specified, columns have the default affinity ** 'BLOB' with a default size of 4 bytes. */ pCol->affinity = affinity; - pCol->eType = eType; + pCol->eCType = eType; pCol->szEst = szEst; #ifdef SQLITE_ENABLE_SORTER_REFERENCES if( affinity==SQLITE_AFF_BLOB ){ @@ -1874,7 +1874,7 @@ void sqlite3AddPrimaryKey( } if( nTerm==1 && pCol - && pCol->eType==COLTYPE_INTEGER + && pCol->eCType==COLTYPE_INTEGER && sortOrder!=SQLITE_SO_DESC ){ if( IN_RENAME_OBJECT && pList ){ @@ -2346,7 +2346,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ */ if( !db->init.imposterTable ){ for(i=0; inCol; i++){ - if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){ + if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 + && (pTab->aCol[i].notNull==OE_None) + ){ pTab->aCol[i].notNull = OE_Abort; } } @@ -2579,7 +2581,7 @@ void sqlite3EndTable( Parse *pParse, /* Parse context */ Token *pCons, /* The ',' token after the last column defn. */ Token *pEnd, /* The ')' before options in the CREATE TABLE */ - u8 tabOpts, /* Extra table options. Usually 0. */ + u32 tabOpts, /* Extra table options. Usually 0. */ Select *pSelect /* Select from a "CREATE ... AS SELECT" */ ){ Table *p; /* The new table */ @@ -2615,6 +2617,44 @@ void sqlite3EndTable( if( p->tnum==1 ) p->tabFlags |= TF_Readonly; } + /* Special processing for tables that include the STRICT keyword: + ** + ** * Do not allow custom column datatypes. Every column must have + ** a datatype that is one of INT, INTEGER, REAL, TEXT, or BLOB. + ** + ** * If a PRIMARY KEY is defined, other than the INTEGER PRIMARY KEY, + ** then all columns of the PRIMARY KEY must have a NOT NULL + ** constraint. + */ + if( tabOpts & TF_Strict ){ + int ii; + p->tabFlags |= TF_Strict; + for(ii=0; iinCol; ii++){ + Column *pCol = &p->aCol[ii]; + if( pCol->eCType==COLTYPE_CUSTOM ){ + if( pCol->colFlags & COLFLAG_HASTYPE ){ + sqlite3ErrorMsg(pParse, + "unknown datatype for %s.%s: \"%s\"", + p->zName, pCol->zCnName, sqlite3ColumnType(pCol, "") + ); + }else{ + sqlite3ErrorMsg(pParse, "missing datatype for %s.%s", + p->zName, pCol->zCnName); + } + return; + }else if( pCol->eCType==COLTYPE_ANY ){ + pCol->affinity = SQLITE_AFF_BLOB; + } + if( (pCol->colFlags & COLFLAG_PRIMKEY)!=0 + && p->iPKey!=ii + && pCol->notNull == OE_None + ){ + pCol->notNull = OE_Abort; + p->tabFlags |= TF_HasNotNull; + } + } + } + assert( (p->tabFlags & TF_HasPrimaryKey)==0 || p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 ); assert( (p->tabFlags & TF_HasPrimaryKey)!=0 @@ -4368,7 +4408,7 @@ exit_create_index: ** The list was already ordered when this routine was entered, so at this ** point at most a single index (the newly added index) will be out of ** order. So we have to reorder at most one index. */ - Index **ppFrom = &pTab->pIndex; + Index **ppFrom; Index *pThis; for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){ Index *pNext; diff --git a/src/date.c b/src/date.c index f88f544e3a..fb83ad1876 100644 --- a/src/date.c +++ b/src/date.c @@ -1009,131 +1009,100 @@ static void strftimeFunc( sqlite3_value **argv ){ DateTime x; - u64 n; size_t i,j; - char *z; sqlite3 *db; const char *zFmt; - char zBuf[100]; + sqlite3_str sRes; + + if( argc==0 ) return; zFmt = (const char*)sqlite3_value_text(argv[0]); if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; db = sqlite3_context_db_handle(context); - for(i=0, n=1; zFmt[i]; i++, n++){ - if( zFmt[i]=='%' ){ - switch( zFmt[i+1] ){ - case 'd': - case 'H': - case 'm': - case 'M': - case 'S': - case 'W': - n++; - /* fall thru */ - case 'w': - case '%': - break; - case 'f': - n += 8; - break; - case 'j': - n += 3; - break; - case 'Y': - n += 8; - break; - case 's': - case 'J': - n += 50; - break; - default: - return; /* ERROR. return a NULL */ - } - i++; - } - } - testcase( n==sizeof(zBuf)-1 ); - testcase( n==sizeof(zBuf) ); - testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); - testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ); - if( n(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlite3_result_error_toobig(context); - return; - }else{ - z = sqlite3DbMallocRawNN(db, (int)n); - if( z==0 ){ - sqlite3_result_error_nomem(context); - return; - } - } + sqlite3StrAccumInit(&sRes, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); + computeJD(&x); computeYMD_HMS(&x); for(i=j=0; zFmt[i]; i++){ - if( zFmt[i]!='%' ){ - z[j++] = zFmt[i]; - }else{ - i++; - switch( zFmt[i] ){ - case 'd': sqlite3_snprintf(3, &z[j],"%02d",x.D); j+=2; break; - case 'f': { - double s = x.s; - if( s>59.999 ) s = 59.999; - sqlite3_snprintf(7, &z[j],"%06.3f", s); - j += sqlite3Strlen30(&z[j]); - break; + if( zFmt[i]!='%' ) continue; + if( j59.999 ) s = 59.999; + sqlite3_str_appendf(&sRes, "%06.3f", s); + break; + } + case 'H': { + sqlite3_str_appendf(&sRes, "%02d", x.h); + break; + } + case 'W': /* Fall thru */ + case 'j': { + int nDay; /* Number of days since 1st day of year */ + DateTime y = x; + y.validJD = 0; + y.M = 1; + y.D = 1; + computeJD(&y); + nDay = (int)((x.iJD-y.iJD+43200000)/86400000); + if( zFmt[i]=='W' ){ + int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ + wd = (int)(((x.iJD+43200000)/86400000)%7); + sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); + }else{ + sqlite3_str_appendf(&sRes,"%03d",nDay+1); } - case 'H': sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break; - case 'W': /* Fall thru */ - case 'j': { - int nDay; /* Number of days since 1st day of year */ - DateTime y = x; - y.validJD = 0; - y.M = 1; - y.D = 1; - computeJD(&y); - nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - if( zFmt[i]=='W' ){ - int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ - wd = (int)(((x.iJD+43200000)/86400000)%7); - sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7); - j += 2; - }else{ - sqlite3_snprintf(4, &z[j],"%03d",nDay+1); - j += 3; - } - break; - } - case 'J': { - sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); - j+=sqlite3Strlen30(&z[j]); - break; - } - case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; - case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; - case 's': { - i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); - sqlite3Int64ToText(iS, &z[j]); - j += sqlite3Strlen30(&z[j]); - break; - } - case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; - case 'w': { - z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; - break; - } - case 'Y': { - sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlite3Strlen30(&z[j]); - break; - } - default: z[j++] = '%'; break; + break; + } + case 'J': { + sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); + break; + } + case 'm': { + sqlite3_str_appendf(&sRes,"%02d",x.M); + break; + } + case 'M': { + sqlite3_str_appendf(&sRes,"%02d",x.m); + break; + } + case 's': { + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); + sqlite3_str_appendf(&sRes,"%lld",iS); + break; + } + case 'S': { + sqlite3_str_appendf(&sRes,"%02d",(int)x.s); + break; + } + case 'w': { + sqlite3_str_appendchar(&sRes, 1, + (char)(((x.iJD+129600000)/86400000) % 7) + '0'); + break; + } + case 'Y': { + sqlite3_str_appendf(&sRes,"%04d",x.Y); + break; + } + case '%': { + sqlite3_str_appendchar(&sRes, 1, '%'); + break; + } + default: { + sqlite3_str_reset(&sRes); + return; } } } - z[j] = 0; - sqlite3_result_text(context, z, -1, - z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC); + if( jaPg; statClearCells(p); - sqlite3PagerUnref(p->pPg); sqlite3_free(p->zPath); memset(p, 0, sizeof(StatPage)); + p->aPg = aPg; } static void statResetCsr(StatCursor *pCsr){ int i; - sqlite3_reset(pCsr->pStmt); + /* In some circumstances, specifically if an OOM has occurred, the call + ** to sqlite3_reset() may cause the pager to be reset (emptied). It is + ** important that statClearPage() is called to free any page refs before + ** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */ for(i=0; iaPage); i++){ statClearPage(&pCsr->aPage[i]); + sqlite3_free(pCsr->aPage[i].aPg); + pCsr->aPage[i].aPg = 0; } + sqlite3_reset(pCsr->pStmt); pCsr->iPage = 0; sqlite3_free(pCsr->zPath); pCsr->zPath = 0; @@ -382,7 +397,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ int isLeaf; int szPage; - u8 *aData = sqlite3PagerGetData(p->pPg); + u8 *aData = p->aPg; u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; p->flags = aHdr[0]; @@ -453,7 +468,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ if( nPayload>(u32)nLocal ){ int j; int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); - if( iOff+nLocal>nUsable || nPayload>0x7fffffff ){ + if( iOff+nLocal+4>nUsable || nPayload>0x7fffffff ){ goto statPageIsCorrupt; } pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); @@ -512,6 +527,38 @@ static void statSizeAndOffset(StatCursor *pCsr){ } } +/* +** Load a copy of the page data for page iPg into the buffer belonging +** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK +** if successful, or an SQLite error code otherwise. +*/ +static int statGetPage( + Btree *pBt, /* Load page from this b-tree */ + u32 iPg, /* Page number to load */ + StatPage *pPg /* Load page into this object */ +){ + int pgsz = sqlite3BtreeGetPageSize(pBt); + DbPage *pDbPage = 0; + int rc; + + if( pPg->aPg==0 ){ + pPg->aPg = (u8*)sqlite3_malloc(pgsz + DBSTAT_PAGE_PADDING_BYTES); + if( pPg->aPg==0 ){ + return SQLITE_NOMEM_BKPT; + } + memset(&pPg->aPg[pgsz], 0, DBSTAT_PAGE_PADDING_BYTES); + } + + rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPg, &pDbPage, 0); + if( rc==SQLITE_OK ){ + const u8 *a = sqlite3PagerGetData(pDbPage); + memcpy(pPg->aPg, a, pgsz); + sqlite3PagerUnref(pDbPage); + } + + return rc; +} + /* ** Move a DBSTAT cursor to the next entry. Normally, the next ** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0), @@ -530,7 +577,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){ pCsr->zPath = 0; statNextRestart: - if( pCsr->aPage[0].pPg==0 ){ + if( pCsr->iPage<0 ){ /* Start measuring space on the next btree */ statResetCounts(pCsr); rc = sqlite3_step(pCsr->pStmt); @@ -542,7 +589,7 @@ statNextRestart: pCsr->isEof = 1; return sqlite3_reset(pCsr->pStmt); } - rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0); + rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]); pCsr->aPage[0].iPgno = iRoot; pCsr->aPage[0].iCell = 0; if( !pCsr->isAgg ){ @@ -593,9 +640,8 @@ statNextRestart: if( !p->iRightChildPg || p->iCell>p->nCell ){ statClearPage(p); - if( pCsr->iPage>0 ){ - pCsr->iPage--; - }else if( pCsr->isAgg ){ + pCsr->iPage--; + if( pCsr->isAgg && pCsr->iPage<0 ){ /* label-statNext-done: When computing aggregate space usage over ** an entire btree, this is the exit point from this function */ return SQLITE_OK; @@ -614,7 +660,7 @@ statNextRestart: }else{ p[1].iPgno = p->aCell[p->iCell].iChildPg; } - rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0); + rc = statGetPage(pBt, p[1].iPgno, &p[1]); pCsr->nPage++; p[1].iCell = 0; if( !pCsr->isAgg ){ @@ -744,6 +790,7 @@ static int statFilter( } if( rc==SQLITE_OK ){ + pCsr->iPage = -1; rc = statNext(pCursor); } return rc; diff --git a/src/expr.c b/src/expr.c index 34f14e3687..14fe8e5bdb 100644 --- a/src/expr.c +++ b/src/expr.c @@ -21,7 +21,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree); /* ** Return the affinity character for a single column of a table. */ -char sqlite3TableColumnAffinity(Table *pTab, int iCol){ +char sqlite3TableColumnAffinity(const Table *pTab, int iCol){ assert( iColnCol ); return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER; } @@ -92,7 +92,7 @@ char sqlite3ExprAffinity(const Expr *pExpr){ ** and the pExpr parameter is returned unchanged. */ Expr *sqlite3ExprAddCollateToken( - Parse *pParse, /* Parsing context */ + const Parse *pParse, /* Parsing context */ Expr *pExpr, /* Add the "COLLATE" clause to this expression */ const Token *pCollName, /* Name of collating sequence */ int dequote /* True to dequote pCollName */ @@ -107,7 +107,11 @@ Expr *sqlite3ExprAddCollateToken( } return pExpr; } -Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){ +Expr *sqlite3ExprAddCollateString( + const Parse *pParse, /* Parsing context */ + Expr *pExpr, /* Add the "COLLATE" clause to this expression */ + const char *zC /* The collating sequence name */ +){ Token s; assert( zC!=0 ); sqlite3TokenInit(&s, (char*)zC); @@ -409,7 +413,7 @@ static int codeCompare( ** But a TK_SELECT might be either a vector or a scalar. It is only ** considered a vector if it has two or more result columns. */ -int sqlite3ExprIsVector(Expr *pExpr){ +int sqlite3ExprIsVector(const Expr *pExpr){ return sqlite3ExprVectorSize(pExpr)>1; } @@ -419,7 +423,7 @@ int sqlite3ExprIsVector(Expr *pExpr){ ** is a sub-select, return the number of columns in the sub-select. For ** any other type of expression, return 1. */ -int sqlite3ExprVectorSize(Expr *pExpr){ +int sqlite3ExprVectorSize(const Expr *pExpr){ u8 op = pExpr->op; if( op==TK_REGISTER ) op = pExpr->op2; if( op==TK_VECTOR ){ @@ -512,9 +516,16 @@ Expr *sqlite3ExprForVectorField( pRet->pLeft = pVector; } }else{ - if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; + if( pVector->op==TK_VECTOR ){ + Expr **ppVector = &pVector->x.pList->a[iField].pExpr; + pVector = *ppVector; + if( IN_RENAME_OBJECT ){ + /* This must be a vector UPDATE inside a trigger */ + *ppVector = 0; + return pVector; + } + } pRet = sqlite3ExprDup(pParse->db, pVector, 0); - sqlite3RenameTokenRemap(pParse, pRet, pVector); } return pRet; } @@ -707,14 +718,14 @@ int sqlite3ExprCheckHeight(Parse *pParse, int nHeight){ ** to by pnHeight, the second parameter, then set *pnHeight to that ** value. */ -static void heightOfExpr(Expr *p, int *pnHeight){ +static void heightOfExpr(const Expr *p, int *pnHeight){ if( p ){ if( p->nHeight>*pnHeight ){ *pnHeight = p->nHeight; } } } -static void heightOfExprList(ExprList *p, int *pnHeight){ +static void heightOfExprList(const ExprList *p, int *pnHeight){ if( p ){ int i; for(i=0; inExpr; i++){ @@ -722,8 +733,8 @@ static void heightOfExprList(ExprList *p, int *pnHeight){ } } } -static void heightOfSelect(Select *pSelect, int *pnHeight){ - Select *p; +static void heightOfSelect(const Select *pSelect, int *pnHeight){ + const Select *p; for(p=pSelect; p; p=p->pPrior){ heightOfExpr(p->pWhere, pnHeight); heightOfExpr(p->pHaving, pnHeight); @@ -775,7 +786,7 @@ void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ ** Return the maximum height of any expression tree referenced ** by the select statement passed as an argument. */ -int sqlite3SelectExprHeight(Select *p){ +int sqlite3SelectExprHeight(const Select *p){ int nHeight = 0; heightOfSelect(p, &nHeight); return nHeight; @@ -1028,7 +1039,7 @@ Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ Expr *sqlite3ExprFunction( Parse *pParse, /* Parsing context */ ExprList *pList, /* Argument list */ - Token *pToken, /* Name of the function */ + const Token *pToken, /* Name of the function */ int eDistinct /* SF_Distinct or SF_ALL or 0 */ ){ Expr *pNew; @@ -1066,8 +1077,8 @@ Expr *sqlite3ExprFunction( */ void sqlite3ExprFunctionUsable( Parse *pParse, /* Parsing and code generating context */ - Expr *pExpr, /* The function invocation */ - FuncDef *pDef /* The function being invoked */ + const Expr *pExpr, /* The function invocation */ + const FuncDef *pDef /* The function being invoked */ ){ assert( !IN_RENAME_OBJECT ); assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); @@ -1247,7 +1258,7 @@ void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){ ** passed as the first argument. This is always one of EXPR_FULLSIZE, ** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. */ -static int exprStructSize(Expr *p){ +static int exprStructSize(const Expr *p){ if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE; if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE; return EXPR_FULLSIZE; @@ -1287,7 +1298,7 @@ static int exprStructSize(Expr *p){ ** of dupedExprStructSize() contain multiple assert() statements that attempt ** to enforce this constraint. */ -static int dupedExprStructSize(Expr *p, int flags){ +static int dupedExprStructSize(const Expr *p, int flags){ int nSize; assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ assert( EXPR_FULLSIZE<=0xfff ); @@ -1318,7 +1329,7 @@ static int dupedExprStructSize(Expr *p, int flags){ ** of the Expr structure and a copy of the Expr.u.zToken string (if that ** string is defined.) */ -static int dupedExprNodeSize(Expr *p, int flags){ +static int dupedExprNodeSize(const Expr *p, int flags){ int nByte = dupedExprStructSize(p, flags) & 0xfff; if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ nByte += sqlite3Strlen30NN(p->u.zToken)+1; @@ -1339,7 +1350,7 @@ static int dupedExprNodeSize(Expr *p, int flags){ ** and Expr.pRight variables (but not for any structures pointed to or ** descended from the Expr.x.pList or Expr.x.pSelect variables). */ -static int dupedExprSize(Expr *p, int flags){ +static int dupedExprSize(const Expr *p, int flags){ int nByte = 0; if( p ){ nByte = dupedExprNodeSize(p, flags); @@ -1358,7 +1369,7 @@ static int dupedExprSize(Expr *p, int flags){ ** if any. Before returning, *pzBuffer is set to the first byte past the ** portion of the buffer copied into by this function. */ -static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ +static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){ Expr *pNew; /* Value to return */ u8 *zAlloc; /* Memory space from which to build Expr object */ u32 staticFlag; /* EP_Static if space not obtained from malloc */ @@ -1539,13 +1550,14 @@ static void gatherSelectWindows(Select *p){ ** truncated version of the usual Expr structure that will be stored as ** part of the in-memory representation of the database schema. */ -Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){ +Expr *sqlite3ExprDup(sqlite3 *db, const Expr *p, int flags){ assert( flags==0 || flags==EXPRDUP_REDUCE ); return p ? exprDup(db, p, flags, 0) : 0; } -ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ +ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int flags){ ExprList *pNew; - struct ExprList_item *pItem, *pOldItem; + struct ExprList_item *pItem; + const struct ExprList_item *pOldItem; int i; Expr *pPriorSelectColOld = 0; Expr *pPriorSelectColNew = 0; @@ -1597,7 +1609,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ || !defined(SQLITE_OMIT_SUBQUERY) -SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ +SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ SrcList *pNew; int i; int nByte; @@ -1609,7 +1621,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ pNew->nSrc = pNew->nAlloc = p->nSrc; for(i=0; inSrc; i++){ SrcItem *pNewItem = &pNew->a[i]; - SrcItem *pOldItem = &p->a[i]; + const SrcItem *pOldItem = &p->a[i]; Table *pTab; pNewItem->pSchema = pOldItem->pSchema; pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); @@ -1641,7 +1653,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ } return pNew; } -IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){ +IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ IdList *pNew; int i; assert( db!=0 ); @@ -1665,11 +1677,11 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){ } return pNew; } -Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ +Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int flags){ Select *pRet = 0; Select *pNext = 0; Select **pp = &pRet; - Select *p; + const Select *p; assert( db!=0 ); for(p=pDup; p; p=p->pPrior){ @@ -1910,7 +1922,7 @@ void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int eNulls){ void sqlite3ExprListSetName( Parse *pParse, /* Parsing context */ ExprList *pList, /* List to which to add the span. */ - Token *pName, /* Name to be added */ + const Token *pName, /* Name to be added */ int dequote /* True to cause the name to be dequoted */ ){ assert( pList!=0 || pParse->db->mallocFailed!=0 ); @@ -1928,7 +1940,7 @@ void sqlite3ExprListSetName( ** to the token-map. */ sqlite3Dequote(pItem->zEName); if( IN_RENAME_OBJECT ){ - sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); + sqlite3RenameTokenMap(pParse, (const void*)pItem->zEName, pName); } } } @@ -2356,7 +2368,7 @@ int sqlite3ExprContainsSubquery(Expr *p){ ** in *pValue. If the expression is not an integer or if it is too big ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. */ -int sqlite3ExprIsInteger(Expr *p, int *pValue){ +int sqlite3ExprIsInteger(const Expr *p, int *pValue){ int rc = 0; if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */ @@ -2489,7 +2501,7 @@ int sqlite3IsRowid(const char *z){ ** table, then return NULL. */ #ifndef SQLITE_OMIT_SUBQUERY -static Select *isCandidateForInOpt(Expr *pX){ +static Select *isCandidateForInOpt(const Expr *pX){ Select *p; SrcList *pSrc; ExprList *pEList; @@ -2867,7 +2879,7 @@ int sqlite3FindInIndex( ** It is the responsibility of the caller to ensure that the returned ** string is eventually freed using sqlite3DbFree(). */ -static char *exprINAffinity(Parse *pParse, Expr *pExpr){ +static char *exprINAffinity(Parse *pParse, const Expr *pExpr){ Expr *pLeft = pExpr->pLeft; int nVal = sqlite3ExprVectorSize(pLeft); Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0; @@ -3866,6 +3878,7 @@ static int exprCodeInlineFunction( ** Test-only SQL functions that are only usable if enabled ** via SQLITE_TESTCTRL_INTERNAL_FUNCTIONS */ +#if !defined(SQLITE_UNTESTABLE) case INLINEFUNC_expr_compare: { /* Compare two expressions using sqlite3ExprCompare() */ assert( nFarg==2 ); @@ -3899,7 +3912,6 @@ static int exprCodeInlineFunction( break; } -#ifdef SQLITE_DEBUG case INLINEFUNC_affinity: { /* The AFFINITY() function evaluates to a string that describes ** the type affinity of the argument. This is used for testing of @@ -3913,7 +3925,7 @@ static int exprCodeInlineFunction( (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]); break; } -#endif +#endif /* !defined(SQLITE_UNTESTABLE) */ } return target; } @@ -4786,7 +4798,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); if( inReg!=target ){ u8 op; - if( ExprHasProperty(pExpr,EP_Subquery) ){ + if( ALWAYS(pExpr) && ExprHasProperty(pExpr,EP_Subquery) ){ op = OP_Copy; }else{ op = OP_SCopy; @@ -5324,7 +5336,11 @@ void sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, int dest,int jumpIfNull){ ** Otherwise, if the values are not the same or if pExpr is not a simple ** SQL value, zero is returned. */ -static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ +static int exprCompareVariable( + const Parse *pParse, + const Expr *pVar, + const Expr *pExpr +){ int res = 0; int iVar; sqlite3_value *pL, *pR = 0; @@ -5376,7 +5392,12 @@ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ ** Argument pParse should normally be NULL. If it is not NULL and pA or ** pB causes a return value of 2. */ -int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ +int sqlite3ExprCompare( + const Parse *pParse, + const Expr *pA, + const Expr *pB, + int iTab +){ u32 combinedFlags; if( pA==0 || pB==0 ){ return pB==pA ? 0 : 2; @@ -5460,7 +5481,7 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ ** Two NULL pointers are considered to be the same. But a NULL pointer ** always differs from a non-NULL pointer. */ -int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ +int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){ int i; if( pA==0 && pB==0 ) return 0; if( pA==0 || pB==0 ) return 1; @@ -5479,7 +5500,7 @@ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ ** Like sqlite3ExprCompare() except COLLATE operators at the top-level ** are ignored. */ -int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ +int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ return sqlite3ExprCompare(0, sqlite3ExprSkipCollateAndLikely(pA), sqlite3ExprSkipCollateAndLikely(pB), @@ -5493,9 +5514,9 @@ int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ ** non-NULL if pNN is not NULL */ static int exprImpliesNotNull( - Parse *pParse, /* Parsing context */ - Expr *p, /* The expression to be checked */ - Expr *pNN, /* The expression that is NOT NULL */ + const Parse *pParse,/* Parsing context */ + const Expr *p, /* The expression to be checked */ + const Expr *pNN, /* The expression that is NOT NULL */ int iTab, /* Table being evaluated */ int seenNot /* Return true only if p can be any non-NULL value */ ){ @@ -5588,7 +5609,12 @@ static int exprImpliesNotNull( ** improvement. Returning false might cause a performance reduction, but ** it will always give the correct answer and is hence always safe. */ -int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, int iTab){ +int sqlite3ExprImpliesExpr( + const Parse *pParse, + const Expr *pE1, + const Expr *pE2, + int iTab +){ if( sqlite3ExprCompare(pParse, pE1, pE2, iTab)==0 ){ return 1; } diff --git a/src/func.c b/src/func.c index f79b541718..754ffc50ee 100644 --- a/src/func.c +++ b/src/func.c @@ -571,9 +571,9 @@ static void last_insert_rowid( /* ** Implementation of the changes() SQL function. ** -** IMP: R-62073-11209 The changes() SQL function is a wrapper -** around the sqlite3_changes64() C/C++ function and hence follows the same -** rules for counting changes. +** IMP: R-32760-32347 The changes() SQL function is a wrapper +** around the sqlite3_changes64() C/C++ function and hence follows the +** same rules for counting changes. */ static void changes( sqlite3_context *context, @@ -596,8 +596,8 @@ static void total_changes( ){ sqlite3 *db = sqlite3_context_db_handle(context); UNUSED_PARAMETER2(NotUsed, NotUsed2); - /* IMP: R-52756-41993 This function was a wrapper around the - ** sqlite3_total_changes() C/C++ interface. */ + /* IMP: R-11217-42568 This function is a wrapper around the + ** sqlite3_total_changes64() C/C++ interface. */ sqlite3_result_int64(context, sqlite3_total_changes64(db)); } @@ -1715,97 +1715,163 @@ static void minMaxFinalize(sqlite3_context *context){ /* ** group_concat(EXPR, ?SEPARATOR?) +** +** The SEPARATOR goes before the EXPR string. This is tragic. The +** groupConcatInverse() implementation would have been easier if the +** SEPARATOR were appended after EXPR. And the order is undocumented, +** so we could change it, in theory. But the old behavior has been +** around for so long that we dare not, for fear of breaking something. */ +typedef struct { + StrAccum str; /* The accumulated concatenation */ +#ifndef SQLITE_OMIT_WINDOWFUNC + int nAccum; /* Number of strings presently concatenated */ + int nFirstSepLength; /* Used to detect separator length change */ + /* If pnSepLengths!=0, refs an array of inter-string separator lengths, + ** stored as actually incorporated into presently accumulated result. + ** (Hence, its slots in use number nAccum-1 between method calls.) + ** If pnSepLengths==0, nFirstSepLength is the length used throughout. + */ + int *pnSepLengths; +#endif +} GroupConcatCtx; + static void groupConcatStep( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zVal; - StrAccum *pAccum; + GroupConcatCtx *pGCC; const char *zSep; int nVal, nSep; assert( argc==1 || argc==2 ); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum)); - - if( pAccum ){ + pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); + if( pGCC ){ sqlite3 *db = sqlite3_context_db_handle(context); - int firstTerm = pAccum->mxAlloc==0; - pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; - if( !firstTerm ){ - if( argc==2 ){ - zSep = (char*)sqlite3_value_text(argv[1]); - nSep = sqlite3_value_bytes(argv[1]); - }else{ - zSep = ","; - nSep = 1; + int firstTerm = pGCC->str.mxAlloc==0; + pGCC->str.mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; + if( argc==1 ){ + if( !firstTerm ){ + sqlite3_str_appendchar(&pGCC->str, 1, ','); } - if( zSep ) sqlite3_str_append(pAccum, zSep, nSep); +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + pGCC->nFirstSepLength = 1; + } +#endif + }else if( !firstTerm ){ + zSep = (char*)sqlite3_value_text(argv[1]); + nSep = sqlite3_value_bytes(argv[1]); + if( zSep ){ + sqlite3_str_append(&pGCC->str, zSep, nSep); + } +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + nSep = 0; + } + if( nSep != pGCC->nFirstSepLength || pGCC->pnSepLengths != 0 ){ + int *pnsl = pGCC->pnSepLengths; + if( pnsl == 0 ){ + /* First separator length variation seen, start tracking them. */ + pnsl = (int*)sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int)); + if( pnsl!=0 ){ + int i = 0, nA = pGCC->nAccum-1; + while( inFirstSepLength; + } + }else{ + pnsl = (int*)sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int)); + } + if( pnsl!=0 ){ + if( ALWAYS(pGCC->nAccum>0) ){ + pnsl[pGCC->nAccum-1] = nSep; + } + pGCC->pnSepLengths = pnsl; + }else{ + sqlite3StrAccumSetError(&pGCC->str, SQLITE_NOMEM); + } + } +#endif } +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + pGCC->nFirstSepLength = sqlite3_value_bytes(argv[1]); + } + pGCC->nAccum += 1; +#endif zVal = (char*)sqlite3_value_text(argv[0]); nVal = sqlite3_value_bytes(argv[0]); - if( zVal ) sqlite3_str_append(pAccum, zVal, nVal); + if( zVal ) sqlite3_str_append(&pGCC->str, zVal, nVal); } } + #ifndef SQLITE_OMIT_WINDOWFUNC static void groupConcatInverse( sqlite3_context *context, int argc, sqlite3_value **argv ){ - int n; - StrAccum *pAccum; + GroupConcatCtx *pGCC; assert( argc==1 || argc==2 ); + (void)argc; /* Suppress unused parameter warning */ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum)); - /* pAccum is always non-NULL since groupConcatStep() will have always + pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); + /* pGCC is always non-NULL since groupConcatStep() will have always ** run frist to initialize it */ - if( ALWAYS(pAccum) ){ - n = sqlite3_value_bytes(argv[0]); - if( argc==2 ){ - n += sqlite3_value_bytes(argv[1]); + if( ALWAYS(pGCC) ){ + int nVS = sqlite3_value_bytes(argv[0]); + pGCC->nAccum -= 1; + if( pGCC->pnSepLengths!=0 ){ + assert(pGCC->nAccum >= 0); + if( pGCC->nAccum>0 ){ + nVS += *pGCC->pnSepLengths; + memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1, + (pGCC->nAccum-1)*sizeof(int)); + } }else{ - n++; + /* If removing single accumulated string, harmlessly over-do. */ + nVS += pGCC->nFirstSepLength; } - if( n>=(int)pAccum->nChar ){ - pAccum->nChar = 0; + if( nVS>=(int)pGCC->str.nChar ){ + pGCC->str.nChar = 0; }else{ - pAccum->nChar -= n; - memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar); + pGCC->str.nChar -= nVS; + memmove(pGCC->str.zText, &pGCC->str.zText[nVS], pGCC->str.nChar); + } + if( pGCC->str.nChar==0 ){ + pGCC->str.mxAlloc = 0; + sqlite3_free(pGCC->pnSepLengths); + pGCC->pnSepLengths = 0; } - if( pAccum->nChar==0 ) pAccum->mxAlloc = 0; } } #else # define groupConcatInverse 0 #endif /* SQLITE_OMIT_WINDOWFUNC */ static void groupConcatFinalize(sqlite3_context *context){ - StrAccum *pAccum; - pAccum = sqlite3_aggregate_context(context, 0); - if( pAccum ){ - if( pAccum->accError==SQLITE_TOOBIG ){ - sqlite3_result_error_toobig(context); - }else if( pAccum->accError==SQLITE_NOMEM ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1, - sqlite3_free); - } + GroupConcatCtx *pGCC + = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0); + if( pGCC ){ + sqlite3ResultStrAccum(context, &pGCC->str); +#ifndef SQLITE_OMIT_WINDOWFUNC + sqlite3_free(pGCC->pnSepLengths); +#endif } } #ifndef SQLITE_OMIT_WINDOWFUNC static void groupConcatValue(sqlite3_context *context){ - sqlite3_str *pAccum; - pAccum = (sqlite3_str*)sqlite3_aggregate_context(context, 0); - if( pAccum ){ + GroupConcatCtx *pGCC + = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0); + if( pGCC ){ + StrAccum *pAccum = &pGCC->str; if( pAccum->accError==SQLITE_TOOBIG ){ sqlite3_result_error_toobig(context); }else if( pAccum->accError==SQLITE_NOMEM ){ sqlite3_result_error_nomem(context); }else{ const char *zText = sqlite3_str_value(pAccum); - sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); + sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT); } } } @@ -2123,12 +2189,12 @@ void sqlite3RegisterBuiltinFunctions(void){ */ static FuncDef aBuiltinFunc[] = { /***** Functions only available with SQLITE_TESTCTRL_INTERNAL_FUNCTIONS *****/ +#if !defined(SQLITE_UNTESTABLE) TEST_FUNC(implies_nonnull_row, 2, INLINEFUNC_implies_nonnull_row, 0), TEST_FUNC(expr_compare, 2, INLINEFUNC_expr_compare, 0), TEST_FUNC(expr_implies_expr, 2, INLINEFUNC_expr_implies_expr, 0), -#ifdef SQLITE_DEBUG - TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0), -#endif + TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0), +#endif /* !defined(SQLITE_UNTESTABLE) */ /***** Regular functions *****/ #ifdef SQLITE_SOUNDEX FUNCTION(soundex, 1, 0, 0, soundexFunc ), diff --git a/src/global.c b/src/global.c index a1398fef5d..675cdec236 100644 --- a/src/global.c +++ b/src/global.c @@ -351,16 +351,38 @@ const char sqlite3StrBINARY[] = "BINARY"; /* ** Standard typenames. These names must match the COLTYPE_* definitions. ** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. +** +** sqlite3StdType[] The actual names of the datatypes. +** +** sqlite3StdTypeLen[] The length (in bytes) of each entry +** in sqlite3StdType[]. +** +** sqlite3StdTypeAffinity[] The affinity associated with each entry +** in sqlite3StdType[]. +** +** sqlite3StdTypeMap[] The type value (as returned from +** sqlite3_column_type() or sqlite3_value_type()) +** for each entry in sqlite3StdType[]. */ -const unsigned char sqlite3StdTypeLen[] = { 4, 3, 7, 4, 4 }; +const unsigned char sqlite3StdTypeLen[] = { 3, 4, 3, 7, 4, 4 }; const char sqlite3StdTypeAffinity[] = { + SQLITE_AFF_NUMERIC, SQLITE_AFF_BLOB, SQLITE_AFF_INTEGER, SQLITE_AFF_INTEGER, SQLITE_AFF_REAL, SQLITE_AFF_TEXT }; +const char sqlite3StdTypeMap[] = { + 0, + SQLITE_BLOB, + SQLITE_INTEGER, + SQLITE_INTEGER, + SQLITE_FLOAT, + SQLITE_TEXT +}; const char *sqlite3StdType[] = { + "ANY", "BLOB", "INT", "INTEGER", diff --git a/src/insert.c b/src/insert.c index 0692198b1e..acaafa0239 100644 --- a/src/insert.c +++ b/src/insert.c @@ -110,28 +110,68 @@ const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ } /* +** Make changes to the evolving bytecode to do affinity transformations +** of values that are about to be gathered into a row for table pTab. +** +** For ordinary (legacy, non-strict) tables: +** ----------------------------------------- +** ** Compute the affinity string for table pTab, if it has not already been ** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities. ** -** If the affinity exists (if it is not entirely SQLITE_AFF_BLOB values) and -** if iReg>0 then code an OP_Affinity opcode that will set the affinities -** for register iReg and following. Or if affinities exists and iReg==0, +** If the affinity string is empty (because it was all SQLITE_AFF_BLOB entries +** which were then optimized out) then this routine becomes a no-op. +** +** Otherwise if iReg>0 then code an OP_Affinity opcode that will set the +** affinities for register iReg and following. Or if iReg==0, ** then just set the P4 operand of the previous opcode (which should be ** an OP_MakeRecord) to the affinity string. ** ** A column affinity string has one character per column: ** -** Character Column affinity -** ------------------------------ -** 'A' BLOB -** 'B' TEXT -** 'C' NUMERIC -** 'D' INTEGER -** 'E' REAL +** Character Column affinity +** --------- --------------- +** 'A' BLOB +** 'B' TEXT +** 'C' NUMERIC +** 'D' INTEGER +** 'E' REAL +** +** For STRICT tables: +** ------------------ +** +** Generate an appropropriate OP_TypeCheck opcode that will verify the +** datatypes against the column definitions in pTab. If iReg==0, that +** means an OP_MakeRecord opcode has already been generated and should be +** the last opcode generated. The new OP_TypeCheck needs to be inserted +** before the OP_MakeRecord. The new OP_TypeCheck should use the same +** register set as the OP_MakeRecord. If iReg>0 then register iReg is +** the first of a series of registers that will form the new record. +** Apply the type checking to that array of registers. */ void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ int i, j; - char *zColAff = pTab->zColAff; + char *zColAff; + if( pTab->tabFlags & TF_Strict ){ + if( iReg==0 ){ + /* Move the previous opcode (which should be OP_MakeRecord) forward + ** by one slot and insert a new OP_TypeCheck where the current + ** OP_MakeRecord is found */ + VdbeOp *pPrev; + sqlite3VdbeAppendP4(v, pTab, P4_TABLE); + pPrev = sqlite3VdbeGetOp(v, -1); + assert( pPrev!=0 ); + assert( pPrev->opcode==OP_MakeRecord || sqlite3VdbeDb(v)->mallocFailed ); + pPrev->opcode = OP_TypeCheck; + sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3); + }else{ + /* Insert an isolated OP_Typecheck */ + sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol); + sqlite3VdbeAppendP4(v, pTab, P4_TABLE); + } + return; + } + zColAff = pTab->zColAff; if( zColAff==0 ){ sqlite3 *db = sqlite3VdbeDb(v); zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1); @@ -157,6 +197,8 @@ void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ if( iReg ){ sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i); }else{ + assert( sqlite3VdbeGetOp(v, -1)->opcode==OP_MakeRecord + || sqlite3VdbeDb(v)->mallocFailed ); sqlite3VdbeChangeP4(v, -1, zColAff, i); } } @@ -2795,6 +2837,9 @@ static int xferOptimization( if( pDest->iPKey!=pSrc->iPKey ){ return 0; /* Both tables must have the same INTEGER PRIMARY KEY */ } + if( (pDest->tabFlags & TF_Strict)!=0 && (pSrc->tabFlags & TF_Strict)==0 ){ + return 0; /* Cannot feed from a non-strict into a strict table */ + } for(i=0; inCol; i++){ Column *pDestCol = &pDest->aCol[i]; Column *pSrcCol = &pSrc->aCol[i]; diff --git a/src/main.c b/src/main.c index 12ba8641ef..23e1752fd2 100644 --- a/src/main.c +++ b/src/main.c @@ -3217,7 +3217,15 @@ static int openDatabase( db->nextAutovac = -1; db->szMmap = sqlite3GlobalConfig.szMmap; db->nextPagesize = 0; + db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */ +#ifdef SQLITE_ENABLE_SORTER_MMAP + /* Beginning with version 3.37.0, using the VFS xFetch() API to memory-map + ** the temporary files used to do external sorts (see code in vdbesort.c) + ** is disabled. It can still be used either by defining + ** SQLITE_ENABLE_SORTER_MMAP at compile time or by using the + ** SQLITE_TESTCTRL_SORTER_MMAP test-control at runtime. */ db->nMaxSorterMmap = 0x7FFFFFFF; +#endif db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_EnableView @@ -4507,9 +4515,11 @@ sqlite3_int64 sqlite3_uri_int64( ** corruption. */ const char *sqlite3_filename_database(const char *zFilename){ + if( zFilename==0 ) return 0; return databaseName(zFilename); } const char *sqlite3_filename_journal(const char *zFilename){ + if( zFilename==0 ) return 0; zFilename = databaseName(zFilename); zFilename += sqlite3Strlen30(zFilename) + 1; while( zFilename[0] ){ @@ -4523,7 +4533,7 @@ const char *sqlite3_filename_wal(const char *zFilename){ return 0; #else zFilename = sqlite3_filename_journal(zFilename); - zFilename += sqlite3Strlen30(zFilename) + 1; + if( zFilename ) zFilename += sqlite3Strlen30(zFilename) + 1; return zFilename; #endif } diff --git a/src/malloc.c b/src/malloc.c index b8a88f128c..932cecc210 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -316,7 +316,7 @@ void *sqlite3_malloc64(sqlite3_uint64 n){ ** TRUE if p is a lookaside memory allocation from db */ #ifndef SQLITE_OMIT_LOOKASIDE -static int isLookaside(sqlite3 *db, void *p){ +static int isLookaside(sqlite3 *db, const void *p){ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); } #else @@ -327,18 +327,18 @@ static int isLookaside(sqlite3 *db, void *p){ ** Return the size of a memory allocation previously obtained from ** sqlite3Malloc() or sqlite3_malloc(). */ -int sqlite3MallocSize(void *p){ +int sqlite3MallocSize(const void *p){ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - return sqlite3GlobalConfig.m.xSize(p); + return sqlite3GlobalConfig.m.xSize((void*)p); } -static int lookasideMallocSize(sqlite3 *db, void *p){ +static int lookasideMallocSize(sqlite3 *db, const void *p){ #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE return plookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL; #else return db->lookaside.szTrue; #endif } -int sqlite3DbMallocSize(sqlite3 *db, void *p){ +int sqlite3DbMallocSize(sqlite3 *db, const void *p){ assert( p!=0 ); #ifdef SQLITE_DEBUG if( db==0 || !isLookaside(db,p) ){ @@ -365,7 +365,7 @@ int sqlite3DbMallocSize(sqlite3 *db, void *p){ } } } - return sqlite3GlobalConfig.m.xSize(p); + return sqlite3GlobalConfig.m.xSize((void*)p); } sqlite3_uint64 sqlite3_msize(void *p){ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); diff --git a/src/memdb.c b/src/memdb.c index dc29a2db7f..0d970b6ca3 100644 --- a/src/memdb.c +++ b/src/memdb.c @@ -508,7 +508,7 @@ static int memdbOpen( if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFd, flags, pOutFlags); } - memset(pFile, 0, sizeof(*p)); + memset(pFile, 0, sizeof(*pFile)); szName = sqlite3Strlen30(zName); if( szName>1 && zName[0]=='/' ){ int i; diff --git a/src/os.c b/src/os.c index f1798ff96e..cf26b8ae5d 100644 --- a/src/os.c +++ b/src/os.c @@ -161,6 +161,7 @@ int sqlite3OsSectorSize(sqlite3_file *id){ return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); } int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ + if( NEVER(id->pMethods==0) ) return 0; return id->pMethods->xDeviceCharacteristics(id); } #ifndef SQLITE_OMIT_WAL diff --git a/src/pager.c b/src/pager.c index 12a78cde80..00ab38e512 100644 --- a/src/pager.c +++ b/src/pager.c @@ -679,8 +679,8 @@ struct Pager { i16 nReserve; /* Number of unused bytes at end of each page */ u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */ u32 sectorSize; /* Assumed sector size during rollback */ - int pageSize; /* Number of bytes in a page */ Pgno mxPgno; /* Maximum allowed size of the database */ + i64 pageSize; /* Number of bytes in a page */ i64 journalSizeLimit; /* Size limit for persistent journal files */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ @@ -4858,6 +4858,7 @@ int sqlite3PagerOpen( pPager->zWal = 0; } #endif + (void)pPtr; /* Suppress warning about unused pPtr value */ if( nPathname ) sqlite3DbFree(0, zPathname); pPager->pVfs = pVfs; @@ -6737,8 +6738,8 @@ int sqlite3PagerRefcount(Pager *pPager){ ** used by the pager and its associated cache. */ int sqlite3PagerMemUsed(Pager *pPager){ - int perPageSize = pPager->pageSize + pPager->nExtra + sizeof(PgHdr) - + 5*sizeof(void*); + int perPageSize = pPager->pageSize + pPager->nExtra + + (int)(sizeof(PgHdr) + 5*sizeof(void*)); return perPageSize*sqlite3PcachePagecount(pPager->pPCache) + sqlite3MallocSize(pPager) + pPager->pageSize; @@ -6932,14 +6933,14 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ } pPager->nSavepoint = nNew; - /* If this is a release of the outermost savepoint, truncate - ** the sub-journal to zero bytes in size. */ + /* Truncate the sub-journal so that it only includes the parts + ** that are still in use. */ if( op==SAVEPOINT_RELEASE ){ PagerSavepoint *pRel = &pPager->aSavepoint[nNew]; if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){ /* Only truncate if it is an in-memory sub-journal. */ if( sqlite3JournalIsInMemory(pPager->sjfd) ){ - i64 sz = (pPager->pageSize+4)*pRel->iSubRec; + i64 sz = (pPager->pageSize+4)*(i64)pRel->iSubRec; rc = sqlite3OsTruncate(pPager->sjfd, sz); assert( rc==SQLITE_OK ); } diff --git a/src/parse.y b/src/parse.y index 24c539bdb3..54bed4389a 100644 --- a/src/parse.y +++ b/src/parse.y @@ -196,16 +196,19 @@ ifnotexists(A) ::= IF NOT EXISTS. {A = 1;} temp(A) ::= TEMP. {A = pParse->db->init.busy==0;} %endif SQLITE_OMIT_TEMPDB temp(A) ::= . {A = 0;} -create_table_args ::= LP columnlist conslist_opt(X) RP(E) table_options(F). { +create_table_args ::= LP columnlist conslist_opt(X) RP(E) table_option_set(F). { sqlite3EndTable(pParse,&X,&E,F,0); } create_table_args ::= AS select(S). { sqlite3EndTable(pParse,0,0,0,S); sqlite3SelectDelete(pParse->db, S); } -%type table_options {int} -table_options(A) ::= . {A = 0;} -table_options(A) ::= WITHOUT nm(X). { +%type table_option_set {u32} +%type table_option {u32} +table_option_set(A) ::= . {A = 0;} +table_option_set(A) ::= table_option(A). +table_option_set(A) ::= table_option_set(X) COMMA table_option(Y). {A = X|Y;} +table_option(A) ::= WITHOUT nm(X). { if( X.n==5 && sqlite3_strnicmp(X.z,"rowid",5)==0 ){ A = TF_WithoutRowid | TF_NoVisibleRowid; }else{ @@ -213,6 +216,14 @@ table_options(A) ::= WITHOUT nm(X). { sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z); } } +table_option(A) ::= nm(X). { + if( X.n==6 && sqlite3_strnicmp(X.z,"strict",6)==0 ){ + A = TF_Strict; + }else{ + A = 0; + sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z); + } +} columnlist ::= columnlist COMMA columnname carglist. columnlist ::= columnname carglist. columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);} @@ -1627,7 +1638,8 @@ cmd ::= ANALYZE nm(X) dbnm(Y). {sqlite3Analyze(pParse, &X, &Y);} %endif //////////////////////// ALTER TABLE table ... //////////////////////////////// -%ifndef SQLITE_OMIT_ALTERTABLE +%ifndef SQLITE_OMIT_ALTERTABLE +%ifndef SQLITE_OMIT_VIRTUALTABLE cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). { sqlite3AlterRenameTable(pParse,X,&Z); } @@ -1651,7 +1663,8 @@ cmd ::= ALTER TABLE fullname(X) RENAME kwcolumn_opt nm(Y) TO nm(Z). { kwcolumn_opt ::= . kwcolumn_opt ::= COLUMNKW. -%endif SQLITE_OMIT_ALTERTABLE +%endif SQLITE_OMIT_VIRTUALTABLE +%endif SQLITE_OMIT_ALTERTABLE //////////////////////// CREATE VIRTUAL TABLE ... ///////////////////////////// %ifndef SQLITE_OMIT_VIRTUALTABLE diff --git a/src/pcache.c b/src/pcache.c index 36829be415..14d1e7cde0 100644 --- a/src/pcache.c +++ b/src/pcache.c @@ -243,11 +243,14 @@ static int numberOfCachePages(PCache *p){ ** suggested cache size is set to N. */ return p->szCache; }else{ + i64 n; /* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the ** number of cache pages is adjusted to be a number of pages that would ** use approximately abs(N*1024) bytes of memory based on the current ** page size. */ - return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); + n = ((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); + if( n>1000000000 ) n = 1000000000; + return (int)n; } } diff --git a/src/pcache1.c b/src/pcache1.c index 286a98d36a..a93b146894 100644 --- a/src/pcache1.c +++ b/src/pcache1.c @@ -817,15 +817,18 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ */ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){ PCache1 *pCache = (PCache1 *)p; + u32 n; + assert( nMax>=0 ); if( pCache->bPurgeable ){ PGroup *pGroup = pCache->pGroup; pcache1EnterMutex(pGroup); - if( nMax > 0xffff0000 - pGroup->nMaxPage + pCache->nMax ){ - nMax = 0xffff0000 - pGroup->nMaxPage + pCache->nMax; + n = (u32)nMax; + if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){ + n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax; } - pGroup->nMaxPage += (nMax - pCache->nMax); + pGroup->nMaxPage += (n - pCache->nMax); pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pCache->nMax = nMax; + pCache->nMax = n; pCache->n90pct = pCache->nMax*9/10; pcache1EnforceMaxPage(pCache); pcache1LeaveMutex(pGroup); diff --git a/src/pragma.c b/src/pragma.c index 3bf61b4147..d9476a066d 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1200,6 +1200,54 @@ void sqlite3Pragma( } break; + /* + ** PRAGMA table_list + ** + ** Return a single row for each table, virtual table, or view in the + ** entire schema. + ** + ** schema: Name of attached database hold this table + ** name: Name of the table itself + ** type: "table", "view", "virtual", "shadow" + ** ncol: Number of columns + ** wr: True for a WITHOUT ROWID table + ** strict: True for a STRICT table + */ + case PragTyp_TABLE_LIST: { + int ii; + pParse->nMem = 6; + sqlite3CodeVerifyNamedSchema(pParse, zDb); + for(ii=0; iinDb; ii++){ + HashElem *k; + Hash *pHash; + if( zDb && sqlite3_stricmp(zDb, db->aDb[ii].zDbSName)!=0 ) continue; + pHash = &db->aDb[ii].pSchema->tblHash; + for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k) ){ + Table *pTab = sqliteHashData(k); + const char *zType; + if( zRight && sqlite3_stricmp(zRight, pTab->zName)!=0 ) continue; + if( IsView(pTab) ){ + zType = "view"; + }else if( IsVirtual(pTab) ){ + zType = "virtual"; + }else if( pTab->tabFlags & TF_Shadow ){ + zType = "shadow"; + }else{ + zType = "table"; + } + sqlite3VdbeMultiLoad(v, 1, "sssiii", + db->aDb[ii].zDbSName, + pTab->zName, + zType, + pTab->nCol, + (pTab->tabFlags & TF_WithoutRowid)!=0, + (pTab->tabFlags & TF_Strict)!=0 + ); + } + } + } + break; + #ifdef SQLITE_DEBUG case PragTyp_STATS: { Index *pIdx; @@ -1659,6 +1707,7 @@ void sqlite3Pragma( int loopTop; int iDataCur, iIdxCur; int r1 = -1; + int bStrict; if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ if( pObjTab && pObjTab!=pTab ) continue; @@ -1680,23 +1729,48 @@ void sqlite3Pragma( /* Sanity check on record header decoding */ sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3); sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); + VdbeComment((v, "(right-most column)")); } - /* Verify that all NOT NULL columns really are NOT NULL */ + /* Verify that all NOT NULL columns really are NOT NULL. At the + ** same time verify the type of the content of STRICT tables */ + bStrict = (pTab->tabFlags & TF_Strict)!=0; for(j=0; jnCol; j++){ char *zErr; - int jmp2; + Column *pCol = pTab->aCol + j; + int doError, jmp2; if( j==pTab->iPKey ) continue; - if( pTab->aCol[j].notNull==0 ) continue; + if( pCol->notNull==0 && !bStrict ) continue; + doError = bStrict ? sqlite3VdbeMakeLabel(pParse) : 0; sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); if( sqlite3VdbeGetOp(v,-1)->opcode==OP_Column ){ sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); } - jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v); - zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, - pTab->aCol[j].zCnName); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); - integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, jmp2); + if( pCol->notNull ){ + jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v); + zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, + pCol->zCnName); + sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + if( bStrict ){ + sqlite3VdbeGoto(v, doError); + }else{ + integrityCheckResultRow(v); + } + sqlite3VdbeJumpHere(v, jmp2); + } + if( (pTab->tabFlags & TF_Strict)!=0 + && pCol->eCType!=COLTYPE_ANY + ){ + jmp2 = sqlite3VdbeAddOp3(v, OP_IsNullOrType, 3, 0, + sqlite3StdTypeMap[pCol->eCType-1]); + VdbeCoverage(v); + zErr = sqlite3MPrintf(db, "non-%s value in %s.%s", + sqlite3StdType[pCol->eCType-1], + pTab->zName, pTab->aCol[j].zCnName); + sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + sqlite3VdbeResolveLabel(v, doError); + integrityCheckResultRow(v); + sqlite3VdbeJumpHere(v, jmp2); + } } /* Verify CHECK constraints */ if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ diff --git a/src/pragma.h b/src/pragma.h index aace42174f..7bae226cca 100644 --- a/src/pragma.h +++ b/src/pragma.h @@ -43,13 +43,14 @@ #define PragTyp_SOFT_HEAP_LIMIT 35 #define PragTyp_SYNCHRONOUS 36 #define PragTyp_TABLE_INFO 37 -#define PragTyp_TEMP_STORE 38 -#define PragTyp_TEMP_STORE_DIRECTORY 39 -#define PragTyp_THREADS 40 -#define PragTyp_WAL_AUTOCHECKPOINT 41 -#define PragTyp_WAL_CHECKPOINT 42 -#define PragTyp_LOCK_STATUS 43 -#define PragTyp_STATS 44 +#define PragTyp_TABLE_LIST 38 +#define PragTyp_TEMP_STORE 39 +#define PragTyp_TEMP_STORE_DIRECTORY 40 +#define PragTyp_THREADS 41 +#define PragTyp_WAL_AUTOCHECKPOINT 42 +#define PragTyp_WAL_CHECKPOINT 43 +#define PragTyp_LOCK_STATUS 44 +#define PragTyp_STATS 45 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ @@ -89,45 +90,51 @@ static const char *const pragCName[] = { /* 13 */ "pk", /* 14 */ "hidden", /* table_info reuses 8 */ - /* 15 */ "seqno", /* Used by: index_xinfo */ - /* 16 */ "cid", - /* 17 */ "name", - /* 18 */ "desc", - /* 19 */ "coll", - /* 20 */ "key", - /* 21 */ "name", /* Used by: function_list */ - /* 22 */ "builtin", - /* 23 */ "type", - /* 24 */ "enc", - /* 25 */ "narg", - /* 26 */ "flags", - /* 27 */ "tbl", /* Used by: stats */ - /* 28 */ "idx", - /* 29 */ "wdth", - /* 30 */ "hght", - /* 31 */ "flgs", - /* 32 */ "seq", /* Used by: index_list */ - /* 33 */ "name", - /* 34 */ "unique", - /* 35 */ "origin", - /* 36 */ "partial", - /* 37 */ "table", /* Used by: foreign_key_check */ - /* 38 */ "rowid", - /* 39 */ "parent", - /* 40 */ "fkid", - /* index_info reuses 15 */ - /* 41 */ "seq", /* Used by: database_list */ - /* 42 */ "name", - /* 43 */ "file", - /* 44 */ "busy", /* Used by: wal_checkpoint */ - /* 45 */ "log", - /* 46 */ "checkpointed", - /* collation_list reuses 32 */ - /* 47 */ "database", /* Used by: lock_status */ - /* 48 */ "status", - /* 49 */ "cache_size", /* Used by: default_cache_size */ + /* 15 */ "schema", /* Used by: table_list */ + /* 16 */ "name", + /* 17 */ "type", + /* 18 */ "ncol", + /* 19 */ "wr", + /* 20 */ "strict", + /* 21 */ "seqno", /* Used by: index_xinfo */ + /* 22 */ "cid", + /* 23 */ "name", + /* 24 */ "desc", + /* 25 */ "coll", + /* 26 */ "key", + /* 27 */ "name", /* Used by: function_list */ + /* 28 */ "builtin", + /* 29 */ "type", + /* 30 */ "enc", + /* 31 */ "narg", + /* 32 */ "flags", + /* 33 */ "tbl", /* Used by: stats */ + /* 34 */ "idx", + /* 35 */ "wdth", + /* 36 */ "hght", + /* 37 */ "flgs", + /* 38 */ "seq", /* Used by: index_list */ + /* 39 */ "name", + /* 40 */ "unique", + /* 41 */ "origin", + /* 42 */ "partial", + /* 43 */ "table", /* Used by: foreign_key_check */ + /* 44 */ "rowid", + /* 45 */ "parent", + /* 46 */ "fkid", + /* index_info reuses 21 */ + /* 47 */ "seq", /* Used by: database_list */ + /* 48 */ "name", + /* 49 */ "file", + /* 50 */ "busy", /* Used by: wal_checkpoint */ + /* 51 */ "log", + /* 52 */ "checkpointed", + /* collation_list reuses 38 */ + /* 53 */ "database", /* Used by: lock_status */ + /* 54 */ "status", + /* 55 */ "cache_size", /* Used by: default_cache_size */ /* module_list pragma_list reuses 9 */ - /* 50 */ "timeout", /* Used by: busy_timeout */ + /* 56 */ "timeout", /* Used by: busy_timeout */ }; /* Definitions of all built-in pragmas */ @@ -178,7 +185,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "busy_timeout", /* ePragTyp: */ PragTyp_BUSY_TIMEOUT, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 50, 1, + /* ColNames: */ 56, 1, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "cache_size", @@ -217,7 +224,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "collation_list", /* ePragTyp: */ PragTyp_COLLATION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 32, 2, + /* ColNames: */ 38, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) @@ -252,14 +259,14 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "database_list", /* ePragTyp: */ PragTyp_DATABASE_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_OneSchema, - /* ColNames: */ 41, 3, + /* ColNames: */ 47, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) {/* zName: */ "default_cache_size", /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema, - /* ColNames: */ 49, 1, + /* ColNames: */ 55, 1, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -289,7 +296,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "foreign_key_check", /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 37, 4, + /* ColNames: */ 43, 4, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FOREIGN_KEY) @@ -332,7 +339,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "function_list", /* ePragTyp: */ PragTyp_FUNCTION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 21, 6, + /* ColNames: */ 27, 6, /* iArg: */ 0 }, #endif #endif @@ -361,17 +368,17 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "index_info", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 15, 3, + /* ColNames: */ 21, 3, /* iArg: */ 0 }, {/* zName: */ "index_list", /* ePragTyp: */ PragTyp_INDEX_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 32, 5, + /* ColNames: */ 38, 5, /* iArg: */ 0 }, {/* zName: */ "index_xinfo", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 15, 6, + /* ColNames: */ 21, 6, /* iArg: */ 1 }, #endif #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) @@ -411,7 +418,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "lock_status", /* ePragTyp: */ PragTyp_LOCK_STATUS, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 47, 2, + /* ColNames: */ 53, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -550,7 +557,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "stats", /* ePragTyp: */ PragTyp_STATS, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema, - /* ColNames: */ 27, 5, + /* ColNames: */ 33, 5, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -566,6 +573,11 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 8, 6, /* iArg: */ 0 }, + {/* zName: */ "table_list", + /* ePragTyp: */ PragTyp_TABLE_LIST, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, + /* ColNames: */ 15, 6, + /* iArg: */ 1 }, {/* zName: */ "table_xinfo", /* ePragTyp: */ PragTyp_TABLE_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, @@ -641,7 +653,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "wal_checkpoint", /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_OneSchema, - /* ColNames: */ 44, 3, + /* ColNames: */ 50, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -652,4 +664,4 @@ static const PragmaName aPragmaName[] = { /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, #endif }; -/* Number of pragmas: 67 on by default, 77 total. */ +/* Number of pragmas: 68 on by default, 78 total. */ diff --git a/src/prepare.c b/src/prepare.c index 2145cd49f5..ab047871cb 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -170,7 +170,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ } } db->init.orphanTrigger = 0; - db->init.azInit = argv; + db->init.azInit = (const char**)argv; pStmt = 0; #ifdef SQLITE_ENABLE_SHARED_SCHEMA TESTONLY(rcp = ) sqlite3LockAndPrepare(db, argv[4], -1, 0, 0, &pStmt, 0); @@ -198,6 +198,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ } } } + db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */ sqlite3_finalize(pStmt); }else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){ corruptSchema(pData, argv, 0); diff --git a/src/printf.c b/src/printf.c index f663e1b1ed..e63518450c 100644 --- a/src/printf.c +++ b/src/printf.c @@ -145,7 +145,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ /* ** Set the StrAccum object to an error mode. */ -static void setStrAccumError(StrAccum *p, u8 eError){ +void sqlite3StrAccumSetError(StrAccum *p, u8 eError){ assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); p->accError = eError; if( p->mxAlloc ) sqlite3_str_reset(p); @@ -181,12 +181,12 @@ static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){ char *z; if( pAccum->accError ) return 0; if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ - setStrAccumError(pAccum, SQLITE_TOOBIG); + sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG); return 0; } z = sqlite3DbMallocRaw(pAccum->db, n); if( z==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); + sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM); } return z; } @@ -925,7 +925,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ return 0; } if( p->mxAlloc==0 ){ - setStrAccumError(p, SQLITE_TOOBIG); + sqlite3StrAccumSetError(p, SQLITE_TOOBIG); return p->nAlloc - p->nChar - 1; }else{ char *zOld = isMalloced(p) ? p->zText : 0; @@ -938,7 +938,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ } if( szNew > p->mxAlloc ){ sqlite3_str_reset(p); - setStrAccumError(p, SQLITE_TOOBIG); + sqlite3StrAccumSetError(p, SQLITE_TOOBIG); return 0; }else{ p->nAlloc = (int)szNew; @@ -956,7 +956,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ p->printfFlags |= SQLITE_PRINTF_MALLOCED; }else{ sqlite3_str_reset(p); - setStrAccumError(p, SQLITE_NOMEM); + sqlite3StrAccumSetError(p, SQLITE_NOMEM); return 0; } } @@ -1029,7 +1029,7 @@ static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){ memcpy(zText, p->zText, p->nChar+1); p->printfFlags |= SQLITE_PRINTF_MALLOCED; }else{ - setStrAccumError(p, SQLITE_NOMEM); + sqlite3StrAccumSetError(p, SQLITE_NOMEM); } p->zText = zText; return zText; @@ -1044,6 +1044,22 @@ char *sqlite3StrAccumFinish(StrAccum *p){ return p->zText; } +/* +** Use the content of the StrAccum passed as the second argument +** as the result of an SQL function. +*/ +void sqlite3ResultStrAccum(sqlite3_context *pCtx, StrAccum *p){ + if( p->accError ){ + sqlite3_result_error_code(pCtx, p->accError); + sqlite3_str_reset(p); + }else if( isMalloced(p) ){ + sqlite3_result_text(pCtx, p->zText, p->nChar, SQLITE_DYNAMIC); + }else{ + sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); + sqlite3_str_reset(p); + } +} + /* ** This singleton is an sqlite3_str object that is returned if ** sqlite3_malloc() fails to provide space for a real one. This diff --git a/src/resolve.c b/src/resolve.c index 3c1311417b..f5065e1167 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1119,9 +1119,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); - sqlite3ResolveNotValid(pParse, pNC, "subqueries", - NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); - sqlite3WalkSelect(pWalker, pExpr->x.pSelect); + if( pNC->ncFlags & NC_SelfRef ){ + notValidImpl(pParse, pNC, "subqueries", pExpr); + }else{ + sqlite3WalkSelect(pWalker, pExpr->x.pSelect); + } assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); diff --git a/src/select.c b/src/select.c index 375050bdcd..a06031e02d 100644 --- a/src/select.c +++ b/src/select.c @@ -347,6 +347,9 @@ static void addWhereTerm( pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2); + assert( pE2!=0 || pEq==0 ); /* Due to db->mallocFailed test + ** in sqlite3DbMallocRawNN() called from + ** sqlite3PExpr(). */ if( pEq && isOuterJoin ){ ExprSetProperty(pEq, EP_FromJoin); assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); @@ -2193,7 +2196,7 @@ void sqlite3SelectAddColumnTypeAndCollation( } if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff; pColl = sqlite3ExprCollSeq(pParse, p); - if( pColl && (pCol->colFlags & COLFLAG_HASCOLL)==0 ){ + if( pColl ){ assert( pTab->pIndex==0 ); sqlite3ColumnSetColl(db, pCol, pColl->zName); } @@ -2431,7 +2434,7 @@ static void generateWithRecursiveQuery( SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */ int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ - Select *pSetup = p->pPrior; /* The setup query */ + Select *pSetup; /* The setup query */ Select *pFirstRec; /* Left-most recursive term */ int addrTop; /* Top of the loop */ int addrCont, addrBreak; /* CONTINUE and BREAK addresses */ @@ -2515,7 +2518,6 @@ static void generateWithRecursiveQuery( ** iDistinct table. pFirstRec is left pointing to the left-most ** recursive term of the CTE. */ - pFirstRec = p; for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){ if( pFirstRec->selFlags & SF_Aggregate ){ sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); @@ -3015,7 +3017,11 @@ static int multiSelect( multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; - sqlite3SelectDelete(db, pDelete); + if( pDelete ){ + sqlite3ParserAddCleanup(pParse, + (void(*)(sqlite3*,void*))sqlite3SelectDelete, + pDelete); + } return rc; } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ @@ -5492,9 +5498,9 @@ static int selectExpander(Walker *pWalker, Select *p){ pTab->zName); } pFrom->pSelect = sqlite3SelectDup(db, pTab->u.view.pSelect, 0); - }else + } #ifndef SQLITE_OMIT_VIRTUALTABLE - if( ALWAYS(IsVirtual(pTab)) + else if( ALWAYS(IsVirtual(pTab)) && pFrom->fg.fromDDL && ALWAYS(pTab->u.vtab.p!=0) && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) @@ -6583,7 +6589,8 @@ int sqlite3Select( ** inside the subquery. This can help the subquery to run more efficiently. */ if( OptimizationEnabled(db, SQLITE_PushDown) - && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) + && (pItem->fg.isCte==0 + || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, (pItem->fg.jointype & JT_OUTER)!=0) ){ @@ -6644,6 +6651,7 @@ int sqlite3Select( sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e); if( pItem->iCursor!=pCteUse->iCur ){ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur); + VdbeComment((v, "%!S", pItem)); } pSub->nSelectRow = pCteUse->nRowEst; }else if( (pPrior = isSelfJoinView(pTabList, pItem))!=0 ){ diff --git a/src/shell.c.in b/src/shell.c.in index 802537e196..98b7976016 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -635,19 +635,38 @@ static int strlenChar(const char *z){ } /* -** Return true if zFile does not exist or if it is not an ordinary file. +** Return open FILE * if zFile exists, can be opened for read +** and is an ordinary file or a character stream source. +** Otherwise return 0. */ +static FILE * openChrSource(const char *zFile){ #ifdef _WIN32 -# define notNormalFile(X) 0 + struct _stat x = {0}; +# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) + /* On Windows, open first, then check the stream nature. This order + ** is necessary because _stat() and sibs, when checking a named pipe, + ** effectively break the pipe as its supplier sees it. */ + FILE *rv = fopen(zFile, "rb"); + if( rv==0 ) return 0; + if( _fstat(_fileno(rv), &x) != 0 + || !STAT_CHR_SRC(x.st_mode)){ + fclose(rv); + rv = 0; + } + return rv; #else -static int notNormalFile(const char *zFile){ - struct stat x; - int rc; - memset(&x, 0, sizeof(x)); - rc = stat(zFile, &x); - return rc || !S_ISREG(x.st_mode); -} + struct stat x = {0}; + int rc = stat(zFile, &x); +# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) + if( rc!=0 ) return 0; + if( STAT_CHR_SRC(x.st_mode) ){ + return fopen(zFile, "rb"); + }else{ + return 0; + } #endif +#undef STAT_CHR_SRC +} /* ** This routine reads a line of text from FILE in, stores @@ -1104,6 +1123,8 @@ struct ShellState { u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ + u8 bSafeMode; /* True to prohibit unsafe operations */ + u8 bSafeModePersist; /* The long-term value of bSafeMode */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ int outCount; /* Revert to stdout when reaching zero */ @@ -1155,8 +1176,9 @@ struct ShellState { int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ + char *zNonce; /* Nonce for temporary safe-mode excapes */ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ - ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ + ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ }; @@ -1201,7 +1223,7 @@ struct ShellState { #define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ #define SHFLG_CountChanges 0x00000020 /* .changes setting */ #define SHFLG_Echo 0x00000040 /* .echo or --echo setting */ -#define SHFLG_HeaderSet 0x00000080 /* .header has been used */ +#define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */ #define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */ #define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */ @@ -1293,6 +1315,27 @@ static void shellPutsFunc( sqlite3_result_value(pCtx, apVal[0]); } +/* +** If in safe mode, print an error message described by the arguments +** and exit immediately. +*/ +static void failIfSafeMode( + ShellState *p, + const char *zErrMsg, + ... +){ + if( p->bSafeMode ){ + va_list ap; + char *zMsg; + va_start(ap, zErrMsg); + zMsg = sqlite3_vmprintf(zErrMsg, ap); + va_end(ap); + raw_printf(stderr, "line %d: ", p->lineno); + utf8_printf(stderr, "%s\n", zMsg); + exit(1); + } +} + /* ** SQL function: edit(VALUE) ** edit(VALUE,EDITOR) @@ -1413,7 +1456,6 @@ static void editFunc( sqlite3_int64 i, j; if( hasCRNL ){ /* If the original contains \r\n then do no conversions back to \n */ - j = sz; }else{ /* If the file did not originally contain \r\n then convert any new ** \r\n back into \n */ @@ -1770,6 +1812,49 @@ static BOOL WINAPI ConsoleCtrlHandler( #endif #ifndef SQLITE_OMIT_AUTHORIZATION +/* +** This authorizer runs in safe mode. +*/ +static int safeModeAuth( + void *pClientData, + int op, + const char *zA1, + const char *zA2, + const char *zA3, + const char *zA4 +){ + ShellState *p = (ShellState*)pClientData; + static const char *azProhibitedFunctions[] = { + "edit", + "fts3_tokenizer", + "load_extension", + "readfile", + "writefile", + "zipfile", + "zipfile_cds", + }; + UNUSED_PARAMETER(zA2); + UNUSED_PARAMETER(zA3); + UNUSED_PARAMETER(zA4); + switch( op ){ + case SQLITE_ATTACH: { + failIfSafeMode(p, "cannot run ATTACH in safe mode"); + break; + } + case SQLITE_FUNCTION: { + int i; + for(i=0; iout, "\n"); + if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); return SQLITE_OK; } #endif @@ -3098,9 +3184,9 @@ static void exec_prepared_stmt_columnar( z = (const char*)sqlite3_column_text(pStmt,i); azData[nRow*nColumn + i] = z ? strdup(z) : 0; } - }while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ); + }while( sqlite3_step(pStmt)==SQLITE_ROW ); if( nColumn>p->nWidth ){ - p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); + p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int)); if( p->colWidth==0 ) shell_out_of_memory(); for(i=p->nWidth; icolWidth[i] = 0; p->nWidth = nColumn; @@ -3242,7 +3328,7 @@ static void exec_prepared_stmt( int nCol = sqlite3_column_count(pStmt); void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1); if( !pData ){ - rc = SQLITE_NOMEM; + shell_out_of_memory(); }else{ char **azCols = (char **)pData; /* Names of result columns */ char **azVals = &azCols[nCol]; /* Results */ @@ -3985,6 +4071,7 @@ static const char *(azHelp[]) = { " table ASCII-art table", " tabs Tab-separated values", " tcl TCL list elements", + ".nonce STRING Disable safe mode for one command if the nonce matches", ".nullvalue STRING Use STRING in place of NULL values", ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", @@ -4388,7 +4475,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ &x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]); if( rc==17 ){ k = iOffset+j; - if( k+16<=n ){ + if( k+16<=n && k>=0 ){ int ii; for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff; } @@ -4710,6 +4797,9 @@ static void open_db(ShellState *p, int openFlags){ } #endif } + if( p->bSafeModePersist && p->db!=0 ){ + sqlite3_set_authorizer(p->db, safeModeAuth, p); + } } /* @@ -5307,7 +5397,7 @@ static void tryToCloneSchema( zQuery); goto end_schema_xfer; } - while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ + while( sqlite3_step(pQuery)==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); printf("%s... ", zName); fflush(stdout); @@ -5695,8 +5785,7 @@ static void newTempFile(ShellState *p, const char *zSuffix){ p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); } if( p->zTempFile==0 ){ - raw_printf(stderr, "out of memory\n"); - exit(1); + shell_out_of_memory(); } } @@ -7704,6 +7793,8 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); if( booleanValue(azArg[1]) ){ sqlite3_set_authorizer(p->db, shellAuth, p); + }else if( p->bSafeModePersist ){ + sqlite3_set_authorizer(p->db, safeModeAuth, p); }else{ sqlite3_set_authorizer(p->db, 0, 0); } @@ -7713,6 +7804,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){ open_db(p, 0); + failIfSafeMode(p, "cannot run .archive in safe mode"); rc = arDotCommand(p, 0, azArg, nArg); }else #endif @@ -7727,6 +7819,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int j; int bAsync = 0; const char *zVfs = 0; + failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); for(j=1; jmode==MODE_Ascii ){ xRead = ascii_read_one_field; @@ -8735,7 +8831,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); i = 0; - while( sqlite3_step(pStmt)==SQLITE_ROW ){ + while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ char zLabel[20]; const char *zCol = (const char*)sqlite3_column_text(pStmt,2); i++; @@ -8880,6 +8976,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='l' && strncmp(azArg[0], "load", n)==0 ){ const char *zFile, *zProc; char *zErrMsg = 0; + failIfSafeMode(p, "cannot run .load in safe mode"); if( nArg<2 ){ raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); rc = 1; @@ -8898,6 +8995,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #endif if( c=='l' && strncmp(azArg[0], "log", n)==0 ){ + failIfSafeMode(p, "cannot run .log in safe mode"); if( nArg!=2 ){ raw_printf(stderr, "Usage: .log FILENAME\n"); rc = 1; @@ -8968,6 +9066,20 @@ static int do_meta_command(char *zLine, ShellState *p){ p->cMode = p->mode; }else + if( c=='n' && strcmp(azArg[0], "nonce")==0 ){ + if( nArg!=2 ){ + raw_printf(stderr, "Usage: .nonce NONCE\n"); + rc = 1; + }else if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){ + raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]); + exit(1); + }else{ + p->bSafeMode = 0; + return 0; /* Return immediately to bypass the safe mode reset + ** at the end of this procedure */ + } + }else + if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, @@ -9061,7 +9173,14 @@ static int do_meta_command(char *zLine, ShellState *p){ } /* If a filename is specified, try to open it first */ if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){ - if( newFlag ) shellDeleteFile(zNewFilename); + if( newFlag && !p->bSafeMode ) shellDeleteFile(zNewFilename); + if( p->bSafeMode + && p->openMode!=SHELL_OPEN_HEXDB + && zNewFilename + && strcmp(zNewFilename,":memory:")!=0 + ){ + failIfSafeMode(p, "cannot open disk-based database files in safe mode"); + } p->pAuxDb->zDbFilename = zNewFilename; open_db(p, OPEN_DB_KEEPALIVE); if( p->db==0 ){ @@ -9089,6 +9208,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int bBOM = 0; int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ + failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( c=='e' ){ eMode = 'x'; bOnce = 2; @@ -9218,7 +9338,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rx = sqlite3_prepare_v2(p->db, "SELECT key, quote(value) " "FROM temp.sqlite_parameters;", -1, &pStmt, 0); - while( sqlite3_step(pStmt)==SQLITE_ROW ){ + while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), sqlite3_column_text(pStmt,1)); } @@ -9361,6 +9481,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){ FILE *inSaved = p->in; int savedLineno = p->lineno; + failIfSafeMode(p, "cannot run .read in safe mode"); if( nArg!=2 ){ raw_printf(stderr, "Usage: .read FILE\n"); rc = 1; @@ -9381,7 +9502,7 @@ static int do_meta_command(char *zLine, ShellState *p){ pclose(p->in); } #endif - }else if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){ + }else if( (p->in = openChrSource(azArg[1]))==0 ){ utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ @@ -9399,6 +9520,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_backup *pBackup; int nTimeout = 0; + failIfSafeMode(p, "cannot run .restore in safe mode"); if( nArg==2 ){ zSrcFile = azArg[1]; zDb = "main"; @@ -9648,6 +9770,7 @@ static int do_meta_command(char *zLine, ShellState *p){ */ if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){ FILE *out = 0; + failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]); if( nCmd!=2 ) goto session_syntax_error; if( pSession->p==0 ) goto session_not_open; out = fopen(azCmd[1], "wb"); @@ -10069,6 +10192,7 @@ static int do_meta_command(char *zLine, ShellState *p){ ){ char *zCmd; int i, x; + failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( nArg<2 ){ raw_printf(stderr, "Usage: .system COMMAND\n"); rc = 1; @@ -10194,8 +10318,10 @@ static int do_meta_command(char *zLine, ShellState *p){ } } rc = sqlite3_finalize(pStmt); - appendText(&s, " ORDER BY 1", 0); - rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0); + if( rc==SQLITE_OK ){ + appendText(&s, " ORDER BY 1", 0); + rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0); + } freeText(&s); if( rc ) return shellDatabaseError(p->db); @@ -10721,7 +10847,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int j; assert( nArg<=ArraySize(azArg) ); p->nWidth = nArg-1; - p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2); + p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2); if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory(); if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth]; for(j=1; joutCount--; if( p->outCount==0 ) output_reset(p); } + p->bSafeMode = p->bSafeModePersist; return rc; } -/* -** Return TRUE if a semicolon occurs anywhere in the first N characters -** of string z[]. +/* Line scan result and intermediate states (supporting scan resumption) */ -static int line_contains_semicolon(const char *z, int N){ - int i; - for(i=0; iout, "changes: %3lld total_changes: %lld\n", + char zLineBuf[2000]; + sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, + "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); + raw_printf(p->out, "%s\n", zLineBuf); } return 0; } @@ -10870,10 +11056,10 @@ static int process_input(ShellState *p){ int nLine; /* Length of current line */ int nSql = 0; /* Bytes of zSql[] used */ int nAlloc = 0; /* Allocated zSql[] space */ - int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ int rc; /* Error code */ int errCnt = 0; /* Number of errors seen */ int startline = 0; /* Line number for start of current input */ + QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */ p->lineno = 0; while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ @@ -10889,8 +11075,17 @@ static int process_input(ShellState *p){ seenInterrupt = 0; } p->lineno++; - if( nSql==0 && _all_whitespace(zLine) ){ - if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); + if( QSS_INPLAIN(qss) + && line_is_command_terminator(zLine) + && line_is_complete(zSql, nSql) ){ + memcpy(zLine,";",2); + } + qss = quickscan(zLine, qss); + if( QSS_PLAINWHITE(qss) && nSql==0 ){ + if( ShellHasFlag(p, SHFLG_Echo) ) + printf("%s\n", zLine); + /* Just swallow single-line whitespace */ + qss = QSS_Start; continue; } if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){ @@ -10903,18 +11098,17 @@ static int process_input(ShellState *p){ errCnt++; } } + qss = QSS_Start; continue; } - if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){ - memcpy(zLine,";",2); - } + /* No single-line dispositions remain; accumulate line(s). */ nLine = strlen30(zLine); if( nSql+nLine+2>=nAlloc ){ - nAlloc = nSql+nLine+100; + /* Grow buffer by half-again increments when big. */ + nAlloc = nSql+(nSql>>1)+nLine+100; zSql = realloc(zSql, nAlloc); if( zSql==0 ) shell_out_of_memory(); } - nSqlPrior = nSql; if( nSql==0 ){ int i; for(i=0; zLine[i] && IsSpace(zLine[i]); i++){} @@ -10927,8 +11121,7 @@ static int process_input(ShellState *p){ memcpy(zSql+nSql, zLine, nLine+1); nSql += nLine; } - if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) - && sqlite3_complete(zSql) ){ + if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){ errCnt += runOneSqlLine(p, zSql, p->in, startline); nSql = 0; if( p->outCount ){ @@ -10937,12 +11130,15 @@ static int process_input(ShellState *p){ }else{ clearTempFile(p); } - }else if( nSql && _all_whitespace(zSql) ){ + p->bSafeMode = p->bSafeModePersist; + qss = QSS_Start; + }else if( nSql && QSS_PLAINWHITE(qss) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql); nSql = 0; + qss = QSS_Start; } } - if( nSql && !_all_whitespace(zSql) ){ + if( nSql && QSS_PLAINDARK(qss) ){ errCnt += runOneSqlLine(p, zSql, p->in, startline); } free(zSql); @@ -11103,10 +11299,12 @@ static const char zOptions[] = #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nofollow refuse to open symbolic links to database files\n" + " -nonce STRING set the safe-mode escape nonce\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" + " -safe enable safe-mode\n" " -separator SEP set output column separator. Default: '|'\n" #ifdef SQLITE_ENABLE_SORTER_REFERENCES " -sorterref SIZE sorter references threshold size\n" @@ -11454,6 +11652,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ sqlite3MemTraceActivate(stderr); }else if( strcmp(z,"-bail")==0 ){ bail_on_error = 1; + }else if( strcmp(z,"-nonce")==0 ){ + free(data.zNonce); + data.zNonce = strdup(argv[++i]); + }else if( strcmp(z,"-safe")==0 ){ + /* no-op - catch this on the second pass */ } } verify_uninitialized(); @@ -11582,8 +11785,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; - }else if( strcmp(z,"-noheader")==0 ){ + ShellSetFlag(&data, SHFLG_HeaderSet); + }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; + ShellSetFlag(&data, SHFLG_HeaderSet); }else if( strcmp(z,"-echo")==0 ){ ShellSetFlag(&data, SHFLG_Echo); }else if( strcmp(z,"-eqp")==0 ){ @@ -11618,6 +11823,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ i+=2; }else if( strcmp(z,"-threadsafe")==0 ){ i+=2; + }else if( strcmp(z,"-nonce")==0 ){ + i += 2; }else if( strcmp(z,"-mmap")==0 ){ i++; }else if( strcmp(z,"-memtrace")==0 ){ @@ -11676,6 +11883,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ readStdin = 0; break; #endif + }else if( strcmp(z,"-safe")==0 ){ + data.bSafeMode = data.bSafeModePersist = 1; }else{ utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); raw_printf(stderr,"Use -help for a list of options.\n"); @@ -11778,6 +11987,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ free(argvToFree); #endif free(data.colWidth); + free(data.zNonce); /* Clear the global data structure so that valgrind will detect memory ** leaks */ memset(&data, 0, sizeof(data)); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 6705370fae..e93c414f1f 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -561,6 +561,7 @@ int sqlite3_exec( #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) +#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 5cbb242132..f18b07a0fc 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -578,6 +578,13 @@ # undef SQLITE_ENABLE_EXPLAIN_COMMENTS #endif +/* +** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE +*/ +#if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) +# define SQLITE_OMIT_ALTERTABLE +#endif + /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() @@ -1554,7 +1561,7 @@ struct sqlite3 { unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ unsigned imposterTable : 1; /* Building an imposter table */ unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ - char **azInit; /* "type", "name", and "tbl_name" columns */ + const char **azInit; /* "type", "name", and "tbl_name" columns */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ @@ -1782,12 +1789,12 @@ struct sqlite3 { ** The numbers are randomly selected such that a minimum of three bits must ** change to convert any number to another or to zero */ -#define SQLITE_STATE_OPEN 0x3b /* Database is open */ -#define SQLITE_STATE_CLOSED 0x63 /* Database is closed */ -#define SQLITE_STATE_SICK 0x77 /* Error and awaiting close */ -#define SQLITE_STATE_BUSY 0x7d /* Database currently in use */ -#define SQLITE_STATE_ERROR 0xb5 /* An SQLITE_MISUSE error occurred */ -#define SQLITE_STATE_ZOMBIE 0xe5 /* Close with last statement close */ +#define SQLITE_STATE_OPEN 0x76 /* Database is open */ +#define SQLITE_STATE_CLOSED 0xce /* Database is closed */ +#define SQLITE_STATE_SICK 0xba /* Error and awaiting close */ +#define SQLITE_STATE_BUSY 0x6d /* Database currently in use */ +#define SQLITE_STATE_ERROR 0xd5 /* An SQLITE_MISUSE error occurred */ +#define SQLITE_STATE_ZOMBIE 0xa7 /* Close with last statement close */ /* ** Each SQL function is defined by an instance of the following @@ -2056,7 +2063,7 @@ struct Module { struct Column { char *zCnName; /* Name of this column */ unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */ - unsigned eType :4; /* One of the standard types */ + unsigned eCType :4; /* One of the standard types */ char affinity; /* One of the SQLITE_AFF_... values */ u8 szEst; /* Est size of value in this column. sizeof(INT)==1 */ u8 hName; /* Column name hash for faster lookup */ @@ -2064,7 +2071,7 @@ struct Column { u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ }; -/* Allowed values for Column.eType. +/* Allowed values for Column.eCType. ** ** Values must match entries in the global constant arrays ** sqlite3StdTypeLen[] and sqlite3StdType[]. Each value is one more @@ -2072,12 +2079,13 @@ struct Column { ** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. */ #define COLTYPE_CUSTOM 0 /* Type appended to zName */ -#define COLTYPE_BLOB 1 -#define COLTYPE_INT 2 -#define COLTYPE_INTEGER 3 -#define COLTYPE_REAL 4 -#define COLTYPE_TEXT 5 -#define SQLITE_N_STDTYPE 5 /* Number of standard types */ +#define COLTYPE_ANY 1 +#define COLTYPE_BLOB 2 +#define COLTYPE_INT 3 +#define COLTYPE_INTEGER 4 +#define COLTYPE_REAL 5 +#define COLTYPE_TEXT 6 +#define SQLITE_N_STDTYPE 6 /* Number of standard types */ /* Allowed values for Column.colFlags. ** @@ -2304,6 +2312,7 @@ struct Table { #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ #define TF_Ephemeral 0x00004000 /* An ephemeral table */ #define TF_Eponymous 0x00008000 /* An eponymous virtual table */ +#define TF_Strict 0x00010000 /* STRICT mode */ /* ** Allowed values for Table.eTabType @@ -4131,7 +4140,7 @@ void sqlite3WindowListDelete(sqlite3 *db, Window *p); Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); void sqlite3WindowAttach(Parse*, Expr*, Window*); void sqlite3WindowLink(Select *pSel, Window *pWin); -int sqlite3WindowCompare(Parse*, Window*, Window*, int); +int sqlite3WindowCompare(const Parse*, const Window*, const Window*, int); void sqlite3WindowCodeInit(Parse*, Select*); void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int); int sqlite3WindowRewrite(Parse*, Select*); @@ -4263,8 +4272,8 @@ void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64); void *sqlite3DbRealloc(sqlite3 *, void *, u64); void sqlite3DbFree(sqlite3*, void*); void sqlite3DbFreeNN(sqlite3*, void*); -int sqlite3MallocSize(void*); -int sqlite3DbMallocSize(sqlite3*, void*); +int sqlite3MallocSize(const void*); +int sqlite3DbMallocSize(sqlite3*, const void*); void *sqlite3PageMalloc(int); void sqlite3PageFree(void*); void sqlite3MemSetDefault(void); @@ -4400,8 +4409,8 @@ Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); void sqlite3PExprAddSelect(Parse*, Expr*, Select*); Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*); Expr *sqlite3ExprSimplifiedAndOr(Expr*); -Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*, int); -void sqlite3ExprFunctionUsable(Parse*,Expr*,FuncDef*); +Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); +void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); void sqlite3ExprDelete(sqlite3*, Expr*); void sqlite3ExprDeferredDelete(Parse*, Expr*); @@ -4410,7 +4419,7 @@ ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); Select *sqlite3ExprListToValues(Parse*, int, ExprList*); void sqlite3ExprListSetSortOrder(ExprList*,int,int); -void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); +void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); void sqlite3ExprListDelete(sqlite3*, ExprList*); u32 sqlite3ExprListFlags(const ExprList*); @@ -4458,7 +4467,7 @@ void sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*); void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*); void sqlite3AddCollateType(Parse*, Token*); void sqlite3AddGenerated(Parse*,Expr*,Token*); -void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); +void sqlite3EndTable(Parse*,Token*,Token*,u32,Select*); void sqlite3AddReturning(Parse*,ExprList*); int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); @@ -4593,11 +4602,11 @@ void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); void sqlite3Vacuum(Parse*,Token*,Expr*); int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*); -char *sqlite3NameFromToken(sqlite3*, Token*); -int sqlite3ExprCompare(Parse*,Expr*, Expr*, int); -int sqlite3ExprCompareSkip(Expr*, Expr*, int); -int sqlite3ExprListCompare(ExprList*, ExprList*, int); -int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); +char *sqlite3NameFromToken(sqlite3*, const Token*); +int sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int); +int sqlite3ExprCompareSkip(Expr*,Expr*,int); +int sqlite3ExprListCompare(const ExprList*,const ExprList*, int); +int sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); int sqlite3ExprImpliesNonNullRow(Expr*,int); void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); @@ -4628,7 +4637,7 @@ int sqlite3ExprIsTableConstant(Expr*,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS int sqlite3ExprContainsSubquery(Expr*); #endif -int sqlite3ExprIsInteger(Expr*, int*); +int sqlite3ExprIsInteger(const Expr*, int*); int sqlite3ExprCanBeNull(const Expr*); int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); int sqlite3IsRowid(const char*); @@ -4653,11 +4662,11 @@ void sqlite3MayAbort(Parse*); void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); void sqlite3UniqueConstraint(Parse*, int, Index*); void sqlite3RowidConstraint(Parse*, int, Table*); -Expr *sqlite3ExprDup(sqlite3*,Expr*,int); -ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); -SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); -IdList *sqlite3IdListDup(sqlite3*,IdList*); -Select *sqlite3SelectDup(sqlite3*,Select*,int); +Expr *sqlite3ExprDup(sqlite3*,const Expr*,int); +ExprList *sqlite3ExprListDup(sqlite3*,const ExprList*,int); +SrcList *sqlite3SrcListDup(sqlite3*,const SrcList*,int); +IdList *sqlite3IdListDup(sqlite3*,const IdList*); +Select *sqlite3SelectDup(sqlite3*,const Select*,int); FuncDef *sqlite3FunctionSearch(int,const char*); void sqlite3InsertBuiltinFuncs(FuncDef*,int); FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); @@ -4795,7 +4804,7 @@ const char *sqlite3IndexAffinityStr(sqlite3*, Index*); void sqlite3TableAffinity(Vdbe*, Table*, int); char sqlite3CompareAffinity(const Expr *pExpr, char aff2); int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); -char sqlite3TableColumnAffinity(Table*,int); +char sqlite3TableColumnAffinity(const Table*,int); char sqlite3ExprAffinity(const Expr *pExpr); int sqlite3Atoi64(const char*, i64*, int, u8); int sqlite3DecOrHexToI64(const char*, i64*); @@ -4824,8 +4833,8 @@ void sqlite3SetTextEncoding(sqlite3 *db, u8); CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr); CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr); int sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*); -Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); -Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); +Expr *sqlite3ExprAddCollateToken(const Parse *pParse, Expr*, const Token*, int); +Expr *sqlite3ExprAddCollateString(const Parse*,Expr*,const char*); Expr *sqlite3ExprSkipCollate(Expr*); Expr *sqlite3ExprSkipCollateAndLikely(Expr*); int sqlite3CheckCollSeq(Parse *, CollSeq *); @@ -4856,13 +4865,14 @@ sqlite3_value *sqlite3ValueNew(sqlite3 *); #ifndef SQLITE_OMIT_UTF16 char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); #endif -int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); +int sqlite3ValueFromExpr(sqlite3 *, const Expr *, u8, u8, sqlite3_value **); void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION extern const unsigned char sqlite3OpcodeProperty[]; extern const char sqlite3StrBINARY[]; extern const unsigned char sqlite3StdTypeLen[]; extern const char sqlite3StdTypeAffinity[]; +extern const char sqlite3StdTypeMap[]; extern const char *sqlite3StdType[]; extern const unsigned char sqlite3UpperToLower[]; extern const unsigned char *sqlite3aLTb; @@ -4907,9 +4917,9 @@ int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); void sqlite3ColumnDefault(Vdbe *, Table *, int, int); void sqlite3AlterFinishAddColumn(Parse *, Token *); void sqlite3AlterBeginAddColumn(Parse *, SrcList *); -void sqlite3AlterDropColumn(Parse*, SrcList*, Token*); -void *sqlite3RenameTokenMap(Parse*, void*, Token*); -void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom); +void sqlite3AlterDropColumn(Parse*, SrcList*, const Token*); +const void *sqlite3RenameTokenMap(Parse*, const void*, const Token*); +void sqlite3RenameTokenRemap(Parse*, const void *pTo, const void *pFrom); void sqlite3RenameExprUnmap(Parse*, Expr*); void sqlite3RenameExprlistUnmap(Parse*, ExprList*); CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); @@ -4976,6 +4986,8 @@ int sqlite3OpenTempDatabase(Parse *); void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); char *sqlite3StrAccumFinish(StrAccum*); +void sqlite3StrAccumSetError(StrAccum*, u8); +void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*); void sqlite3SelectDestInit(SelectDest*,int,int); Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); @@ -5028,7 +5040,7 @@ void sqlite3AutoLoadExtensions(sqlite3*); #endif #ifdef SQLITE_OMIT_VIRTUALTABLE -# define sqlite3VtabClear(Y) +# define sqlite3VtabClear(D,T) # define sqlite3VtabSync(X,Y) SQLITE_OK # define sqlite3VtabRollback(X) # define sqlite3VtabCommit(X) @@ -5207,7 +5219,7 @@ void sqlite3MemJournalOpen(sqlite3_file *); void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); #if SQLITE_MAX_EXPR_DEPTH>0 - int sqlite3SelectExprHeight(Select *); + int sqlite3SelectExprHeight(const Select *); int sqlite3ExprCheckHeight(Parse*, int); #else #define sqlite3SelectExprHeight(x) 0 @@ -5304,8 +5316,8 @@ int sqlite3DbpageRegister(sqlite3*); int sqlite3DbstatRegister(sqlite3*); #endif -int sqlite3ExprVectorSize(Expr *pExpr); -int sqlite3ExprIsVector(Expr *pExpr); +int sqlite3ExprVectorSize(const Expr *pExpr); +int sqlite3ExprIsVector(const Expr *pExpr); Expr *sqlite3VectorFieldSubexpr(Expr*, int); Expr *sqlite3ExprForVectorField(Parse*,Expr*,int,int); void sqlite3VectorErrorMsg(Parse*, Expr*); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index e7457bcbc1..a4ded26945 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -181,6 +181,7 @@ struct SqliteDb { int nVMStep; /* Another statistic for most recent operation */ int nTransaction; /* Number of nested [transaction] methods */ int openFlags; /* Flags used to open. (SQLITE_OPEN_URI) */ + int nRef; /* Delete object when this reaches 0 */ #ifdef SQLITE_TEST int bLegacyPrepare; /* True to use sqlite3_prepare() */ #endif @@ -517,64 +518,84 @@ static void flushStmtCache(SqliteDb *pDb){ pDb->stmtList = 0; } +/* +** Increment the reference counter on the SqliteDb object. The reference +** should be released by calling delDatabaseRef(). +*/ +static void addDatabaseRef(SqliteDb *pDb){ + pDb->nRef++; +} + +/* +** Decrement the reference counter associated with the SqliteDb object. +** If it reaches zero, delete the object. +*/ +static void delDatabaseRef(SqliteDb *pDb){ + assert( pDb->nRef>0 ); + pDb->nRef--; + if( pDb->nRef==0 ){ + flushStmtCache(pDb); + closeIncrblobChannels(pDb); + sqlite3_close(pDb->db); + while( pDb->pFunc ){ + SqlFunc *pFunc = pDb->pFunc; + pDb->pFunc = pFunc->pNext; + assert( pFunc->pDb==pDb ); + Tcl_DecrRefCount(pFunc->pScript); + Tcl_Free((char*)pFunc); + } + while( pDb->pCollate ){ + SqlCollate *pCollate = pDb->pCollate; + pDb->pCollate = pCollate->pNext; + Tcl_Free((char*)pCollate); + } + if( pDb->zBusy ){ + Tcl_Free(pDb->zBusy); + } + if( pDb->zTrace ){ + Tcl_Free(pDb->zTrace); + } + if( pDb->zTraceV2 ){ + Tcl_Free(pDb->zTraceV2); + } + if( pDb->zProfile ){ + Tcl_Free(pDb->zProfile); + } + if( pDb->zBindFallback ){ + Tcl_Free(pDb->zBindFallback); + } + if( pDb->zAuth ){ + Tcl_Free(pDb->zAuth); + } + if( pDb->zNull ){ + Tcl_Free(pDb->zNull); + } + if( pDb->pUpdateHook ){ + Tcl_DecrRefCount(pDb->pUpdateHook); + } + if( pDb->pPreUpdateHook ){ + Tcl_DecrRefCount(pDb->pPreUpdateHook); + } + if( pDb->pRollbackHook ){ + Tcl_DecrRefCount(pDb->pRollbackHook); + } + if( pDb->pWalHook ){ + Tcl_DecrRefCount(pDb->pWalHook); + } + if( pDb->pCollateNeeded ){ + Tcl_DecrRefCount(pDb->pCollateNeeded); + } + Tcl_Free((char*)pDb); + } +} + /* ** TCL calls this procedure when an sqlite3 database command is ** deleted. */ static void SQLITE_TCLAPI DbDeleteCmd(void *db){ SqliteDb *pDb = (SqliteDb*)db; - flushStmtCache(pDb); - closeIncrblobChannels(pDb); - sqlite3_close(pDb->db); - while( pDb->pFunc ){ - SqlFunc *pFunc = pDb->pFunc; - pDb->pFunc = pFunc->pNext; - assert( pFunc->pDb==pDb ); - Tcl_DecrRefCount(pFunc->pScript); - Tcl_Free((char*)pFunc); - } - while( pDb->pCollate ){ - SqlCollate *pCollate = pDb->pCollate; - pDb->pCollate = pCollate->pNext; - Tcl_Free((char*)pCollate); - } - if( pDb->zBusy ){ - Tcl_Free(pDb->zBusy); - } - if( pDb->zTrace ){ - Tcl_Free(pDb->zTrace); - } - if( pDb->zTraceV2 ){ - Tcl_Free(pDb->zTraceV2); - } - if( pDb->zProfile ){ - Tcl_Free(pDb->zProfile); - } - if( pDb->zBindFallback ){ - Tcl_Free(pDb->zBindFallback); - } - if( pDb->zAuth ){ - Tcl_Free(pDb->zAuth); - } - if( pDb->zNull ){ - Tcl_Free(pDb->zNull); - } - if( pDb->pUpdateHook ){ - Tcl_DecrRefCount(pDb->pUpdateHook); - } - if( pDb->pPreUpdateHook ){ - Tcl_DecrRefCount(pDb->pPreUpdateHook); - } - if( pDb->pRollbackHook ){ - Tcl_DecrRefCount(pDb->pRollbackHook); - } - if( pDb->pWalHook ){ - Tcl_DecrRefCount(pDb->pWalHook); - } - if( pDb->pCollateNeeded ){ - Tcl_DecrRefCount(pDb->pCollateNeeded); - } - Tcl_Free((char*)pDb); + delDatabaseRef(pDb); } /* @@ -1246,6 +1267,7 @@ static int SQLITE_TCLAPI DbTransPostCmd( } pDb->disableAuth--; + delDatabaseRef(pDb); return rc; } @@ -1579,6 +1601,7 @@ static void dbEvalInit( Tcl_IncrRefCount(pArray); } p->evalFlags = evalFlags; + addDatabaseRef(p->pDb); } /* @@ -1719,6 +1742,7 @@ static void dbEvalFinalize(DbEvalContext *p){ } Tcl_DecrRefCount(p->pSql); dbReleaseColumnNames(p); + delDatabaseRef(p->pDb); } /* @@ -3435,6 +3459,7 @@ deserialize_error: ** opened above. If not using NRE, evaluate the script directly, then ** call function DbTransPostCmd() to commit (or rollback) the transaction ** or savepoint. */ + addDatabaseRef(pDb); /* DbTransPostCmd() calls delDatabaseRef() */ if( DbUseNre() ){ Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); (void)Tcl_NREvalObj(interp, pScript, 0); @@ -3855,6 +3880,7 @@ static int SQLITE_TCLAPI DbMain( }else{ Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); } + p->nRef = 1; return TCL_OK; } diff --git a/src/test_tclsh.c b/src/test_tclsh.c index 73d6e97d63..c3ef6bf5bb 100644 --- a/src/test_tclsh.c +++ b/src/test_tclsh.c @@ -88,6 +88,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){ extern int Sqlitetestintarray_Init(Tcl_Interp*); extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int Sqlitetestrtree_Init(Tcl_Interp*); + extern int Sqlitetestrtreedoc_Init(Tcl_Interp*); extern int Sqlitequota_Init(Tcl_Interp*); extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); @@ -158,6 +159,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){ Sqlitetestintarray_Init(interp); Sqlitetestvfs_Init(interp); Sqlitetestrtree_Init(interp); + Sqlitetestrtreedoc_Init(interp); Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); diff --git a/src/util.c b/src/util.c index 89e3e56f1f..44c0195caf 100644 --- a/src/util.c +++ b/src/util.c @@ -60,11 +60,21 @@ int sqlite3FaultSim(int iTest){ #ifndef SQLITE_OMIT_FLOATING_POINT /* ** Return true if the floating point value is Not a Number (NaN). +** +** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. +** Otherwise, we have our own implementation that works on most systems. */ int sqlite3IsNaN(double x){ + int rc; /* The value return */ +#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN u64 y; memcpy(&y,&x,sizeof(y)); - return IsNaN(y); + rc = IsNaN(y); +#else + rc = isnan(x); +#endif /* HAVE_ISNAN */ + testcase( rc ); + return rc; } #endif /* SQLITE_OMIT_FLOATING_POINT */ @@ -91,9 +101,9 @@ int sqlite3Strlen30(const char *z){ char *sqlite3ColumnType(Column *pCol, char *zDflt){ if( pCol->colFlags & COLFLAG_HASTYPE ){ return pCol->zCnName + strlen(pCol->zCnName) + 1; - }else if( pCol->eType ){ - assert( pCol->eType<=SQLITE_N_STDTYPE ); - return (char*)sqlite3StdType[pCol->eType-1]; + }else if( pCol->eCType ){ + assert( pCol->eCType<=SQLITE_N_STDTYPE ); + return (char*)sqlite3StdType[pCol->eCType-1]; }else{ return zDflt; } diff --git a/src/vdbe.c b/src/vdbe.c index d778011b5b..7ad771e789 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -671,6 +671,19 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){ } } +/* +** Return the symbolic name for the data type of a pMem +*/ +static const char *vdbeMemTypeName(Mem *pMem){ + static const char *azTypes[] = { + /* SQLITE_INTEGER */ "INT", + /* SQLITE_FLOAT */ "REAL", + /* SQLITE_TEXT */ "TEXT", + /* SQLITE_BLOB */ "BLOB", + /* SQLITE_NULL */ "NULL" + }; + return azTypes[sqlite3_value_type(pMem)-1]; +} /* ** Execute as much of a VDBE program as we can. @@ -2502,6 +2515,22 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ break; } +/* Opcode: IsNullOrType P1 P2 P3 * * +** Synopsis: if typeof(r[P1]) IN (P3,5) goto P2 +** +** Jump to P2 if the value in register P1 is NULL or has a datatype P3. +** P3 is an integer which should be one of SQLITE_INTEGER, SQLITE_FLOAT, +** SQLITE_BLOB, SQLITE_NULL, or SQLITE_TEXT. +*/ +case OP_IsNullOrType: { /* jump, in1 */ + int doTheJump; + pIn1 = &aMem[pOp->p1]; + doTheJump = (pIn1->flags & MEM_Null)!=0 || sqlite3_value_type(pIn1)==pOp->p3; + VdbeBranchTaken( doTheJump, 2); + if( doTheJump ) goto jump_to_p2; + break; +} + /* Opcode: ZeroOrNull P1 P2 P3 * * ** Synopsis: r[P2] = 0 OR NULL ** @@ -2870,6 +2899,100 @@ op_column_corrupt: } } +/* Opcode: TypeCheck P1 P2 * P4 * +** Synopsis: typecheck(r[P1@P2]) +** +** Apply affinities to the range of P2 registers beginning with P1. +** Take the affinities from the Table object in P4. If any value +** cannot be coerced into the correct type, then raise an error. +** +** This opcode is similar to OP_Affinity except that this opcode +** forces the register type to the Table column type. This is used +** to implement "strict affinity". +** +** Preconditions: +** +**
    +**
  • P2 should be the number of non-virtual columns in the +** table of P4. +**
  • Table P4 should be a STRICT table. +**
+** +** If any precondition is false, an assertion fault occurs. +*/ +case OP_TypeCheck: { + Table *pTab; + Column *aCol; + int i; + + assert( pOp->p4type==P4_TABLE ); + pTab = pOp->p4.pTab; + assert( pTab->tabFlags & TF_Strict ); + assert( pTab->nNVCol==pOp->p2 ); + aCol = pTab->aCol; + pIn1 = &aMem[pOp->p1]; + for(i=0; inCol; i++){ + if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue; + assert( pIn1 < &aMem[pOp->p1+pOp->p2] ); + applyAffinity(pIn1, aCol[i].affinity, encoding); + if( (pIn1->flags & MEM_Null)==0 ){ + switch( aCol[i].eCType ){ + case COLTYPE_BLOB: { + if( (pIn1->flags & MEM_Blob)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_INTEGER: + case COLTYPE_INT: { + if( (pIn1->flags & MEM_Int)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_TEXT: { + if( (pIn1->flags & MEM_Str)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_REAL: { + if( pIn1->flags & MEM_Int ){ + /* When applying REAL affinity, if the result is still an MEM_Int + ** that will fit in 6 bytes, then change the type to MEM_IntReal + ** so that we keep the high-resolution integer value but know that + ** the type really wants to be REAL. */ + testcase( pIn1->u.i==140737488355328LL ); + testcase( pIn1->u.i==140737488355327LL ); + testcase( pIn1->u.i==-140737488355328LL ); + testcase( pIn1->u.i==-140737488355329LL ); + if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL){ + pIn1->flags |= MEM_IntReal; + pIn1->flags &= ~MEM_Int; + }else{ + pIn1->u.r = (double)pIn1->u.i; + pIn1->flags |= MEM_Real; + pIn1->flags &= ~MEM_Int; + } + }else if( (pIn1->flags & MEM_Real)==0 ){ + goto vdbe_type_error; + } + break; + } + default: { + /* COLTYPE_ANY. Accept anything. */ + break; + } + } + } + REGISTER_TRACE((int)(pIn1-aMem), pIn1); + pIn1++; + } + assert( pIn1 == &aMem[pOp->p1+pOp->p2] ); + break; + +vdbe_type_error: + sqlite3VdbeError(p, "cannot store %s value in %s column %s.%s", + vdbeMemTypeName(pIn1), sqlite3StdType[aCol[i].eCType-1], + pTab->zName, aCol[i].zCnName); + rc = SQLITE_CONSTRAINT_DATATYPE; + goto abort_due_to_error; +} + /* Opcode: Affinity P1 P2 * P4 * ** Synopsis: affinity(r[P1@P2]) ** @@ -5920,7 +6043,8 @@ case OP_SorterInsert: { /* in2 */ ** an UPDATE or DELETE statement and the index entry to be updated ** or deleted is not found. For some uses of IdxDelete ** (example: the EXCEPT operator) it does not matter that no matching -** entry is found. For those cases, P5 is zero. +** entry is found. For those cases, P5 is zero. Also, do not raise +** this (self-correcting and non-critical) error if in writable_schema mode. */ case OP_IdxDelete: { VdbeCursor *pC; @@ -5946,7 +6070,7 @@ case OP_IdxDelete: { if( res==0 ){ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); if( rc ) goto abort_due_to_error; - }else if( pOp->p5 ){ + }else if( pOp->p5 && !sqlite3WritableSchema(db) ){ rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); goto abort_due_to_error; } @@ -7611,7 +7735,6 @@ case OP_VFilter: { /* jump */ iQuery = (int)pQuery->u.i; /* Invoke the xFilter method */ - res = 0; apArg = p->apArg; for(i = 0; iapCsr[pOp->p1]; assert( pCur->eCurType==CURTYPE_VTAB ); if( pCur->nullRow ){ @@ -8243,6 +8365,11 @@ abort_due_to_error: rc = SQLITE_CORRUPT_BKPT; } assert( rc ); +#ifdef SQLITE_DEBUG + if( db->flags & SQLITE_VdbeTrace ){ + printf("ABORT-due-to-error. rc=%d\n", rc); + } +#endif if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); } diff --git a/src/vdbemem.c b/src/vdbemem.c index dc177161b4..e67cd64124 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1388,7 +1388,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ #ifdef SQLITE_ENABLE_STAT4 static int valueFromFunction( sqlite3 *db, /* The database connection */ - Expr *p, /* The expression to evaluate */ + const Expr *p, /* The expression to evaluate */ u8 enc, /* Encoding to use */ u8 aff, /* Affinity to use */ sqlite3_value **ppVal, /* Write the new value here */ @@ -1482,7 +1482,7 @@ static int valueFromFunction( */ static int valueFromExpr( sqlite3 *db, /* The database connection */ - Expr *pExpr, /* The expression to evaluate */ + const Expr *pExpr, /* The expression to evaluate */ u8 enc, /* Encoding to use */ u8 affinity, /* Affinity to use */ sqlite3_value **ppVal, /* Write the new value here */ @@ -1637,7 +1637,7 @@ no_mem: */ int sqlite3ValueFromExpr( sqlite3 *db, /* The database connection */ - Expr *pExpr, /* The expression to evaluate */ + const Expr *pExpr, /* The expression to evaluate */ u8 enc, /* Encoding to use */ u8 affinity, /* Affinity to use */ sqlite3_value **ppVal /* Write the new value here */ diff --git a/src/vdbetrace.c b/src/vdbetrace.c index 1095e7f589..ae8ad3115f 100644 --- a/src/vdbetrace.c +++ b/src/vdbetrace.c @@ -84,11 +84,9 @@ char *sqlite3VdbeExpandSql( #ifndef SQLITE_OMIT_UTF16 Mem utf8; /* Used to convert UTF16 into UTF8 for display */ #endif - char zBase[100]; /* Initial working space */ db = p->db; - sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase), - db->aLimit[SQLITE_LIMIT_LENGTH]); + sqlite3StrAccumInit(&out, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); if( db->nVdbeExec>1 ){ while( *zRawSql ){ const char *zStart = zRawSql; diff --git a/src/vtab.c b/src/vtab.c index b1ec940ba9..a580e6ce1b 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -840,6 +840,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ Table *pTab; char *zErr = 0; Parse sParse; + int initBusy; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ @@ -859,6 +860,12 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ memset(&sParse, 0, sizeof(sParse)); sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; sParse.db = db; + /* We should never be able to reach this point while loading the + ** schema. Nevertheless, defend against that (turn off db->init.busy) + ** in case a bug arises. */ + assert( db->init.busy==0 ); + initBusy = db->init.busy; + db->init.busy = 0; sParse.nQueryLoop = 1; if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr) && sParse.pNewTable @@ -905,6 +912,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ } sqlite3DeleteTable(db, sParse.pNewTable); sqlite3ParserReset(&sParse); + db->init.busy = initBusy; assert( (rc&0xff)==rc ); rc = sqlite3ApiExit(db, rc); diff --git a/src/where.c b/src/where.c index 5073b6c82c..751d622352 100644 --- a/src/where.c +++ b/src/where.c @@ -2011,7 +2011,8 @@ static void whereUndoExprMods(WhereInfo *pWInfo){ /* ** Return TRUE if all of the following are true: ** -** (1) X has the same or lower cost that Y +** (1) X has the same or lower cost, or returns the same or fewer rows, +** than Y. ** (2) X uses fewer WHERE clause terms than Y ** (3) Every WHERE clause term used by X is also used by Y ** (4) X skips at least as many columns as Y @@ -2034,11 +2035,8 @@ static int whereLoopCheaperProperSubset( if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ return 0; /* X is not a subset of Y */ } + if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; if( pY->nSkip > pX->nSkip ) return 0; - if( pX->rRun >= pY->rRun ){ - if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */ - if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */ - } for(i=pX->nLTerm-1; i>=0; i--){ if( pX->aLTerm[i]==0 ) continue; for(j=pY->nLTerm-1; j>=0; j--){ @@ -2054,8 +2052,8 @@ static int whereLoopCheaperProperSubset( } /* -** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so -** that: +** Try to adjust the cost and number of output rows of WhereLoop pTemplate +** upwards or downwards so that: ** ** (1) pTemplate costs less than any other WhereLoops that are a proper ** subset of pTemplate @@ -2076,16 +2074,20 @@ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ /* Adjust pTemplate cost downward so that it is cheaper than its ** subset p. */ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", - pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1)); - pTemplate->rRun = p->rRun; - pTemplate->nOut = p->nOut - 1; + pTemplate->rRun, pTemplate->nOut, + MIN(p->rRun, pTemplate->rRun), + MIN(p->nOut - 1, pTemplate->nOut))); + pTemplate->rRun = MIN(p->rRun, pTemplate->rRun); + pTemplate->nOut = MIN(p->nOut - 1, pTemplate->nOut); }else if( whereLoopCheaperProperSubset(pTemplate, p) ){ /* Adjust pTemplate cost upward so that it is costlier than p since ** pTemplate is a proper subset of p */ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", - pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1)); - pTemplate->rRun = p->rRun; - pTemplate->nOut = p->nOut + 1; + pTemplate->rRun, pTemplate->nOut, + MAX(p->rRun, pTemplate->rRun), + MAX(p->nOut + 1, pTemplate->nOut))); + pTemplate->rRun = MAX(p->rRun, pTemplate->rRun); + pTemplate->nOut = MAX(p->nOut + 1, pTemplate->nOut); } } } @@ -4592,6 +4594,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ int j; Table *pTab; Index *pIdx; + WhereScan scan; pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; @@ -4605,9 +4608,10 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ pLoop = pBuilder->pNew; pLoop->wsFlags = 0; pLoop->nSkip = 0; - pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); + pTerm = whereScanInit(&scan, pWC, iCur, -1, WO_EQ|WO_IS, 0); if( pTerm ){ testcase( pTerm->eOperator & WO_IS ); + assert( pTerm->prereqRight==0 ); pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; pLoop->aLTerm[0] = pTerm; pLoop->nLTerm = 1; @@ -4624,7 +4628,8 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ ) continue; opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ; for(j=0; jnKeyCol; j++){ - pTerm = sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx); + pTerm = whereScanInit(&scan, pWC, iCur, j, opMask, pIdx); + while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); if( pTerm==0 ) break; testcase( pTerm->eOperator & WO_IS ); pLoop->aLTerm[j] = pTerm; @@ -4653,8 +4658,14 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } + if( scan.iEquiv>1 ) pLoop->wsFlags |= WHERE_TRANSCONS; #ifdef SQLITE_DEBUG pLoop->cId = '0'; +#endif +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace ){ + sqlite3DebugPrintf("whereShortCut() used to compute solution\n"); + } #endif return 1; } diff --git a/src/wherecode.c b/src/wherecode.c index 28b417c9ae..5d9bed27c9 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1500,9 +1500,6 @@ Bitmask sqlite3WhereCodeOneLoopStart( sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); VdbeCoverage(v); pLevel->op = OP_Noop; - if( (pTerm->prereqAll & pLevel->notReady)==0 ){ - pTerm->wtFlags |= TERM_CODED; - } }else if( (pLoop->wsFlags & WHERE_IPK)!=0 && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 ){ diff --git a/src/whereexpr.c b/src/whereexpr.c index 3492769db9..9b5d2cce9d 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -263,7 +263,7 @@ static int isLikeOrGlob( */ if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT - || IsVirtual(pLeft->y.pTab) /* Value might be numeric */ + || (pLeft->y.pTab && IsVirtual(pLeft->y.pTab)) /* Might be numeric */ ){ int isNum; double rDummy; diff --git a/src/window.c b/src/window.c index 4a05392ad8..ba0e14e00d 100644 --- a/src/window.c +++ b/src/window.c @@ -1066,6 +1066,9 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ ("New window-function subquery in FROM clause of (%u/%p)\n", p->selId, p)); p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); + assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside + ** of sqlite3DbMallocRawNN() called from + ** sqlite3SrcListAppend() */ if( p->pSrc ){ Table *pTab2; p->pSrc->a[0].pSelect = pSub; @@ -1342,7 +1345,12 @@ void sqlite3WindowLink(Select *pSel, Window *pWin){ ** different, or 2 if it cannot be determined if the objects are identical ** or not. Identical window objects can be processed in a single scan. */ -int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, int bFilter){ +int sqlite3WindowCompare( + const Parse *pParse, + const Window *p1, + const Window *p2, + int bFilter +){ int res; if( NEVER(p1==0) || NEVER(p2==0) ) return 1; if( p1->eFrmType!=p2->eFrmType ) return 1; diff --git a/test/altercorrupt.test b/test/altercorrupt.test index 5c50fa4a47..f24cb309a3 100644 --- a/test/altercorrupt.test +++ b/test/altercorrupt.test @@ -14,6 +14,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix altercorrupt +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + database_may_be_corrupt #-------------------------------------------------------------------------- diff --git a/test/altermalloc3.test b/test/altermalloc3.test index 5e4e75149d..4c10f48fed 100644 --- a/test/altermalloc3.test +++ b/test/altermalloc3.test @@ -43,5 +43,36 @@ do_faultsim_test 1 -prep { faultsim_test_result {0 {}} } +#------------------------------------------------------------------------- +# dbsqlfuzz e3dd84cda3848016a6a6024c7249d09bc2ef2615 +# +reset_db +do_execsql_test 2.0 { + CREATE TABLE t2(k,v); + CREATE TRIGGER r2 AFTER INSERT ON t2 BEGIN + UPDATE t2 SET (k,v)= ( + (WITH cte1(a) AS ( SELECT 1 FROM ( SELECT * FROM t2 ) ) + SELECT a FROM cte1 + ), 1); + END; +} + +faultsim_save_and_close +faultsim_restore_and_reopen + +do_execsql_test 2.1 { + ALTER TABLE t2 RENAME TO t2x; +} + +do_faultsim_test 2.2 -prep { + faultsim_restore_and_reopen + db eval { SELECT * FROM sqlite_master } +} -body { + execsql { + ALTER TABLE t2 RENAME TO t2x; + } +} -test { + faultsim_test_result {0 {}} +} finish_test diff --git a/test/alterqf.test b/test/alterqf.test index ce00f3b596..6a89641865 100644 --- a/test/alterqf.test +++ b/test/alterqf.test @@ -17,6 +17,13 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix alterqf +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db do_execsql_test 1.0 { diff --git a/test/altertab3.test b/test/altertab3.test index 1823b21edf..2b9aac3ef8 100644 --- a/test/altertab3.test +++ b/test/altertab3.test @@ -648,4 +648,42 @@ do_catchsql_test 26.6 { } {1 {error in trigger xx: ambiguous column name: xx}} +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 27.1 { + CREATE TABLE t1(a, b AS ((WITH w1 (xyz) AS ( SELECT t1.b FROM t1 ) SELECT 123) IN ()), c); +} + +do_execsql_test 27.2 { + ALTER TABLE t1 DROP COLUMN c; + SELECT sql FROM sqlite_schema WHERE name = 't1'; +} { + {CREATE TABLE t1(a, b AS ((WITH w1 (xyz) AS ( SELECT t1.b FROM t1 ) SELECT 123) IN ()))} +} + +do_execsql_test 27.3 { + CREATE TABLE t0(c0 , c1 AS (CASE TRUE NOT IN () WHEN NULL THEN CASE + 0xa ISNULL WHEN NOT + 0x9 THEN t0.c1 ELSE CURRENT_TIME LIKE CAST (t0.c1 REGEXP '-([1-9]\d*.\d*|0\.\d*[1-9]\d*)'ESCAPE (c1) COLLATE BINARY BETWEEN c1 AND c1 NOT IN (WITH t4 (c0) AS (WITH t3 (c0) AS NOT MATERIALIZED (WITH RECURSIVE t2 (c0) AS (WITH RECURSIVE t1 AS (VALUES (x'717171ff71717171' ) ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY 0x9 ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c1 ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY typeof(0x9 ) ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY typeof(typeof(0x9 ) ) ) IN t0 BETWEEN typeof(typeof(typeof(hex(*) FILTER (WHERE + x'5ccd1e68' ) ) ) ) AND 1 >0xa AS BLOB (+4.4E4 , -0xe ) ) END <> c1 IN () END ) VIRTUAL , c35 PRIMARY KEY , c60 , c64 NUMERIC (-6.8 , -0xE ) ) WITHOUT ROWID ; +} {} + +do_execsql_test 27.4 { + ALTER TABLE t0 DROP COLUMN c60; +} {} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 28.1 { + CREATE TABLE t1(a,b,c,d); + CREATE TRIGGER AFTER INSERT ON t1 BEGIN + UPDATE t1 SET (c,d)=(a,b); + END; + ALTER TABLE t1 RENAME TO t2; +} + +do_execsql_test 28.2 { + SELECT sql FROM sqlite_schema WHERE type='trigger' +} {{CREATE TRIGGER AFTER INSERT ON "t2" BEGIN + UPDATE "t2" SET (c,d)=(a,b); + END}} + finish_test diff --git a/test/analyze4.test b/test/analyze4.test index e3b0d23f6f..d4f1921e4c 100644 --- a/test/analyze4.test +++ b/test/analyze4.test @@ -20,6 +20,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + do_test analyze4-1.0 { db eval { CREATE TABLE t1(a,b); diff --git a/test/auth3.test b/test/auth3.test index 4377bcdc0f..abc973433e 100644 --- a/test/auth3.test +++ b/test/auth3.test @@ -115,18 +115,20 @@ do_test auth3-2.2 { # an authorizer failure during an ALTER TABLE. The solution (I think) is # to disable the authorizer during schema parsing. # -proc auth {code args} { - if {$code=="SQLITE_READ" && [regexp {DoNotRead} $args]} { - return SQLITE_DENY +ifcapable altertable { + proc auth {code args} { + if {$code=="SQLITE_READ" && [regexp {DoNotRead} $args]} { + return SQLITE_DENY + } + return SQLITE_OK } - return SQLITE_OK + do_execsql_test auth3-3.0 { + CREATE TEMPORARY TABLE TempTable ( + key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, + value TEXT NOT NULL ON CONFLICT FAIL); + ALTER TABLE TempTable RENAME TO DoNotRead; + SELECT name FROM temp.sqlite_master; + } {DoNotRead sqlite_autoindex_DoNotRead_1} } -do_execsql_test auth3-3.0 { - CREATE TEMPORARY TABLE TempTable ( - key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, - value TEXT NOT NULL ON CONFLICT FAIL); - ALTER TABLE TempTable RENAME TO DoNotRead; - SELECT name FROM temp.sqlite_master; -} {DoNotRead sqlite_autoindex_DoNotRead_1} finish_test diff --git a/test/columncount.test b/test/columncount.test index 669a35a72f..d9956b40f5 100644 --- a/test/columncount.test +++ b/test/columncount.test @@ -16,6 +16,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix columncount +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + proc do_ccsql_test {tn sql res} { uplevel [list do_test $tn [subst -nocommands { diff --git a/test/corruptL.test b/test/corruptL.test index 5ade7010d7..b7ff45d2ed 100644 --- a/test/corruptL.test +++ b/test/corruptL.test @@ -1179,9 +1179,12 @@ do_catchsql_test 14.1 { PRAGMA integrity_check; } {1 {database disk image is malformed}} -do_catchsql_test 14.2 { - ALTER TABLE t1 RENAME TO alkjalkjdfiiiwuer987lkjwer82mx97sf98788s9789s; -} {1 {database disk image is malformed}} +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable altertable { + do_catchsql_test 14.2 { + ALTER TABLE t1 RENAME TO alkjalkjdfiiiwuer987lkjwer82mx97sf98788s9789s; + } {1 {database disk image is malformed}} +} extra_schema_checks 1 #------------------------------------------------------------------------- diff --git a/test/e_changes.test b/test/e_changes.test index a77e22a2ee..2eb77d3130 100644 --- a/test/e_changes.test +++ b/test/e_changes.test @@ -25,10 +25,10 @@ proc do_changes_test {tn sql res} { #-------------------------------------------------------------------------- -# EVIDENCE-OF: R-15996-49369 This function returns the number of rows -# modified, inserted or deleted by the most recently completed INSERT, -# UPDATE or DELETE statement on the database connection specified by the -# only parameter. +# EVIDENCE-OF: R-58361-29089 The changes() function returns the number +# of database rows that were changed or inserted or deleted by the most +# recently completed INSERT, DELETE, or UPDATE statement, exclusive of +# statements in lower-level triggers. # do_execsql_test 1.0 { CREATE TABLE t1(a, b); @@ -108,7 +108,7 @@ foreach {tn schema} { #-------------------------------------------------------------------------- -# EVIDENCE-OF: R-44877-05564 Executing any other type of SQL statement +# X-EVIDENCE-OF: R-44877-05564 Executing any other type of SQL statement # does not modify the value returned by this function. # reset_db @@ -123,7 +123,9 @@ do_changes_test 2.2 { do_changes_test 2.3 { SELECT count(x) FROM t1 } {47 47} do_changes_test 2.4 { DROP TABLE t1 } 47 do_changes_test 2.5 { CREATE TABLE t1(x) } 47 -do_changes_test 2.6 { ALTER TABLE t1 ADD COLUMN b } 47 +ifcapable altertable { + do_changes_test 2.6 { ALTER TABLE t1 ADD COLUMN b } 47 +} #-------------------------------------------------------------------------- diff --git a/test/e_expr.test b/test/e_expr.test index 242c503dc6..93ca0268c8 100644 --- a/test/e_expr.test +++ b/test/e_expr.test @@ -84,12 +84,12 @@ db func regexp -argcount 2 regexfunc # in the documentation exist and that the relative precedences of the # operators are also as the documentation suggests. # -# EVIDENCE-OF: R-15514-65163 SQLite understands the following binary +# X-EVIDENCE-OF: R-15514-65163 SQLite understands the following binary # operators, in order from highest to lowest precedence: || * / % + - # << >> & | < <= > >= = == != <> IS IS # NOT IN LIKE GLOB MATCH REGEXP AND OR # -# EVIDENCE-OF: R-38759-38789 Operators IS and IS NOT have the same +# X-EVIDENCE-OF: R-38759-38789 Operators IS and IS NOT have the same # precedence as =. # @@ -180,7 +180,7 @@ do_execsql_test e_expr-1.6 { # Check that the four unary prefix operators mentioned in the # documentation exist. # -# EVIDENCE-OF: R-13958-53419 Supported unary prefix operators are these: +# X-EVIDENCE-OF: R-13958-53419 Supported unary prefix operators are these: # - + ~ NOT # do_execsql_test e_expr-2.1 { SELECT - 10 } {-10} @@ -368,7 +368,7 @@ db collate reverse reverse_collate # EVIDENCE-OF: R-59577-33471 The COLLATE operator is a unary postfix # operator that assigns a collating sequence to an expression. # -# EVIDENCE-OF: R-36231-30731 The COLLATE operator has a higher +# X-EVIDENCE-OF: R-36231-30731 The COLLATE operator has a higher # precedence (binds more tightly) than any binary operator and any unary # prefix operator except "~". # @@ -860,7 +860,7 @@ foreach {tn x expr res nEval} { } [list $nEval $res] } -# EVIDENCE-OF: R-05155-34454 The precedence of the BETWEEN operator is +# X-EVIDENCE-OF: R-05155-34454 The precedence of the BETWEEN operator is # the same as the precedence as operators == and != and LIKE and groups # left to right. # diff --git a/test/e_totalchanges.test b/test/e_totalchanges.test index ee163c914f..bb5cfba8a5 100644 --- a/test/e_totalchanges.test +++ b/test/e_totalchanges.test @@ -32,10 +32,9 @@ do_execsql_test 1.0 { #-------------------------------------------------------------------------- -# EVIDENCE-OF: R-65438-26258 This function returns the total number of -# rows inserted, modified or deleted by all INSERT, UPDATE or DELETE -# statements completed since the database connection was opened, -# including those executed as part of trigger programs. +# EVIDENCE-OF: R-38914-26427 The total_changes() function returns the +# number of row changes caused by INSERT, UPDATE or DELETE statements +# since the current database connection was opened. # # 1.1.*: different types of I/U/D statements, # 1.2.*: trigger programs. @@ -95,24 +94,26 @@ do_tc_test 1.2.2 { #-------------------------------------------------------------------------- # EVIDENCE-OF: R-61766-15253 Executing any other type of SQL statement # does not affect the value returned by sqlite3_total_changes(). -do_tc_test 2.1 { - INSERT INTO t1 VALUES(1, 2), (3, 4); - INSERT INTO t2 VALUES(1, 2), (3, 4); -} {15} -do_tc_test 2.2 { - SELECT count(*) FROM t1; -} {2 15} -do_tc_test 2.3 { - CREATE TABLE t4(a, b); - ALTER TABLE t4 ADD COLUMN c; - CREATE INDEX i4 ON t4(c); - ALTER TABLE t4 RENAME TO t5; - ANALYZE; - BEGIN; - DROP TABLE t2; - ROLLBACK; - VACUUM; -} {15} +ifcapable altertable { + do_tc_test 2.1 { + INSERT INTO t1 VALUES(1, 2), (3, 4); + INSERT INTO t2 VALUES(1, 2), (3, 4); + } {15} + do_tc_test 2.2 { + SELECT count(*) FROM t1; + } {2 15} + do_tc_test 2.3 { + CREATE TABLE t4(a, b); + ALTER TABLE t4 ADD COLUMN c; + CREATE INDEX i4 ON t4(c); + ALTER TABLE t4 RENAME TO t5; + ANALYZE; + BEGIN; + DROP TABLE t2; + ROLLBACK; + VACUUM; + } {15} +} #-------------------------------------------------------------------------- diff --git a/test/func.test b/test/func.test index 38c0037735..ca1027f508 100644 --- a/test/func.test +++ b/test/func.test @@ -1459,12 +1459,15 @@ do_execsql_test func-33.10 { do_catchsql_test func-33.11 { INSERT INTO t33a VALUES(1,2); } {1 {unsafe use of testdirectonly()}} + +ifcapable altertable { do_execsql_test func-33.20 { ALTER TABLE t33a RENAME COLUMN a TO aaa; SELECT sql FROM sqlite_master WHERE name='r1'; } {{CREATE TRIGGER r1 AFTER INSERT ON t33a BEGIN INSERT INTO t33b(x,y) VALUES(testdirectonly(new.aaa),new.b); END}} +} # 2020-01-09 Yongheng fuzzer find # The bug is in the register-validity debug logic, not in the SQLite core diff --git a/test/fuzzdata8.db b/test/fuzzdata8.db index 5273e5883c..272bc5c8bd 100644 Binary files a/test/fuzzdata8.db and b/test/fuzzdata8.db differ diff --git a/test/gencol1.test b/test/gencol1.test index f09b880d67..ee0ebc53f5 100644 --- a/test/gencol1.test +++ b/test/gencol1.test @@ -587,19 +587,32 @@ do_execsql_test gencol1-20.2 { # 2021-07-30 forum https://sqlite.org/forum/forumpost/ff3ffe09251c105b?t=h # +ifcapable vtab { reset_db -do_execsql_test gencol1-21.1 { - CREATE TABLE t1( - a integer primary key, - b int generated always as (a+5), - c text GENERATED ALWAYS as (printf('%08x',a)), - d Generated - Always - AS ('xyzzy'), - e int Always default(5) - ); - INSERT INTO t1(a) VALUES(5); - SELECT name, type FROM pragma_table_xinfo('t1'); -} {a INTEGER b INT c TEXT d {} e INT} + do_execsql_test gencol1-21.1 { + CREATE TABLE t1( + a integer primary key, + b int generated always as (a+5), + c text GENERATED ALWAYS as (printf('%08x',a)), + d Generated + Always + AS ('xyzzy'), + e int Always default(5) + ); + INSERT INTO t1(a) VALUES(5); + SELECT name, type FROM pragma_table_xinfo('t1'); + } {a INTEGER b INT c TEXT d {} e INT} +} + +# 2021-09-07 forum https://sqlite.org/forum/forumpost/699b44b3ee +# +reset_db +do_execsql_test gencol1-22.1 { + CREATE TABLE t0(a PRIMARY KEY,b TEXT AS ('2') UNIQUE); + INSERT INTO t0(a) VALUES(2); + SELECT * FROM t0 AS x JOIN t0 AS y + WHERE x.b='2' + AND (y.a=2 OR (x.b LIKE '2*' AND y.a=x.b)); +} {2 2 2 2} finish_test diff --git a/test/hook.test b/test/hook.test index 1dba87f272..bb868df8b4 100644 --- a/test/hook.test +++ b/test/hook.test @@ -677,30 +677,32 @@ do_preupdate_test 7.4.2.3 { DELETE main t5 1 1 a 1 } -do_execsql_test 7.5.1.0 { - CREATE TABLE t7(a, b); - INSERT INTO t7 VALUES('one', 'two'); - INSERT INTO t7 VALUES('three', 'four'); - ALTER TABLE t7 ADD COLUMN c DEFAULT NULL; -} - -do_preupdate_test 7.5.1.1 { - DELETE FROM t7 WHERE a = 'one' -} { - DELETE main t7 1 1 one two {} -} - -do_preupdate_test 7.5.1.2 { - UPDATE t7 SET b = 'five' -} { - UPDATE main t7 2 2 three four {} three five {} -} - -do_execsql_test 7.5.2.0 { - CREATE TABLE t8(a, b); - INSERT INTO t8 VALUES('one', 'two'); - INSERT INTO t8 VALUES('three', 'four'); - ALTER TABLE t8 ADD COLUMN c DEFAULT 'xxx'; +ifcapable altertable { + do_execsql_test 7.5.1.0 { + CREATE TABLE t7(a, b); + INSERT INTO t7 VALUES('one', 'two'); + INSERT INTO t7 VALUES('three', 'four'); + ALTER TABLE t7 ADD COLUMN c DEFAULT NULL; + } + + do_preupdate_test 7.5.1.1 { + DELETE FROM t7 WHERE a = 'one' + } { + DELETE main t7 1 1 one two {} + } + + do_preupdate_test 7.5.1.2 { + UPDATE t7 SET b = 'five' + } { + UPDATE main t7 2 2 three four {} three five {} + } + + do_execsql_test 7.5.2.0 { + CREATE TABLE t8(a, b); + INSERT INTO t8 VALUES('one', 'two'); + INSERT INTO t8 VALUES('three', 'four'); + ALTER TABLE t8 ADD COLUMN c DEFAULT 'xxx'; + } } if 0 { @@ -848,48 +850,53 @@ do_preupdate_test 7.6.4 { } # No preupdate callbacks for modifying sqlite_master. -do_preupdate_test 8.1 { CREATE TABLE x1(x, y); } { } -do_preupdate_test 8.2 { ALTER TABLE x1 ADD COLUMN z } { } -do_preupdate_test 8.3 { ALTER TABLE x1 RENAME TO y1 } { } -do_preupdate_test 8.4 { CREATE INDEX y1x ON y1(x) } { } -do_preupdate_test 8.5 { CREATE VIEW v1 AS SELECT * FROM y1 } { } -do_preupdate_test 8.6 { DROP TABLE y1 } { } +ifcapable altertable { + do_preupdate_test 8.1 { CREATE TABLE x1(x, y); } { } + do_preupdate_test 8.2 { ALTER TABLE x1 ADD COLUMN z } { } + do_preupdate_test 8.3 { ALTER TABLE x1 RENAME TO y1 } { } + do_preupdate_test 8.4 { CREATE INDEX y1x ON y1(x) } { } + do_preupdate_test 8.5 { CREATE VIEW v1 AS SELECT * FROM y1 } { } + do_preupdate_test 8.6 { DROP TABLE y1 } { } +} #------------------------------------------------------------------------- reset_db db preupdate hook preupdate_hook -do_execsql_test 9.0 { - CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); - CREATE TABLE t2(a, b INTEGER PRIMARY KEY); + +ifcapable altertable { + do_execsql_test 9.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); + CREATE TABLE t2(a, b INTEGER PRIMARY KEY); + } + do_preupdate_test 9.1 { + INSERT INTO t1 VALUES(456, NULL, NULL); + } { + INSERT main t1 456 456 0 456 {} {} + } + do_execsql_test 9.2 { + ALTER TABLE t1 ADD COLUMN d; + } + do_preupdate_test 9.3 { + INSERT INTO t1(a, b, c) VALUES(457, NULL, NULL); + } { + INSERT main t1 457 457 0 457 {} {} {} + } + do_preupdate_test 9.4 { + DELETE FROM t1 WHERE a=456 + } { + DELETE main t1 456 456 0 456 {} {} {} + } + do_preupdate_test 9.5 { + INSERT INTO t2 DEFAULT VALUES; + } { + INSERT main t2 1 1 0 {} 1 + } + do_preupdate_test 9.6 { + INSERT INTO t1 DEFAULT VALUES; + } { + INSERT main t1 458 458 0 458 {} {} {} + } } -do_preupdate_test 9.1 { - INSERT INTO t1 VALUES(456, NULL, NULL); -} { - INSERT main t1 456 456 0 456 {} {} -} -do_execsql_test 9.2 { - ALTER TABLE t1 ADD COLUMN d; -} -do_preupdate_test 9.3 { - INSERT INTO t1(a, b, c) VALUES(457, NULL, NULL); -} { - INSERT main t1 457 457 0 457 {} {} {} -} -do_preupdate_test 9.4 { - DELETE FROM t1 WHERE a=456 -} { - DELETE main t1 456 456 0 456 {} {} {} -} -do_preupdate_test 9.5 { - INSERT INTO t2 DEFAULT VALUES; -} { - INSERT main t2 1 1 0 {} 1 -} -do_preupdate_test 9.6 { - INSERT INTO t1 DEFAULT VALUES; -} { - INSERT main t1 458 458 0 458 {} {} {} -} do_execsql_test 10.0 { diff --git a/test/indexexpr1.test b/test/indexexpr1.test index a31b876955..92dfc8689b 100644 --- a/test/indexexpr1.test +++ b/test/indexexpr1.test @@ -75,18 +75,20 @@ do_execsql_test indexexpr1-150eqp { ORDER BY +rowid; } {/USING INDEX t1abx/} -do_execsql_test indexexpr1-160 { - ALTER TABLE t1 ADD COLUMN d; - UPDATE t1 SET d=length(a); - CREATE INDEX t1a2 ON t1(SUBSTR(a, 27, 3)) WHERE d>=29; - SELECT rowid, b, c FROM t1 - WHERE substr(a,27,3)=='ord' AND d>=29; -} {1 1 1} -do_execsql_test indexexpr1-160eqp { - EXPLAIN QUERY PLAN - SELECT rowid, b, c FROM t1 - WHERE substr(a,27,3)=='ord' AND d>=29; -} {/USING INDEX t1a2/} +ifcapable altertable { + do_execsql_test indexexpr1-160 { + ALTER TABLE t1 ADD COLUMN d; + UPDATE t1 SET d=length(a); + CREATE INDEX t1a2 ON t1(SUBSTR(a, 27, 3)) WHERE d>=29; + SELECT rowid, b, c FROM t1 + WHERE substr(a,27,3)=='ord' AND d>=29; + } {1 1 1} + do_execsql_test indexexpr1-160eqp { + EXPLAIN QUERY PLAN + SELECT rowid, b, c FROM t1 + WHERE substr(a,27,3)=='ord' AND d>=29; + } {/USING INDEX t1a2/} +} # ORDER BY using an indexed expression # @@ -166,18 +168,20 @@ do_execsql_test indexexpr1-250eqp { ORDER BY +id; } {/USING INDEX t1abx/} -do_execsql_test indexexpr1-260 { - ALTER TABLE t1 ADD COLUMN d; - UPDATE t1 SET d=length(a); - CREATE INDEX t1a2 ON t1(SUBSTR(a, 27, 3)) WHERE d>=29; - SELECT id, b, c FROM t1 - WHERE substr(a,27,3)=='ord' AND d>=29; -} {1 1 1} -do_execsql_test indexexpr1-260eqp { - EXPLAIN QUERY PLAN - SELECT id, b, c FROM t1 - WHERE substr(a,27,3)=='ord' AND d>=29; -} {/USING INDEX t1a2/} +ifcapable altertable { + do_execsql_test indexexpr1-260 { + ALTER TABLE t1 ADD COLUMN d; + UPDATE t1 SET d=length(a); + CREATE INDEX t1a2 ON t1(SUBSTR(a, 27, 3)) WHERE d>=29; + SELECT id, b, c FROM t1 + WHERE substr(a,27,3)=='ord' AND d>=29; + } {1 1 1} + do_execsql_test indexexpr1-260eqp { + EXPLAIN QUERY PLAN + SELECT id, b, c FROM t1 + WHERE substr(a,27,3)=='ord' AND d>=29; + } {/USING INDEX t1a2/} +} do_catchsql_test indexexpr1-300 { diff --git a/test/istrue.test b/test/istrue.test index 13eccabc25..f1ba63248f 100644 --- a/test/istrue.test +++ b/test/istrue.test @@ -143,20 +143,22 @@ foreach {tn val} [list 1 NaN 2 -NaN 3 NaN0 4 -NaN0 5 Inf 6 -Inf] { } {0} } -do_execsql_test istrue-700 { - CREATE TABLE t7( - a INTEGER PRIMARY KEY, - b BOOLEAN DEFAULT false, - c BOOLEAN DEFAULT true - ); - INSERT INTO t7(a) VALUES(1); - INSERT INTO t7(a,b,c) VALUES(2,true,false); - ALTER TABLE t7 ADD COLUMN d BOOLEAN DEFAULT false; - ALTER TABLE t7 ADD COLUMN e BOOLEAN DEFAULT true; - INSERT INTO t7(a,b,c) VALUES(3,true,false); - INSERT INTO t7 VALUES(4,false,true,true,false); - SELECT *,'x' FROM t7 ORDER BY a; -} {1 0 1 0 1 x 2 1 0 0 1 x 3 1 0 0 1 x 4 0 1 1 0 x} +ifcapable altertable { + do_execsql_test istrue-700 { + CREATE TABLE t7( + a INTEGER PRIMARY KEY, + b BOOLEAN DEFAULT false, + c BOOLEAN DEFAULT true + ); + INSERT INTO t7(a) VALUES(1); + INSERT INTO t7(a,b,c) VALUES(2,true,false); + ALTER TABLE t7 ADD COLUMN d BOOLEAN DEFAULT false; + ALTER TABLE t7 ADD COLUMN e BOOLEAN DEFAULT true; + INSERT INTO t7(a,b,c) VALUES(3,true,false); + INSERT INTO t7 VALUES(4,false,true,true,false); + SELECT *,'x' FROM t7 ORDER BY a; + } {1 0 1 0 1 x 2 1 0 0 1 x 3 1 0 0 1 x 4 0 1 1 0 x} +} do_execsql_test istrue-710 { SELECT 0.5 IS TRUE COLLATE NOCASE; diff --git a/test/minmax.test b/test/minmax.test index 295fac4e93..81bd46dbe2 100644 --- a/test/minmax.test +++ b/test/minmax.test @@ -646,6 +646,16 @@ do_execsql_test 14.2 { SELECT min(a) FROM t14 WHERE b='2' AND a>'50'; } {100} - +# 2021-08-21. https://sqlite.org/forum/forumpost/cfcb4b461d +# +reset_db +do_execsql_test 15.1 { + CREATE TABLE t1(a); + CREATE TABLE t2(b); + CREATE TABLE t3(c); + INSERT INTO t1 VALUES(0); + INSERT INTO t2 VALUES(5); + SELECT MIN((SELECT b FROM t2 UNION SELECT x FROM (SELECT x FROM (SELECT 1 AS x WHERE t1.a=1) UNION ALL SELECT c FROM t3))) FROM t1; +} {5} finish_test diff --git a/test/pager1.test b/test/pager1.test index a1e06d15cc..7cb158c992 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -1940,20 +1940,22 @@ do_test pager1-18.4 { } {1 {database disk image is malformed}} db2 close extra_schema_checks 0 -do_test pager1-18.5 { - sqlite3 db "" - sqlite3_db_config db DEFENSIVE 0 - execsql { - CREATE TABLE t1(a, b); - CREATE TABLE t2(a, b); - PRAGMA writable_schema = 1; - UPDATE sqlite_master SET rootpage=5 WHERE tbl_name = 't1'; - PRAGMA writable_schema = 0; - ALTER TABLE t1 RENAME TO x1; - } - catchsql { SELECT * FROM x1 } -} {1 {database disk image is malformed}} -db close +ifcapable altertable { + do_test pager1-18.5 { + sqlite3 db "" + sqlite3_db_config db DEFENSIVE 0 + execsql { + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); + PRAGMA writable_schema = 1; + UPDATE sqlite_master SET rootpage=5 WHERE tbl_name = 't1'; + PRAGMA writable_schema = 0; + ALTER TABLE t1 RENAME TO x1; + } + catchsql { SELECT * FROM x1 } + } {1 {database disk image is malformed}} + db close +} extra_schema_checks 1 do_test pager1-18.6 { diff --git a/test/permutations.test b/test/permutations.test index b652f8571c..34404f8876 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -1236,5 +1236,6 @@ if {[file tail $argv0] == "permutations.test"} { } } main $argv + set argv {} finish_test } diff --git a/test/pragma.test b/test/pragma.test index c4b9d334ae..ba61882d5f 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -532,29 +532,31 @@ Page 6 is never used} {row 1 missing from index i2}} # Verify that PRAGMA integrity_check catches UNIQUE and NOT NULL # constraint violations. # -sqlite3_db_config db DEFENSIVE 0 -do_execsql_test pragma-3.20 { - CREATE TABLE t1(a,b); - CREATE INDEX t1a ON t1(a); - INSERT INTO t1 VALUES(1,1),(2,2),(3,3),(2,4),(NULL,5),(NULL,6); - PRAGMA writable_schema=ON; - UPDATE sqlite_master SET sql='CREATE UNIQUE INDEX t1a ON t1(a)' - WHERE name='t1a'; - UPDATE sqlite_master SET sql='CREATE TABLE t1(a NOT NULL,b)' - WHERE name='t1'; - PRAGMA writable_schema=OFF; - ALTER TABLE t1 RENAME TO t1x; - PRAGMA integrity_check; -} {{non-unique entry in index t1a} {NULL value in t1x.a} {non-unique entry in index t1a} {NULL value in t1x.a}} -do_execsql_test pragma-3.21 { - PRAGMA integrity_check(3); -} {{non-unique entry in index t1a} {NULL value in t1x.a} {non-unique entry in index t1a}} -do_execsql_test pragma-3.22 { - PRAGMA integrity_check(2); -} {{non-unique entry in index t1a} {NULL value in t1x.a}} -do_execsql_test pragma-3.23 { - PRAGMA integrity_check(1); -} {{non-unique entry in index t1a}} +ifcapable altertable { + sqlite3_db_config db DEFENSIVE 0 + do_execsql_test pragma-3.20 { + CREATE TABLE t1(a,b); + CREATE INDEX t1a ON t1(a); + INSERT INTO t1 VALUES(1,1),(2,2),(3,3),(2,4),(NULL,5),(NULL,6); + PRAGMA writable_schema=ON; + UPDATE sqlite_master SET sql='CREATE UNIQUE INDEX t1a ON t1(a)' + WHERE name='t1a'; + UPDATE sqlite_master SET sql='CREATE TABLE t1(a NOT NULL,b)' + WHERE name='t1'; + PRAGMA writable_schema=OFF; + ALTER TABLE t1 RENAME TO t1x; + PRAGMA integrity_check; + } {{non-unique entry in index t1a} {NULL value in t1x.a} {non-unique entry in index t1a} {NULL value in t1x.a}} + do_execsql_test pragma-3.21 { + PRAGMA integrity_check(3); + } {{non-unique entry in index t1a} {NULL value in t1x.a} {non-unique entry in index t1a}} + do_execsql_test pragma-3.22 { + PRAGMA integrity_check(2); + } {{non-unique entry in index t1a} {NULL value in t1x.a}} + do_execsql_test pragma-3.23 { + PRAGMA integrity_check(1); + } {{non-unique entry in index t1a}} +} # PRAGMA integrity check (or more specifically the sqlite3BtreeCount() # interface) used to leave index cursors in an inconsistent state @@ -564,7 +566,7 @@ do_execsql_test pragma-3.23 { # that problem has been fixed. # do_test pragma-3.30 { - db close + catch { db close } delete_file test.db sqlite3 db test.db db eval { @@ -1957,14 +1959,16 @@ do_test 23.3 { capture_pragma db2 out {PRAGMA index_list(t1)} db2 eval {SELECT seq, name, "unique", origin, '|' FROM out ORDER BY seq} } {0 i3 0 c | 1 i2 0 c | 2 i2x 0 c | 3 i1 0 c |} -do_test 23.4 { - db eval { - ALTER TABLE t1 ADD COLUMN e; - } - db2 eval { - PRAGMA table_info(t1); - } -} {/4 e {} 0 {} 0/} +ifcapable altertable { + do_test 23.4 { + db eval { + ALTER TABLE t1 ADD COLUMN e; + } + db2 eval { + PRAGMA table_info(t1); + } + } {/4 e {} 0 {} 0/} +} do_test 23.5 { db eval { DROP TABLE t2; diff --git a/test/quote.test b/test/quote.test index 212885c05c..9810a3ca03 100644 --- a/test/quote.test +++ b/test/quote.test @@ -141,43 +141,45 @@ do_execsql_test 2.5 { # 2021-03-13 # ticket 1c24a659e6d7f3a1 -reset_db -do_catchsql_test 3.0 { - CREATE TABLE t1(a,b); - CREATE INDEX x1 on t1("b"); - ALTER TABLE t1 DROP COLUMN b; -} {1 {error in index x1 after drop column: no such column: b}} -do_catchsql_test 3.1 { - DROP TABLE t1; - CREATE TABLE t1(a,"b"); - CREATE INDEX x1 on t1("b"); - ALTER TABLE t1 DROP COLUMN b; -} {1 {error in index x1 after drop column: no such column: b}} -do_catchsql_test 3.2 { - DROP TABLE t1; - CREATE TABLE t1(a,'b'); - CREATE INDEX x1 on t1("b"); - ALTER TABLE t1 DROP COLUMN b; -} {1 {error in index x1 after drop column: no such column: b}} -do_catchsql_test 3.3 { - DROP TABLE t1; - CREATE TABLE t1(a,"b"); - CREATE INDEX x1 on t1('b'); - ALTER TABLE t1 DROP COLUMN b; -} {1 {error in index x1 after drop column: no such column: b}} -do_catchsql_test 3.4 { - DROP TABLE t1; - CREATE TABLE t1(a, b, c); - CREATE INDEX x1 ON t1("a"||"b"); - INSERT INTO t1 VALUES(1,2,3),(1,4,5); - ALTER TABLE t1 DROP COLUMN b; -} {1 {error in index x1 after drop column: no such column: b}} -do_catchsql_test 3.5 { - DROP TABLE t1; - CREATE TABLE t1(a, b, c); - CREATE INDEX x1 ON t1("a"||"x"); - INSERT INTO t1 VALUES(1,2,3),(1,4,5); - ALTER TABLE t1 DROP COLUMN b; -} {0 {}} +ifcapable altertable { + reset_db + do_catchsql_test 3.0 { + CREATE TABLE t1(a,b); + CREATE INDEX x1 on t1("b"); + ALTER TABLE t1 DROP COLUMN b; + } {1 {error in index x1 after drop column: no such column: b}} + do_catchsql_test 3.1 { + DROP TABLE t1; + CREATE TABLE t1(a,"b"); + CREATE INDEX x1 on t1("b"); + ALTER TABLE t1 DROP COLUMN b; + } {1 {error in index x1 after drop column: no such column: b}} + do_catchsql_test 3.2 { + DROP TABLE t1; + CREATE TABLE t1(a,'b'); + CREATE INDEX x1 on t1("b"); + ALTER TABLE t1 DROP COLUMN b; + } {1 {error in index x1 after drop column: no such column: b}} + do_catchsql_test 3.3 { + DROP TABLE t1; + CREATE TABLE t1(a,"b"); + CREATE INDEX x1 on t1('b'); + ALTER TABLE t1 DROP COLUMN b; + } {1 {error in index x1 after drop column: no such column: b}} + do_catchsql_test 3.4 { + DROP TABLE t1; + CREATE TABLE t1(a, b, c); + CREATE INDEX x1 ON t1("a"||"b"); + INSERT INTO t1 VALUES(1,2,3),(1,4,5); + ALTER TABLE t1 DROP COLUMN b; + } {1 {error in index x1 after drop column: no such column: b}} + do_catchsql_test 3.5 { + DROP TABLE t1; + CREATE TABLE t1(a, b, c); + CREATE INDEX x1 ON t1("a"||"x"); + INSERT INTO t1 VALUES(1,2,3),(1,4,5); + ALTER TABLE t1 DROP COLUMN b; + } {0 {}} +} finish_test diff --git a/test/releasetest_data.tcl b/test/releasetest_data.tcl index 00be202286..d90ae2c74f 100644 --- a/test/releasetest_data.tcl +++ b/test/releasetest_data.tcl @@ -149,6 +149,7 @@ array set ::Configs [strip_comments { -DSQLITE_ENABLE_RBU -DSQLITE_MAX_ATTACHED=125 -DSQLITE_MAX_MMAP_SIZE=12884901888 + -DSQLITE_ENABLE_SORTER_MMAP=1 -DLONGDOUBLE_TYPE=double --enable-session } diff --git a/test/schema3.test b/test/schema3.test index ba7d745eb7..39d4632330 100644 --- a/test/schema3.test +++ b/test/schema3.test @@ -16,6 +16,12 @@ source $testdir/tester.tcl source $testdir/malloc_common.tcl source $testdir/lock_common.tcl +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + # This block tests that if one client modifies the database schema, a # second client updates its internal cache of the database schema before # executing any queries. Specifically, it does not return a "no such column" diff --git a/test/shell1.test b/test/shell1.test index 330276120d..d54964fd81 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -18,6 +18,8 @@ # shell1-1.*: Basic command line option handling. # shell1-2.*: Basic "dot" command token parsing. # shell1-3.*: Basic test that "dot" command can be called. +# shell1-{4-8}.*: Test various "dot" commands's functionality. +# shell1-9.*: Basic test that "dot" commands and SQL intermix ok. # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -1224,4 +1226,22 @@ do_test shell1-8.4 { | 6683623321994527 | -47 | +------------------+-----+}} +#---------------------------------------------------------------------------- +# Test cases shell1-9.*: Basic test that "dot" commands and SQL intermix ok. +# +do_test shell1-9.1 { + catchcmd :memory: { +.mode csv +/* +x */ select 1,2; --x + -- .nada +; +.mode csv +--x +select 2,1; select 3,4; +} +} {0 {1,2 +2,1 +3,4}} + finish_test diff --git a/test/shell2.test b/test/shell2.test index 2de6bf7514..4f2f4f3f63 100644 --- a/test/shell2.test +++ b/test/shell2.test @@ -43,10 +43,10 @@ do_test shell2-1.1.1 { # Shell silently ignores extra parameters. # Ticket [f5cb008a65]. do_test shell2-1.2.1 { - set rc [catch { eval exec $CLI \":memory:\" \"select+3\" \"select+4\" } msg] - list $rc $msg + catchcmdex {:memory: "select+3" "select+4"} } {0 {3 -4}} +4 +}} # Test a problem reported on the mailing list. The shell was at one point # returning the generic SQLITE_ERROR message ("SQL error or missing database") @@ -123,7 +123,7 @@ SELECT * FROM foo;} # NB. whitespace is important do_test shell2-1.4.5 { forcedelete foo.db - catchcmd "foo.db" {.echo ON + catchcmdex "foo.db" {.echo ON CREATE TABLE foo1(a); INSERT INTO foo1(a) VALUES(1); CREATE TABLE foo2(b); @@ -155,7 +155,7 @@ SELECT * FROM foo2; # NB. whitespace is important do_test shell2-1.4.6 { forcedelete foo.db - catchcmd "foo.db" {.echo ON + catchcmdex "foo.db" {.echo ON .headers ON CREATE TABLE foo1(a); INSERT INTO foo1(a) VALUES(1); diff --git a/test/shell3.test b/test/shell3.test index 63c30a2682..714a4781ea 100644 --- a/test/shell3.test +++ b/test/shell3.test @@ -18,6 +18,7 @@ # # shell3-1.*: Basic tests for running SQL statments from command line. # shell3-2.*: Basic tests for running SQL file from command line. +# shell3-3.*: Basic tests for processing odd SQL constructs. # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -26,6 +27,7 @@ db close forcedelete test.db test.db-journal test.db-wal sqlite3 db test.db + # There are inconsistencies in command-line argument quoting on Windows. # In particular, individual applications are responsible for command-line # parsing in Windows, not the shell. Depending on whether the sqlite3.exe @@ -98,4 +100,39 @@ do_test shell3-2.7 { catchcmd "foo.db" "CREATE TABLE" } {1 {Error: near line 1: incomplete input}} + +#---------------------------------------------------------------------------- +# shell3-3.*: Basic tests for processing odd SQL constructs. +# + +# Run combinations of odd identifiers, comments, semicolon placement +do_test shell3-3.1 { + forcedelete foo.db + set rc [ catchcmd "foo.db" {CREATE TABLE t1(" +a--. +" --x +); CREATE TABLE t2("a[""b""]"); +.header on +INSERT INTO t1 VALUES (' +x''y'); +INSERT INTO t2 VALUES (' +/*. +.*/ x +''y'); +SELECT * from t1 limit 1; +SELECT * from t2 limit 1; +} ] + set fexist [file exist foo.db] + list $rc $fexist +} {{0 { +a--. + + +x'y +a["b"] + +/*. +.*/ x +'y}} 1} + finish_test diff --git a/test/shrink.test b/test/shrink.test index 7c9bed08b0..bc4a707bff 100644 --- a/test/shrink.test +++ b/test/shrink.test @@ -24,7 +24,7 @@ do_test shrink-1.1 { CREATE TABLE t1(x,y); INSERT INTO t1 VALUES(randomblob(1000000),1); } - set ::baseline sqlite3_memory_used + set ::baseline [sqlite3_memory_used] # EVIDENCE-OF: R-58814-63508 The sqlite3_db_release_memory(D) interface # attempts to free as much heap memory as possible from database # connection D. diff --git a/test/skipscan2.test b/test/skipscan2.test index 47b2b3fffa..aa870d4565 100644 --- a/test/skipscan2.test +++ b/test/skipscan2.test @@ -157,7 +157,6 @@ do_execsql_test skipscan2-2.1 { CREATE INDEX peoplew_idx1 ON peoplew(role, height); INSERT INTO peoplew(name,role,height) SELECT name, role, height FROM people; - ALTER TABLE people RENAME TO old_people; SELECT name FROM peoplew WHERE height>=180 ORDER BY +name; } {David Jack Patrick Quiana Xavier} do_execsql_test skipscan2-2.2 { diff --git a/test/sorterref.test b/test/sorterref.test index 28445c6e72..adf1cf53b2 100644 --- a/test/sorterref.test +++ b/test/sorterref.test @@ -14,6 +14,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sorterref +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES(1, 2, 3); diff --git a/test/statfault.test b/test/statfault.test index ce79e328d8..b5980d417d 100644 --- a/test/statfault.test +++ b/test/statfault.test @@ -41,5 +41,15 @@ do_faultsim_test 1 -faults * -prep { faultsim_test_result {0 8} } +do_faultsim_test 2 -faults * -prep { + faultsim_restore_and_reopen + register_dbstat_vtab db + execsql { SELECT 1 FROM sqlite_master LIMIT 1 } +} -body { + db eval { SELECT * FROM sss } { db eval { SELECT randomblob(5000) } } +} -test { + faultsim_test_result {0 {}} +} finish_test + diff --git a/test/strict1.test b/test/strict1.test new file mode 100644 index 0000000000..606136abe0 --- /dev/null +++ b/test/strict1.test @@ -0,0 +1,119 @@ +# 2021-08-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. +# +#*********************************************************************** +# +# This file implements regression tests for SQLite library. The +# focus of this file is testing STRICT tables. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix strict1 + +# STRICT tables have on a limited number of allowed datatypes. +# +do_catchsql_test strict1-1.1 { + CREATE TABLE t1(a) STRICT; +} {1 {missing datatype for t1.a}} +do_catchsql_test strict1-1.2 { + CREATE TABLE t1(a PRIMARY KEY) STRICT, WITHOUT ROWID; +} {1 {missing datatype for t1.a}} +do_catchsql_test strict1-1.3 { + CREATE TABLE t1(a PRIMARY KEY) WITHOUT ROWID, STRICT; +} {1 {missing datatype for t1.a}} +do_catchsql_test strict1-1.4 { + CREATE TABLE t1(a BANJO PRIMARY KEY) WITHOUT ROWID, STRICT; +} {1 {unknown datatype for t1.a: "BANJO"}} +do_catchsql_test strict1-1.5 { + CREATE TABLE t1(a TEXT PRIMARY KEY, b INT, c INTEGER, d REAL, e BLOB, f DATE) strict; +} {1 {unknown datatype for t1.f: "DATE"}} +do_catchsql_test strict1-1.6 { + CREATE TABLE t1(a TEXT PRIMARY KEY, b INT, c INTEGER, d REAL, e BLOB, f TEXT(50)) WITHOUT ROWID, STRICT; +} {1 {unknown datatype for t1.f: "TEXT(50)"}} + +do_execsql_test strict1-2.0 { + CREATE TABLE t1( + a INT, + b INTEGER, + c BLOB, + d TEXT, + e REAL + ) STRICT; +} {} +ifcapable vtab { + do_execsql_test strict1-2.0a { + SELECT strict FROM pragma_table_list('t1'); + } {1} +} +do_catchsql_test strict1-2.1 { + INSERT INTO t1(a) VALUES('xyz'); +} {1 {cannot store TEXT value in INT column t1.a}} +do_catchsql_test strict1-2.2 { + INSERT INTO t1(b) VALUES('xyz'); +} {1 {cannot store TEXT value in INTEGER column t1.b}} +do_catchsql_test strict1-2.3 { + INSERT INTO t1(c) VALUES('xyz'); +} {1 {cannot store TEXT value in BLOB column t1.c}} +do_catchsql_test strict1-2.4 { + INSERT INTO t1(d) VALUES(x'3142536475'); +} {1 {cannot store BLOB value in TEXT column t1.d}} +do_catchsql_test strict1-2.5 { + INSERT INTO t1(e) VALUES('xyz'); +} {1 {cannot store TEXT value in REAL column t1.e}} + + +do_execsql_test strict1-3.1 { + INSERT INTO t1(a, b) VALUES(1,2),('3','4'),(5.0, 6.0),(null,null); + SELECT a, b, '|' FROM t1; +} {1 2 | 3 4 | 5 6 | {} {} |} +do_catchsql_test strict1-3.2 { + INSERT INTO t1(a) VALUES(1.2); +} {1 {cannot store REAL value in INT column t1.a}} +do_catchsql_test strict1-3.3 { + INSERT INTO t1(a) VALUES(x'313233'); +} {1 {cannot store BLOB value in INT column t1.a}} +do_catchsql_test strict1-3.4 { + INSERT INTO t1(b) VALUES(1.2); +} {1 {cannot store REAL value in INTEGER column t1.b}} +do_catchsql_test strict1-3.5 { + INSERT INTO t1(b) VALUES(x'313233'); +} {1 {cannot store BLOB value in INTEGER column t1.b}} + +do_execsql_test strict1-4.1 { + DELETE FROM t1; + INSERT INTO t1(c) VALUES(x'313233'), (NULL); + SELECT typeof(c), c FROM t1; +} {blob 123 null {}} +do_catchsql_test strict1-4.2 { + INSERT INTO t1(c) VALUES('456'); +} {1 {cannot store TEXT value in BLOB column t1.c}} + +do_execsql_test strict1-5.1 { + DELETE FROM t1; + INSERT INTO t1(d) VALUES('xyz'),(4),(5.5),(NULL); + SELECT typeof(d), d FROM t1; +} {text xyz text 4 text 5.5 null {}} +do_catchsql_test strict1-5.2 { + INSERT INTO t1(d) VALUES(x'4567'); +} {1 {cannot store BLOB value in TEXT column t1.d}} + +do_execsql_test strict1-6.1 { + DELETE FROM t1; + INSERT INTO t1(e) VALUES(1),(2.5),('3'),('4.5'),(6.0),(NULL); + SELECT typeof(e), e FROM t1; +} {real 1.0 real 2.5 real 3.0 real 4.5 real 6.0 null {}} +do_catchsql_test strict1-6.2 { + INSERT INTO t1(e) VALUES('xyz'); +} {1 {cannot store TEXT value in REAL column t1.e}} +do_catchsql_test strict1-6.3 { + INSERT INTO t1(e) VALUES(x'3456'); +} {1 {cannot store BLOB value in REAL column t1.e}} + +finish_test diff --git a/test/strict2.test b/test/strict2.test new file mode 100644 index 0000000000..0a6d3a2965 --- /dev/null +++ b/test/strict2.test @@ -0,0 +1,148 @@ +# 2021-08-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. The +# focus of this file is testing STRICT tables. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix strict2 + +# PRAGMA integrity_check on a STRICT table should verify that +# all of the values are of the correct type. +# +do_execsql_test strict2-1.1 { + CREATE TABLE t1( + a INT, + b INTEGER, + c TEXT, + d REAL, + e BLOB + ) STRICT; + CREATE TABLE t1nn( + a INT NOT NULL, + b INTEGER NOT NULL, + c TEXT NOT NULL, + d REAL NOT NULL, + e BLOB NOT NULL + ) STRICT; + CREATE TABLE t2(a,b,c,d,e); + INSERT INTO t1(a,b,c,d,e) VALUES(1,1,'one',1.0,x'b1'),(2,2,'two',2.25,x'b2b2b2'); + PRAGMA writable_schema=on; + UPDATE sqlite_schema SET rootpage=(SELECT rootpage FROM sqlite_schema WHERE name='t1'); +} {} +db close +sqlite3 db test.db +do_execsql_test strict2-1.2 { + PRAGMA quick_check('t1'); +} {ok} +do_execsql_test strict2-1.3 { + UPDATE t2 SET a=2.5 WHERE b=2; + PRAGMA quick_check('t1'); +} {{non-INT value in t1.a}} +do_execsql_test strict2-1.4 { + UPDATE t2 SET a='xyz' WHERE b=2; + PRAGMA quick_check('t1'); +} {{non-INT value in t1.a}} +do_execsql_test strict2-1.5 { + UPDATE t2 SET a=x'445566' WHERE b=2; + PRAGMA quick_check('t1'); +} {{non-INT value in t1.a}} +do_execsql_test strict2-1.6 { + UPDATE t2 SET a=2.5 WHERE b=2; + PRAGMA quick_check('t1nn'); +} {{non-INT value in t1nn.a}} +do_execsql_test strict2-1.7 { + UPDATE t2 SET a='xyz' WHERE b=2; + PRAGMA quick_check('t1nn'); +} {{non-INT value in t1nn.a}} +do_execsql_test strict2-1.8 { + UPDATE t2 SET a=x'445566' WHERE b=2; + PRAGMA quick_check('t1nn'); +} {{non-INT value in t1nn.a}} + +do_execsql_test strict2-1.13 { + UPDATE t2 SET a=2 WHERE b=2; + UPDATE t2 SET b=2.5 WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-INTEGER value in t1.b}} +do_execsql_test strict2-1.14 { + UPDATE t2 SET b='two' WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-INTEGER value in t1.b}} +do_execsql_test strict2-1.15 { + UPDATE t2 SET b=x'b0b1b2b3b4' WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-INTEGER value in t1.b}} +do_execsql_test strict2-1.16 { + UPDATE t2 SET b=NULL WHERE a=2; + PRAGMA quick_check('t1'); +} {ok} +do_execsql_test strict2-1.17 { + UPDATE t2 SET b=2.5 WHERE a=2; + PRAGMA quick_check('t1nn'); +} {{non-INTEGER value in t1nn.b}} +do_execsql_test strict2-1.18 { + UPDATE t2 SET b=NULL WHERE a=2; + PRAGMA quick_check('t1nn'); +} {{NULL value in t1nn.b}} + +do_execsql_test strict2-1.23 { + UPDATE t2 SET b=2 WHERE a=2; + UPDATE t2 SET c=9 WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-TEXT value in t1.c}} +do_execsql_test strict2-1.24 { + UPDATE t2 SET c=9.5 WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-TEXT value in t1.c}} +do_execsql_test strict2-1.25 { + UPDATE t2 SET c=x'b0b1b2b3b4' WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-TEXT value in t1.c}} + +do_execsql_test strict2-1.33 { + UPDATE t2 SET c='two' WHERE a=2; + UPDATE t2 SET d=9 WHERE a=2; + PRAGMA quick_check('t1'); +} {ok} +do_execsql_test strict2-1.34 { + UPDATE t2 SET d='nine' WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-REAL value in t1.d}} +do_execsql_test strict2-1.35 { + UPDATE t2 SET d=x'b0b1b2b3b4' WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-REAL value in t1.d}} + +do_execsql_test strict2-1.43 { + UPDATE t2 SET d=2.5 WHERE a=2; + UPDATE t2 SET e=9 WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-BLOB value in t1.e}} +do_execsql_test strict2-1.44 { + UPDATE t2 SET e=9.5 WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-BLOB value in t1.e}} +do_execsql_test strict2-1.45 { + UPDATE t2 SET e='hello' WHERE a=2; + PRAGMA quick_check('t1'); +} {{non-BLOB value in t1.e}} + +do_execsql_test strict2-2.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a INT, b ANY) STRICT; + INSERT INTO t2(a,b) VALUES(1,2),(3,4.5),(5,'six'),(7,x'8888'),(9,NULL); + PRAGMA integrity_check(t2); +} {ok} + +finish_test diff --git a/test/tclsqlite.test b/test/tclsqlite.test index 1706a3923b..e99f8c4788 100644 --- a/test/tclsqlite.test +++ b/test/tclsqlite.test @@ -851,4 +851,40 @@ do_catchsql_test 19.911 { } {1 {invalid command name "bind_fallback_does_not_exist"}} db bind_fallback {} +#------------------------------------------------------------------------- +do_test 20.0 { + db transaction { + db close + } +} {} + +do_test 20.1 { + sqlite3 db test.db + set rc [catch { + db eval {SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3} { db close } + } msg] + list $rc $msg +} {1 {invalid command name "db"}} + + +proc closedb {} { + db close + return 10 +} +proc func1 {} { return 1 } + +sqlite3 db test.db +db func closedb closedb +db func func1 func1 + +do_test 20.2 { + set rc [catch { + db eval { + SELECT closedb(),func1() UNION ALL SELECT 20,30 UNION ALL SELECT 30,40 + } + } msg] + list $rc $msg +} {0 {10 1 20 30 30 40}} + finish_test + diff --git a/test/tester.tcl b/test/tester.tcl index d7dc21157a..349a5d1232 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -89,6 +89,9 @@ # verbose # +# Only run this script once. If sourced a second time, make it a no-op +if {[info exists ::tester_tcl_has_run]} return + # Set the precision of FP arithmatic used by the interpreter. And # configure SQLite to take database file locks on the page that begins # 64KB into the database file instead of the one 1GB in. This means @@ -1203,6 +1206,17 @@ proc speed_trial_summary {name} { # Run this routine last # proc finish_test {} { + global argv + if {[llength $argv]>0} { + # If additional test scripts are specified on the command-line, + # run them also, before quitting. + proc finish_test {} {return} + foreach extra $argv { + puts "Running \"$extra\"" + db_delete_and_reopen + uplevel #0 source $extra + } + } catch {db close} catch {db1 close} catch {db2 close} @@ -2497,3 +2511,5 @@ extra_schema_checks 1 source $testdir/thread_common.tcl source $testdir/malloc_common.tcl + +set tester_tcl_has_run 1 diff --git a/test/tkt-8454a207b9.test b/test/tkt-8454a207b9.test index 88a8614f82..20e142057d 100644 --- a/test/tkt-8454a207b9.test +++ b/test/tkt-8454a207b9.test @@ -18,6 +18,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + do_test tkt-8454a207b9.1 { db eval { CREATE TABLE t1(a); diff --git a/test/tkt-f67b41381a.test b/test/tkt-f67b41381a.test index 1ddec988cd..43e5cc7dbb 100644 --- a/test/tkt-f67b41381a.test +++ b/test/tkt-f67b41381a.test @@ -15,6 +15,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix tkt-f67b41381a +ifcapable !altertable { + finish_test + return +} + do_execsql_test 1.0 { CREATE TABLE t1(a); INSERT INTO t1 VALUES(1); diff --git a/test/transitive1.test b/test/transitive1.test index 8755338492..1161a4454e 100644 --- a/test/transitive1.test +++ b/test/transitive1.test @@ -380,4 +380,36 @@ do_execsql_test transitive1-630 { SELECT ALL * FROM t1,t0 WHERE (likely(t1.c0=t0.c1) AND t1.c0=t0.c0); } {} +#------------------------------------------------------------------------- +# 2021-08-31 forum https://sqlite.org/forum/forumpost/8d1b58f112 +reset_db +do_execsql_test transitive1-700 { + CREATE TABLE t1(a INT PRIMARY KEY); + INSERT INTO t1(a) VALUES(1),(2),(3); + CREATE TABLE t2(x INTEGER PRIMARY KEY,y INT); + INSERT INTO t2(y) VALUES(2),(3); +} + +do_execsql_test transitive1-710 { + SELECT * FROM t1 CROSS JOIN t2 WHERE t2.y=t1.a AND t1.a=t2.x +} {} + +do_execsql_test transitive1-720 { + SELECT * FROM t1 CROSS JOIN t2 WHERE likely(t2.y=t1.a) AND unlikely(t1.a=t2.x) +} {} + +# 2021-10-04 forum https://sqlite.org/forum/forumpost/a65cacbf5e1c41ba +# +reset_db +do_execsql_test transitive1-800 { + CREATE TABLE t1(a INT); + INSERT INTO t1 VALUES(0),(3); + CREATE TABLE t2(b INT UNIQUE, c INT); + INSERT INTO t2 VALUES(1,4) ,(0,5); + SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a AND b IS a); + SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE a=c AND a IS b); + SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE a=c AND b IS a); + SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a AND a IS b); +} {} + finish_test diff --git a/test/update.test b/test/update.test index dd96124b4d..7be360726f 100644 --- a/test/update.test +++ b/test/update.test @@ -619,16 +619,18 @@ do_test update-14.4 { # Ticket [https://www.sqlite.org/src/tktview/43107840f1c02] on 2014-10-29 # An assertion fault on UPDATE # -do_execsql_test update-15.1 { - CREATE TABLE t15(a INTEGER PRIMARY KEY, b); - INSERT INTO t15(a,b) VALUES(10,'abc'),(20,'def'),(30,'ghi'); - ALTER TABLE t15 ADD COLUMN c; - CREATE INDEX t15c ON t15(c); - INSERT INTO t15(a,b) - VALUES(5,'zyx'),(15,'wvu'),(25,'tsr'),(35,'qpo'); - UPDATE t15 SET c=printf("y%d",a) WHERE c IS NULL; - SELECT a,b,c,'|' FROM t15 ORDER BY a; -} {5 zyx y5 | 10 abc y10 | 15 wvu y15 | 20 def y20 | 25 tsr y25 | 30 ghi y30 | 35 qpo y35 |} +ifcapable altertable { + do_execsql_test update-15.1 { + CREATE TABLE t15(a INTEGER PRIMARY KEY, b); + INSERT INTO t15(a,b) VALUES(10,'abc'),(20,'def'),(30,'ghi'); + ALTER TABLE t15 ADD COLUMN c; + CREATE INDEX t15c ON t15(c); + INSERT INTO t15(a,b) + VALUES(5,'zyx'),(15,'wvu'),(25,'tsr'),(35,'qpo'); + UPDATE t15 SET c=printf("y%d",a) WHERE c IS NULL; + SELECT a,b,c,'|' FROM t15 ORDER BY a; + } {5 zyx y5 | 10 abc y10 | 15 wvu y15 | 20 def y20 | 25 tsr y25 | 30 ghi y30 | 35 qpo y35 |} +} # Unreleased bug in UPDATE caused by the UPSERT changes. # Found by OSSFuzz as soon as the UPSERT changes landed on trunk. diff --git a/test/view.test b/test/view.test index b30d4162d0..85202d7d06 100644 --- a/test/view.test +++ b/test/view.test @@ -55,6 +55,11 @@ do_test view-1.1.110 { SELECT * FROM v1temp ORDER BY a; } } {0 {1 2 4 5 7 8 1 2 4 5 7 8}} +ifcapable vtab { + do_execsql_test view-1.1.120 { + SELECT name, type FROM pragma_table_list('v1'); + } {v1 view} +} do_test view-1.2 { catchsql { ROLLBACK; diff --git a/test/vtabK.test b/test/vtabK.test new file mode 100644 index 0000000000..dc91687a8a --- /dev/null +++ b/test/vtabK.test @@ -0,0 +1,83 @@ +# 2020-09-24 +# +# 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 tests for a strange scenario discovered by +# dbsqlfuzz (0ad6d441f9bf3dfc32626a9900bc1700495b16f9) in which a +# virtual table is named "sqlite_stat1". +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix vtabK + +ifcapable !vtab||!rtree||!fts5 { + finish_test + return +} + +do_execsql_test 100 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(123); + PRAGMA writable_schema=ON; + CREATE VIRTUAL TABLE sqlite_stat1 USING fts5(a); + PRAGMA writable_schema=OFF; + CREATE VIRTUAL TABLE t3 USING fts5(b); + INSERT INTO t3 VALUES('this is a test'); +} +do_catchsql_test 110 { + CREATE VIRTUAL TABLE t2 USING rtree(id,x,y); +} {1 {no such column: stat}} +do_execsql_test 120 { + SELECT * FROM t1; +} {123} +do_execsql_test 130 { + INSERT INTO t3(b) VALUES('Four score and seven years ago'); + SELECT * FROM t3 WHERE t3 MATCH 'this'; +} {{this is a test}} +do_execsql_test 140 { + SELECT * FROM t3 WHERE t3 MATCH 'four seven'; +} {{Four score and seven years ago}} +do_execsql_test 150 { + INSERT INTO sqlite_stat1(a) + VALUES('We hold these truths to be self-evident...'); + SELECT * FROM sqlite_stat1; +} {{We hold these truths to be self-evident...}} +do_catchsql_test 160 { + ANALYZE; +} {1 {database disk image is malformed}} +do_execsql_test 170 { + PRAGMA integrity_check; +} {ok} + +# Follow-on dbsqlfuzz bc02a0cde82dee801a8d6f653d2831680f87dca1 +reset_db +do_execsql_test 200 { + CREATE TABLE t1(a); + INSERT INTO t1 VALUES('Ebed-malech'); + CREATE TABLE x(a); + PRAGMA writable_schema=ON; + CREATE VIRTUAL TABLE sqlite_stat1 USING fts5(a); +} {} +do_catchsql_test 210 { + CREATE VIRTUAL TABLE t2 USING rtree(id,x,y); +} {1 {no such column: stat}} +do_execsql_test 220 { + SELECT * FROM t1; +} {Ebed-malech} + +# Follow-on dbsqlfuzz a097eaad43c3c845b236126df92fb49b25449b0c +reset_db +do_catchsql_test 300 { + CREATE VIRTUAL TABLE t1 USING rtree(a,b,c); + CREATE TABLE t2(x); + ALTER TABLE t2 ADD d GENERATED ALWAYS AS (c IN (SELECT 1 FROM t1)) VIRTUAL; +} {1 {error in table t2 after rename: subqueries prohibited in generated columns}} + +finish_test diff --git a/test/whereE.test b/test/whereE.test index 31086b2492..cd9f81d531 100644 --- a/test/whereE.test +++ b/test/whereE.test @@ -18,6 +18,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix whereE +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + do_execsql_test 1.1 { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,10), (2,20), (3,30), (2,22), (3, 33); diff --git a/test/window1.test b/test/window1.test index 8db23c55a6..f595d40e2e 100644 --- a/test/window1.test +++ b/test/window1.test @@ -1267,13 +1267,15 @@ do_catchsql_test 31.3 { } {1 {frame ending offset must be a non-negative integer}} # 2019-11-16 chromium issue 1025467 -db close -sqlite3 db :memory: -do_catchsql_test 32.10 { - CREATE VIEW a AS SELECT NULL INTERSECT SELECT NULL ORDER BY s() OVER R; - CREATE TABLE a0 AS SELECT 0; - ALTER TABLE a0 RENAME TO S; -} {1 {error in view a: 1st ORDER BY term does not match any column in the result set}} +ifcapable altertable { + db close + sqlite3 db :memory: + do_catchsql_test 32.10 { + CREATE VIEW a AS SELECT NULL INTERSECT SELECT NULL ORDER BY s() OVER R; + CREATE TABLE a0 AS SELECT 0; + ALTER TABLE a0 RENAME TO S; + } {1 {error in view a: 1st ORDER BY term does not match any column in the result set}} +} reset_db do_execsql_test 33.1 { diff --git a/test/windowB.test b/test/windowB.test index 30380aee20..52221c445c 100644 --- a/test/windowB.test +++ b/test/windowB.test @@ -9,6 +9,7 @@ # #*********************************************************************** # Test cases for RANGE BETWEEN and especially with NULLS LAST +# and for varying separator handling by group_concat(). # set testdir [file dirname $argv0] @@ -356,5 +357,12 @@ do_execsql_test 8.1 { FROM t1; } {111 660 938 979} +do_execsql_test 9.0 { + CREATE TABLE seps(x); + INSERT INTO seps(x) VALUES ('1'), ('22'), ('333'), ('4444'); + SELECT group_concat('-', x) + OVER ( ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING ) + FROM seps; +} {-22- -22-333- -333-4444- -4444-} finish_test diff --git a/test/windowC.test b/test/windowC.test new file mode 100644 index 0000000000..54eb7cadc4 --- /dev/null +++ b/test/windowC.test @@ -0,0 +1,66 @@ +# 2021-09-29 +# +# 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. +# +#*********************************************************************** +# Test cases for varying separator handling by group_concat(). +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix windowB + +ifcapable !windowfunc { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE TABLE x1(i INTEGER PRIMARY KEY, x); +} + +foreach {tn bBlob seps} { + 1 0 {a b c def g} + 2 0 {abcdefg {} {} abcdefg} + 3 0 {a bc def ghij klmno pqrstu} + 4 1 {a bc def ghij klmno pqrstu} + 5 1 {, , , , , , , , , , , , ....... , ,} +} { + foreach type {text blob} { + do_test 1.$type.$tn.1 { + execsql { DELETE FROM x1 } + foreach s $seps { + if {$type=="text"} { + execsql {INSERT INTO x1 VALUES(NULL, $s)} + } else { + execsql {INSERT INTO x1 VALUES(NULL, CAST ($s AS blob))} + } + } + } {} + + foreach {tn2 win} { + 1 "ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING" + 2 "ROWS BETWEEN 2 PRECEDING AND CURRENT ROW" + 3 "ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" + } { + do_test 1.$type.$tn.2.$tn2 { + db eval " + SELECT group_concat('val', x) OVER ( ORDER BY i $win ) AS val FROM x1 + " { + if {[string range $val 0 2]!="val" + || [string range $val end-2 end]!="val" + } { + error "unexpected return value: $val" + } + } + } {} + } + } +} + +finish_test diff --git a/test/windowfault.test b/test/windowfault.test index b5d7433112..5340ce08fd 100644 --- a/test/windowfault.test +++ b/test/windowfault.test @@ -292,4 +292,43 @@ do_faultsim_test 12 -faults oom* -prep { faultsim_test_result {0 {}} } +#------------------------------------------------------------------------- +reset_db +do_execsql_test 13.0 { + CREATE TABLE t1(id INTEGER PRIMARY KEY, a, b); + INSERT INTO t1 VALUES(1, '1', 'a'); + INSERT INTO t1 VALUES(2, '22', 'b'); + INSERT INTO t1 VALUES(3, '333', 'c'); + INSERT INTO t1 VALUES(4, '4444', 'dddd'); + INSERT INTO t1 VALUES(5, '55555', 'e'); + INSERT INTO t1 VALUES(6, '666666', 'f'); + INSERT INTO t1 VALUES(7, '7777777', 'gggggggggg'); +} {} + +set queryres [list {*}{ + 1b22 + 1b22c333 + 22c333dddd4444 + 333dddd4444e55555 + 4444e55555f666666 + 55555f666666gggggggggg7777777 + 666666gggggggggg7777777 +}] +do_execsql_test 13.1 { + SELECT group_concat(a, b) OVER ( + ORDER BY id RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 +} $queryres + +do_faultsim_test 13 -faults oom* -prep { +} -body { + execsql { + SELECT group_concat(a, b) OVER ( + ORDER BY id RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 + } +} -test { + faultsim_test_result [list 0 $::queryres] +} + finish_test diff --git a/test/with2.test b/test/with2.test index 02f808ea23..660df52b77 100644 --- a/test/with2.test +++ b/test/with2.test @@ -552,63 +552,77 @@ do_execsql_test 10.1 { # 2021-05-21 # Forum post https://sqlite.org/forum/forumpost/aa4a7a3980 # +ifcapable altertable { reset_db -do_execsql_test 11.1 { - CREATE TABLE t1(a); - CREATE VIEW v2(c) AS - WITH x AS ( - WITH y AS ( - WITH z AS(SELECT * FROM t1) - SELECT * FROM v2 - ) SELECT a - ) SELECT * from t1; - ALTER TABLE t1 RENAME COLUMN a TO b; - SELECT sql FROM sqlite_schema WHERE name='t1'; -} {{CREATE TABLE t1(b)}} -do_catchsql_test 11.2 { - INSERT INTO t1 VALUES(55); - SELECT * FROM v2; -} {0 55} -do_catchsql_test 11.3 { - DROP VIEW v2; - CREATE VIEW v2(c) AS - WITH x AS ( - WITH y AS ( - WITH z AS(SELECT * FROM t1) - SELECT * FROM v2 - ) SELECT a - ) SELECT * from t1, x; - SELECT * FROM v2; -} {1 {no such column: a}} -do_catchsql_test 11.4 { - DROP VIEW v2; - CREATE VIEW v2(c) AS - WITH x AS ( - WITH y AS ( - WITH z AS(SELECT * FROM t1) - SELECT * FROM v2 - ) SELECT * - ) SELECT * from t1, x; - SELECT * FROM v2; -} {1 {no tables specified}} -do_catchsql_test 11.5 { - WITH x AS ( - WITH y AS ( - WITH z AS(SELECT * FROM t1) - SELECT * FROM no_such_table - ) SELECT a - ) SELECT * from t1; -} {0 55} + do_execsql_test 11.1 { + CREATE TABLE t1(a); + CREATE VIEW v2(c) AS + WITH x AS ( + WITH y AS ( + WITH z AS(SELECT * FROM t1) + SELECT * FROM v2 + ) SELECT a + ) SELECT * from t1; + ALTER TABLE t1 RENAME COLUMN a TO b; + SELECT sql FROM sqlite_schema WHERE name='t1'; + } {{CREATE TABLE t1(b)}} + do_catchsql_test 11.2 { + INSERT INTO t1 VALUES(55); + SELECT * FROM v2; + } {0 55} + do_catchsql_test 11.3 { + DROP VIEW v2; + CREATE VIEW v2(c) AS + WITH x AS ( + WITH y AS ( + WITH z AS(SELECT * FROM t1) + SELECT * FROM v2 + ) SELECT a + ) SELECT * from t1, x; + SELECT * FROM v2; + } {1 {no such column: a}} + do_catchsql_test 11.4 { + DROP VIEW v2; + CREATE VIEW v2(c) AS + WITH x AS ( + WITH y AS ( + WITH z AS(SELECT * FROM t1) + SELECT * FROM v2 + ) SELECT * + ) SELECT * from t1, x; + SELECT * FROM v2; + } {1 {no tables specified}} + do_catchsql_test 11.5 { + WITH x AS ( + WITH y AS ( + WITH z AS(SELECT * FROM t1) + SELECT * FROM no_such_table + ) SELECT a + ) SELECT * from t1; + } {0 55} +} # 2021-05-23 dbsqlfuzz 6b7a144674e215f06ddfeb9042c873d9ee956ac0 */ reset_db -do_execsql_test 12.1 { - CREATE TABLE t1(a); - INSERT INTO t1 VALUES(1),('hello'),(4.25),(NULL),(x'3c626c6f623e'); - CREATE VIEW v2(c) AS WITH x AS (WITH y AS (WITH z AS(SELECT * FROM t1) SELECT * FROM v2) SELECT a) SELECT * from t1; - CREATE VIEW v3(c) AS WITH x AS (WITH y AS (WITH z AS(SELECT * FROM v2) SELECT * FROM v3) SELECT a) SELECT * from t1; - ALTER TABLE t1 RENAME TO t1x; - SELECT quote(c) FROM v3; -} {1 'hello' 4.25 NULL X'3C626C6F623E'} +ifcapable altertable { + do_execsql_test 12.1 { + CREATE TABLE t1(a); + INSERT INTO t1 VALUES(1),('hello'),(4.25),(NULL),(x'3c626c6f623e'); + CREATE VIEW v2(c) AS WITH x AS (WITH y AS (WITH z AS(SELECT * FROM t1) SELECT * FROM v2) SELECT a) SELECT * from t1; + CREATE VIEW v3(c) AS WITH x AS (WITH y AS (WITH z AS(SELECT * FROM v2) SELECT * FROM v3) SELECT a) SELECT * from t1; + ALTER TABLE t1 RENAME TO t1x; + SELECT quote(c) FROM v3; + } {1 'hello' 4.25 NULL X'3C626C6F623E'} +} + +# 2021-08-11 https://sqlite.org/forum/forumpost/d496c3d29bc93736 +reset_db +do_execsql_test 13.1 { + WITH + t1(x) AS (SELECT 111), + t2(y) AS (SELECT 222), + t3(z) AS (SELECT * FROM t2 WHERE false UNION ALL SELECT * FROM t2) + SELECT * FROM t1, t3; +} {111 222} finish_test diff --git a/test/without_rowid1.test b/test/without_rowid1.test index e4e69eb727..c712392688 100644 --- a/test/without_rowid1.test +++ b/test/without_rowid1.test @@ -38,6 +38,9 @@ integrity_check without_rowid1-1.0ic do_execsql_test_if_vtab without_rowid1-1.0ixi { SELECT name, key FROM pragma_index_xinfo('t1'); } {c 1 a 1 b 0 d 0} +do_execsql_test_if_vtab without_rowid1-1.0tl { + SELECT wr FROM pragma_table_list('t1'); +} {1} do_execsql_test without_rowid1-1.1 { SELECT *, '|' FROM t1 ORDER BY +c, a; @@ -455,15 +458,17 @@ do_execsql_test 13.10 { # 2021-05-13 https://sqlite.org/forum/forumpost/6c8960f545 reset_db -do_execsql_test 14.1 { - CREATE TABLE t1(a INT PRIMARY KEY) WITHOUT ROWID; - INSERT INTO t1(a) VALUES(10); - ALTER TABLE t1 ADD COLUMN b INT; - SELECT * FROM t1 WHERE a=20 OR (a=10 AND b=10); -} {} -do_execsql_test 14.2 { - CREATE TABLE dual AS SELECT 'X' AS dummy; - EXPLAIN QUERY PLAN SELECT * FROM dual, t1 WHERE a=10 AND b=10; -} {~/b=/} +ifcapable altertable { + do_execsql_test 14.1 { + CREATE TABLE t1(a INT PRIMARY KEY) WITHOUT ROWID; + INSERT INTO t1(a) VALUES(10); + ALTER TABLE t1 ADD COLUMN b INT; + SELECT * FROM t1 WHERE a=20 OR (a=10 AND b=10); + } {} + do_execsql_test 14.2 { + CREATE TABLE dual AS SELECT 'X' AS dummy; + EXPLAIN QUERY PLAN SELECT * FROM dual, t1 WHERE a=10 AND b=10; + } {~/b=/} +} finish_test diff --git a/test/without_rowid5.test b/test/without_rowid5.test index 31a440ad87..29d226a757 100644 --- a/test/without_rowid5.test +++ b/test/without_rowid5.test @@ -186,6 +186,87 @@ do_execsql_test without_rowid5-5.9 { SELECT count(*) FROM nnw; } {1} +# Ticket f2be158c57aaa8c6 (2021-08-18) +# NOT NULL ON CONFLICT clauses work on WITHOUT ROWID tables now. +# +do_test without_rowid5-5.100 { + db eval { + DROP TABLE IF EXISTS t5; + CREATE TABLE t5( + a INT NOT NULL ON CONFLICT ROLLBACK, + b TEXT, + c TEXT, + PRIMARY KEY(a,b) + ) WITHOUT ROWID; + BEGIN; + INSERT INTO t5(a,b,c) VALUES(1,2,3); + } + catch {db eval {INSERT INTO t5(a,b,c) VALUES(NULL,6,7);}} + db eval { + SELECT * FROM t5; + } +} {} +do_test without_rowid5-5.101 { + db eval { + DROP TABLE IF EXISTS t5; + CREATE TABLE t5( + a INT NOT NULL ON CONFLICT ABORT, + b TEXT, + c TEXT, + PRIMARY KEY(a,b) + ) WITHOUT ROWID; + BEGIN; + INSERT INTO t5(a,b,c) VALUES(1,2,3); + } + catch {db eval {INSERT INTO t5(a,b,c) VALUES(NULL,6,7);}} + db eval { + COMMIT; + SELECT * FROM t5; + } +} {1 2 3} +do_test without_rowid5-5.102 { + db eval { + DROP TABLE IF EXISTS t5; + CREATE TABLE t5( + a INT NOT NULL ON CONFLICT FAIL, + b TEXT, + c TEXT, + PRIMARY KEY(a,b) + ) WITHOUT ROWID; + } + catch {db eval {INSERT INTO t5(a,b,c) VALUES(1,2,3),(NULL,4,5),(6,7,8);}} + db eval { + SELECT * FROM t5; + } +} {1 2 3} +do_test without_rowid5-5.103 { + db eval { + DROP TABLE IF EXISTS t5; + CREATE TABLE t5( + a INT NOT NULL ON CONFLICT IGNORE, + b TEXT, + c TEXT, + PRIMARY KEY(a,b) + ) WITHOUT ROWID; + INSERT INTO t5(a,b,c) VALUES(1,2,3),(NULL,4,5),(6,7,8); + SELECT * FROM t5; + } +} {1 2 3 6 7 8} +do_test without_rowid5-5.104 { + db eval { + DROP TABLE IF EXISTS t5; + CREATE TABLE t5( + a INT NOT NULL ON CONFLICT REPLACE DEFAULT 3, + b TEXT, + c TEXT, + PRIMARY KEY(a,b) + ) WITHOUT ROWID; + INSERT INTO t5(a,b,c) VALUES(1,2,3),(NULL,4,5),(6,7,8); + SELECT * FROM t5; + } +} {1 2 3 3 4 5 6 7 8} + + # EVIDENCE-OF: R-12643-30541 The incremental blob I/O mechanism does not # work for WITHOUT ROWID tables. # diff --git a/tool/lemon.c b/tool/lemon.c index 75fc7aa2fb..edf17cae74 100644 --- a/tool/lemon.c +++ b/tool/lemon.c @@ -3571,7 +3571,9 @@ PRIVATE int compute_action(struct lemon *lemp, struct action *ap) /* Since a SHIFT is inherient after a prior REDUCE, convert any ** SHIFTREDUCE action with a nonterminal on the LHS into a simple ** REDUCE action: */ - if( ap->sp->index>=lemp->nterminal ){ + if( ap->sp->index>=lemp->nterminal + && (lemp->errsym==0 || ap->sp->index!=lemp->errsym->index) + ){ act = lemp->minReduce + ap->x.rp->iRule; }else{ act = lemp->minShiftReduce + ap->x.rp->iRule; diff --git a/tool/lempar.c b/tool/lempar.c index 35c3768bb9..d5ebe69424 100644 --- a/tool/lempar.c +++ b/tool/lempar.c @@ -981,14 +981,13 @@ void Parse( yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); yymajor = YYNOCODE; }else{ - while( yypParser->yytos >= yypParser->yystack - && (yyact = yy_find_reduce_action( - yypParser->yytos->stateno, - YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE - ){ + while( yypParser->yytos > yypParser->yystack ){ + yyact = yy_find_reduce_action(yypParser->yytos->stateno, + YYERRORSYMBOL); + if( yyact<=YY_MAX_SHIFTREDUCE ) break; yy_pop_parser_stack(yypParser); } - if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ + if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c index bbb0ccf293..a330ef0c87 100644 --- a/tool/mkkeywordhash.c +++ b/tool/mkkeywordhash.c @@ -52,7 +52,7 @@ struct Keyword { /* ** Define masks used to determine which keywords are allowed */ -#ifdef SQLITE_OMIT_ALTERTABLE +#if defined(SQLITE_OMIT_ALTERTABLE) || defined(SQLITE_OMIT_VIRTUALTABLE) # define ALTER 0 #else # define ALTER 0x00000001 diff --git a/tool/mkpragmatab.tcl b/tool/mkpragmatab.tcl index d69f3c3f86..caf45f339e 100644 --- a/tool/mkpragmatab.tcl +++ b/tool/mkpragmatab.tcl @@ -231,6 +231,12 @@ set pragma_def { COLS: cid name type notnull dflt_value pk hidden IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) + NAME: table_list + TYPE: TABLE_LIST + FLAG: NeedSchema Result1 SchemaOpt + COLS: schema name type ncol wr strict + IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) + NAME: stats FLAG: NeedSchema Result0 SchemaReq OneSchema COLS: tbl idx wdth hght flgs diff --git a/tool/replace.tcl b/tool/replace.tcl index 5a1ac5983c..e87cb922f2 100644 --- a/tool/replace.tcl +++ b/tool/replace.tcl @@ -9,7 +9,7 @@ fconfigure stderr -translation binary -encoding binary set mode [string tolower [lindex $argv 0]] set from [lindex $argv 1] set to [lindex $argv 2] -if {$mode ni [list exact regsub include]} {exit 1} +if {-1 == [lsearch -exact [list exact regsub include] $mode]} {exit 1} if {[string length $from]==0} {exit 2} while {![eof stdin]} { set line [gets stdin] diff --git a/tool/sqldiff.c b/tool/sqldiff.c index b8b9e005b9..da444bf9af 100644 --- a/tool/sqldiff.c +++ b/tool/sqldiff.c @@ -35,6 +35,7 @@ struct GlobalVars { int bSchemaPK; /* Use the schema-defined PK, not the true PK */ int bHandleVtab; /* Handle fts3, fts4, fts5 and rtree vtabs */ unsigned fDebug; /* Debug flags */ + int bSchemaCompare; /* Doing single-table sqlite_schema compare */ sqlite3 *db; /* The database connection */ } g; @@ -192,14 +193,14 @@ static void namelistFree(char **az){ } /* -** Return a list of column names for the table zDb.zTab. Space to +** Return a list of column names [a] for the table zDb.zTab. Space to ** hold the list is obtained from sqlite3_malloc() and should released ** using namelistFree() when no longer needed. ** ** Primary key columns are listed first, followed by data columns. ** The number of columns in the primary key is returned in *pnPkey. ** -** Normally, the "primary key" in the previous sentence is the true +** Normally [a], the "primary key" in the previous sentence is the true ** primary key - the rowid or INTEGER PRIMARY KEY for ordinary tables ** or the declared PRIMARY KEY for WITHOUT ROWID tables. However, if ** the g.bSchemaPK flag is set, then the schema-defined PRIMARY KEY is @@ -209,6 +210,9 @@ static void namelistFree(char **az){ ** If the primary key for a table is the rowid but rowid is inaccessible, ** then this routine returns a NULL pointer. ** +** [a. If the lone, named table is "sqlite_schema", "rootpage" column is +** omitted and the "type" and "name" columns are made to be the PK.] +** ** Examples: ** CREATE TABLE t1(a INT UNIQUE, b INTEGER, c TEXT, PRIMARY KEY(c)); ** *pnPKey = 1; @@ -299,19 +303,36 @@ static char **columnNames( if( nPK==0 ) nPK = 1; truePk = 1; } + if( g.bSchemaCompare ){ + assert( sqlite3_stricmp(zTab,"sqlite_schema")==0 + || sqlite3_stricmp(zTab,"sqlite_master")==0 ); + /* For sqlite_schema, will use type and name as the PK. */ + nPK = 2; + truePk = 0; + } *pnPKey = nPK; naz = nPK; az = sqlite3_malloc( sizeof(char*)*(nPK+1) ); if( az==0 ) runtimeError("out of memory"); memset(az, 0, sizeof(char*)*(nPK+1)); + if( g.bSchemaCompare ){ + az[0] = sqlite3_mprintf("%s", "type"); + az[1] = sqlite3_mprintf("%s", "name"); + } while( SQLITE_ROW==sqlite3_step(pStmt) ){ + char * sid = safeId((char*)sqlite3_column_text(pStmt,1)); int iPKey; if( truePk && (iPKey = sqlite3_column_int(pStmt,5))>0 ){ - az[iPKey-1] = safeId((char*)sqlite3_column_text(pStmt,1)); + az[iPKey-1] = sid; }else{ - az = sqlite3_realloc(az, sizeof(char*)*(naz+2) ); - if( az==0 ) runtimeError("out of memory"); - az[naz++] = safeId((char*)sqlite3_column_text(pStmt,1)); + if( !g.bSchemaCompare + || !(strcmp(sid,"rootpage")==0 + ||strcmp(sid,"name")==0 + ||strcmp(sid,"type")==0)){ + az = sqlite3_realloc(az, sizeof(char*)*(naz+2) ); + if( az==0 ) runtimeError("out of memory"); + az[naz++] = sid; + } } } sqlite3_finalize(pStmt); @@ -322,9 +343,11 @@ static char **columnNames( if( pbRowid ) *pbRowid = (az[0]==0); /* If this table has an implicit rowid for a PK, figure out how to refer - ** to it. There are three options - "rowid", "_rowid_" and "oid". Any - ** of these will work, unless the table has an explicit column of the - ** same name. */ + ** to it. There are usually three options - "rowid", "_rowid_" and "oid". + ** Any of these will work, unless the table has an explicit column of the + ** same name or the sqlite_schema tables are to be compared. In the latter + ** case, pretend that the "true" primary key is the name column, which + ** avoids extraneous diffs against the schemas due to rowid variance. */ if( az[0]==0 ){ const char *azRowid[] = { "rowid", "_rowid_", "oid" }; for(i=0; i