Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -416,12 +416,10 @@ $(TOP)/ext/fts3/fts3_test.c \ $(TOP)/ext/session/test_session.c \ $(TOP)/ext/recover/sqlite3recover.c \ $(TOP)/ext/recover/dbdata.c \ $(TOP)/ext/recover/test_recover.c \ - $(TOP)/ext/intck/test_intck.c \ - $(TOP)/ext/intck/sqlite3intck.c \ $(TOP)/ext/rbu/test_rbu.c # Statically linked extensions # TESTSRC += \ Index: Makefile.msc ================================================================== --- Makefile.msc +++ Makefile.msc @@ -1593,12 +1593,10 @@ $(TOP)\ext\misc\unionvtab.c \ $(TOP)\ext\misc\wholenumber.c \ $(TOP)\ext\rtree\test_rtreedoc.c \ $(TOP)\ext\recover\sqlite3recover.c \ $(TOP)\ext\recover\test_recover.c \ - $(TOP)\ext\intck\test_intck.c \ - $(TOP)\ext\intck\sqlite3intck.c \ $(TOP)\ext\recover\dbdata.c # If use of zlib is enabled, add the "zipfile.c" source file. # !IF $(USE_ZLIB)!=0 DELETED art/icon-243x273.gif Index: art/icon-243x273.gif ================================================================== --- art/icon-243x273.gif +++ /dev/null cannot compute difference between binary files DELETED art/icon-80x90.gif Index: art/icon-80x90.gif ================================================================== --- art/icon-80x90.gif +++ /dev/null cannot compute difference between binary files Index: doc/lemon.html ================================================================== --- doc/lemon.html +++ doc/lemon.html @@ -681,11 +681,10 @@
  • %destructor
  • %else
  • %endif
  • %extra_argument
  • %fallback -
  • %free
  • %if
  • %ifdef
  • %ifndef
  • %include
  • %left @@ -692,11 +691,10 @@
  • %name
  • %nonassoc
  • %parse_accept
  • %parse_failure
  • %right -
  • %realloc
  • %stack_overflow
  • %stack_size
  • %start_symbol
  • %syntax_error
  • %token @@ -1200,25 +1198,10 @@

    When the generated parser has the choice of matching an input against the wildcard token and some other token, the other token is always used. The wildcard token is only matched if there are no alternatives.

    - -

    4.4.26 The %realloc and %free directives

    - -

    The %realloc and %free directives defines function -that allocate and free heap memory. The signatures of these functions -should be the same as the realloc() and free() functions from the standard -C library. - -

    If both of these functions are defined -then these functions are used to allocate and free -memory for supplemental parser stack space, if the initial -parse stack space is exceeded. The initial parser stack size -is specified by either %stack_size or the --DYYSTACKDEPTH compile-time flag. -

    5.0 Error Processing

    After extensive experimentation over several years, it has been discovered that the error recovery strategy used by yacc is about @@ -1238,11 +1221,10 @@ %parse_failure routine is invoked and the parser resets itself to its start state, ready to begin parsing a new file. This is what will happen at the very first syntax error, of course, if there are no instances of the "error" non-terminal in your grammar.

    -

    6.0 History of Lemon

    Lemon was originally written by Richard Hipp sometime in the late Index: ext/consio/console_io.c ================================================================== --- ext/consio/console_io.c +++ ext/consio/console_io.c @@ -27,13 +27,10 @@ # include "sqlite3.h" #endif #ifndef HAVE_CONSOLE_IO_H # include "console_io.h" #endif -#if defined(_MSC_VER) -# pragma warning(disable : 4204) -#endif #ifndef SQLITE_CIO_NO_TRANSLATE # if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT # ifndef SHELL_NO_SYSINC # include @@ -128,14 +125,10 @@ ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); return ppst->reachesConsole; # endif } -# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING -# define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4) -# endif - # if CIO_WIN_WC_XLATE /* Define console modes for use with the Windows Console API. */ # define SHELL_CONI_MODE \ (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) @@ -682,10 +675,6 @@ } # endif } #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ -#if defined(_MSC_VER) -# pragma warning(default : 4204) -#endif - #undef SHELL_INVALID_FILE_PTR Index: ext/fts3/fts3.c ================================================================== --- ext/fts3/fts3.c +++ ext/fts3/fts3.c @@ -4012,28 +4012,26 @@ const char *zTabname, /* Name of the pVTab table */ int isQuick, /* True if this is a quick_check */ char **pzErr /* Write error message here */ ){ Fts3Table *p = (Fts3Table*)pVtab; - int rc = SQLITE_OK; + int rc; int bOk = 0; UNUSED_PARAMETER(isQuick); rc = sqlite3Fts3IntegrityCheck(p, &bOk); - assert( rc!=SQLITE_CORRUPT_VTAB ); - if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ + assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 ); + if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" " FTS%d table %s.%s: %s", p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); - if( *pzErr ) rc = SQLITE_OK; - }else if( rc==SQLITE_OK && bOk==0 ){ + }else if( bOk==0 ){ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s", p->bFts4 ? 4 : 3, zSchema, zTabname); - if( *pzErr==0 ) rc = SQLITE_NOMEM; } sqlite3Fts3SegmentsClose(p); - return rc; + return SQLITE_OK; } static const sqlite3_module fts3Module = { Index: ext/fts3/fts3_write.c ================================================================== --- ext/fts3/fts3_write.c +++ ext/fts3/fts3_write.c @@ -5370,16 +5370,11 @@ } sqlite3_finalize(pStmt); } - if( rc==SQLITE_CORRUPT_VTAB ){ - rc = SQLITE_OK; - *pbOk = 0; - }else{ - *pbOk = (rc==SQLITE_OK && cksum1==cksum2); - } + *pbOk = (rc==SQLITE_OK && cksum1==cksum2); return rc; } /* ** Run the integrity-check. If no error occurs and the current contents of Index: ext/fts5/fts5_index.c ================================================================== --- ext/fts5/fts5_index.c +++ ext/fts5/fts5_index.c @@ -6835,30 +6835,27 @@ ** a rowid of iFrom or greater. */ static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ int ii; Fts5TokenDataIter *pT = pIter->pTokenDataIter; - Fts5Index *pIndex = pIter->pIndex; for(ii=0; iinIter; ii++){ Fts5Iter *p = pT->apIter[ii]; if( p->base.bEof==0 && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowidpIndex, p, bFrom, iFrom); while( bFrom && p->base.bEof==0 && p->base.iRowidrc==SQLITE_OK + && p->pIndex->rc==SQLITE_OK ){ - fts5MultiIterNext(pIndex, p, 0, 0); + fts5MultiIterNext(p->pIndex, p, 0, 0); } } } - if( pIndex->rc==SQLITE_OK ){ - fts5IterSetOutputsTokendata(pIter); - } + fts5IterSetOutputsTokendata(pIter); } /* ** If the segment-iterator passed as the first argument is at EOF, then ** set pIter->term to a copy of buffer pTerm. Index: ext/fts5/fts5_main.c ================================================================== --- ext/fts5/fts5_main.c +++ ext/fts5/fts5_main.c @@ -2977,19 +2977,18 @@ UNUSED_PARAM(isQuick); rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s", zSchema, zTabname); - rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM; }else if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" " FTS5 table %s.%s: %s", zSchema, zTabname, sqlite3_errstr(rc)); } sqlite3Fts5IndexCloseReader(pTab->p.pIndex); - return rc; + return SQLITE_OK; } static int fts5Init(sqlite3 *db){ static const sqlite3_module fts5Mod = { /* iVersion */ 4, Index: ext/fts5/fts5_tcl.c ================================================================== --- ext/fts5/fts5_tcl.c +++ ext/fts5/fts5_tcl.c @@ -1167,11 +1167,11 @@ /* ** Delete the OriginTextCtx object indicated by the only argument. */ static void f5tOrigintextTokenizerDelete(void *pCtx){ OriginTextCtx *p = (OriginTextCtx*)pCtx; - ckfree((char*)p); + ckfree(p); } static int f5tOrigintextCreate( void *pCtx, const char **azArg, Index: ext/fts5/test/fts5fault8.test ================================================================== --- ext/fts5/test/fts5fault8.test +++ ext/fts5/test/fts5fault8.test @@ -54,10 +54,11 @@ faultsim_test_result {0 {1 3}} {1 SQLITE_NOMEM} } } } ;# foreach_detail_mode... + do_execsql_test 4.0 { CREATE VIRTUAL TABLE x2 USING fts5(a); INSERT INTO x2(x2, rank) VALUES('crisismerge', 2); INSERT INTO x2(x2, rank) VALUES('pgsz', 32); @@ -77,20 +78,7 @@ execsql { INSERT INTO x2(x2) VALUES('optimize') } } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} } -set TMPDBERROR {1 {unable to open a temporary database file for storing temporary tables}} - -do_faultsim_test 5 -faults oom-t* -prep { - faultsim_restore_and_reopen - execsql { PRAGMA temp_store = memory } -} -body { - execsql { PRAGMA integrity_check } -} -test { - if {[string match {*error code=7*} $testresult]==0} { - faultsim_test_result {0 ok} {1 SQLITE_NOMEM} $::TMPDBERROR - } -} - finish_test Index: ext/fts5/test/fts5faultH.test ================================================================== --- ext/fts5/test/fts5faultH.test +++ ext/fts5/test/fts5faultH.test @@ -125,26 +125,17 @@ INSERT INTO t1(rowid, x) VALUES(34, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(35, 'aaa bbb BBB'); COMMIT; } -do_faultsim_test 3.1 -faults oom* -prep { +do_faultsim_test 3 -faults oom* -prep { } -body { execsql { SELECT rowid FROM t1('BBB AND AAA'); } } -test { faultsim_integrity_check faultsim_test_result {0 {10 35}} } -do_faultsim_test 3.2 -faults oom* -prep { -} -body { - execsql { - SELECT count(*) FROM t1('BBB'); - } -} -test { - faultsim_integrity_check - faultsim_test_result {0 27} -} finish_test DELETED ext/intck/intck1.test Index: ext/intck/intck1.test ================================================================== --- ext/intck/intck1.test +++ /dev/null @@ -1,331 +0,0 @@ -# 2008 Feb 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. -# -#*********************************************************************** -# -# The focus of this file is testing the incremental integrity check -# (intck) extension. -# - -source [file join [file dirname [info script]] intck_common.tcl] -set testprefix intck1 - -foreach {tn sql} { - 1 "CREATE TABLE t1(a PRIMARY KEY, b)" - 2 "CREATE TABLE t2(a PRIMARY KEY, b) WITHOUT ROWID " - 3 "CREATE TABLE t3(a PRIMARY KEY, b) WITHOUT rowID;" - 4 "CREATE TABLE t4(a PRIMARY KEY, ROWID)" - 5 {CREATE TABLE t5(a PRIMARY KEY, ROWID) WITHOUT ROWID - } -} { - do_test 1.1.$tn { - db eval $sql - set {} {} - } {} -} - -set space " \n\v\t\r\f" - -do_execsql_test 1.2 { - SELECT name, (rtrim(sql, $space) LIKE '%rowid') - FROM sqlite_schema WHERE type='table' - ORDER BY 1 -} { - t1 0 - t2 1 - t3 1 - t4 0 - t5 1 -} - -do_execsql_test 1.3 { - CREATE TABLE x1(a COLLATE nocase, b INTEGER, c BLOB); - INSERT INTO x1 VALUES('lEtTeRs', 1234, 1234); -} -do_execsql_test 1.3.1 { - WITH wrapper(c1, c2, c3) AS ( - SELECT a, b, c FROM x1 - ) - SELECT * FROM wrapper WHERE c1='letters'; -} {lEtTeRs 1234 1234} -do_execsql_test 1.3.2 { - WITH wrapper(c1, c2, c3) AS ( - SELECT a, b, c FROM x1 - ) - SELECT * FROM wrapper WHERE c2='1234'; -} {lEtTeRs 1234 1234} -do_execsql_test 1.3.2 { - WITH wrapper(c1, c2, c3) AS ( - SELECT a, b, c FROM x1 - ) - SELECT * FROM wrapper WHERE c3='1234'; -} {} - -do_execsql_test 1.4 { - CREATE TABLE z1(a, b); - CREATE INDEX z1ab ON z1(a+b COLLATE nocase); -} -do_execsql_test 1.4.1 { - SELECT * FROM z1 INDEXED BY z1ab -} - -do_catchsql_test 1.5.1 { - CREATE INDEX z1b ON z1(b ASC NULLS LAST); -} {1 {unsupported use of NULLS LAST}} -do_catchsql_test 1.5.2 { - CREATE INDEX z1b ON z1(b DESC NULLS LAST); -} {1 {unsupported use of NULLS LAST}} -do_catchsql_test 1.5.3 { - CREATE INDEX z1b ON z1(b ASC NULLS FIRST); -} {1 {unsupported use of NULLS FIRST}} -do_catchsql_test 1.5.4 { - CREATE INDEX z1b ON z1(b DESC NULLS FIRST); -} {1 {unsupported use of NULLS FIRST}} - - -reset_db -do_execsql_test 1.6.1 { - CREATE TABLE t1(i INTEGER PRIMARY KEY, b, c); - CREATE INDEX i1 ON t1(b); - ANALYZE; - INSERT INTO sqlite_stat1 VALUES('t1', 'i1', '10000 10000'); - ANALYZE sqlite_schema; -} {} -do_eqp_test 1.6.2 { - SELECT 1 FROM t1 INDEXED BY i1 WHERE (b, i) IS (?, ?); -} {SEARCH} - - - -#------------------------------------------------------------------------- -reset_db - -do_test 2.0 { - set ic [sqlite3_intck db main] - $ic close -} {} - -do_execsql_test 2.1 { - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, 2); - INSERT INTO t1 VALUES(3, 4); - - CREATE INDEX i1 ON t1(a COLLATE nocase); - CREATE INDEX i2 ON t1(b, a); - CREATE INDEX i3 ON t1(b + a COLLATE nocase) WHERE a!=1; -} - -do_intck_test 2.2 { -} - -# Delete a row from each of the i1 and i2 indexes using the imposter -# table interface. -# -do_test 2.3 { - db eval {SELECT name, rootpage FROM sqlite_schema} { - set R($name) $rootpage - } - sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(i1) - db eval { CREATE TABLE imp1(a PRIMARY KEY, rowid) WITHOUT ROWID; } - sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(i2) - db eval { CREATE TABLE imp2(b, a, rowid, PRIMARY KEY(b, a)) WITHOUT ROWID; } - sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 - - db eval { - DELETE FROM imp1 WHERE rowid=1; - DELETE FROM imp2 WHERE rowid=2; - } - - db close - sqlite3 db test.db -} {} - -do_intck_test 2.4 { - {entry (1,1) missing from index i1} - {entry (4,3,2) missing from index i2} -} - -#------------------------------------------------------------------------- -reset_db -do_execsql_test 3.0 { - CREATE TABLE x1(a, b, c, PRIMARY KEY(c, b)) WITHOUT ROWID; - CREATE INDEX x1a ON x1(a COLLATE nocase); - - INSERT INTO x1 VALUES(1, 2, 'three'); - INSERT INTO x1 VALUES(4, 5, 'six'); - INSERT INTO x1 VALUES(7, 8, 'nine'); -} - -do_intck_test 3.1 { } - -do_test 3.2 { - db eval {SELECT name, rootpage FROM sqlite_schema} { - set R($name) $rootpage - } - sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(x1a) - db eval { CREATE TABLE imp1(c, b, a, PRIMARY KEY(c, b)) WITHOUT ROWID } - sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 - - db eval { - DELETE FROM imp1 WHERE a=5; - } - execsql_pp { - } - - db close - sqlite3 db test.db -} {} - -do_intck_test 3.3 { - {entry (4,'six',5) missing from index x1a} -} - -#------------------------------------------------------------------------- -reset_db -do_execsql_test 4.0 { - CREATE TABLE www(x, y, z); - CREATE INDEX w1 ON www( (x+1), z ); - INSERT INTO www VALUES(1, 1, 1), (2, 2, 2); -} - -do_intck_test 4.1 { } - -#------------------------------------------------------------------------- -reset_db -do_execsql_test 5.0 { - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a COLLATE NOCASE); - INSERT INTO t1 VALUES(1, 1); - INSERT INTO t1 VALUES(2, 2); -} - -do_test 5.1 { - set ic [sqlite3_intck db nosuchdb] - $ic step -} {SQLITE_ERROR} - -do_test 5.2 { - $ic close - set ic [sqlite3_intck db {}] - while {[$ic step]=="SQLITE_OK"} {} - set res [$ic error] - $ic close - set res -} {SQLITE_OK {}} - -do_test 5.3 { test_do_intck db "main" } {} - -do_test 5.4 { - set ret {} - set ic [sqlite3_intck db main] - db eval [$ic test_sql t1] { - if {$error_message!=""} { lappend ret $error_message } - } - $ic close - set ret -} {} - -do_test 5.5 { - set ret {} - set ic [sqlite3_intck db main] - db eval [$ic test_sql {}] { - if {$error_message!=""} { lappend ret $error_message } - } - $ic close - set ret -} {} - -db cache flush - -do_test 5.6 { - set ret {} - set ic [sqlite3_intck db main] - $ic step - db eval [$ic test_sql {}] { - if {$error_message!=""} { lappend ret $error_message } - } - $ic close - set ret -} {} - -#------------------------------------------------------------------------- -reset_db - -do_execsql_test 6.0 { - CREATE TABLE t1(x, y, PRIMARY KEY(x)) WITHOUT ROWID; - CREATE INDEX i1 ON t1(y, x); - INSERT INTO t1 VALUES(X'0000', X'1111'); -} - -do_intck_test 6.1 {} - -do_execsql_test 6.2.1 { - PRAGMA writable_schema = 1; - UPDATE sqlite_schema SET sql = 'CREATE INDEX i1' WHERE name='i1'; -} {} -do_intck_test 6.2.2 {} - -do_execsql_test 6.3.1 { - UPDATE sqlite_schema SET sql = 'CREATE INDEX i1(y' WHERE name='i1'; -} {} -do_intck_test 6.3.2 {} - -do_execsql_test 6.4.1 { - UPDATE sqlite_schema - SET sql = 'CREATE INDEX i1(y) hello world' - WHERE name='i1'; -} {} -do_intck_test 6.4.2 {} - -do_execsql_test 6.5.1 { - UPDATE sqlite_schema - SET sql = 'CREATE INDEX i1(y, x) WHERE 1 ' - WHERE name='i1'; -} {} -do_intck_test 6.5.2 {} - -do_execsql_test 6.6.1 { - UPDATE sqlite_schema - SET sql = 'CREATE INDEX i1( , ) WHERE 1 ' - WHERE name='i1'; -} {} - -do_test 6.7.2 { - set ic [sqlite3_intck db main] - $ic step -} {SQLITE_ERROR} -do_test 6.5.3 { - $ic error -} {SQLITE_ERROR {near "AS": syntax error}} -$ic close - -do_execsql_test 6.6.1 { - UPDATE sqlite_schema - SET sql = 'CREATE INDEX i1([y' - WHERE name='i1'; -} {} -do_intck_test 6.6.2 {} - -#------------------------------------------------------------------------- -reset_db -do_execsql_test 7.0 { - CREATE TABLE x1("1", "22", "3333", four); - CREATE INDEX i1 ON x1( "1" , "22", NULL); - INSERT INTO x1 VALUES(1, 22, 3333, NULL); - INSERT INTO x1 VALUES(1, 22, 3333, NULL); -} -do_execsql_test 7.1 " CREATE INDEX i2 ON x1( \"1\"\r\n\t ) " -do_execsql_test 7.2 { CREATE INDEX i3 ON x1( "22" || 'abc''def' || `1` ) } -do_execsql_test 7.3 { CREATE INDEX i4 ON x1( [22] + [1] ) } -do_execsql_test 7.4 { CREATE INDEX i5 ON x1( four||'hello' ) } - -do_intck_test 7.5 {} - - -finish_test DELETED ext/intck/intck2.test Index: ext/intck/intck2.test ================================================================== --- ext/intck/intck2.test +++ /dev/null @@ -1,176 +0,0 @@ -# 2024 Feb 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. -# -#*********************************************************************** -# -# The focus of this file is testing the incremental integrity check -# (intck) extension. -# - -source [file join [file dirname [info script]] intck_common.tcl] -set testprefix intck2 - - -do_execsql_test 1.0 { - CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); - INSERT INTO t1 VALUES(1, 'one'); - INSERT INTO t1 VALUES(2, 'two'); - INSERT INTO t1 VALUES(3, 'three'); - CREATE INDEX i1 ON t1(b); -} - -proc imposter_edit {obj create sql} { - sqlite3 xdb test.db - set pgno [xdb one {SELECT rootpage FROM sqlite_schema WHERE name=$obj}] - - sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER xdb main 1 $pgno - xdb eval $create - sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER xdb main 0 0 - xdb eval $sql - xdb close -} - -imposter_edit i1 { - CREATE TABLE imp(b, a, PRIMARY KEY(b)) WITHOUT ROWID; -} { - DELETE FROM imp WHERE b='two'; - INSERT INTO imp(b, a) VALUES('four', 4); -} - -do_intck_test 1.1 { - {surplus entry ('four',4) in index i1} - {entry ('two',2) missing from index i1} -} - -#------------------------------------------------------------------------- -reset_db -do_execsql_test 2.0 { - CREATE TABLE x1(a, b, "c d"); - CREATE INDEX x1a ON x1(a COLLATE nocase DESC , b ASC); - CREATE INDEX x1b ON x1( a || b || ' "''" ' COLLATE binary ASC ); - CREATE INDEX x1c ON x1( format('%s', a)ASC, format('%d', "c d" ) ); - INSERT INTO x1 VALUES('one', 2, 3); - INSERT INTO x1 VALUES('One', 4, 5); - INSERT INTO x1 VALUES('ONE', 6, 7); - INSERT INTO x1 VALUES(NULL, NULL, NULL); -} - -do_intck_test 2.1 {} - -imposter_edit x1 { - CREATE TABLE imp(a, b, c); -} { - DELETE FROM imp WHERE c=7; -} -do_intck_test 2.2 { - {surplus entry ('ONE',6,3) in index x1a} - {surplus entry ('ONE6 "''" ',3) in index x1b} - {surplus entry ('ONE','7',3) in index x1c} -} - -#------------------------------------------------------------------------- -reset_db -do_execsql_test 3.0 { - CREATE TABLE x1(a, b, c); - CREATE INDEX x1all ON x1(a DESC, b ASC, c DESC); - INSERT INTO x1 VALUES(2, 1, 2); - INSERT INTO x1 VALUES(2, 1, 1); - INSERT INTO x1 VALUES(2, 2, 2); - INSERT INTO x1 VALUES(2, 2, 1); - INSERT INTO x1 VALUES(1, 1, 2); - INSERT INTO x1 VALUES(1, 1, 1); - INSERT INTO x1 VALUES(1, 2, 2); - INSERT INTO x1 VALUES(1, 2, 1); -} - -do_intck_test 3.1 { -} - -imposter_edit x1 { - CREATE TABLE imp(a, b, c); -} { - DELETE FROM imp WHERE 1; -} - -db close -sqlite3 db test.db - -do_intck_test 3.2 { - {surplus entry (2,1,2,1) in index x1all} - {surplus entry (2,1,1,2) in index x1all} - {surplus entry (2,2,2,3) in index x1all} - {surplus entry (2,2,1,4) in index x1all} - {surplus entry (1,1,2,5) in index x1all} - {surplus entry (1,1,1,6) in index x1all} - {surplus entry (1,2,2,7) in index x1all} - {surplus entry (1,2,1,8) in index x1all} -} - -do_execsql_test 3.3 { - DELETE FROM x1; - INSERT INTO x1 VALUES(NULL, NULL, NULL); - INSERT INTO x1 VALUES(NULL, NULL, NULL); - INSERT INTO x1 VALUES(NULL, NULL, NULL); - INSERT INTO x1 VALUES(NULL, NULL, NULL); -} - -do_intck_test 3.4 { -} - -imposter_edit x1 { - CREATE TABLE imp(a, b, c); -} { - DELETE FROM imp WHERE 1; - INSERT INTO imp(rowid) VALUES(-123); - INSERT INTO imp(rowid) VALUES(456); -} - -db close -sqlite3 db test.db - -do_intck_test 3.5 { - {entry (NULL,NULL,NULL,-123) missing from index x1all} - {entry (NULL,NULL,NULL,456) missing from index x1all} - {surplus entry (NULL,NULL,NULL,1) in index x1all} - {surplus entry (NULL,NULL,NULL,2) in index x1all} - {surplus entry (NULL,NULL,NULL,3) in index x1all} - {surplus entry (NULL,NULL,NULL,4) in index x1all} -} - -reset_db - -do_execsql_test 3.6 { - CREATE TABLE w1(a PRIMARY KEY, b, c); - INSERT INTO w1 VALUES(1.0, NULL, NULL); - INSERT INTO w1 VALUES(33.0, NULL, NULL); - INSERT INTO w1 VALUES(100.0, NULL, NULL); - CREATE INDEX w1bc ON w1(b, c); -} - -do_intck_test 3.7 { -} - -imposter_edit w1 { - CREATE TABLE imp(a, b, c); -} { - DELETE FROM imp WHERE a=33; - INSERT INTO imp(a) VALUES(1234.5); - INSERT INTO imp(a) VALUES(-1234.5); -} - -do_intck_test 3.8 { - {surplus entry (33.0,2) in index sqlite_autoindex_w1_1} - {entry (1234.5,4) missing from index sqlite_autoindex_w1_1} - {entry (NULL,NULL,4) missing from index w1bc} - {entry (-1234.5,5) missing from index sqlite_autoindex_w1_1} - {entry (NULL,NULL,5) missing from index w1bc} - {surplus entry (NULL,NULL,2) in index w1bc} -} - -finish_test DELETED ext/intck/intck_common.tcl Index: ext/intck/intck_common.tcl ================================================================== --- ext/intck/intck_common.tcl +++ /dev/null @@ -1,56 +0,0 @@ -# 2024 Feb 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. -# -#*********************************************************************** -# - -if {![info exists testdir]} { - set testdir [file join [file dirname [info script]] .. .. test] -} -source $testdir/tester.tcl - -proc do_intck {db {bSuspend 0}} { - set ic [sqlite3_intck $db main] - - set ret [list] - while {"SQLITE_OK"==[$ic step]} { - set msg [$ic message] - if {$msg!=""} { - lappend ret $msg - } - if {$bSuspend} { - $ic unlock - #puts "SQL: [$ic test_sql {}]" - #execsql_pp "EXPLAIN query plan [$ic test_sql {}]" - #explain_i [$ic test_sql {}] - } - } - - set err [$ic error] - if {[lindex $err 0]!="SQLITE_OK"} { - error $err - } - $ic close - - return $ret -} - -proc intck_sql {db tbl} { - set ic [sqlite3_intck $db main] - set sql [$ic test_sql $tbl] - $ic close - return $sql -} - -proc do_intck_test {tn expect} { - uplevel [list do_test $tn.a [list do_intck db] [list {*}$expect]] - uplevel [list do_test $tn.b [list do_intck db 1] [list {*}$expect]] -} - - DELETED ext/intck/intckbusy.test Index: ext/intck/intckbusy.test ================================================================== --- ext/intck/intckbusy.test +++ /dev/null @@ -1,48 +0,0 @@ -# 2024 February 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. -# -#*********************************************************************** -# - -source [file join [file dirname [info script]] intck_common.tcl] -set testprefix intckbusy - - - -do_execsql_test 1.0 { - CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); - INSERT INTO t1 VALUES(1, 2, 3); - INSERT INTO t1 VALUES(2, 'two', 'three'); - INSERT INTO t1 VALUES(3, NULL, NULL); - CREATE INDEX i1 ON t1(b, c); -} - -sqlite3 db2 test.db - -do_execsql_test -db db2 1.1 { - BEGIN EXCLUSIVE; - INSERT INTO t1 VALUES(4, 5, 6); -} - -do_test 1.2 { - set ic [sqlite3_intck db main] - $ic step -} {SQLITE_BUSY} -do_test 1.3 { - $ic unlock -} {SQLITE_BUSY} -do_test 1.4 { - $ic error -} {SQLITE_BUSY {database is locked}} -do_test 1.4 { - $ic close -} {} - -finish_test - DELETED ext/intck/intckcorrupt.test Index: ext/intck/intckcorrupt.test ================================================================== --- ext/intck/intckcorrupt.test +++ /dev/null @@ -1,235 +0,0 @@ -# 2024 Feb 21 -# -# 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 intck extensions response -# to corruption at the b-tree level. -# - -source [file join [file dirname [info script]] intck_common.tcl] -set testprefix intckcorrupt - -#------------------------------------------------------------------------- -reset_db -do_test 1.0 { - sqlite3 db {} - db deserialize [decode_hexdb { -| size 356352 pagesize 4096 filename crash-acaae0347204ae.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 d0 00 00 00 .....@ ........ -| 32: 40 00 ea 00 00 00 00 00 00 40 00 00 00 40 00 00 @........@...@.. -| 96: 00 00 00 00 0d 00 00 00 04 0e 9c 00 0f ad 0f 4f ...............O -| 112: 0e fc 0e 9c 00 00 00 00 00 00 00 00 00 00 00 00 ................ -| 3728: 00 00 00 00 00 00 00 00 00 00 00 00 5e 04 07 17 ............^... -| 3744: 1f 1f 01 81 0b 74 61 62 6c 65 74 31 5f 70 61 72 .....tablet1_par -| 3760: 65 6e 74 74 31 5f 70 61 72 65 6e 74 04 43 52 45 entt1_parent.CRE -| 3776: 41 54 45 20 54 41 42 4c 45 20 22 74 31 5f 70 61 ATE TABLE .t1_pa -| 3792: 72 65 6e 74 22 28 6e 6f 64 65 6e 6f 20 49 4e 54 rent.(nodeno INT -| 3808: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY -| 3824: 2c 70 61 72 65 6e 74 6e 6f 64 65 29 51 03 06 17 ,parentnode)Q... -| 3840: 1b 1b 01 7b 74 61 62 6c 65 74 31 5f 6e 6f 64 65 ....tablet1_node -| 3856: 74 31 5f 6e 6f 64 65 03 43 52 45 41 54 45 20 54 t1_node.CREATE T -| 3872: 41 42 4c 45 20 22 74 31 5f 6e 6f 64 65 22 28 6e ABLE .t1_node.(n -| 3888: 6f 64 65 6e 6f 20 49 4e 54 45 47 45 52 20 50 52 odeno INTEGER PR -| 3904: 49 4d 41 52 59 20 4b 45 59 2c 64 61 74 61 29 5c IMARY KEY,data). -| 3920: 02 07 17 1d 1d 01 81 0b 74 61 62 6c 65 74 31 5f ........tablet1_ -| 3936: 72 6f 77 69 64 74 31 5f 72 6f 77 69 64 02 43 52 rowidt1_rowid.CR -| 3952: 45 41 54 45 20 54 41 42 4c 45 20 22 74 31 5f 72 EATE TABLE .t1_r -| 3968: 6f 77 69 64 22 28 72 6f 77 69 64 20 49 4e 54 45 owid.(rowid INTE -| 3984: 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c GER PRIMARY KEY, -| 4000: 6e 6f 64 65 6e 6f 2c 61 30 2c 61 31 29 51 01 07 nodeno,a0,a1)Q.. -| 4016: 17 11 11 08 81 0f 74 61 62 6c 65 74 31 74 31 43 ......tablet1t1C -| 4032: 52 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 REATE VIRTUAL TA -| 4048: 42 4c 45 20 74 31 20 55 53 49 4e 47 20 72 74 72 BLE t1 USING rtr -| 4064: 65 65 28 69 64 2c 78 30 20 50 52 49 4d 41 52 59 ee(id,x0 PRIMARY -| 4080: 20 4b 45 59 2c 70 61 72 65 6e 74 6e 6f 64 65 29 KEY,parentnode) -| page 2 offset 4096 -| 0: 51 03 06 17 1b 1b 01 7b 74 61 62 6c 65 74 31 5f Q.......tablet1_ -| 16: 6e 6f 64 65 74 31 5f 6e 6f 64 65 03 43 52 45 41 nodet1_node.CREA -| 32: 54 45 20 54 41 42 4c 45 20 22 74 31 5f 6e 6f 64 TE TABLE .t1_nod -| 48: 65 22 28 6e 6f 64 65 6e 6f 20 49 4e 54 45 47 45 e.(nodeno INTEGE -| 64: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 64 61 R PRIMARY KEY,da -| 80: 74 61 29 5c 02 07 17 1d 1d 01 81 0b 74 61 62 6c ta).........tabl -| 96: 65 74 31 5f 72 6f 77 69 64 74 31 5f 72 6f 77 69 et1_rowidt1_rowi -| 112: 64 02 43 52 45 41 54 45 20 54 41 42 4c 45 00 00 d.CREATE TABLE.. -| 128: 01 0a 02 00 00 00 01 0e 0d 00 00 00 00 24 0e 0d .............$.. -| 144: 0c 1a 06 85 50 46 60 27 70 08 00 00 00 00 00 00 ....PF`'p....... -| 3824: 00 00 00 00 00 00 00 0d 0e 05 00 09 1d 00 74 6f ..............to -| 3840: 79 20 68 61 6c 66 10 0d 05 00 09 23 00 62 6f 74 y half.....#.bot -| 3856: 74 6f 6d 20 68 61 6c 66 0f 0c 05 00 09 21 00 72 tom half.....!.r -| 3872: 69 67 68 74 20 68 61 6c 66 0e 0b 05 00 09 1f 00 ight half....... -| 3888: 6c 65 66 74 20 43 15 f6 e6 f6 46 50 34 35 24 54 left C....FP45$T -| 3904: 15 44 52 05 44 14 24 c4 52 02 27 43 15 f6 e6 f6 .DR.D.$.R.'C.... -| 3920: 46 52 22 8e 6f 64 65 6e 6f 20 49 4e 54 45 47 45 FR..odeno INTEGE -| 3936: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 64 61 R PRIMARY KEY,da -| 3952: 74 61 29 5c 02 07 17 1d 1d 01 81 0b 74 61 62 6c ta).........tabl -| 3968: 65 74 31 5f 72 6f 74 74 6f 6d 20 65 64 67 65 0f et1_rottom edge. -| 3984: 07 05 00 09 21 00 72 69 67 68 74 20 65 64 67 65 ....!.right edge -| 4000: 0e 06 05 00 09 1f 00 6c 65 66 74 20 65 64 67 65 .......left edge -| 4016: 0b 05 05 00 09 19 00 63 65 6e 74 65 72 17 04 05 .......center... -| 4032: 00 09 31 00 75 70 70 65 72 2d 72 69 67 68 74 20 ..1.upper-right -| 4048: 63 6f 72 6e 65 72 17 03 05 00 09 31 00 6c 6f 77 corner.....1.low -| 4064: 65 72 2d 72 69 67 68 74 20 63 6f 72 6e 65 72 16 er-right corner. -| 4080: 02 05 00 09 2f 00 75 70 70 65 72 2d 6c 65 66 74 ..../.upper-left -| page 3 offset 8192 -| 0: 20 63 6f 72 6e 65 72 16 01 05 00 09 2f 01 8c 6f corner...../..o -| 16: 77 65 72 2d 6c 53 51 4c 69 74 65 20 66 6f 72 6d wer-lSQLite form -| 32: 61 74 20 33 00 10 00 01 01 00 40 20 20 00 00 00 at 3......@ ... -| 48: 00 00 00 00 2f 00 00 0d eb 13 00 00 00 03 00 00 ..../........... -| 64: 00 04 00 00 00 00 00 00 00 06 00 00 00 01 00 00 ................ -| 80: 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................ -| page 6 offset 20480 -| 128: 00 00 00 00 00 00 00 00 97 3d 04 ae 7c 01 00 00 .........=..|... -| 624: 00 00 00 00 00 00 21 97 3d 04 ae 7c 01 00 00 00 ......!.=..|.... -| 1120: 00 00 00 00 00 20 97 3d 04 ae 7c 01 00 00 00 00 ..... .=..|..... -| 1616: 00 00 00 00 1f 97 3d 04 ae 7c 01 00 00 00 00 00 ......=..|...... -| 2112: 00 00 00 1e 97 3d 04 ae 7c 01 00 00 00 00 00 00 .....=..|....... -| 2608: 00 00 1d 97 d3 d0 4a e7 c0 00 00 00 00 00 00 00 ......J......... -| 3088: 00 00 00 00 00 00 00 00 00 00 00 00 01 f3 00 00 ................ -| 3600: 23 97 3d 04 ae 7c 01 00 00 00 00 00 00 00 00 00 #.=..|.......... -| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26 ...............& -| page 8 offset 28672 -| 0: 0d 00 00 00 01 04 30 00 04 30 00 00 00 00 00 00 ......0..0...... -| 1072: 97 4d 1e 14 00 ae 7c 00 00 00 00 00 00 00 00 00 .M....|......... -| 1088: 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 ................ -| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................ -| page 10 offset 36864 -| 0: 0d 00 00 00 01 04 30 00 04 30 00 00 00 00 00 00 ......0..0...... -| 1072: 9a ee c1 80 fd 78 1f ce 1b ae eb b4 00 00 00 00 .....x.......... -| 1088: 13 20 ff 20 00 70 00 00 00 60 50 00 00 00 11 e0 . . .p...`P..... -| 1104: 00 00 00 70 00 00 00 60 50 05 35 14 c6 97 46 52 ...p...`P.5...FR -| 1120: 06 66 f7 26 d6 17 42 03 30 01 00 00 10 10 04 02 .f.&..B.0....... -| 1136: 02 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 .........@...... -| 1152: 00 00 00 00 00 40 00 00 00 40 00 00 00 00 00 00 .....@...@...... -| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 ................ -| page 12 offset 45056 -| 0: 0d 00 00 00 01 04 30 00 04 30 e1 b4 30 97 4d 46 ......0..0..0.MF -| 16: 14 00 ae 7c 00 00 00 00 00 00 00 03 00 00 43 00 ...|..........C. -| page 47 offset 188416 -| 2512: 00 00 00 00 00 00 00 00 be 00 00 00 00 00 00 00 ................ -| page 87 offset 352256 -| 2512: 00 00 00 00 00 00 00 00 aa 00 00 00 00 00 00 00 ................ -| end crash-acaae0347204ae.db -}]} {} - -do_intck_test 1.1 { - {corruption found while reading database schema} -} - -#------------------------------------------------------------------------- -reset_db -do_test 2.0 { - sqlite3 db {} - db deserialize [decode_hexdb { -| size 28672 pagesize 4096 filename crash-3afa1ca9e9c1bd.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 07 .....@ ........ -| 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................ -| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................ -| 96: 00 00 00 00 0d 00 00 00 06 0e 88 00 0f b8 0f 6d ...............m -| 112: 0f 3a 0f 0b 0e d5 0e 88 01 00 00 00 00 00 00 00 .:.............. -| 3712: 00 00 00 00 00 00 00 00 4b 06 06 17 25 25 01 5b ........K...%%.[ -| 3728: 74 61 62 6c 65 73 71 6c 69 74 65 5f 73 74 61 74 tablesqlite_stat -| 3744: 31 73 71 6c 69 74 65 5f 73 74 61 74 31 07 43 52 1sqlite_stat1.CR -| 3760: 45 41 54 45 20 54 41 42 4c 45 20 73 71 6c 69 74 EATE TABLE sqlit -| 3776: 65 5f 73 74 61 74 31 28 74 62 6c 2c 69 64 78 2c e_stat1(tbl,idx, -| 3792: 73 74 61 74 29 34 05 06 17 13 11 01 53 69 6e 64 stat)4......Sind -| 3808: 65 78 63 31 63 63 31 06 43 52 45 41 54 45 20 55 exc1cc1.CREATE U -| 3824: 4e 49 51 55 45 20 49 4e 44 45 58 20 63 31 63 20 NIQUE INDEX c1c -| 3840: 4f 4e 20 63 31 28 63 2c 20 62 29 2d 04 06 17 13 ON c1(c, b)-.... -| 3856: 11 01 45 69 6e 64 65 78 63 31 64 63 31 05 43 52 ..Eindexc1dc1.CR -| 3872: 45 41 54 45 20 49 4e 44 45 58 20 63 31 64 20 4f EATE INDEX c1d O -| 3888: 4e 20 63 31 28 64 2c 20 62 29 31 03 06 17 13 11 N c1(d, b)1..... -| 3904: 01 4d 69 6e 64 65 78 62 31 63 62 31 05 43 52 45 .Mindexb1cb1.CRE -| 3920: 41 54 45 20 55 4e 49 51 55 45 20 49 4e 44 45 58 ATE UNIQUE INDEX -| 3936: 20 62 31 63 20 4f 4e 20 62 31 28 63 29 49 02 06 b1c ON b1(c)I.. -| 3952: 17 11 11 0f 7f 74 61 62 6c 65 63 31 63 31 03 43 .....tablec1c1.C -| 3968: 52 45 41 54 45 20 54 41 42 4c 45 20 63 31 28 61 REATE TABLE c1(a -| 3984: 20 49 4e 54 20 50 52 49 4d 41 52 59 20 4b 45 59 INT PRIMARY KEY -| 4000: 2c 20 62 2c 20 63 2c 20 64 29 20 57 49 54 48 4f , b, c, d) WITHO -| 4016: 55 54 20 52 4f 57 49 44 46 01 06 17 11 11 01 79 UT ROWIDF......y -| 4032: 74 61 62 6c 65 62 31 62 31 02 43 52 45 41 54 45 tableb1b1.CREATE -| 4048: 20 54 41 42 4c 45 20 62 31 28 61 20 49 4e 54 20 TABLE b1(a INT -| 4064: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 2c 20 PRIMARY KEY, b, -| 4080: 63 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 c) WITHOUT ROWID -| page 2 offset 4096 -| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f f2 0f ea 0f e2 ................ -| 16: 0f da 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................ -| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 06 ................ -| 4048: 67 07 07 04 01 0f 01 06 66 06 07 04 01 0f 01 05 g.......f....... -| 4064: 65 05 07 04 01 0f 01 04 64 04 07 04 01 0f 01 03 e.......d....... -| 4080: 63 03 07 04 01 0f 01 02 62 0f 05 04 09 0f 09 61 c.......b......a -| page 3 offset 8192 -| 0: 0a 00 00 00 07 0f bd 00 0f f9 0f ef 0f e5 0f db ................ -| 16: 0f d1 0f c7 0f bd 00 00 00 00 01 00 00 00 00 00 ................ -| 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 09 05 01 ................ -| 4032: 0f 01 01 07 61 07 07 09 05 01 0f 01 01 06 61 06 ....a.........a. -| 4048: 06 09 05 01 0f 01 01 05 61 05 05 09 05 01 0f 01 ........a....... -| 4064: 01 04 61 04 04 09 05 01 0f 01 01 03 61 03 03 09 ..a.........a... -| 4080: 05 01 0f 01 01 02 61 0f 02 06 05 09 0f 09 09 61 ......a........a -| page 4 offset 12288 -| 0: 0a 00 00 00 07 0f d8 00 0f fc 0f f0 0f ea 0f e4 ................ -| 16: 0f de 0f d8 0f f6 00 00 00 00 00 00 00 00 00 00 ................ -| 4048: 00 00 00 00 00 00 00 00 05 03 01 01 07 07 05 03 ................ -| 4064: 01 01 06 06 05 03 01 01 05 05 05 03 01 01 04 04 ................ -| 4080: 05 03 01 01 03 03 05 03 01 01 0f 02 03 03 09 09 ................ -| page 5 offset 16384 -| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f f2 0f ea 0f 00 ................ -| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 07 ................ -| 4048: 61 07 07 04 01 0f 01 06 61 06 07 04 01 0f 01 05 a.......a....... -| 4064: 61 05 07 04 01 1f 01 04 61 04 07 04 01 0f 01 03 a.......a....... -| 4080: 61 03 07 04 01 0f 01 02 61 02 05 04 09 0f 09 61 a.......a......a -| page 6 offset 20480 -| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f ea 0f e2 00 00 ................ -| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 07 ................ -| 4048: 61 07 07 04 01 0f 01 06 61 06 07 04 01 0f 01 05 a.......a....... -| 4064: 61 05 07 04 01 0f 01 04 61 04 07 04 01 0f 01 03 a.......a....... -| 4080: 61 03 07 04 01 0f 01 0f 61 02 05 04 09 0f 09 61 a.......a......a -| page 7 offset 24576 -| 0: 0d 00 00 00 05 0f 1c 00 0f f0 0f e0 0f d3 0f c5 ................ -| 16: 0f b8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ -| 4016: 00 00 00 00 00 00 00 00 0b 05 04 11 11 13 62 31 ..............b1 -| 4032: 62 31 37 20 31 0c 04 04 11 13 13 62 31 62 31 63 b17 1......b1b1c -| 4048: 37 20 31 0b 03 04 11 11 13 63 31 63 31 37 20 31 7 1......c1c17 1 -| 4064: 0e 02 04 11 13 07 63 31 63 31 64 37 20 31 20 31 ......c1c1d7 1 1 -| 4080: 0e 01 04 11 13 17 63 31 63 31 63 37 20 31 00 00 ......c1c1c7 1.. -| end crash-3afa1ca9e9c1bd.db -}]} {} - -do_intck_test 2.1 { - {corruption found while reading database schema} -} - -#------------------------------------------------------------------------- -reset_db -do_execsql_test 3.0 { - PRAGMA page_size = 1024; - CREATE TABLE t1(a, b); - CREATE INDEX i1 ON t1(a); - INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 3); -} - -do_test 3.1 { - set pgno [db one {SELECT rootpage FROM sqlite_schema WHERE name='t1'}] - db close - hexio_write test.db [expr ($pgno-1)*1024] 0000 -} {2} - -sqlite3 db test.db -do_intck_test 3.2 { - {corruption found while scanning database object i1} - {corruption found while scanning database object t1} -} - -finish_test - - DELETED ext/intck/intckfault.test Index: ext/intck/intckfault.test ================================================================== --- ext/intck/intckfault.test +++ /dev/null @@ -1,45 +0,0 @@ -# 2024 February 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. -# -#*********************************************************************** -# - -source [file join [file dirname [info script]] intck_common.tcl] -set testprefix intckfault - - - -do_execsql_test 1.0 { - CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); - INSERT INTO t1 VALUES(1, 2, 3); - INSERT INTO t1 VALUES(2, 'two', 'three'); - INSERT INTO t1 VALUES(3, NULL, NULL); - CREATE INDEX i1 ON t1(b, c); -} - -do_faultsim_test 1 -faults oom-t* -prep { -} -body { - set ::ic [sqlite3_intck db main] - set nStep 0 - while {"SQLITE_OK"==[$::ic step]} { - incr nStep - if {$nStep==3} { $::ic unlock } - } - set res [$::ic error] - $::ic close - set res -} -test { - catch { $::ic close } -puts $testresult -puts $testnfail - faultsim_test_result {0 {SQLITE_OK {}}} {0 {SQLITE_NOMEM {}}} {0 {SQLITE_NOMEM {out of memory}}} -} - -finish_test - DELETED ext/intck/sqlite3intck.c Index: ext/intck/sqlite3intck.c ================================================================== --- ext/intck/sqlite3intck.c +++ /dev/null @@ -1,941 +0,0 @@ -/* -** 2024-02-08 -** -** 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. -** -************************************************************************* -*/ - -#include "sqlite3intck.h" -#include -#include - -#include -#include - -/* -** nKeyVal: -** The number of values that make up the 'key' for the current pCheck -** statement. -** -** rc: -** Error code returned by most recent sqlite3_intck_step() or -** sqlite3_intck_unlock() call. This is set to SQLITE_DONE when -** the integrity-check operation is finished. -** -** zErr: -** If the object has entered the error state, this is the error message. -** Is freed using sqlite3_free() when the object is deleted. -** -** zTestSql: -** The value returned by the most recent call to sqlite3_intck_testsql(). -** Each call to testsql() frees the previous zTestSql value (using -** sqlite3_free()) and replaces it with the new value it will return. -*/ -struct sqlite3_intck { - sqlite3 *db; - const char *zDb; /* Copy of zDb parameter to _open() */ - char *zObj; /* Current object. Or NULL. */ - - sqlite3_stmt *pCheck; /* Current check statement */ - char *zKey; - int nKeyVal; - - char *zMessage; - int bCorruptSchema; - - int rc; /* Error code */ - char *zErr; /* Error message */ - char *zTestSql; /* Returned by sqlite3_intck_test_sql() */ -}; - - -/* -** Some error has occurred while using database p->db. Save the error message -** and error code currently held by the database handle in p->rc and p->zErr. -*/ -static void intckSaveErrmsg(sqlite3_intck *p){ - p->rc = sqlite3_errcode(p->db); - sqlite3_free(p->zErr); - p->zErr = sqlite3_mprintf("%s", sqlite3_errmsg(p->db)); -} - -/* -** If the handle passed as the first argument is already in the error state, -** then this function is a no-op (returns NULL immediately). Otherwise, if an -** error occurs within this function, it leaves an error in said handle. -** -** Otherwise, this function attempts to prepare SQL statement zSql and -** return the resulting statement handle to the user. -*/ -static sqlite3_stmt *intckPrepare(sqlite3_intck *p, const char *zSql){ - sqlite3_stmt *pRet = 0; - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3_prepare_v2(p->db, zSql, -1, &pRet, 0); - if( p->rc!=SQLITE_OK ){ - intckSaveErrmsg(p); - assert( pRet==0 ); - } - } - return pRet; -} - -/* -** If the handle passed as the first argument is already in the error state, -** then this function is a no-op (returns NULL immediately). Otherwise, if an -** error occurs within this function, it leaves an error in said handle. -** -** Otherwise, this function treats argument zFmt as a printf() style format -** string. It formats it according to the trailing arguments and then -** attempts to prepare the results and return the resulting prepared -** statement. -*/ -static sqlite3_stmt *intckPrepareFmt(sqlite3_intck *p, const char *zFmt, ...){ - sqlite3_stmt *pRet = 0; - va_list ap; - char *zSql = 0; - va_start(ap, zFmt); - zSql = sqlite3_vmprintf(zFmt, ap); - if( p->rc==SQLITE_OK && zSql==0 ){ - p->rc = SQLITE_NOMEM; - } - pRet = intckPrepare(p, zSql); - sqlite3_free(zSql); - va_end(ap); - return pRet; -} - -/* -** Finalize SQL statement pStmt. If an error occurs and the handle passed -** as the first argument does not already contain an error, store the -** error in the handle. -*/ -static void intckFinalize(sqlite3_intck *p, sqlite3_stmt *pStmt){ - int rc = sqlite3_finalize(pStmt); - if( p->rc==SQLITE_OK && rc!=SQLITE_OK ){ - intckSaveErrmsg(p); - } -} - -/* -** If there is already an error in handle p, return it. Otherwise, call -** sqlite3_step() on the statement handle and return that value. -*/ -static int intckStep(sqlite3_intck *p, sqlite3_stmt *pStmt){ - if( p->rc ) return p->rc; - return sqlite3_step(pStmt); -} - -/* -** Execute SQL statement zSql. There is no way to obtain any results -** returned by the statement. This function uses the sqlite3_intck error -** code convention. -*/ -static void intckExec(sqlite3_intck *p, const char *zSql){ - sqlite3_stmt *pStmt = 0; - pStmt = intckPrepare(p, zSql); - intckStep(p, pStmt); - intckFinalize(p, pStmt); -} - -/* -** A wrapper around sqlite3_mprintf() that uses the sqlite3_intck error -** code convention. -*/ -static char *intckMprintf(sqlite3_intck *p, const char *zFmt, ...){ - va_list ap; - char *zRet = 0; - va_start(ap, zFmt); - zRet = sqlite3_vmprintf(zFmt, ap); - if( p->rc==SQLITE_OK ){ - if( zRet==0 ){ - p->rc = SQLITE_NOMEM; - } - }else{ - sqlite3_free(zRet); - zRet = 0; - } - return zRet; -} - -/* -** This is used by sqlite3_intck_unlock() to save the vector key value -** required to restart the current pCheck query as a nul-terminated string -** in p->zKey. -*/ -static void intckSaveKey(sqlite3_intck *p){ - int ii; - char *zSql = 0; - sqlite3_stmt *pStmt = 0; - sqlite3_stmt *pXinfo = 0; - const char *zDir = 0; - - assert( p->pCheck ); - assert( p->zKey==0 ); - - pXinfo = intckPrepareFmt(p, - "SELECT group_concat(desc, '') FROM %Q.sqlite_schema s, " - "pragma_index_xinfo(%Q, %Q) " - "WHERE s.type='index' AND s.name=%Q", - p->zDb, p->zObj, p->zDb, p->zObj - ); - if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXinfo) ){ - zDir = (const char*)sqlite3_column_text(pXinfo, 0); - } - - if( zDir==0 ){ - /* Object is a table, not an index. This is the easy case,as there are - ** no DESC columns or NULL values in a primary key. */ - const char *zSep = "SELECT '(' || "; - for(ii=0; iinKeyVal; ii++){ - zSql = intckMprintf(p, "%z%squote(?)", zSql, zSep); - zSep = " || ', ' || "; - } - zSql = intckMprintf(p, "%z || ')'", zSql); - }else{ - - /* Object is an index. */ - assert( p->nKeyVal>1 ); - for(ii=p->nKeyVal; ii>0; ii--){ - int bLastIsDesc = zDir[ii-1]=='1'; - int bLastIsNull = sqlite3_column_type(p->pCheck, ii)==SQLITE_NULL; - const char *zLast = sqlite3_column_name(p->pCheck, ii); - char *zLhs = 0; - char *zRhs = 0; - char *zWhere = 0; - - if( bLastIsNull ){ - if( bLastIsDesc ) continue; - zWhere = intckMprintf(p, "'%s IS NOT NULL'", zLast); - }else{ - const char *zOp = bLastIsDesc ? "<" : ">"; - zWhere = intckMprintf(p, "'%s %s ' || quote(?%d)", zLast, zOp, ii); - } - - if( ii>1 ){ - const char *zLhsSep = ""; - const char *zRhsSep = ""; - int jj; - for(jj=0; jjpCheck,jj+1); - zLhs = intckMprintf(p, "%z%s%s", zLhs, zLhsSep, zAlias); - zRhs = intckMprintf(p, "%z%squote(?%d)", zRhs, zRhsSep, jj+1); - zLhsSep = ","; - zRhsSep = " || ',' || "; - } - - zWhere = intckMprintf(p, - "'(%z) IS (' || %z || ') AND ' || %z", - zLhs, zRhs, zWhere); - } - zWhere = intckMprintf(p, "'WHERE ' || %z", zWhere); - - zSql = intckMprintf(p, "%z%s(quote( %z ) )", - zSql, - (zSql==0 ? "VALUES" : ",\n "), - zWhere - ); - } - zSql = intckMprintf(p, - "WITH wc(q) AS (\n%z\n)" - "SELECT 'VALUES' || group_concat('(' || q || ')', ',\n ') FROM wc" - , zSql - ); - } - - pStmt = intckPrepare(p, zSql); - if( p->rc==SQLITE_OK ){ - for(ii=0; iinKeyVal; ii++){ - sqlite3_bind_value(pStmt, ii+1, sqlite3_column_value(p->pCheck, ii+1)); - } - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - p->zKey = intckMprintf(p,"%s",(const char*)sqlite3_column_text(pStmt, 0)); - } - intckFinalize(p, pStmt); - } - - sqlite3_free(zSql); - intckFinalize(p, pXinfo); -} - -/* -** Find the next database object (table or index) to check. If successful, -** set sqlite3_intck.zObj to point to a nul-terminated buffer containing -** the object's name before returning. -*/ -static void intckFindObject(sqlite3_intck *p){ - sqlite3_stmt *pStmt = 0; - char *zPrev = p->zObj; - p->zObj = 0; - - assert( p->rc==SQLITE_OK ); - assert( p->pCheck==0 ); - - pStmt = intckPrepareFmt(p, - "WITH tables(table_name) AS (" - " SELECT name" - " FROM %Q.sqlite_schema WHERE (type='table' OR type='index') AND rootpage" - " UNION ALL " - " SELECT 'sqlite_schema'" - ")" - "SELECT table_name FROM tables " - "WHERE ?1 IS NULL OR table_name%s?1 " - "ORDER BY 1" - , p->zDb, (p->zKey ? ">=" : ">") - ); - - if( p->rc==SQLITE_OK ){ - sqlite3_bind_text(pStmt, 1, zPrev, -1, SQLITE_TRANSIENT); - if( sqlite3_step(pStmt)==SQLITE_ROW ){ - p->zObj = intckMprintf(p,"%s",(const char*)sqlite3_column_text(pStmt, 0)); - } - } - intckFinalize(p, pStmt); - - /* If this is a new object, ensure the previous key value is cleared. */ - if( sqlite3_stricmp(p->zObj, zPrev) ){ - sqlite3_free(p->zKey); - p->zKey = 0; - } - - sqlite3_free(zPrev); -} - -/* -** Return the size in bytes of the first token in nul-terminated buffer z. -** For the purposes of this call, a token is either: -** -** * a quoted SQL string, -* * a contiguous series of ascii alphabet characters, or -* * any other single byte. -*/ -static int intckGetToken(const char *z){ - char c = z[0]; - int iRet = 1; - if( c=='\'' || c=='"' || c=='`' ){ - while( 1 ){ - if( z[iRet]==c ){ - iRet++; - if( z[iRet]!=c ) break; - } - iRet++; - } - } - else if( c=='[' ){ - while( z[iRet++]!=']' && z[iRet] ); - } - else if( (c>='A' && c<='Z') || (c>='a' && c<='z') ){ - while( (z[iRet]>='A' && z[iRet]<='Z') || (z[iRet]>='a' && z[iRet]<='z') ){ - iRet++; - } - } - - return iRet; -} - -/* -** Return true if argument c is an ascii whitespace character. -*/ -static int intckIsSpace(char c){ - return (c==' ' || c=='\t' || c=='\n' || c=='\r'); -} - -/* -** Argument z points to the text of a CREATE INDEX statement. This function -** identifies the part of the text that contains either the index WHERE -** clause (if iCol<0) or the iCol'th column of the index. -** -** If (iCol<0), the identified fragment does not include the "WHERE" keyword, -** only the expression that follows it. If (iCol>=0) then the identified -** fragment does not include any trailing sort-order keywords - "ASC" or -** "DESC". -** -** If the CREATE INDEX statement does not contain the requested field or -** clause, NULL is returned and (*pnByte) is set to 0. Otherwise, a pointer to -** the identified fragment is returned and output parameter (*pnByte) set -** to its size in bytes. -*/ -static const char *intckParseCreateIndex(const char *z, int iCol, int *pnByte){ - int iOff = 0; - int iThisCol = 0; - int iStart = 0; - int nOpen = 0; - - const char *zRet = 0; - int nRet = 0; - - int iEndOfCol = 0; - - /* Skip forward until the first "(" token */ - while( z[iOff]!='(' ){ - iOff += intckGetToken(&z[iOff]); - if( z[iOff]=='\0' ) return 0; - } - assert( z[iOff]=='(' ); - - nOpen = 1; - iOff++; - iStart = iOff; - while( z[iOff] ){ - const char *zToken = &z[iOff]; - int nToken = 0; - - /* Check if this is the end of the current column - either a "," or ")" - ** when nOpen==1. */ - if( nOpen==1 ){ - if( z[iOff]==',' || z[iOff]==')' ){ - if( iCol==iThisCol ){ - int iEnd = iEndOfCol ? iEndOfCol : iOff; - nRet = (iEnd - iStart); - zRet = &z[iStart]; - break; - } - iStart = iOff+1; - while( intckIsSpace(z[iStart]) ) iStart++; - iThisCol++; - } - if( z[iOff]==')' ) break; - } - if( z[iOff]=='(' ) nOpen++; - if( z[iOff]==')' ) nOpen--; - nToken = intckGetToken(zToken); - - if( (nToken==3 && 0==sqlite3_strnicmp(zToken, "ASC", nToken)) - || (nToken==4 && 0==sqlite3_strnicmp(zToken, "DESC", nToken)) - ){ - iEndOfCol = iOff; - }else if( 0==intckIsSpace(zToken[0]) ){ - iEndOfCol = 0; - } - - iOff += nToken; - } - - /* iStart is now the byte offset of 1 byte passed the final ')' in the - ** CREATE INDEX statement. Try to find a WHERE clause to return. */ - while( zRet==0 && z[iOff] ){ - int n = intckGetToken(&z[iOff]); - if( n==5 && 0==sqlite3_strnicmp(&z[iOff], "where", 5) ){ - zRet = &z[iOff+5]; - nRet = strlen(zRet); - } - iOff += n; - } - - /* Trim any whitespace from the start and end of the returned string. */ - if( zRet ){ - while( intckIsSpace(zRet[0]) ){ - nRet--; - zRet++; - } - while( nRet>0 && intckIsSpace(zRet[nRet-1]) ) nRet--; - } - - *pnByte = nRet; - return zRet; -} - -/* -** User-defined SQL function wrapper for intckParseCreateIndex(): -** -** SELECT parse_create_index(, ); -*/ -static void intckParseCreateIndexFunc( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - const char *zSql = (const char*)sqlite3_value_text(apVal[0]); - int idx = sqlite3_value_int(apVal[1]); - const char *zRes = 0; - int nRes = 0; - - assert( nVal==2 ); - if( zSql ){ - zRes = intckParseCreateIndex(zSql, idx, &nRes); - } - sqlite3_result_text(pCtx, zRes, nRes, SQLITE_TRANSIENT); -} - -/* -** Return true if sqlite3_intck.db has automatic indexes enabled, false -** otherwise. -*/ -static int intckGetAutoIndex(sqlite3_intck *p){ - int bRet = 0; - sqlite3_stmt *pStmt = 0; - pStmt = intckPrepare(p, "PRAGMA automatic_index"); - if( SQLITE_ROW==intckStep(p, pStmt) ){ - bRet = sqlite3_column_int(pStmt, 0); - } - intckFinalize(p, pStmt); - return bRet; -} - -/* -** Return true if zObj is an index, or false otherwise. -*/ -static int intckIsIndex(sqlite3_intck *p, const char *zObj){ - int bRet = 0; - sqlite3_stmt *pStmt = 0; - pStmt = intckPrepareFmt(p, - "SELECT 1 FROM %Q.sqlite_schema WHERE name=%Q AND type='index'", - p->zDb, zObj - ); - if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - bRet = 1; - } - intckFinalize(p, pStmt); - return bRet; -} - -/* -** Return a pointer to a nul-terminated buffer containing the SQL statement -** used to check database object zObj (a table or index) for corruption. -** If parameter zPrev is not NULL, then it must be a string containing the -** vector key required to restart the check where it left off last time. -** If pnKeyVal is not NULL, then (*pnKeyVal) is set to the number of -** columns in the vector key value for the specified object. -** -** This function uses the sqlite3_intck error code convention. -*/ -static char *intckCheckObjectSql( - sqlite3_intck *p, /* Integrity check object */ - const char *zObj, /* Object (table or index) to scan */ - const char *zPrev, /* Restart key vector, if any */ - int *pnKeyVal /* OUT: Number of key-values for this scan */ -){ - char *zRet = 0; - sqlite3_stmt *pStmt = 0; - int bAutoIndex = 0; - int bIsIndex = 0; - - const char *zCommon = - /* Relation without_rowid also contains just one row. Column "b" is - ** set to true if the table being examined is a WITHOUT ROWID table, - ** or false otherwise. */ - ", without_rowid(b) AS (" - " SELECT EXISTS (" - " SELECT 1 FROM tabname, pragma_index_list(tab, db) AS l" - " WHERE origin='pk' " - " AND NOT EXISTS (SELECT 1 FROM sqlite_schema WHERE name=l.name)" - " )" - ")" - "" - /* Table idx_cols contains 1 row for each column in each index on the - ** table being checked. Columns are: - ** - ** idx_name: Name of the index. - ** idx_ispk: True if this index is the PK of a WITHOUT ROWID table. - ** col_name: Name of indexed column, or NULL for index on expression. - ** col_expr: Indexed expression, including COLLATE clause. - ** col_alias: Alias used for column in 'intck_wrapper' table. - */ - ", idx_cols(idx_name, idx_ispk, col_name, col_expr, col_alias) AS (" - " SELECT l.name, (l.origin=='pk' AND w.b), i.name, COALESCE((" - " SELECT parse_create_index(sql, i.seqno) FROM " - " sqlite_schema WHERE name = l.name" - " ), format('\"%w\"', i.name) || ' COLLATE ' || quote(i.coll))," - " 'c' || row_number() OVER ()" - " FROM " - " tabname t," - " without_rowid w," - " pragma_index_list(t.tab, t.db) l," - " pragma_index_xinfo(l.name) i" - " WHERE i.key" - " UNION ALL" - " SELECT '', 1, '_rowid_', '_rowid_', 'r1' FROM without_rowid WHERE b=0" - ")" - "" - "" - /* - ** For a PK declared as "PRIMARY KEY(a, b) ... WITHOUT ROWID", where - ** the intck_wrapper aliases of "a" and "b" are "c1" and "c2": - ** - ** o_pk: "o.c1, o.c2" - ** i_pk: "i.'a', i.'b'" - ** ... - ** n_pk: 2 - */ - ", tabpk(db, tab, idx, o_pk, i_pk, q_pk, eq_pk, ps_pk, pk_pk, n_pk) AS (" - " WITH pkfields(f, a) AS (" - " SELECT i.col_name, i.col_alias FROM idx_cols i WHERE i.idx_ispk" - " )" - " SELECT t.db, t.tab, t.idx, " - " group_concat(a, ', '), " - " group_concat('i.'||quote(f), ', '), " - " group_concat('quote(o.'||a||')', ' || '','' || '), " - " format('(%s)==(%s)'," - " group_concat('o.'||a, ', '), " - " group_concat(format('\"%w\"', f), ', ')" - " )," - " group_concat('%s', ',')," - " group_concat('quote('||a||')', ', '), " - " count(*)" - " FROM tabname t, pkfields" - ")" - "" - ", idx(name, match_expr, partial, partial_alias, idx_ps, idx_idx) AS (" - " SELECT idx_name," - " format('(%s,%s) IS (%s,%s)', " - " group_concat(i.col_expr, ', '), i_pk," - " group_concat('o.'||i.col_alias, ', '), o_pk" - " ), " - " parse_create_index(" - " (SELECT sql FROM sqlite_schema WHERE name=idx_name), -1" - " )," - " 'cond' || row_number() OVER ()" - " , group_concat('%s', ',')" - " , group_concat('quote('||i.col_alias||')', ', ')" - " FROM tabpk t, " - " without_rowid w," - " idx_cols i" - " WHERE i.idx_ispk==0 " - " GROUP BY idx_name" - ")" - "" - ", wrapper_with(s) AS (" - " SELECT 'intck_wrapper AS (\n SELECT\n ' || (" - " WITH f(a, b) AS (" - " SELECT col_expr, col_alias FROM idx_cols" - " UNION ALL " - " SELECT partial, partial_alias FROM idx WHERE partial IS NOT NULL" - " )" - " SELECT group_concat(format('%s AS %s', a, b), ',\n ') FROM f" - " )" - " || format('\n FROM %Q.%Q ', t.db, t.tab)" - /* If the object being checked is a table, append "NOT INDEXED". - ** Otherwise, append "INDEXED BY ", and then, if the index - ** is a partial index " WHERE ". */ - " || CASE WHEN t.idx IS NULL THEN " - " 'NOT INDEXED'" - " ELSE" - " format('INDEXED BY %Q%s', t.idx, ' WHERE '||i.partial)" - " END" - " || '\n)'" - " FROM tabname t LEFT JOIN idx i ON (i.name=t.idx)" - ")" - "" - ; - - bAutoIndex = intckGetAutoIndex(p); - if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 0"); - - bIsIndex = intckIsIndex(p, zObj); - if( bIsIndex ){ - pStmt = intckPrepareFmt(p, - /* Table idxname contains a single row. The first column, "db", contains - ** the name of the db containing the table (e.g. "main") and the second, - ** "tab", the name of the table itself. */ - "WITH tabname(db, tab, idx) AS (" - " SELECT %Q, (SELECT tbl_name FROM %Q.sqlite_schema WHERE name=%Q), %Q " - ")" - "" - ", whereclause(w_c) AS (%s)" - "" - "%s" /* zCommon */ - "" - ", case_statement(c) AS (" - " SELECT " - " 'CASE WHEN (' || group_concat(col_alias, ', ') || ', 1) IS (\n' " - " || ' SELECT ' || group_concat(col_expr, ', ') || ', 1 FROM '" - " || format('%%Q.%%Q NOT INDEXED WHERE %%s\n', t.db, t.tab, p.eq_pk)" - " || ' )\n THEN NULL\n '" - " || 'ELSE format(''surplus entry ('" - " || group_concat('%%s', ',') || ',' || p.ps_pk" - " || ') in index ' || t.idx || ''', ' " - " || group_concat('quote('||i.col_alias||')', ', ') || ', ' || p.pk_pk" - " || ')'" - " || '\n END AS error_message'" - " FROM tabname t, tabpk p, idx_cols i WHERE i.idx_name=t.idx" - ")" - "" - ", thiskey(k, n) AS (" - " SELECT group_concat(i.col_alias, ', ') || ', ' || p.o_pk, " - " count(*) + p.n_pk " - " FROM tabpk p, idx_cols i WHERE i.idx_name=p.idx" - ")" - "" - ", main_select(m, n) AS (" - " SELECT format(" - " 'WITH %%s\n' ||" - " ', idx_checker AS (\n' ||" - " ' SELECT %%s,\n' ||" - " ' %%s\n' || " - " ' FROM intck_wrapper AS o\n' ||" - " ')\n'," - " ww.s, c, t.k" - " ), t.n" - " FROM case_statement, wrapper_with ww, thiskey t" - ")" - - "SELECT m || " - " group_concat('SELECT * FROM idx_checker ' || w_c, ' UNION ALL '), n" - " FROM " - "main_select, whereclause " - , p->zDb, p->zDb, zObj, zObj - , zPrev ? zPrev : "VALUES('')", zCommon - ); - }else{ - pStmt = intckPrepareFmt(p, - /* Table tabname contains a single row. The first column, "db", contains - ** the name of the db containing the table (e.g. "main") and the second, - ** "tab", the name of the table itself. */ - "WITH tabname(db, tab, idx, prev) AS (SELECT %Q, %Q, NULL, %Q)" - "" - "%s" /* zCommon */ - - /* expr(e) contains one row for each index on table zObj. Value e - ** is set to an expression that evaluates to NULL if the required - ** entry is present in the index, or an error message otherwise. */ - ", expr(e, p) AS (" - " SELECT format('CASE WHEN EXISTS \n" - " (SELECT 1 FROM %%Q.%%Q AS i INDEXED BY %%Q WHERE %%s%%s)\n" - " THEN NULL\n" - " ELSE format(''entry (%%s,%%s) missing from index %%s'', %%s, %%s)\n" - " END\n'" - " , t.db, t.tab, i.name, i.match_expr, ' AND (' || partial || ')'," - " i.idx_ps, t.ps_pk, i.name, i.idx_idx, t.pk_pk)," - " CASE WHEN partial IS NULL THEN NULL ELSE i.partial_alias END" - " FROM tabpk t, idx i" - ")" - - ", numbered(ii, cond, e) AS (" - " SELECT 0, 'n.ii=0', 'NULL'" - " UNION ALL " - " SELECT row_number() OVER ()," - " '(n.ii='||row_number() OVER ()||COALESCE(' AND '||p||')', ')'), e" - " FROM expr" - ")" - - ", counter_with(w) AS (" - " SELECT 'WITH intck_counter(ii) AS (\n ' || " - " group_concat('SELECT '||ii, ' UNION ALL\n ') " - " || '\n)' FROM numbered" - ")" - "" - ", case_statement(c) AS (" - " SELECT 'CASE ' || " - " group_concat(format('\n WHEN %%s THEN (%%s)', cond, e), '') ||" - " '\nEND AS error_message'" - " FROM numbered" - ")" - "" - - /* This table contains a single row consisting of a single value - - ** the text of an SQL expression that may be used by the main SQL - ** statement to output an SQL literal that can be used to resume - ** the scan if it is suspended. e.g. for a rowid table, an expression - ** like: - ** - ** format('(%d,%d)', _rowid_, n.ii) - */ - ", thiskey(k, n) AS (" - " SELECT o_pk || ', ii', n_pk+1 FROM tabpk" - ")" - "" - ", whereclause(w_c) AS (" - " SELECT CASE WHEN prev!='' THEN " - " '\nWHERE (' || o_pk ||', n.ii) > ' || prev" - " ELSE ''" - " END" - " FROM tabpk, tabname" - ")" - "" - ", main_select(m, n) AS (" - " SELECT format(" - " '%%s, %%s\nSELECT %%s,\n%%s\nFROM intck_wrapper AS o" - ", intck_counter AS n%%s\nORDER BY %%s', " - " w, ww.s, c, thiskey.k, whereclause.w_c, t.o_pk" - " ), thiskey.n" - " FROM case_statement, tabpk t, counter_with, " - " wrapper_with ww, thiskey, whereclause" - ")" - - "SELECT m, n FROM main_select", - p->zDb, zObj, zPrev, zCommon - ); - } - - while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - zRet = intckMprintf(p, "%s", (const char*)sqlite3_column_text(pStmt, 0)); - if( pnKeyVal ){ - *pnKeyVal = sqlite3_column_int(pStmt, 1); - } - } - intckFinalize(p, pStmt); - - if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 1"); - return zRet; -} - -/* -** Open a new integrity-check object. -*/ -int sqlite3_intck_open( - sqlite3 *db, /* Database handle to operate on */ - const char *zDbArg, /* "main", "temp" etc. */ - sqlite3_intck **ppOut /* OUT: New integrity-check handle */ -){ - sqlite3_intck *pNew = 0; - int rc = SQLITE_OK; - const char *zDb = zDbArg ? zDbArg : "main"; - int nDb = strlen(zDb); - - pNew = (sqlite3_intck*)sqlite3_malloc(sizeof(*pNew) + nDb + 1); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pNew, 0, sizeof(*pNew)); - pNew->db = db; - pNew->zDb = (const char*)&pNew[1]; - memcpy(&pNew[1], zDb, nDb+1); - rc = sqlite3_create_function(db, "parse_create_index", - 2, SQLITE_UTF8, 0, intckParseCreateIndexFunc, 0, 0 - ); - if( rc!=SQLITE_OK ){ - sqlite3_intck_close(pNew); - pNew = 0; - } - } - - *ppOut = pNew; - return rc; -} - -/* -** Free the integrity-check object. -*/ -void sqlite3_intck_close(sqlite3_intck *p){ - if( p ){ - sqlite3_finalize(p->pCheck); - sqlite3_create_function( - p->db, "parse_create_index", 1, SQLITE_UTF8, 0, 0, 0, 0 - ); - sqlite3_free(p->zObj); - sqlite3_free(p->zKey); - sqlite3_free(p->zTestSql); - sqlite3_free(p->zErr); - sqlite3_free(p->zMessage); - sqlite3_free(p); - } -} - -/* -** Step the integrity-check object. -*/ -int sqlite3_intck_step(sqlite3_intck *p){ - if( p->rc==SQLITE_OK ){ - - if( p->zMessage ){ - sqlite3_free(p->zMessage); - p->zMessage = 0; - } - - if( p->bCorruptSchema ){ - p->rc = SQLITE_DONE; - }else - if( p->pCheck==0 ){ - intckFindObject(p); - if( p->rc==SQLITE_OK ){ - if( p->zObj ){ - char *zSql = 0; - zSql = intckCheckObjectSql(p, p->zObj, p->zKey, &p->nKeyVal); - p->pCheck = intckPrepare(p, zSql); - sqlite3_free(zSql); - sqlite3_free(p->zKey); - p->zKey = 0; - }else{ - p->rc = SQLITE_DONE; - } - }else if( p->rc==SQLITE_CORRUPT ){ - p->rc = SQLITE_OK; - p->zMessage = intckMprintf(p, "%s", - "corruption found while reading database schema" - ); - p->bCorruptSchema = 1; - } - } - - if( p->pCheck ){ - assert( p->rc==SQLITE_OK ); - if( sqlite3_step(p->pCheck)==SQLITE_ROW ){ - /* Normal case, do nothing. */ - }else{ - intckFinalize(p, p->pCheck); - p->pCheck = 0; - p->nKeyVal = 0; - if( p->rc==SQLITE_CORRUPT ){ - p->rc = SQLITE_OK; - p->zMessage = intckMprintf(p, - "corruption found while scanning database object %s", p->zObj - ); - } - } - } - } - - return p->rc; -} - -/* -** Return a message describing the corruption encountered by the most recent -** call to sqlite3_intck_step(), or NULL if no corruption was encountered. -*/ -const char *sqlite3_intck_message(sqlite3_intck *p){ - assert( p->pCheck==0 || p->zMessage==0 ); - if( p->zMessage ){ - return p->zMessage; - } - if( p->pCheck ){ - return (const char*)sqlite3_column_text(p->pCheck, 0); - } - return 0; -} - -/* -** Return the error code and message. -*/ -int sqlite3_intck_error(sqlite3_intck *p, const char **pzErr){ - if( pzErr ) *pzErr = p->zErr; - return (p->rc==SQLITE_DONE ? SQLITE_OK : p->rc); -} - -/* -** Close any read transaction the integrity-check object is holding open -** on the database. -*/ -int sqlite3_intck_unlock(sqlite3_intck *p){ - if( p->rc==SQLITE_OK && p->pCheck ){ - assert( p->zKey==0 && p->nKeyVal>0 ); - intckSaveKey(p); - intckFinalize(p, p->pCheck); - p->pCheck = 0; - } - return p->rc; -} - -/* -** Return the SQL statement used to check object zObj. Or, if zObj is -** NULL, the current SQL statement. -*/ -const char *sqlite3_intck_test_sql(sqlite3_intck *p, const char *zObj){ - sqlite3_free(p->zTestSql); - if( zObj ){ - p->zTestSql = intckCheckObjectSql(p, zObj, 0, 0); - }else{ - if( p->zObj ){ - p->zTestSql = intckCheckObjectSql(p, p->zObj, p->zKey, 0); - }else{ - sqlite3_free(p->zTestSql); - p->zTestSql = 0; - } - } - return p->zTestSql; -} - DELETED ext/intck/sqlite3intck.h Index: ext/intck/sqlite3intck.h ================================================================== --- ext/intck/sqlite3intck.h +++ /dev/null @@ -1,171 +0,0 @@ -/* -** 2024-02-08 -** -** 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. -** -************************************************************************* -*/ - -/* -** Incremental Integrity-Check Extension -** ------------------------------------- -** -** This module contains code to check whether or not an SQLite database -** is well-formed or corrupt. This is the same task as performed by SQLite's -** built-in "PRAGMA integrity_check" command. This module differs from -** "PRAGMA integrity_check" in that: -** -** + It is less thorough - this module does not detect certain types -** of corruption that are detected by the PRAGMA command. However, -** it does detect all kinds of corruption that are likely to cause -** errors in SQLite applications. -** -** + It is slower. Sometimes up to three times slower. -** -** + It allows integrity-check operations to be split into multiple -** transactions, so that the database does not need to be read-locked -** for the duration of the integrity-check. -** -** One way to use the API to run integrity-check on the "main" database -** of handle db is: -** -** int rc = SQLITE_OK; -** sqlite3_intck *p = 0; -** -** sqlite3_intck_open(db, "main", &p); -** while( SQLITE_OK==sqlite3_intck_step(p) ){ -** const char *zMsg = sqlite3_intck_message(p); -** if( zMsg ) printf("corruption: %s\n", zMsg); -** } -** rc = sqlite3_intck_error(p, &zErr); -** if( rc!=SQLITE_OK ){ -** printf("error occured (rc=%d), (errmsg=%s)\n", rc, zErr); -** } -** sqlite3_intck_close(p); -** -** Usually, the sqlite3_intck object opens a read transaction within the -** first call to sqlite3_intck_step() and holds it open until the -** integrity-check is complete. However, if sqlite3_intck_unlock() is -** called, the read transaction is ended and a new read transaction opened -** by the subsequent call to sqlite3_intck_step(). -*/ - -#ifndef _SQLITE_INTCK_H -#define _SQLITE_INTCK_H - -#include "sqlite3.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* -** An ongoing incremental integrity-check operation is represented by an -** opaque pointer of the following type. -*/ -typedef struct sqlite3_intck sqlite3_intck; - -/* -** Open a new incremental integrity-check object. If successful, populate -** output variable (*ppOut) with the new object handle and return SQLITE_OK. -** Or, if an error occurs, set (*ppOut) to NULL and return an SQLite error -** code (e.g. SQLITE_NOMEM). -** -** The integrity-check will be conducted on database zDb (which must be "main", -** "temp", or the name of an attached database) of database handle db. Once -** this function has been called successfully, the caller should not use -** database handle db until the integrity-check object has been destroyed -** using sqlite3_intck_close(). -*/ -int sqlite3_intck_open( - sqlite3 *db, /* Database handle */ - const char *zDb, /* Database name ("main", "temp" etc.) */ - sqlite3_intck **ppOut /* OUT: New sqlite3_intck handle */ -); - -/* -** Close and release all resources associated with a handle opened by an -** earlier call to sqlite3_intck_open(). The results of using an -** integrity-check handle after it has been passed to this function are -** undefined. -*/ -void sqlite3_intck_close(sqlite3_intck *pCk); - -/* -** Do the next step of the integrity-check operation specified by the handle -** passed as the only argument. This function returns SQLITE_DONE if the -** integrity-check operation is finished, or an SQLite error code if -** an error occurs, or SQLITE_OK if no error occurs but the integrity-check -** is not finished. It is not considered an error if database corruption -** is encountered. -** -** Following a successful call to sqlite3_intck_step() (one that returns -** SQLITE_OK), sqlite3_intck_message() returns a non-NULL value if -** corruption was detected in the db. -** -** If an error occurs and a value other than SQLITE_OK or SQLITE_DONE is -** returned, then the integrity-check handle is placed in an error state. -** In this state all subsequent calls to sqlite3_intck_step() or -** sqlite3_intck_unlock() will immediately return the same error. The -** sqlite3_intck_error() method may be used to obtain an English language -** error message in this case. -*/ -int sqlite3_intck_step(sqlite3_intck *pCk); - -/* -** If the previous call to sqlite3_intck_step() encountered corruption -** within the database, then this function returns a pointer to a buffer -** containing a nul-terminated string describing the corruption in -** English. If the previous call to sqlite3_intck_step() did not encounter -** corruption, or if there was no previous call, this function returns -** NULL. -*/ -const char *sqlite3_intck_message(sqlite3_intck *pCk); - -/* -** Close any read-transaction opened by an earlier call to -** sqlite3_intck_step(). Any subsequent call to sqlite3_intck_step() will -** open a new transaction. Return SQLITE_OK if successful, or an SQLite error -** code otherwise. -** -** If an error occurs, then the integrity-check handle is placed in an error -** state. In this state all subsequent calls to sqlite3_intck_step() or -** sqlite3_intck_unlock() will immediately return the same error. The -** sqlite3_intck_error() method may be used to obtain an English language -** error message in this case. -*/ -int sqlite3_intck_unlock(sqlite3_intck *pCk); - -/* -** If an error has occurred in an earlier call to sqlite3_intck_step() -** or sqlite3_intck_unlock(), then this method returns the associated -** SQLite error code. Additionally, if pzErr is not NULL, then (*pzErr) -** may be set to point to a nul-terminated string containing an English -** language error message. Or, if no error message is available, to -** NULL. -** -** If no error has occurred within sqlite3_intck_step() or -** sqlite_intck_unlock() calls on the handle passed as the first argument, -** then SQLITE_OK is returned and (*pzErr) set to NULL. -*/ -int sqlite3_intck_error(sqlite3_intck *pCk, const char **pzErr); - -/* -** This API is used for testing only. It returns the full-text of an SQL -** statement used to test object zObj, which may be a table or index. -** The returned buffer is valid until the next call to either this function -** or sqlite3_intck_close() on the same sqlite3_intck handle. -*/ -const char *sqlite3_intck_test_sql(sqlite3_intck *pCk, const char *zObj); - - -#ifdef __cplusplus -} /* end of the 'extern "C"' block */ -#endif - -#endif /* ifndef _SQLITE_INTCK_H */ DELETED ext/intck/test_intck.c Index: ext/intck/test_intck.c ================================================================== --- ext/intck/test_intck.c +++ /dev/null @@ -1,238 +0,0 @@ -/* -** 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" -#include "sqlite3intck.h" - -#if defined(INCLUDE_SQLITE_TCL_H) -# include "sqlite_tcl.h" -#else -# include "tcl.h" -#endif - -#include -#include - -/* In test1.c */ -int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); -const char *sqlite3ErrName(int); - -typedef struct TestIntck TestIntck; -struct TestIntck { - sqlite3_intck *intck; -}; - -static int testIntckCmd( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - struct Subcmd { - const char *zName; - int nArg; - const char *zExpect; - } aCmd[] = { - {"close", 0, ""}, /* 0 */ - {"step", 0, ""}, /* 1 */ - {"message", 0, ""}, /* 2 */ - {"error", 0, ""}, /* 3 */ - {"unlock", 0, ""}, /* 4 */ - {"test_sql", 1, ""}, /* 5 */ - {0 , 0} - }; - int rc = TCL_OK; - int iIdx = -1; - TestIntck *p = (TestIntck*)clientData; - - if( objc<2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); - return TCL_ERROR; - } - - rc = Tcl_GetIndexFromObjStruct( - interp, objv[1], aCmd, sizeof(aCmd[0]), "SUB-COMMAND", 0, &iIdx - ); - if( rc ) return rc; - - if( objc!=2+aCmd[iIdx].nArg ){ - Tcl_WrongNumArgs(interp, 2, objv, aCmd[iIdx].zExpect); - return TCL_ERROR; - } - - switch( iIdx ){ - case 0: assert( 0==strcmp("close", aCmd[iIdx].zName) ); { - Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0)); - break; - } - - case 1: assert( 0==strcmp("step", aCmd[iIdx].zName) ); { - int rc = sqlite3_intck_step(p->intck); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); - break; - } - - case 2: assert( 0==strcmp("message", aCmd[iIdx].zName) ); { - const char *z = sqlite3_intck_message(p->intck); - Tcl_SetObjResult(interp, Tcl_NewStringObj(z ? z : "", -1)); - break; - } - - case 3: assert( 0==strcmp("error", aCmd[iIdx].zName) ); { - const char *zErr = 0; - int rc = sqlite3_intck_error(p->intck, 0); - Tcl_Obj *pRes = Tcl_NewObj(); - Tcl_ListObjAppendElement( - interp, pRes, Tcl_NewStringObj(sqlite3ErrName(rc), -1) - ); - sqlite3_intck_error(p->intck, &zErr); - Tcl_ListObjAppendElement( - interp, pRes, Tcl_NewStringObj(zErr ? zErr : 0, -1) - ); - Tcl_SetObjResult(interp, pRes); - break; - } - - case 4: assert( 0==strcmp("unlock", aCmd[iIdx].zName) ); { - int rc = sqlite3_intck_unlock(p->intck); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); - break; - } - - case 5: assert( 0==strcmp("test_sql", aCmd[iIdx].zName) ); { - const char *zObj = Tcl_GetString(objv[2]); - const char *zSql = sqlite3_intck_test_sql(p->intck, zObj[0] ? zObj : 0); - Tcl_SetObjResult(interp, Tcl_NewStringObj(zSql, -1)); - break; - } - } - - return TCL_OK; -} - -/* -** Destructor for commands created by test_sqlite3_intck(). -*/ -static void testIntckFree(void *clientData){ - TestIntck *p = (TestIntck*)clientData; - sqlite3_intck_close(p->intck); - ckfree(p); -} - -/* -** tclcmd: sqlite3_intck DB DBNAME -*/ -static int test_sqlite3_intck( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - char zName[64]; - int iName = 0; - Tcl_CmdInfo info; - TestIntck *p = 0; - sqlite3 *db = 0; - const char *zDb = 0; - int rc = SQLITE_OK; - - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME"); - return TCL_ERROR; - } - - p = (TestIntck*)ckalloc(sizeof(TestIntck)); - memset(p, 0, sizeof(TestIntck)); - - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ - return TCL_ERROR; - } - zDb = Tcl_GetString(objv[2]); - if( zDb[0]=='\0' ) zDb = 0; - - rc = sqlite3_intck_open(db, zDb, &p->intck); - if( rc!=SQLITE_OK ){ - ckfree(p); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errstr(rc), -1)); - return TCL_ERROR; - } - - do { - sprintf(zName, "intck%d", iName++); - }while( Tcl_GetCommandInfo(interp, zName, &info)!=0 ); - Tcl_CreateObjCommand(interp, zName, testIntckCmd, (void*)p, testIntckFree); - Tcl_SetObjResult(interp, Tcl_NewStringObj(zName, -1)); - - return TCL_OK; -} - -/* -** tclcmd: test_do_intck DB DBNAME -*/ -static int test_do_intck( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite3 *db = 0; - const char *zDb = 0; - int rc = SQLITE_OK; - sqlite3_intck *pCk = 0; - Tcl_Obj *pRet = 0; - const char *zErr = 0; - - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME"); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ - return TCL_ERROR; - } - zDb = Tcl_GetString(objv[2]); - - pRet = Tcl_NewObj(); - Tcl_IncrRefCount(pRet); - - rc = sqlite3_intck_open(db, zDb, &pCk); - if( rc==SQLITE_OK ){ - while( sqlite3_intck_step(pCk)==SQLITE_OK ){ - const char *zMsg = sqlite3_intck_message(pCk); - if( zMsg ){ - Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zMsg, -1)); - } - } - rc = sqlite3_intck_error(pCk, &zErr); - } - if( rc!=SQLITE_OK ){ - if( zErr ){ - Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1)); - }else{ - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); - } - }else{ - Tcl_SetObjResult(interp, pRet); - } - Tcl_DecrRefCount(pRet); - sqlite3_intck_close(pCk); - sqlite3_intck_close(0); - return rc ? TCL_ERROR : TCL_OK; -} - -int Sqlitetestintck_Init(Tcl_Interp *interp){ - Tcl_CreateObjCommand(interp, "sqlite3_intck", test_sqlite3_intck, 0, 0); - Tcl_CreateObjCommand(interp, "test_do_intck", test_do_intck, 0, 0); - return TCL_OK; -} Index: ext/recover/test_recover.c ================================================================== --- ext/recover/test_recover.c +++ ext/recover/test_recover.c @@ -234,11 +234,11 @@ } if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR; zDb = Tcl_GetString(objv[2]); if( zDb[0]=='\0' ) zDb = 0; - pNew = (TestRecover*)ckalloc(sizeof(TestRecover)); + pNew = ckalloc(sizeof(TestRecover)); if( bSql==0 ){ zUri = Tcl_GetString(objv[3]); pNew->p = sqlite3_recover_init(db, zDb, zUri); }else{ pNew->interp = interp; Index: ext/rtree/rtree.c ================================================================== --- ext/rtree/rtree.c +++ ext/rtree/rtree.c @@ -692,13 +692,15 @@ /* ** Clear the Rtree.pNodeBlob object */ static void nodeBlobReset(Rtree *pRtree){ - sqlite3_blob *pBlob = pRtree->pNodeBlob; - pRtree->pNodeBlob = 0; - sqlite3_blob_close(pBlob); + if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){ + sqlite3_blob *pBlob = pRtree->pNodeBlob; + pRtree->pNodeBlob = 0; + sqlite3_blob_close(pBlob); + } } /* ** Obtain a reference to an r-tree node. */ @@ -738,10 +740,11 @@ rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName, "data", iNode, 0, &pRtree->pNodeBlob); } if( rc ){ + nodeBlobReset(pRtree); *ppNode = 0; /* If unable to open an sqlite3_blob on the desired row, that can only ** be because the shadow tables hold erroneous data. */ if( rc==SQLITE_ERROR ){ rc = SQLITE_CORRUPT_VTAB; @@ -797,11 +800,10 @@ rc = SQLITE_CORRUPT_VTAB; RTREE_IS_CORRUPT(pRtree); } *ppNode = pNode; }else{ - nodeBlobReset(pRtree); if( pNode ){ pRtree->nNodeRef--; sqlite3_free(pNode); } *ppNode = 0; @@ -942,11 +944,10 @@ RtreeNode *pNode, /* The node from which to extract a coordinate */ int iCell, /* The index of the cell within the node */ int iCoord, /* Which coordinate to extract */ RtreeCoord *pCoord /* OUT: Space to write result to */ ){ - assert( iCellzData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); } /* ** Deserialize cell iCell of node pNode. Populate the structure pointed @@ -1132,13 +1133,11 @@ assert( pRtree->nCursor>0 ); resetCursor(pCsr); sqlite3_finalize(pCsr->pReadAux); sqlite3_free(pCsr); pRtree->nCursor--; - if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){ - nodeBlobReset(pRtree); - } + nodeBlobReset(pRtree); return SQLITE_OK; } /* ** Rtree virtual table module xEof method. @@ -1719,15 +1718,11 @@ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc==SQLITE_OK && ALWAYS(p) ){ - if( p->iCell>=NCELL(pNode) ){ - rc = SQLITE_ABORT; - }else{ - *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); - } + *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); } return rc; } /* @@ -1741,11 +1736,10 @@ int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; if( NEVER(p==0) ) return SQLITE_OK; - if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c); #ifndef SQLITE_RTREE_INT_ONLY @@ -3224,11 +3218,11 @@ ** Called when a transaction starts. */ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; assert( pRtree->inWrTrans==0 ); - pRtree->inWrTrans = 1; + pRtree->inWrTrans++; return SQLITE_OK; } /* ** Called when a transaction completes (either by COMMIT or ROLLBACK). @@ -3238,13 +3232,10 @@ Rtree *pRtree = (Rtree *)pVtab; pRtree->inWrTrans = 0; nodeBlobReset(pRtree); return SQLITE_OK; } -static int rtreeRollback(sqlite3_vtab *pVtab){ - return rtreeEndTransaction(pVtab); -} /* ** The xRename method for rtree module virtual tables. */ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){ @@ -3359,11 +3350,11 @@ rtreeRowid, /* xRowid - read data */ rtreeUpdate, /* xUpdate - write data */ rtreeBeginTransaction, /* xBegin - begin transaction */ rtreeEndTransaction, /* xSync - sync transaction */ rtreeEndTransaction, /* xCommit - commit transaction */ - rtreeRollback, /* xRollback - rollback transaction */ + rtreeEndTransaction, /* xRollback - rollback transaction */ 0, /* xFindFunction - function overloading */ rtreeRename, /* xRename - rename the table */ rtreeSavepoint, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ DELETED ext/rtree/rtreeJ.test Index: ext/rtree/rtreeJ.test ================================================================== --- ext/rtree/rtreeJ.test +++ /dev/null @@ -1,273 +0,0 @@ -# 2024-02-03 -# -# 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. -# -#*********************************************************************** -# -# ROLLBACK in the middle of an RTREE query -# -if {![info exists testdir]} { - set testdir [file join [file dirname [info script]] .. .. test] -} -source $testdir/tester.tcl -set testprefix rtreeJ -ifcapable !rtree { finish_test ; return } - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2); - INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2); -} {} - -do_execsql_test 1.1 { - SELECT * FROM t1 -} {1 1.0 1.0 2 2.0 2.0} - -# If a ROLLBACK occurs that backs out changes to the RTREE, then -# all pending queries to the RTREE are aborted. -# -do_test 1.2 { - db eval { - BEGIN; - INSERT INTO t1 VALUES(3, 3, 3); - INSERT INTO t1 VALUES(4, 4, 4); - } - set rc [catch { - db eval { SELECT * FROM t1 } { - if {$id==1} { - db eval { ROLLBACK } - } - lappend res $id $x1 $x2 - } - } msg] - list $rc $msg -} {1 {query aborted}} - -do_execsql_test 1.3 { - SELECT * FROM t1; -} {1 1.0 1.0 2 2.0 2.0} - -# A COMMIT of changes to the RTREE does not affect pending queries -# -do_test 1.4 { - set res {} - db eval { - BEGIN; - INSERT INTO t1 VALUES(5, 5, 5); - INSERT INTO t1 VALUES(6, 6, 6); - } - db eval { SELECT * FROM t1 } { - if {$id==1} { - db eval { COMMIT } - } - lappend res $id $x1 $x2 - } - set res -} {1 1.0 1.0 2 2.0 2.0 5 5.0 5.0 6 6.0 6.0} - -do_execsql_test 1.5 { - SELECT * FROM t1; -} {1 1.0 1.0 2 2.0 2.0 5 5.0 5.0 6 6.0 6.0} - -do_execsql_test 1.6 { - DELETE FROM t1; - INSERT INTO t1 VALUES(1,1,1),(2,2,2),(3,3,3),(4,4,4); - CREATE TABLE t2(x); - SELECT * FROM t1; -} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0} - -# A rollback that does not affect the rtree table because -# the rtree table has not been written to does not cause -# a query abort. -# -do_test 1.7 { - set res {} - db eval { - BEGIN; - INSERT INTO t2(x) VALUES(12345); - } - db eval { SELECT * FROM t1 } { - if {$id==1} { - db eval { ROLLBACK } - } - lappend res $id $x1 $x2 - } - set res -} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0} - -# ROLLBACK TO that affects the RTREE does cause a query abort. -# -do_test 1.8 { - db eval { - DELETE FROM t1 WHERE rowid>1; - BEGIN; - DELETE FROM t2; - INSERT INTO t2(x) VALUES(23456); - SAVEPOINT 'one'; - INSERT INTO t1 VALUES(2,2,2),(3,3,3); - } - set rc [catch { - db eval { SELECT * FROM t1 } { - if {$id==1} { - db eval { ROLLBACK TO 'one'; } - } - lappend res $id $x1 $x2 - } - } msg] - list $rc $msg -} {1 {query aborted}} - -do_execsql_test 1.9 { - COMMIT; - SELECT * FROM t1; -} {1 1.0 1.0} - -# ROLLBACK TO that does not affect the RTREE does not cause a query abort. -# -do_execsql_test 1.10 { - DELETE FROM t1; - INSERT INTO t1 VALUES(1,1,1),(2,2,2),(3,3,3); - BEGIN; - DELETE FROM t2; - INSERT INTO t2(x) VALUES(34567); - SAVEPOINT 'one'; - INSERT INTO t2(x) VALUES('a string'); - SELECT * FROM t1; -} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0} -do_test 1.11 { - set rc [catch { - set res {} - db eval { SELECT * FROM t1 } { - if {$id==2} { - # db eval { ROLLBACK TO 'one'; } - } - lappend res $id $x1 $x2 - } - set res - } msg] - list $rc $msg -} {0 {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0}} - -do_execsql_test 1.12 { - COMMIT; - SELECT * FROM t1; -} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0} - -#---------------------------------------------------------------------- - -reset_db -do_execsql_test 2.0 { - CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2); - INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2); - CREATE TABLE t2(x); -} {} - -do_test 2.1 { - db eval { - BEGIN; - INSERT INTO t1 VALUES(3, 3, 3); - PRAGMA writable_schema = RESET; - } - - set rc [catch { - db eval { SELECT x1, x2 FROM t1 } { - if {$x1==1} { - db eval { ROLLBACK } - } - lappend res $x1 $x2 - } - } msg] - list $rc $msg -} {1 {query aborted}} - -do_execsql_test 2.1 { - CREATE TABLE bak_node(nodeno, data); - CREATE TABLE bak_parent(nodeno, parentnode); - CREATE TABLE bak_rowid(rowid, nodeno); -} -proc save_t1 {} { - db eval { - DELETE FROM bak_node; - DELETE FROM bak_parent; - DELETE FROM bak_rowid; - INSERT INTO bak_node SELECT * FROM t1_node; - INSERT INTO bak_parent SELECT * FROM t1_parent; - INSERT INTO bak_rowid SELECT * FROM t1_rowid; - } -} -proc restore_t1 {} { - db eval { - DELETE FROM t1_node; - DELETE FROM t1_parent; - DELETE FROM t1_rowid; - INSERT INTO t1_node SELECT * FROM bak_node; - INSERT INTO t1_parent SELECT * FROM bak_parent; - INSERT INTO t1_rowid SELECT * FROM bak_rowid; - } -} - -do_test 2.3 { - save_t1 - db eval { - INSERT INTO t1 VALUES(3, 3, 3); - } - set rc [catch { - db eval { SELECT rowid, x1, x2 FROM t1 } { - if {$x1==1} { - restore_t1 - } - lappend res $x1 $x2 - } - } msg] - list $rc $msg -} {1 {query aborted}} -do_execsql_test 2.4 { - SELECT * FROM t1 -} {1 1.0 1.0 2 2.0 2.0} - -do_test 2.5 { - save_t1 - db eval { - INSERT INTO t1 VALUES(3, 3, 3); - } - set rc [catch { - db eval { SELECT x1 FROM t1 } { - if {$x1==1} { - restore_t1 - } - lappend res $x1 $x2 - } - } msg] - list $rc $msg -} {1 {query aborted}} -do_execsql_test 2.6 { - SELECT * FROM t1 -} {1 1.0 1.0 2 2.0 2.0} - -do_test 2.7 { - save_t1 - db eval { - INSERT INTO t1 VALUES(3, 3, 3); - } - set ::res [list] - set rc [catch { - db eval { SELECT 'abc' FROM t1 } { - if {$::res==[list]} { - restore_t1 - set ::bDone 1 - } - lappend res abc - } - } msg] - set res -} {abc abc abc} -do_execsql_test 2.6 { - SELECT * FROM t1 -} {1 1.0 1.0 2 2.0 2.0} - - -finish_test Index: ext/session/sessionstat1.test ================================================================== --- ext/session/sessionstat1.test +++ ext/session/sessionstat1.test @@ -90,11 +90,11 @@ ANALYZE; } } {} do_execsql_test -db db2 2.2 { - SELECT * FROM sqlite_stat1 ORDER BY tbl, idx + SELECT * FROM sqlite_stat1 } { t1 sqlite_autoindex_t1_1 {32 1} t1 t1b {32 4} t1 t1c {32 16} } @@ -102,11 +102,11 @@ do_test 2.3 { do_then_apply_sql -ignorenoop { DROP INDEX t1c } } {} do_execsql_test -db db2 2.4 { - SELECT * FROM sqlite_stat1 ORDER BY tbl, idx; + SELECT * FROM sqlite_stat1 } { t1 sqlite_autoindex_t1_1 {32 1} t1 t1b {32 4} } Index: ext/userauth/user-auth.txt ================================================================== --- ext/userauth/user-auth.txt +++ ext/userauth/user-auth.txt @@ -1,17 +1,5 @@ -*********************************** NOTICE ************************************ -* This extension is deprecated. The SQLite developers do not maintain this * -* extension. At some point in the future, it might disappear from the source * -* tree. * -* * -* If you are using this extension and think it should be supported moving * -* forward, visit the SQLite Forum (https://sqlite.org/forum) and argue your * -* case there. * -* * -* This deprecation notice was added on 2024-01-22. * -******************************************************************************* - Activate the user authentication logic by including the ext/userauth/userauth.c source code file in the build and adding the -DSQLITE_USER_AUTHENTICATION compile-time option. The ext/userauth/sqlite3userauth.h header file is available to applications to define the interface. Index: ext/wasm/GNUmakefile ================================================================== --- ext/wasm/GNUmakefile +++ ext/wasm/GNUmakefile @@ -979,13 +979,10 @@ emcc.speedtest1.common += --no-entry emcc.speedtest1.common += -sABORTING_MALLOC emcc.speedtest1.common += -sSTRICT_JS=0 emcc.speedtest1.common += -sMODULARIZE emcc.speedtest1.common += -Wno-limited-postlink-optimizations -emcc.speedtest1.common += -Wno-unused-main -# ^^^^ -Wno-unused-main is for emcc 3.1.52+. speedtest1 has a wasm_main() which is -# exported and called by the JS code. EXPORTED_FUNCTIONS.speedtest1 := $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1) emcc.speedtest1.common += -sSTACK_SIZE=512KB emcc.speedtest1.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1) emcc.speedtest1.common += $(emcc.exportedRuntimeMethods) emcc.speedtest1.common += -sALLOW_TABLE_GROWTH Index: ext/wasm/SQLTester/SQLTester.mjs ================================================================== --- ext/wasm/SQLTester/SQLTester.mjs +++ ext/wasm/SQLTester/SQLTester.mjs @@ -172,21 +172,15 @@ //! "Special" characters - we have to escape output if it contains any. special: /[\x00-\x20\x22\x5c\x7b\x7d]/, squiggly: /[{}]/ }); - - const Util = newObj({ toss, - unlink: function f(fn){ - if(!f.unlink){ - f.unlink = sqlite3.wasm.xWrap('sqlite3__wasm_vfs_unlink','int', - ['*','string']); - } - return 0==f.unlink(0,fn); + unlink: function(fn){ + return 0==sqlite3.wasm.sqlite3_wasm_vfs_unlink(0,fn); }, argvToString: (list)=>{ const m = [...list]; m.shift() /* strip command name */; @@ -201,11 +195,11 @@ ); }, utf8Encode: (str)=>__utf8Encoder.encode(str), - strglob: sqlite3.wasm.xWrap('sqlite3__wasm_SQLTester_strglob','int', + strglob: sqlite3.wasm.xWrap('sqlite3_wasm_SQLTester_strglob','int', ['string','string']) })/*Util*/; class Outer { #lnBuf = []; Index: ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js ================================================================== --- ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js +++ ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js @@ -1135,13 +1135,12 @@ Imports the contents of an SQLite database, provided as a byte array or ArrayBuffer, under the given name, overwriting any existing content. Throws if the pool has no available file slots, on I/O error, or if the input does not appear to be a database. In the latter case, only a cursory examination is made. - Results are undefined if the given db name refers to an opened - db. Note that this routine is _only_ for importing database - files, not arbitrary files, the reason being that this VFS will + Note that this routine is _only_ for importing database files, + not arbitrary files, the reason being that this VFS will automatically clean up any non-database files so importing them is pointless. If passed a function for its second argument, its behavior changes to asynchronous and it imports its data in chunks fed to Index: ext/wasm/api/sqlite3-vfs-opfs.c-pp.js ================================================================== --- ext/wasm/api/sqlite3-vfs-opfs.c-pp.js +++ ext/wasm/api/sqlite3-vfs-opfs.c-pp.js @@ -1191,22 +1191,20 @@ /** Asynchronously imports the given bytes (a byte array or ArrayBuffer) into the given database file. - Results are undefined if the given db name refers to an opened - db. - If passed a function for its second argument, its behaviour - changes: imports its data in chunks fed to it by the given - callback function. It calls the callback (which may be async) - repeatedly, expecting either a Uint8Array or ArrayBuffer (to - denote new input) or undefined (to denote EOF). For so long as - the callback continues to return non-undefined, it will append - incoming data to the given VFS-hosted database file. When - called this way, the resolved value of the returned Promise is - the number of bytes written to the target file. + changes to async and it imports its data in chunks fed to it by + the given callback function. It calls the callback (which may + be async) repeatedly, expecting either a Uint8Array or + ArrayBuffer (to denote new input) or undefined (to denote + EOF). For so long as the callback continues to return + non-undefined, it will append incoming data to the given + VFS-hosted database file. When called this way, the resolved + value of the returned Promise is the number of bytes written to + the target file. It very specifically requires the input to be an SQLite3 database and throws if that's not the case. It does so in order to prevent this function from taking on a larger scope than it is specifically intended to. i.e. we do not want it to Index: ext/wasm/api/sqlite3-worker1-promiser.c-pp.js ================================================================== --- ext/wasm/api/sqlite3-worker1-promiser.c-pp.js +++ ext/wasm/api/sqlite3-worker1-promiser.c-pp.js @@ -154,19 +154,18 @@ }; const toss = (...args)=>{throw new Error(args.join(' '))}; if(!config.worker) config.worker = callee.defaultConfig.worker; if('function'===typeof config.worker) config.worker = config.worker(); let dbId; - let promiserFunc; config.worker.onmessage = function(ev){ ev = ev.data; debug('worker1.onmessage',ev); let msgHandler = handlerMap[ev.messageId]; if(!msgHandler){ if(ev && 'sqlite3-api'===ev.type && 'worker1-ready'===ev.result) { /*fired one time when the Worker1 API initializes*/ - if(config.onready) config.onready(promiserFunc); + if(config.onready) config.onready(); return; } msgHandler = handlerMap[ev.type] /* check for exec per-row callback */; if(msgHandler && msgHandler.onrow){ msgHandler.onrow(ev); @@ -191,11 +190,11 @@ break; } try {msgHandler.resolve(ev)} catch(e){msgHandler.reject(e)} }/*worker.onmessage()*/; - return promiserFunc = function(/*(msgType, msgArgs) || (msgEnvelope)*/){ + return function(/*(msgType, msgArgs) || (msgEnvelope)*/){ let msg; if(1===arguments.length){ msg = arguments[0]; }else if(2===arguments.length){ msg = Object.create(null); Index: ext/wasm/demo-worker1-promiser.js ================================================================== --- ext/wasm/demo-worker1-promiser.js +++ ext/wasm/demo-worker1-promiser.js @@ -47,12 +47,10 @@ debug: 1 ? undefined : (...args)=>console.debug('worker debug',...args), onunhandled: function(ev){ error("Unhandled worker message:",ev.data); }, onready: function(){ - T.affirm(arguments[0] === workerPromise - /* as of version 3.46. Prior to that this callback had no arguments */); self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/; runTests(); }, onerror: function(ev){ error("worker1 error:",ev); Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -373,13 +373,11 @@ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_tok.c \ $(TOP)/ext/rtree/test_rtreedoc.c \ $(TOP)/ext/recover/sqlite3recover.c \ $(TOP)/ext/recover/dbdata.c \ - $(TOP)/ext/recover/test_recover.c \ - $(TOP)/ext/intck/test_intck.c \ - $(TOP)/ext/intck/sqlite3intck.c + $(TOP)/ext/recover/test_recover.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -870,11 +870,11 @@ u64 nDistinct = p->current.anDLt[i] + 1; u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; sqlite3_str_appendf(&sStat, " %llu", iVal); #ifdef SQLITE_ENABLE_STAT4 - assert( p->current.anEq[i] || p->nRow==0 ); + assert( p->current.anEq[i] ); #endif } sqlite3ResultStrAccum(context, &sStat); } #ifdef SQLITE_ENABLE_STAT4 @@ -1055,11 +1055,11 @@ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); sqlite3VdbeLoadString(v, regTabname, pTab->zName); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; /* Number of columns in pIdx. "N" */ - int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */ + int addrRewind; /* Address of "OP_Rewind iIdxCur" */ int addrNextRow; /* Address of "next_row:" */ const char *zIdxName; /* Name of the index */ int nColTest; /* Number of columns to test for changes */ if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; @@ -1079,18 +1079,13 @@ VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName)); /* ** Pseudo-code for loop that calls stat_push(): ** - ** regChng = 0 ** Rewind csr - ** if eof(csr){ - ** stat_init() with count = 0; - ** goto end_of_scan; - ** } - ** count() - ** stat_init() + ** if eof(csr) goto end_of_scan; + ** regChng = 0 ** goto chng_addr_0; ** ** next_row: ** regChng = 0 ** if( idx(0) != regPrev(0) ) goto chng_addr_0 @@ -1125,40 +1120,45 @@ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); - /* Implementation of the following: - ** - ** regChng = 0 - ** Rewind csr - ** if eof(csr){ - ** stat_init() with count = 0; - ** goto end_of_scan; - ** } - ** count() - ** stat_init() - ** goto chng_addr_0; - */ - assert( regTemp2==regStat+4 ); - sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); - - /* Arguments to stat_init(): + /* Invoke the stat_init() function. The arguments are: + ** ** (1) the number of columns in the index including the rowid ** (or for a WITHOUT ROWID table, the number of PK columns), ** (2) the number of columns in the key without the rowid/pk - ** (3) estimated number of rows in the index. */ + ** (3) estimated number of rows in the index, + */ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); assert( regRowid==regStat+2 ); sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); - sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, - OptimizationDisabled(db, SQLITE_Stat4)); +#ifdef SQLITE_ENABLE_STAT4 + if( OptimizationEnabled(db, SQLITE_Stat4) ){ + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp); + addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); + VdbeCoverage(v); + }else +#endif + { + addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1); + } + assert( regTemp2==regStat+4 ); + sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, &statInitFuncdef, 0); - addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); - VdbeCoverage(v); + /* Implementation of the following: + ** + ** Rewind csr + ** if eof(csr) goto end_of_scan; + ** regChng = 0 + ** goto next_push_0; + ** + */ sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); addrNextRow = sqlite3VdbeCurrentAddr(v); if( nColTest>0 ){ int endDistinctTest = sqlite3VdbeMakeLabel(pParse); @@ -1261,16 +1261,10 @@ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); } } /* Add the entry to the stat1 table. */ - if( pIdx->pPartIdxWhere ){ - /* Partial indexes might get a zero-entry in sqlite_stat1. But - ** an empty table is omitted from sqlite_stat1. */ - sqlite3VdbeJumpHere(v, addrGotoEnd); - addrGotoEnd = 0; - } callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); assert( "BBB"[0]==SQLITE_AFF_TEXT ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); @@ -1290,16 +1284,10 @@ int regSampleRowid = regCol + nCol; int addrNext; int addrIsNull; u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; - /* No STAT4 data is generated if the number of rows is zero */ - if( addrGotoEnd==0 ){ - sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER); - addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); - } - if( doOnce ){ int mxCol = nCol; Index *pX; /* Compute the maximum number of columns in any index */ @@ -1348,11 +1336,11 @@ sqlite3VdbeJumpHere(v, addrIsNull); } #endif /* SQLITE_ENABLE_STAT4 */ /* End of analysis */ - if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd); + sqlite3VdbeJumpHere(v, addrRewind); } /* Create a single sqlite_stat1 entry containing NULL as the index ** name and the row count as the content. Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -149,51 +149,12 @@ # define SQLITE_CORRUPT_PAGE(pMemPage) corruptPageError(__LINE__, pMemPage) #else # define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) #endif -/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled -** or if the lock tracking is disabled. This is always the value for -** release builds. -*/ -#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/ - #ifndef SQLITE_OMIT_SHARED_CACHE -#if 0 -/* ^---- Change to 1 and recompile to enable shared-lock tracing -** for debugging purposes. -** -** Print all shared-cache locks on a BtShared. Debugging use only. -*/ -static void sharedLockTrace( - BtShared *pBt, - const char *zMsg, - int iRoot, - int eLockType -){ - BtLock *pLock; - if( iRoot>0 ){ - printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W"); - }else{ - printf("%s-%p:", zMsg, pBt); - } - for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ - printf(" %p/%u%s", pLock->pBtree, pLock->iTable, - pLock->eLock==READ_LOCK ? "R" : "W"); - while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){ - pLock = pLock->pNext; - printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W"); - } - } - printf("\n"); - fflush(stdout); -} -#undef SHARED_LOCK_TRACE -#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE) -#endif /* Shared-lock tracing */ - #ifdef SQLITE_DEBUG /* **** This function is only used as part of an assert() statement. *** ** ** Check to see if pBtree holds the required locks to read or write to the @@ -266,12 +227,10 @@ } }else{ iTab = iRoot; } - SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType); - /* Search for the required lock. Either a write-lock on root-page iTab, a ** write-lock on the schema table, or (if the client is reading) a ** read-lock on iTab will suffice. Return 1 if any of these are found. */ for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ if( pLock->pBtree==pBtree @@ -401,12 +360,10 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pLock = 0; BtLock *pIter; - SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock); - assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); /* A connection with the read-uncommitted flag set will never try to @@ -470,12 +427,10 @@ assert( sqlite3BtreeHoldsMutex(p) ); assert( p->sharable || 0==*ppIter ); assert( p->inTrans>0 ); - SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0); - while( *ppIter ){ BtLock *pLock = *ppIter; assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ @@ -510,13 +465,10 @@ /* ** This function changes all write-locks held by Btree p into read-locks. */ static void downgradeAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; - - SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0); - if( pBt->pWriter==p ){ BtLock *pLock; pBt->pWriter = 0; pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ @@ -5126,16 +5078,13 @@ if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; if( pCur->aOverflow==0 || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) ){ - Pgno *aNew; - if( sqlite3FaultSim(413) ){ - aNew = 0; - }else{ - aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno)); - } + Pgno *aNew = (Pgno*)sqlite3Realloc( + pCur->aOverflow, nOvfl*2*sizeof(Pgno) + ); if( aNew==0 ){ return SQLITE_NOMEM_BKPT; }else{ pCur->aOverflow = aNew; } @@ -6180,14 +6129,14 @@ u8 i; assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - /* Currently this interface is only called by the OP_IfSizeBetween - ** opcode and the OP_Count opcode with P3=1. In either case, - ** the cursor will always be valid unless the btree is empty. */ - if( pCur->eState!=CURSOR_VALID ) return 0; + /* Currently this interface is only called by the OP_IfSmaller + ** opcode, and it that case the cursor will always be valid and + ** will always point to a leaf node. */ + if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1; if( NEVER(pCur->pPage->leaf==0) ) return -1; n = pCur->pPage->nCell; for(i=0; iiPage; i++){ n *= pCur->apPage[i]->nCell; @@ -6329,14 +6278,11 @@ if( pCur->skipNext<0 ) return SQLITE_OK; } } pPage = pCur->pPage; - if( sqlite3FaultSim(412) ) pPage->isInit = 0; - if( !pPage->isInit ){ - return SQLITE_CORRUPT_BKPT; - } + assert( pPage->isInit ); if( !pPage->leaf ){ int idx = pCur->ix; rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); if( rc ) return rc; rc = moveToRightmost(pCur); @@ -7005,14 +6951,11 @@ /* This is the common case where everything fits on the btree page ** and no overflow pages are required. */ n = nHeader + nPayload; testcase( n==3 ); testcase( n==4 ); - if( n<4 ){ - n = 4; - pPayload[nPayload] = 0; - } + if( n<4 ) n = 4; *pnSize = n; assert( nSrc<=nPayload ); testcase( nSrcaData[0]!=apOld[0]->aData[0] ){ - rc = SQLITE_CORRUPT_PAGE(pOld); + rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } /* Load b.apCell[] with pointers to all cells in pOld. If pOld ** contains overflow cells, include them in the b.apCell[] array @@ -8338,11 +8281,11 @@ ** first. */ memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); if( pOld->nOverflow>0 ){ if( NEVER(limitaiOvfl[0]) ){ - rc = SQLITE_CORRUPT_PAGE(pOld); + rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } limit = pOld->aiOvfl[0]; for(j=0; jpBt->pCursor; pOther; pOther=pOther->pNext){ if( pOther!=pCur && pOther->eState==CURSOR_VALID && pOther->pPage==pCur->pPage ){ - return SQLITE_CORRUPT_PAGE(pCur->pPage); + return SQLITE_CORRUPT_BKPT; } } return SQLITE_OK; } @@ -9041,11 +8984,11 @@ } }else if( sqlite3PagerPageRefcount(pPage->pDbPage)>1 ){ /* The page being written is not a root page, and there is currently ** more than one reference to it. This only happens if the page is one ** of its own ancestor pages. Corruption. */ - rc = SQLITE_CORRUPT_PAGE(pPage); + rc = SQLITE_CORRUPT_BKPT; }else{ MemPage * const pParent = pCur->apPage[iPage-1]; int const iIdx = pCur->aiIdx[iPage-1]; rc = sqlite3PagerWrite(pParent->pDbPage); @@ -9205,11 +9148,11 @@ ovflPageSize = pBt->usableSize - 4; do{ rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); if( rc ) return rc; if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ - rc = SQLITE_CORRUPT_PAGE(pPage); + rc = SQLITE_CORRUPT_BKPT; }else{ if( iOffset+ovflPageSize<(u32)nTotal ){ ovflPgno = get4byte(pPage->aData); }else{ ovflPageSize = nTotal - iOffset; @@ -9233,11 +9176,11 @@ MemPage *pPage = pCur->pPage; /* Page being written */ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd || pCur->info.pPayload < pPage->aData + pPage->cellOffset ){ - return SQLITE_CORRUPT_PAGE(pPage); + return SQLITE_CORRUPT_BKPT; } if( pCur->info.nLocal==nTotal ){ /* The entire cell is local */ return btreeOverwriteContent(pPage, pCur->info.pPayload, pX, 0, pCur->info.nLocal); @@ -9314,11 +9257,11 @@ /* This can only happen if the schema is corrupt such that there is more ** than one table or index with the same root page as used by the cursor. ** Which can only happen if the SQLITE_NoSchemaError flag was set when ** the schema was loaded. This cannot be asserted though, as a user might ** set the flag, load the schema, and then unset the flag. */ - return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); + return SQLITE_CORRUPT_BKPT; } } /* Ensure that the cursor is not in the CURSOR_FAULT state and that it ** points to a valid cell. @@ -9437,11 +9380,11 @@ assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); assert( pPage->leaf || !pPage->intKey ); if( pPage->nFree<0 ){ if( NEVER(pCur->eState>CURSOR_INVALID) ){ /* ^^^^^--- due to the moveToRoot() call above */ - rc = SQLITE_CORRUPT_PAGE(pPage); + rc = SQLITE_CORRUPT_BKPT; }else{ rc = btreeComputeFreeSpace(pPage); } if( rc ) return rc; } @@ -9454,14 +9397,11 @@ assert( newCell!=0 ); assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); if( flags & BTREE_PREFORMAT ){ rc = SQLITE_OK; szNew = p->pBt->nPreformatSize; - if( szNew<4 ){ - szNew = 4; - newCell[3] = 0; - } + if( szNew<4 ) szNew = 4; if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){ CellInfo info; pPage->xParseCell(pPage, newCell, &info); if( info.nPayload!=info.nLocal ){ Pgno ovfl = get4byte(&newCell[szNew-4]); @@ -9479,11 +9419,11 @@ pCur->info.nSize = 0; if( loc==0 ){ CellInfo info; assert( idx>=0 ); if( idx>=pPage->nCell ){ - return SQLITE_CORRUPT_PAGE(pPage); + return SQLITE_CORRUPT_BKPT; } rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ){ goto end_insert; } @@ -9506,14 +9446,14 @@ ** This optimization cannot be used on an autovacuum database if the ** new entry uses overflow pages, as the insertCell() call below is ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ - return SQLITE_CORRUPT_PAGE(pPage); + return SQLITE_CORRUPT_BKPT; } if( oldCell+szNew > pPage->aDataEnd ){ - return SQLITE_CORRUPT_PAGE(pPage); + return SQLITE_CORRUPT_BKPT; } memcpy(oldCell, newCell, szNew); return SQLITE_OK; } dropCell(pPage, idx, info.nSize, &rc); @@ -9611,11 +9551,11 @@ } if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); nIn = pSrc->info.nLocal; aIn = pSrc->info.pPayload; if( aIn+nIn>pSrc->pPage->aDataEnd ){ - return SQLITE_CORRUPT_PAGE(pSrc->pPage); + return SQLITE_CORRUPT_BKPT; } nRem = pSrc->info.nPayload; if( nIn==nRem && nInpPage->maxLocal ){ memcpy(aOut, aIn, nIn); pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace); @@ -9636,11 +9576,11 @@ pBt->nPreformatSize += 4; } if( nRem>nIn ){ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){ - return SQLITE_CORRUPT_PAGE(pSrc->pPage); + return SQLITE_CORRUPT_BKPT; } ovflIn = get4byte(&pSrc->info.pPayload[nIn]); } do { @@ -9732,27 +9672,27 @@ if( pCur->eState>=CURSOR_REQUIRESEEK ){ rc = btreeRestoreCursorPosition(pCur); assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); if( rc || pCur->eState!=CURSOR_VALID ) return rc; }else{ - return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); + return SQLITE_CORRUPT_BKPT; } } assert( pCur->eState==CURSOR_VALID ); iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; if( pPage->nCell<=iCellIdx ){ - return SQLITE_CORRUPT_PAGE(pPage); + return SQLITE_CORRUPT_BKPT; } pCell = findCell(pPage, iCellIdx); if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ - return SQLITE_CORRUPT_PAGE(pPage); + return SQLITE_CORRUPT_BKPT; } if( pCell<&pPage->aCellIdx[pPage->nCell] ){ - return SQLITE_CORRUPT_PAGE(pPage); + return SQLITE_CORRUPT_BKPT; } /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must ** be preserved following this delete operation. If the current delete ** will cause a b-tree rebalance, then this is done by saving the cursor @@ -9839,11 +9779,11 @@ n = pCur->apPage[iCellDepth+1]->pgno; }else{ n = pCur->pPage->pgno; } pCell = findCell(pLeaf, pLeaf->nCell-1); - if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf); + if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT; nCell = pLeaf->xCellSize(pLeaf, pCell); assert( MX_CELL_SIZE(pBt) >= nCell ); pTmp = pBt->pTmpSpace; assert( pTmp!=0 ); rc = sqlite3PagerWrite(pLeaf->pDbPage); @@ -9955,11 +9895,11 @@ ** root page of the new table should go. meta[3] is the largest root-page ** created so far, so the new root-page is (meta[3]+1). */ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); if( pgnoRoot>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_PGNO(pgnoRoot); + return SQLITE_CORRUPT_BKPT; } pgnoRoot++; /* The new root-page may not be allocated on a pointer-map page, or the ** PENDING_BYTE page. @@ -10003,11 +9943,11 @@ if( rc!=SQLITE_OK ){ return rc; } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ - rc = SQLITE_CORRUPT_PGNO(pgnoRoot); + rc = SQLITE_CORRUPT_BKPT; } if( rc!=SQLITE_OK ){ releasePage(pRoot); return rc; } @@ -10093,18 +10033,18 @@ int hdr; CellInfo info; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_PGNO(pgno); + return SQLITE_CORRUPT_BKPT; } rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; if( (pBt->openFlags & BTREE_SINGLE)==0 && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) ){ - rc = SQLITE_CORRUPT_PAGE(pPage); + rc = SQLITE_CORRUPT_BKPT; goto cleardatabasepage_out; } hdr = pPage->hdrOffset; for(i=0; inCell; i++){ pCell = findCell(pPage, i); @@ -10204,11 +10144,11 @@ assert( sqlite3BtreeHoldsMutex(p) ); assert( p->inTrans==TRANS_WRITE ); assert( iTable>=2 ); if( iTable>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_PGNO(iTable); + return SQLITE_CORRUPT_BKPT; } rc = sqlite3BtreeClearTable(p, iTable, 0); if( rc ) return rc; rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -187,11 +187,11 @@ sqlite3VdbeJumpHere(v, addrRewind); } } sqlite3VdbeAddOp0(v, OP_Halt); -#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE) +#if SQLITE_USER_AUTHENTICATION if( pParse->nTableLock>0 && db->init.busy==0 ){ sqlite3UserAuthInit(db); if( db->auth.authLevelrc = SQLITE_AUTH_USER; @@ -2921,15 +2921,15 @@ sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0); /* Test for cycles in generated columns and illegal expressions ** in CHECK constraints and in DEFAULT clauses. */ if( p->tabFlags & TF_HasGenerated ){ - sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, + sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0, sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } - sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, + sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0, sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)", db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } /* Add the table to the in-memory representation of the database. Index: src/date.c ================================================================== --- src/date.c +++ src/date.c @@ -1235,54 +1235,34 @@ } } } /* -** Compute the number of days after the most recent January 1. -** -** In other words, compute the zero-based day number for the -** current year: -** -** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ... -** Dec31 = 364 or 365. +** Compute the one-based day of the year for the DateTime pDate. +** Jan01 = 1, Jan02 = 2, ... Dec31 = 356 or 366. */ -static int daysAfterJan01(DateTime *pDate){ +static int dayOfYear(DateTime *pDate){ DateTime jan01 = *pDate; assert( jan01.validYMD ); assert( jan01.validHMS ); assert( pDate->validJD ); jan01.validJD = 0; jan01.M = 1; jan01.D = 1; computeJD(&jan01); - return (int)((pDate->iJD-jan01.iJD+43200000)/86400000); -} - -/* -** Return the number of days after the most recent Monday. -** -** In other words, return the day of the week according -** to this code: -** -** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday. -*/ -static int daysAfterMonday(DateTime *pDate){ - assert( pDate->validJD ); - return (int)((pDate->iJD+43200000)/86400000) % 7; -} - -/* -** Return the number of days after the most recent Sunday. -** -** In other words, return the day of the week according -** to this code: -** -** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday -*/ -static int daysAfterSunday(DateTime *pDate){ - assert( pDate->validJD ); - return (int)((pDate->iJD+129600000)/86400000) % 7; + return (int)((pDate->iJD-jan01.iJD+43200000)/86400000) + 1; +} + +/* +** Return the day of the week. 1==Monday, 2=Tues, ..., 7=Sunday. +*/ +static int dayOfWeek(DateTime *pDate){ + int w; + assert( pDate->validJD ); + w = ((pDate->iJD+129600000)/86400000) % 7; + if( w==0 ) w = 7; + return w; } /* ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) ** @@ -1362,11 +1342,11 @@ case 'G': /* Fall thru */ case 'g': { DateTime y = x; assert( y.validJD ); /* Move y so that it is the Thursday in the same week as x */ - y.iJD += (3 - daysAfterMonday(&x))*86400000; + y.iJD += (4 - dayOfWeek(&x))*86400000; y.validYMD = 0; computeYMD(&y); if( cf=='g' ){ sqlite3_str_appendf(&sRes, "%02d", y.Y%100); }else{ @@ -1386,11 +1366,12 @@ if( h==0 ) h = 12; sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); break; } case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */ - sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1); + int nDay = dayOfYear(&x); + sqlite3_str_appendf(&sRes,"%03d",nDay); break; } case 'J': { /* Julian day number. (Non-standard) */ sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); break; @@ -1434,33 +1415,39 @@ sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); break; } case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */ case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */ - char c = (char)daysAfterSunday(&x) + '0'; + char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; if( c=='0' && cf=='u' ) c = '7'; sqlite3_str_appendchar(&sRes, 1, c); break; } case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */ - sqlite3_str_appendf(&sRes,"%02d", - (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7); + int wd; /* 0=Sunday, 1=Monday, 2=Tuesday, ... 7=Saturday */ + int nDay; /* Day of the year. 0..364 or 0..365 for leapyears */ + nDay = dayOfYear(&x); + wd = (int)(((x.iJD+43200000)/86400000 + 1)%7); + sqlite3_str_appendf(&sRes,"%02d",(nDay+6-wd)/7); break; } case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */ DateTime y = x; /* Adjust y so that is the Thursday in the same week as x */ assert( y.validJD ); - y.iJD += (3 - daysAfterMonday(&x))*86400000; + y.iJD += (4 - dayOfWeek(&x))*86400000; y.validYMD = 0; computeYMD(&y); - sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1); + sqlite3_str_appendf(&sRes,"%02d", (dayOfYear(&y)-1)/7+1); break; } case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */ - sqlite3_str_appendf(&sRes,"%02d", - (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7); + int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ + int nDay; /* Day of the year. 0..364 or 0..365 for leapyears */ + nDay = dayOfYear(&x); + wd = (int)(((x.iJD+43200000)/86400000)%7); + sqlite3_str_appendf(&sRes,"%02d",(nDay+6-wd)/7); break; } case 'Y': { sqlite3_str_appendf(&sRes,"%04d",x.Y); break; Index: src/func.c ================================================================== --- src/func.c +++ src/func.c @@ -1099,17 +1099,17 @@ switch( sqlite3_value_type(pValue) ){ case SQLITE_FLOAT: { double r1, r2; const char *zVal; r1 = sqlite3_value_double(pValue); - sqlite3_str_appendf(pStr, "%!0.15g", r1); + sqlite3_str_appendf(pStr, "%!.15g", r1); zVal = sqlite3_str_value(pStr); if( zVal ){ sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8); if( r1!=r2 ){ sqlite3_str_reset(pStr); - sqlite3_str_appendf(pStr, "%!0.20e", r1); + sqlite3_str_appendf(pStr, "%!.20e", r1); } } break; } case SQLITE_INTEGER: { Index: src/json.c ================================================================== --- src/json.c +++ src/json.c @@ -619,44 +619,10 @@ c = p->zBuf[p->nUsed-1]; if( c=='[' || c=='{' ) return; jsonAppendChar(p, ','); } -/* c is a control character. Append the canonical JSON representation -** of that control character to p. -** -** This routine assumes that the output buffer has already been enlarged -** sufficiently to hold the worst-case encoding plus a nul terminator. -*/ -static void jsonAppendControlChar(JsonString *p, u8 c){ - static const char aSpecial[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - assert( sizeof(aSpecial)==32 ); - assert( aSpecial['\b']=='b' ); - assert( aSpecial['\f']=='f' ); - assert( aSpecial['\n']=='n' ); - assert( aSpecial['\r']=='r' ); - assert( aSpecial['\t']=='t' ); - assert( c>=0 && cnUsed+7 <= p->nAlloc ); - if( aSpecial[c] ){ - p->zBuf[p->nUsed] = '\\'; - p->zBuf[p->nUsed+1] = aSpecial[c]; - p->nUsed += 2; - }else{ - p->zBuf[p->nUsed] = '\\'; - p->zBuf[p->nUsed+1] = 'u'; - p->zBuf[p->nUsed+2] = '0'; - p->zBuf[p->nUsed+3] = '0'; - p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4]; - p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf]; - p->nUsed += 6; - } -} - /* Append the N-byte string in zIn to the end of the JsonString string ** under construction. Enclose the string in double-quotes ("...") and ** escape any double-quotes or backslash characters contained within the ** string. ** @@ -712,18 +678,39 @@ z += k; N -= k; } c = z[0]; if( c=='"' || c=='\\' ){ + json_simple_escape: if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = c; }else if( c=='\'' ){ p->zBuf[p->nUsed++] = c; }else{ + static const char aSpecial[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + assert( sizeof(aSpecial)==32 ); + assert( aSpecial['\b']=='b' ); + assert( aSpecial['\f']=='f' ); + assert( aSpecial['\n']=='n' ); + assert( aSpecial['\r']=='r' ); + assert( aSpecial['\t']=='t' ); + assert( c>=0 && cnUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return; - jsonAppendControlChar(p, c); + p->zBuf[p->nUsed++] = '\\'; + p->zBuf[p->nUsed++] = 'u'; + p->zBuf[p->nUsed++] = '0'; + p->zBuf[p->nUsed++] = '0'; + p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4]; + p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf]; } z++; N--; } p->zBuf[p->nUsed++] = '"'; @@ -1420,14 +1407,11 @@ k = j+sz; while( j=k ){ + }else if( z[j]!='\\' || j+1>=k ){ return j+1; }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){ j++; }else if( z[j+1]=='u' ){ if( j+5>=k ) return j+1; @@ -1621,11 +1605,10 @@ return j+1; } case '[': { /* Parse array */ iThis = pParse->nBlob; - assert( i<=(u32)pParse->nJson ); jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0); iStart = pParse->nBlob; if( pParse->oom ) return -1; if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; @@ -1718,18 +1701,13 @@ }else{ pParse->iErr = j; return -1; } }else if( c<=0x1f ){ - if( c==0 ){ - pParse->iErr = j; - return -1; - } - /* Control characters are not allowed in canonical JSON string - ** literals, but are allowed in JSON5 string literals. */ - opcode = JSONB_TEXT5; - pParse->hasNonstd = 1; + /* Control characters are not allowed in strings */ + pParse->iErr = j; + return -1; }else if( c=='"' ){ opcode = JSONB_TEXT5; } j++; } @@ -2026,14 +2004,10 @@ */ static void jsonReturnStringAsBlob(JsonString *pStr){ JsonParse px; memset(&px, 0, sizeof(px)); jsonStringTerminate(pStr); - if( pStr->eErr ){ - sqlite3_result_error_nomem(pStr->pCtx); - return; - } px.zJson = pStr->zBuf; px.nJson = pStr->nUsed; px.db = sqlite3_context_db_handle(pStr->pCtx); (void)jsonTranslateTextToBlob(&px, 0); if( px.oom ){ @@ -2097,12 +2071,12 @@ } sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) + (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8]; n = 9; } - if( (i64)i+sz+n > pParse->nBlob - && (i64)i+sz+n > pParse->nBlob-pParse->delta + if( i+sz+n > pParse->nBlob + && i+sz+n > pParse->nBlob-pParse->delta ){ sz = 0; n = 0; } *pSz = sz; @@ -2148,20 +2122,18 @@ jsonAppendRawNZ(pOut, "false", 5); return i+1; } case JSONB_INT: case JSONB_FLOAT: { - if( sz==0 ) goto malformed_jsonb; jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); break; } case JSONB_INT5: { /* Integer literal in hexadecimal notation */ u32 k = 2; sqlite3_uint64 u = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; int bOverflow = 0; - if( sz==0 ) goto malformed_jsonb; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); k++; }else if( zIn[0]=='+' ){ k++; @@ -2180,11 +2152,10 @@ break; } case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ u32 k = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; - if( sz==0 ) goto malformed_jsonb; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); k++; } if( zIn[k]=='.' ){ @@ -2210,11 +2181,11 @@ u32 k; u32 sz2 = sz; zIn = (const char*)&pParse->aBlob[i+n]; jsonAppendChar(pOut, '"'); while( sz2>0 ){ - for(k=0; k0 ){ jsonAppendRawNZ(pOut, zIn, k); if( k>=sz2 ){ break; } @@ -2222,17 +2193,10 @@ sz2 -= k; } if( zIn[0]=='"' ){ jsonAppendRawNZ(pOut, "\\\"", 2); zIn++; - sz2--; - continue; - } - if( zIn[0]<=0x1f ){ - if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break; - jsonAppendControlChar(pOut, zIn[0]); - zIn++; sz2--; continue; } assert( zIn[0]=='\\' ); assert( sz2>=1 ); @@ -2301,36 +2265,34 @@ } case JSONB_ARRAY: { jsonAppendChar(pOut, '['); j = i+n; iEnd = j+sz; - while( jeErr==0 ){ + while( jiEnd ) pOut->eErr |= JSTRING_MALFORMED; if( sz>0 ) jsonStringTrimOneChar(pOut); jsonAppendChar(pOut, ']'); break; } case JSONB_OBJECT: { int x = 0; jsonAppendChar(pOut, '{'); j = i+n; iEnd = j+sz; - while( jeErr==0 ){ + while( jiEnd ) pOut->eErr |= JSTRING_MALFORMED; + if( x & 1 ) pOut->eErr |= JSTRING_MALFORMED; if( sz>0 ) jsonStringTrimOneChar(pOut); jsonAppendChar(pOut, '}'); break; } default: { - malformed_jsonb: pOut->eErr |= JSTRING_MALFORMED; break; } } return i+n+sz; @@ -3253,42 +3215,10 @@ jsonBadPathError(ctx, zPath); } return; } -/* -** If pArg is a blob that seems like a JSONB blob, then initialize -** p to point to that JSONB and return TRUE. If pArg does not seem like -** a JSONB blob, then return FALSE; -** -** This routine is only called if it is already known that pArg is a -** blob. The only open question is whether or not the blob appears -** to be a JSONB blob. -*/ -static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){ - u32 n, sz = 0; - p->aBlob = (u8*)sqlite3_value_blob(pArg); - p->nBlob = (u32)sqlite3_value_bytes(pArg); - if( p->nBlob==0 ){ - p->aBlob = 0; - return 0; - } - if( NEVER(p->aBlob==0) ){ - return 0; - } - if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT - && (n = jsonbPayloadSize(p, 0, &sz))>0 - && sz+n==p->nBlob - && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0) - ){ - return 1; - } - p->aBlob = 0; - p->nBlob = 0; - return 0; -} - /* ** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, ** from the SQL function argument pArg. Return a pointer to the new ** JsonParse object. ** @@ -3341,34 +3271,38 @@ p->hasNonstd = pFromCache->hasNonstd; jsonParseFree(pFromCache); return p; } if( eType==SQLITE_BLOB ){ - if( jsonArgIsJsonb(pArg,p) ){ - if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){ - goto json_pfa_oom; - } - return p; - } - /* If the blob is not valid JSONB, fall through into trying to cast - ** the blob into text which is then interpreted as JSON. (tag-20240123-a) - ** - ** This goes against all historical documentation about how the SQLite - ** JSON functions were suppose to work. From the beginning, blob was - ** reserved for expansion and a blob value should have raised an error. - ** But it did not, due to a bug. And many applications came to depend - ** upon this buggy behavior, espeically when using the CLI and reading - ** JSON text using readfile(), which returns a blob. For this reason - ** we will continue to support the bug moving forward. - ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d - */ + u32 n, sz = 0; + p->aBlob = (u8*)sqlite3_value_blob(pArg); + p->nBlob = (u32)sqlite3_value_bytes(pArg); + if( p->nBlob==0 ){ + goto json_pfa_malformed; + } + if( NEVER(p->aBlob==0) ){ + goto json_pfa_oom; + } + if( (p->aBlob[0] & 0x0f)>JSONB_OBJECT ){ + goto json_pfa_malformed; + } + n = jsonbPayloadSize(p, 0, &sz); + if( n==0 + || sz+n!=p->nBlob + || ((p->aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0) + ){ + goto json_pfa_malformed; + } + if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){ + goto json_pfa_oom; + } + return p; } p->zJson = (char*)sqlite3_value_text(pArg); p->nJson = sqlite3_value_bytes(pArg); - if( db->mallocFailed ) goto json_pfa_oom; if( p->nJson==0 ) goto json_pfa_malformed; - assert( p->zJson!=0 ); + if( NEVER(p->zJson==0) ) goto json_pfa_oom; if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){ if( flgs & JSON_KEEPERROR ){ p->nErr = 1; return p; }else{ @@ -3530,14 +3464,14 @@ } if( showContent ){ if( sz==0 && x<=JSONB_FALSE ){ sqlite3_str_append(pOut, "\n", 1); }else{ - u32 j; + u32 i; sqlite3_str_appendall(pOut, ": \""); - for(j=iStart+n; jaBlob[j]; + for(i=iStart+n; iaBlob[i]; if( c<0x20 || c>=0x7f ) c = '.'; sqlite3_str_append(pOut, (char*)&c, 1); } sqlite3_str_append(pOut, "\"\n", 2); } @@ -3584,16 +3518,15 @@ sqlite3StrAccumInit(&out, 0, 0, 0, 1000000); p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; if( argc==1 ){ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out); - sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8); + sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8); }else{ jsonShowParse(p); } jsonParseFree(p); - sqlite3_str_reset(&out); } #endif /* SQLITE_DEBUG */ /**************************************************************************** ** Scalar SQL function implementations @@ -4336,16 +4269,16 @@ sqlite3_result_int(ctx, 0); #endif return; } case SQLITE_BLOB: { - if( jsonFuncArgMightBeBinary(argv[0]) ){ + if( (flags & 0x0c)!=0 && jsonFuncArgMightBeBinary(argv[0]) ){ if( flags & 0x04 ){ /* Superficial checking only - accomplished by the ** jsonFuncArgMightBeBinary() call above. */ res = 1; - }else if( flags & 0x08 ){ + }else{ /* Strict checking. Check by translating BLOB->TEXT->BLOB. If ** no errors occur, call that a "strict check". */ JsonParse px; u32 iErr; memset(&px, 0, sizeof(px)); @@ -4352,15 +4285,12 @@ px.aBlob = (u8*)sqlite3_value_blob(argv[0]); px.nBlob = sqlite3_value_bytes(argv[0]); iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1); res = iErr==0; } - break; } - /* Fall through into interpreting the input as text. See note - ** above at tag-20240123-a. */ - /* no break */ deliberate_fall_through + break; } default: { JsonParse px; if( (flags & 0x3)==0 ) break; memset(&px, 0, sizeof(px)); @@ -4942,13 +4872,10 @@ break; } case JEACH_VALUE: { u32 i = jsonSkipLabel(p); jsonReturnFromBlob(&p->sParse, i, ctx, 1); - if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){ - sqlite3_result_subtype(ctx, JSON_SUBTYPE); - } break; } case JEACH_TYPE: { u32 i = jsonSkipLabel(p); u8 eType = p->sParse.aBlob[i] & 0x0f; @@ -5095,13 +5022,17 @@ jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; memset(&p->sParse, 0, sizeof(p->sParse)); p->sParse.nJPRef = 1; p->sParse.db = p->db; - if( jsonFuncArgMightBeBinary(argv[0]) ){ - p->sParse.nBlob = sqlite3_value_bytes(argv[0]); - p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); + if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ + if( jsonFuncArgMightBeBinary(argv[0]) ){ + p->sParse.nBlob = sqlite3_value_bytes(argv[0]); + p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); + }else{ + goto json_each_malformed_input; + } }else{ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); p->sParse.nJson = sqlite3_value_bytes(argv[0]); if( p->sParse.zJson==0 ){ p->i = p->iEnd = 0; Index: src/malloc.c ================================================================== --- src/malloc.c +++ src/malloc.c @@ -219,28 +219,10 @@ sqlite3_mutex_leave(mem0.mutex); sqlite3_release_memory(nByte); sqlite3_mutex_enter(mem0.mutex); } -#ifdef SQLITE_DEBUG -/* -** This routine is called whenever an out-of-memory condition is seen, -** It's only purpose to to serve as a breakpoint for gdb or similar -** code debuggers when working on out-of-memory conditions, for example -** caused by PRAGMA hard_heap_limit=N. -*/ -static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){ - static u64 nOomFault = 0; - nOomFault += n; - /* The assert() is never reached in a human lifetime. It is here mostly - ** to prevent code optimizers from optimizing out this function. */ - assert( (nOomFault>>32) < 0xffffffff ); -} -#else -# define test_oom_breakpoint(X) /* No-op for production builds */ -#endif - /* ** Do a memory allocation with statistics and alarms. Assume the ** lock is already held. */ static void mallocWithAlarm(int n, void **pp){ @@ -263,11 +245,10 @@ AtomicStore(&mem0.nearlyFull, 1); sqlite3MallocAlarm(nFull); if( mem0.hardLimit ){ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.hardLimit - nFull ){ - test_oom_breakpoint(1); *pp = 0; return; } } }else{ @@ -552,11 +533,10 @@ if( nDiff>0 && (nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >= mem0.alarmThreshold-nDiff ){ sqlite3MallocAlarm(nDiff); if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ sqlite3_mutex_leave(mem0.mutex); - test_oom_breakpoint(1); return 0; } } pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT Index: src/memdb.c ================================================================== --- src/memdb.c +++ src/memdb.c @@ -797,18 +797,10 @@ rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ){ pOut = 0; }else{ sz = sqlite3_column_int64(pStmt, 0)*szPage; - if( sz==0 ){ - sqlite3_reset(pStmt); - sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0); - rc = sqlite3_step(pStmt); - if( rc==SQLITE_ROW ){ - sz = sqlite3_column_int64(pStmt, 0)*szPage; - } - } if( piSize ) *piSize = sz; if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ pOut = 0; }else{ pOut = sqlite3_malloc64( sz ); Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -1293,16 +1293,12 @@ ** ** If the code incorrectly assumes that it is the POSIX version that is ** available, the error message will often be an empty string. Not a ** huge problem. Incorrectly concluding that the GNU version is available ** could lead to a segfault though. - ** - ** Forum post 3f13857fa4062301 reports that the Android SDK may use - ** int-type return, depending on its version. */ -#if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \ - && !defined(ANDROID) && !defined(__ANDROID__) +#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) zErr = # endif strerror_r(iErrno, aErr, sizeof(aErr)-1); #elif SQLITE_THREADSAFE @@ -5443,20 +5439,15 @@ #endif *pp = 0; #if SQLITE_MAX_MMAP_SIZE>0 if( pFd->mmapSizeMax>0 ){ - /* Ensure that there is always at least a 256 byte buffer of addressable - ** memory following the returned page. If the database is corrupt, - ** SQLite may overread the page slightly (in practice only a few bytes, - ** but 256 is safe, round, number). */ - const int nEofBuffer = 256; if( pFd->pMapRegion==0 ){ int rc = unixMapfile(pFd, -1); if( rc!=SQLITE_OK ) return rc; } - if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ + if( pFd->mmapSize >= iOff+nAmt ){ *pp = &((u8 *)pFd->pMapRegion)[iOff]; pFd->nFetchOut++; } } #endif Index: src/os_win.c ================================================================== --- src/os_win.c +++ src/os_win.c @@ -4519,24 +4519,19 @@ OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n", osGetCurrentProcessId(), fd, iOff, nAmt, pp)); #if SQLITE_MAX_MMAP_SIZE>0 if( pFd->mmapSizeMax>0 ){ - /* Ensure that there is always at least a 256 byte buffer of addressable - ** memory following the returned page. If the database is corrupt, - ** SQLite may overread the page slightly (in practice only a few bytes, - ** but 256 is safe, round, number). */ - const int nEofBuffer = 256; if( pFd->pMapRegion==0 ){ int rc = winMapfile(pFd, -1); if( rc!=SQLITE_OK ){ OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n", osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); return rc; } } - if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ + if( pFd->mmapSize >= iOff+nAmt ){ assert( pFd->pMapRegion!=0 ); *pp = &((u8 *)pFd->pMapRegion)[iOff]; pFd->nFetchOut++; } } Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -19,14 +19,10 @@ ** this comment as part of the translated C-code. Edits should be made ** to the original parse.y sources. */ } -// Function used to enlarge the parser stack, if needed -%realloc parserStackRealloc -%free sqlite3_free - // All token codes are small integers with #defines that begin with "TK_" %token_prefix TK_ // The type of the data attached to each token is Token. This is also the // default type for non-terminals. @@ -47,11 +43,11 @@ }else{ sqlite3ErrorMsg(pParse, "incomplete input"); } } %stack_overflow { - sqlite3OomFault(pParse->db); + sqlite3ErrorMsg(pParse, "parser stack overflow"); } // The name of the generated procedure that implements the parser // is as follows: %name sqlite3Parser @@ -549,18 +545,10 @@ }else{ sqlite3WithDelete(pParse->db, pWith); } return pSelect; } - - /* Memory allocator for parser stack resizing. This is a thin wrapper around - ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate - ** testing. - */ - static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){ - return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize); - } } %ifndef SQLITE_OMIT_CTE select(A) ::= WITH wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);} select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). @@ -1936,11 +1924,11 @@ ERROR /* An expression containing an error */ . term(A) ::= QNUMBER(X). { A=tokenExpr(pParse,@X,X); - sqlite3DequoteNumber(pParse, A); + sqlite3DequoteNumber(A); } /* There must be no more than 255 tokens defined above. If this grammar ** is extended with new rules and tokens, they must either be so few in ** number that TK_SPAN is no more than 255, or else the new tokens must Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -28,38 +28,10 @@ ** lexicographical order to facility a binary search of the pragma name. ** Do not edit pragma.h directly. Edit and rerun the script in at ** ../tool/mkpragmatab.tcl. */ #include "pragma.h" -/* -** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands -** will be run with an analysis_limit set to the lessor of the value of -** the following macro or to the actual analysis_limit if it is non-zero, -** in order to prevent PRAGMA optimize from running for too long. -** -** The value of 2000 is chosen emperically so that the worst-case run-time -** for PRAGMA optimize does not exceed 100 milliseconds against a variety -** of test databases on a RaspberryPI-4 compiled using -Os and without -** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of -** this paragraph, "worst-case" means that ANALYZE ends up being -** run on every table in the database. The worst case typically only -** happens if PRAGMA optimize is run on a database file for which ANALYZE -** has not been previously run and the 0x10000 flag is included so that -** all tables are analyzed. The usual case for PRAGMA optimize is that -** no ANALYZE commands will be run at all, or if any ANALYZE happens it -** will be against a single table, so that expected timing for PRAGMA -** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000 -** flag or less than 100 microseconds without the 0x10000 flag. -** -** An analysis limit of 2000 is almost always sufficient for the query -** planner to fully characterize an index. The additional accuracy from -** a larger analysis is not usually helpful. -*/ -#ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT -# define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000 -#endif - /* ** Interpret the given string as a safety level. Return 0 for OFF, ** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or ** unrecognized string argument. The FULL and EXTRA option is disallowed ** if the omitFull parameter it 1. @@ -1788,11 +1760,35 @@ int bStrict; /* True for a STRICT table */ int r2; /* Previous key for WITHOUT ROWID tables */ int mxCol; /* Maximum non-virtual column number */ if( pObjTab && pObjTab!=pTab ) continue; - if( !IsOrdinaryTable(pTab) ) continue; + if( !IsOrdinaryTable(pTab) ){ +#ifndef SQLITE_OMIT_VIRTUALTABLE + sqlite3_vtab *pVTab; + int a1; + if( !IsVirtual(pTab) ) continue; + if( pTab->nCol<=0 ){ + const char *zMod = pTab->u.vtab.azArg[0]; + if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue; + } + sqlite3ViewGetColumnNames(pParse, pTab); + if( pTab->u.vtab.p==0 ) continue; + pVTab = pTab->u.vtab.p->pVtab; + if( NEVER(pVTab==0) ) continue; + if( NEVER(pVTab->pModule==0) ) continue; + if( pVTab->pModule->iVersion<4 ) continue; + if( pVTab->pModule->xIntegrity==0 ) continue; + sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick); + pTab->nTabRef++; + sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF); + a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v); + integrityCheckResultRow(v); + sqlite3VdbeJumpHere(v, a1); +#endif + continue; + } if( isQuick || HasRowid(pTab) ){ pPk = 0; r2 = 0; }else{ pPk = sqlite3PrimaryKeyIndex(pTab); @@ -1923,11 +1919,10 @@ /* OP_IsType does not detect NaN values in the database file ** which should be treated as a NULL. So if the header type ** is REAL, we have to load the actual data using OP_Column ** to reliably determine if the value is a NULL. */ sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3); - sqlite3ColumnDefault(v, pTab, j, 3); jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk); VdbeCoverage(v); } zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, pCol->zCnName); @@ -2114,42 +2109,10 @@ if( pPk ){ sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); } } } - -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* Second pass to invoke the xIntegrity method on all virtual - ** tables. - */ - for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ - Table *pTab = sqliteHashData(x); - sqlite3_vtab *pVTab; - int a1; - if( pObjTab && pObjTab!=pTab ) continue; - if( IsOrdinaryTable(pTab) ) continue; - if( !IsVirtual(pTab) ) continue; - if( pTab->nCol<=0 ){ - const char *zMod = pTab->u.vtab.azArg[0]; - if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue; - } - sqlite3ViewGetColumnNames(pParse, pTab); - if( pTab->u.vtab.p==0 ) continue; - pVTab = pTab->u.vtab.p->pVtab; - if( NEVER(pVTab==0) ) continue; - if( NEVER(pVTab->pModule==0) ) continue; - if( pVTab->pModule->iVersion<4 ) continue; - if( pVTab->pModule->xIntegrity==0 ) continue; - sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick); - pTab->nTabRef++; - sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF); - a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v); - integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, a1); - continue; - } -#endif } { static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList endCode[] = { { OP_AddImm, 1, 0, 0}, /* 0 */ @@ -2409,67 +2372,48 @@ ** to change and improve over time. Applications should anticipate that ** this pragma will perform new optimizations in future releases. ** ** The optional argument is a bitmask of optimizations to perform: ** - ** 0x00001 Debugging mode. Do not actually perform any optimizations - ** but instead return one line of text for each optimization - ** that would have been done. Off by default. - ** - ** 0x00002 Run ANALYZE on tables that might benefit. On by default. - ** See below for additional information. - ** - ** 0x00010 Run all ANALYZE operations using an analysis_limit that - ** is the lessor of the current analysis_limit and the - ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option. - ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is - ** currently (2024-02-19) set to 2000, which is such that - ** the worst case run-time for PRAGMA optimize on a 100MB - ** database will usually be less than 100 milliseconds on - ** a RaspberryPI-4 class machine. On by default. - ** - ** 0x10000 Look at tables to see if they need to be reanalyzed - ** due to growth or shrinkage even if they have not been - ** queried during the current connection. Off by default. - ** - ** The default MASK is and always shall be 0x0fffe. In the current - ** implementation, the default mask only covers the 0x00002 optimization, - ** though additional optimizations that are covered by 0x0fffe might be - ** added in the future. Optimizations that are off by default and must - ** be explicitly requested have masks of 0x10000 or greater. + ** 0x0001 Debugging mode. Do not actually perform any optimizations + ** but instead return one line of text for each optimization + ** that would have been done. Off by default. + ** + ** 0x0002 Run ANALYZE on tables that might benefit. On by default. + ** See below for additional information. + ** + ** 0x0004 (Not yet implemented) Record usage and performance + ** information from the current session in the + ** database file so that it will be available to "optimize" + ** pragmas run by future database connections. + ** + ** 0x0008 (Not yet implemented) Create indexes that might have + ** been helpful to recent queries + ** + ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all + ** of the optimizations listed above except Debug Mode, including new + ** optimizations that have not yet been invented. If new optimizations are + ** ever added that should be off by default, those off-by-default + ** optimizations will have bitmasks of 0x10000 or larger. ** ** DETERMINATION OF WHEN TO RUN ANALYZE ** ** In the current implementation, a table is analyzed if only if all of ** the following are true: ** - ** (1) MASK bit 0x00002 is set. - ** - ** (2) The table is an ordinary table, not a virtual table or view. - ** - ** (3) The table name does not begin with "sqlite_". - ** - ** (4) One or more of the following is true: - ** (4a) The 0x10000 MASK bit is set. - ** (4b) One or more indexes on the table lacks an entry - ** in the sqlite_stat1 table. - ** (4c) The query planner used sqlite_stat1-style statistics for one - ** or more indexes of the table at some point during the lifetime - ** of the current connection. - ** - ** (5) One or more of the following is true: - ** (5a) One or more indexes on the table lacks an entry - ** in the sqlite_stat1 table. (Same as 4a) - ** (5b) The number of rows in the table has increased or decreased by - ** 10-fold. In other words, the current size of the table is - ** 10 times larger than the size in sqlite_stat1 or else the - ** current size is less than 1/10th the size in sqlite_stat1. + ** (1) MASK bit 0x02 is set. + ** + ** (2) The query planner used sqlite_stat1-style statistics for one or + ** more indexes of the table at some point during the lifetime of + ** the current connection. + ** + ** (3) One or more indexes of the table are currently unanalyzed OR + ** the number of rows in the table has increased by 25 times or more + ** since the last time ANALYZE was run. ** ** The rules for when tables are analyzed are likely to change in - ** future releases. Future versions of SQLite might accept a string - ** literal argument to this pragma that contains a mnemonic description - ** of the options rather than a bitmap. + ** future releases. */ case PragTyp_OPTIMIZE: { int iDbLast; /* Loop termination point for the schema loop */ int iTabCur; /* Cursor for a table whose size needs checking */ HashElem *k; /* Loop over tables of a schema */ @@ -2477,122 +2421,56 @@ Table *pTab; /* A table in the schema */ Index *pIdx; /* An index of the table */ LogEst szThreshold; /* Size threshold above which reanalysis needed */ char *zSubSql; /* SQL statement for the OP_SqlExec opcode */ u32 opMask; /* Mask of operations to perform */ - int nLimit; /* Analysis limit to use */ - int nCheck = 0; /* Number of tables to be optimized */ - int nBtree = 0; /* Number of btrees to scan */ - int nIndex; /* Number of indexes on the current table */ if( zRight ){ opMask = (u32)sqlite3Atoi(zRight); if( (opMask & 0x02)==0 ) break; }else{ opMask = 0xfffe; } - if( (opMask & 0x10)==0 ){ - nLimit = 0; - }else if( db->nAnalysisLimit>0 - && db->nAnalysisLimitnTab++; for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){ if( iDb==1 ) continue; sqlite3CodeVerifySchema(pParse, iDb); pSchema = db->aDb[iDb].pSchema; for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ pTab = (Table*)sqliteHashData(k); - /* This only works for ordinary tables */ - if( !IsOrdinaryTable(pTab) ) continue; - - /* Do not scan system tables */ - if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue; - - /* Find the size of the table as last recorded in sqlite_stat1. - ** If any index is unanalyzed, then the threshold is -1 to - ** indicate a new, unanalyzed index - */ - szThreshold = pTab->nRowLogEst; - nIndex = 0; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - nIndex++; - if( !pIdx->hasStat1 ){ - szThreshold = -1; /* Always analyze if any index lacks statistics */ - } - } - - /* If table pTab has not been used in a way that would benefit from - ** having analysis statistics during the current session, then skip it, - ** unless the 0x10000 MASK bit is set. */ - if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){ - /* Check for size change if stat1 has been used for a query */ - }else if( opMask & 0x10000 ){ - /* Check for size change if 0x10000 is set */ - }else if( pTab->pIndex!=0 && szThreshold<0 ){ - /* Do analysis if unanalyzed indexes exists */ - }else{ - /* Otherwise, we can skip this table */ - continue; - } - - nCheck++; - if( nCheck==2 ){ - /* If ANALYZE might be invoked two or more times, hold a write - ** transaction for efficiency */ - sqlite3BeginWriteOperation(pParse, 0, iDb); - } - nBtree += nIndex+1; - - /* Reanalyze if the table is 10 times larger or smaller than - ** the last analysis. Unconditional reanalysis if there are - ** unanalyzed indexes. */ - sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); - if( szThreshold>=0 ){ - const LogEst iRange = 33; /* 10x size change */ - sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur, - sqlite3VdbeCurrentAddr(v)+2+(opMask&1), - szThreshold>=iRange ? szThreshold-iRange : -1, - szThreshold+iRange); - VdbeCoverage(v); - }else{ - sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur, - sqlite3VdbeCurrentAddr(v)+2+(opMask&1)); + /* If table pTab has not been used in a way that would benefit from + ** having analysis statistics during the current session, then skip it. + ** This also has the effect of skipping virtual tables and views */ + if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue; + + /* Reanalyze if the table is 25 times larger than the last analysis */ + szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 ); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( !pIdx->hasStat1 ){ + szThreshold = 0; /* Always analyze if any index lacks statistics */ + break; + } + } + if( szThreshold ){ + sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); + sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur, + sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold); VdbeCoverage(v); } zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"", db->aDb[iDb].zDbSName, pTab->zName); if( opMask & 0x01 ){ int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC); sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1); }else{ - sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0, - zSubSql, P4_DYNAMIC); + sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC); } } } sqlite3VdbeAddOp0(v, OP_Expire); - - /* In a schema with a large number of tables and indexes, scale back - ** the analysis_limit to avoid excess run-time in the worst case. - */ - if( !db->mallocFailed && nLimit>0 && nBtree>100 ){ - int iAddr, iEnd; - VdbeOp *aOp; - nLimit = 100*nLimit/nBtree; - if( nLimit<100 ) nLimit = 100; - aOp = sqlite3VdbeGetOp(v, 0); - iEnd = sqlite3VdbeCurrentAddr(v); - for(iAddr=0; iAddr0 ) precision--; /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ if( xtype==etGENERIC ){ - assert( precision>0 ); - precision--; flag_rtz = !flag_alternateform; if( exp<-4 || exp>precision ){ xtype = etEXP; }else{ precision = precision - exp; Index: src/resolve.c ================================================================== --- src/resolve.c +++ src/resolve.c @@ -77,12 +77,10 @@ sqlite3 *db; /* The database connection */ assert( iCol>=0 && iColnExpr ); pOrig = pEList->a[iCol].pExpr; assert( pOrig!=0 ); - assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); - if( pExpr->pAggInfo ) return; db = pParse->db; pDup = sqlite3ExprDup(db, pOrig, 0); if( db->mallocFailed ){ sqlite3ExprDelete(db, pDup); pDup = 0; @@ -277,11 +275,11 @@ */ static int lookupName( Parse *pParse, /* The parsing context */ const char *zDb, /* Name of the database containing table, or NULL */ const char *zTab, /* Name of table containing column, or NULL */ - const Expr *pRight, /* Name of the column. */ + const char *zCol, /* Name of the column. */ NameContext *pNC, /* The name context used to resolve the name */ Expr *pExpr /* Make this EXPR node point to the selected column */ ){ int i, j; /* Loop counters */ int cnt = 0; /* Number of matching column names */ @@ -294,11 +292,10 @@ Schema *pSchema = 0; /* Schema of the expression */ int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */ Table *pTab = 0; /* Table holding the row */ Column *pCol; /* A column of pTab */ ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ - const char *zCol = pRight->u.zToken; assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ assert( zDb==0 || zTab!=0 ); assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); @@ -754,14 +751,10 @@ zErr = cnt==0 ? "no such column" : "ambiguous column name"; if( zDb ){ sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); }else if( zTab ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); - }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){ - sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a" - " string literal in single-quotes?", - zErr, zCol); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); pParse->checkSchema = 1; @@ -1005,19 +998,20 @@ ** be one call to lookupName(). Then the compiler will in-line ** lookupName() for a size reduction and performance increase. */ case TK_ID: case TK_DOT: { + const char *zColumn; const char *zTable; const char *zDb; Expr *pRight; if( pExpr->op==TK_ID ){ zDb = 0; zTable = 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); - pRight = pExpr; + zColumn = pExpr->u.zToken; }else{ Expr *pLeft = pExpr->pLeft; testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); sqlite3ResolveNotValid(pParse, pNC, "the \".\" operator", @@ -1032,17 +1026,18 @@ pLeft = pRight->pLeft; pRight = pRight->pRight; } assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) ); zTable = pLeft->u.zToken; + zColumn = pRight->u.zToken; assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); } } - return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr); + return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); } /* Resolve function names */ case TK_FUNCTION: { Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -6442,12 +6442,10 @@ /* ** Display all information about an AggInfo object */ static void printAggInfo(AggInfo *pAggInfo){ int ii; - sqlite3DebugPrintf("AggInfo %d/%p:\n", - pAggInfo->selId, pAggInfo); for(ii=0; iinColumn; ii++){ struct AggInfo_col *pCol = &pAggInfo->aCol[ii]; sqlite3DebugPrintf( "agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d" " iSorterColumn=%d %s\n", @@ -8546,16 +8544,10 @@ assert( db->mallocFailed==0 || db->mallocFailed==1 ); assert( db->mallocFailed==0 || pParse->nErr!=0 ); sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ -#if TREETRACE_ENABLED - if( sqlite3TreeTrace & 0x20 ){ - TREETRACE(0x20,pParse,p,("Finished with AggInfo\n")); - printAggInfo(pAggInfo); - } -#endif for(i=0; inColumn; i++){ Expr *pExpr = pAggInfo->aCol[i].pCExpr; if( pExpr==0 ) continue; assert( pExpr->pAggInfo==pAggInfo ); assert( pExpr->iAgg==i ); Index: src/shell.c.in ================================================================== --- src/shell.c.in +++ src/shell.c.in @@ -266,13 +266,10 @@ * * Note that the default stream is whatever has been last set via: * setOutputStream(FILE *pf) * This is normally the stream that CLI normal output goes to. * For the stand-alone CLI, it is stdout with no .output redirect. - * - * The ?putz(z) forms are required for the Fiddle builds for string literal - * output, in aid of enforcing format string to argument correspondence. */ # define sputz(s,z) fPutsUtf8(z,s) # define sputf fPrintfUtf8 # define oputz(z) oPutsUtf8(z) # define oputf oPrintfUtf8 @@ -280,22 +277,16 @@ # define eputf ePrintfUtf8 # define oputb(buf,na) oPutbUtf8(buf,na) #else /* For Fiddle, all console handling and emit redirection is omitted. */ -/* These next 3 macros are for emitting formatted output. When complaints - * from the WASM build are issued for non-formatted output, (when a mere - * string literal is to be emitted, the ?putz(z) forms should be used. - * (This permits compile-time checking of format string / argument mismatch.) - */ +# define sputz(fp,z) fputs(z,fp) +# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) +# define oputz(z) fputs(z,stdout) # define oputf(fmt, ...) printf(fmt,__VA_ARGS__) -# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) -# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) -/* These next 3 macros are for emitting simple string literals. */ -# define oputz(z) fputs(z,stdout) # define eputz(z) fputs(z,stderr) -# define sputz(fp,z) fputs(z,fp) +# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) # define oputb(buf,na) fwrite(buf,1,na,stdout) #endif /* True if the timer is enabled */ static int enableTimer = 0; @@ -1220,13 +1211,10 @@ INCLUDE ../ext/misc/sqlar.c #endif INCLUDE ../ext/expert/sqlite3expert.h INCLUDE ../ext/expert/sqlite3expert.c -INCLUDE ../ext/intck/sqlite3intck.h -INCLUDE ../ext/intck/sqlite3intck.c - #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) #define SQLITE_SHELL_HAVE_RECOVER 1 #else #define SQLITE_SHELL_HAVE_RECOVER 0 #endif @@ -4742,11 +4730,10 @@ ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", - ".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db", #ifdef SQLITE_ENABLE_IOTRACE ",iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", @@ -7652,44 +7639,10 @@ rc = sqlite3_recover_finish(p); return rc; } #endif /* SQLITE_SHELL_HAVE_RECOVER */ -/* -** Implementation of ".intck STEPS_PER_UNLOCK" command. -*/ -static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){ - sqlite3_intck *p = 0; - int rc = SQLITE_OK; - - rc = sqlite3_intck_open(pState->db, "main", &p); - if( rc==SQLITE_OK ){ - i64 nStep = 0; - i64 nError = 0; - const char *zErr = 0; - while( SQLITE_OK==sqlite3_intck_step(p) ){ - const char *zMsg = sqlite3_intck_message(p); - if( zMsg ){ - oputf("%s\n", zMsg); - nError++; - } - nStep++; - if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){ - sqlite3_intck_unlock(p); - } - } - rc = sqlite3_intck_error(p, &zErr); - if( zErr ){ - eputf("%s\n", zErr); - } - sqlite3_intck_close(p); - - oputf("%lld steps, %lld errors\n", nStep, nError); - } - - return rc; -} /* * zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it. * zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE, * close db and set it to 0, and return the columns spec, to later @@ -7929,42 +7882,10 @@ } shellFinalize(&rc, pStmt); return rc; } -/* -** Fault-Simulator state and logic. -*/ -static struct { - int iId; /* ID that triggers a simulated fault. -1 means "any" */ - int iErr; /* The error code to return on a fault */ - int iCnt; /* Trigger the fault only if iCnt is already zero */ - int iInterval; /* Reset iCnt to this value after each fault */ - int eVerbose; /* When to print output */ -} faultsim_state = {-1, 0, 0, 0, 0}; - -/* -** This is the fault-sim callback -*/ -static int faultsim_callback(int iArg){ - if( faultsim_state.iId>0 && faultsim_state.iId!=iArg ){ - return SQLITE_OK; - } - if( faultsim_state.iCnt>0 ){ - faultsim_state.iCnt--; - if( faultsim_state.eVerbose>=2 ){ - oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); - } - return SQLITE_OK; - } - if( faultsim_state.eVerbose>=1 ){ - oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); - } - faultsim_state.iCnt = faultsim_state.iInterval; - return faultsim_state.iErr; -} - /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. @@ -8452,12 +8373,11 @@ sqlite3_free(zSql); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ zSql = sqlite3_mprintf( "SELECT sql FROM sqlite_schema AS o " "WHERE (%s) AND sql NOT NULL" - " AND type IN ('index','trigger','view') " - "ORDER BY type COLLATE NOCASE DESC", + " AND type IN ('index','trigger','view')", zLike ); run_table_dump_query(p, zSql); sqlite3_free(zSql); } @@ -9176,25 +9096,10 @@ } sqlite3_free(zSql); }else #endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */ - if( c=='i' && cli_strncmp(azArg[0], "intck", n)==0 ){ - i64 iArg = 0; - if( nArg==2 ){ - iArg = integerValue(azArg[1]); - if( iArg==0 ) iArg = -1; - } - if( (nArg!=1 && nArg!=2) || iArg<0 ){ - eputf("Usage: .intck STEPS_PER_UNLOCK\n"); - rc = 1; - goto meta_command_exit; - } - open_db(p, 0); - rc = intckDatabaseCmd(p, iArg); - }else - #ifdef SQLITE_ENABLE_IOTRACE if( c=='i' && cli_strncmp(azArg[0], "iotrace", n)==0 ){ SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...); if( iotrace && iotrace!=stdout ) fclose(iotrace); iotrace = 0; @@ -10864,11 +10769,11 @@ {"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" }, /*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/ /*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/ {"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" }, {"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" }, - {"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." }, + /*{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/ {"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" }, {"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"}, {"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" }, {"json_selfcheck", SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN" }, {"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, @@ -11097,68 +11002,10 @@ rc2 = booleanValue(azArg[2]); isOk = 3; } sqlite3_test_control(testctrl, &rc2); break; - case SQLITE_TESTCTRL_FAULT_INSTALL: { - int kk; - int bShowHelp = nArg<=2; - isOk = 3; - for(kk=2; kk0 ) faultsim_state.eVerbose--; - }else if( cli_strcmp(z,"-id")==0 && kk+1=0 ){ oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; @@ -12076,11 +11923,11 @@ if( showDetail ){ eputf("OPTIONS include:\n%s", zOptions); }else{ eputz("Use the -help option for additional information\n"); } - exit(0); + exit(1); } /* ** Internal check: Verify that the SQLite is uninitialized. Print a ** error message if it is initialized. Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -418,12 +418,10 @@ ** is a valid and open [database connection]. **

  • The application must not close the [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. **
  • The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. -**
  • The application must not dereference the arrays or string pointers -** passed as the 3rd and 4th callback parameters after it returns. ** */ int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ @@ -762,15 +760,15 @@ **
  • [SQLITE_LOCK_PENDING], or **
  • [SQLITE_LOCK_EXCLUSIVE]. ** ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to -** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never +** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ** SQLITE_LOCK_NONE. If the database file lock is already at or above the ** requested lock, then the call to xLock() is a no-op. ** xUnlock() downgrades the database file lock to either SHARED or NONE. -** If the lock is already at or below the requested lock state, then the call +* If the lock is already at or below the requested lock state, then the call ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, ** PENDING, or EXCLUSIVE lock on the file. It returns true ** if such a lock exists and false otherwise. Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -886,11 +886,11 @@ #ifndef SQLITE_PTRSIZE # if defined(__SIZEOF_POINTER__) # define SQLITE_PTRSIZE __SIZEOF_POINTER__ # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ - (defined(__APPLE__) && defined(__ppc__)) || \ + (defined(__APPLE__) && defined(__POWERPC__)) || \ (defined(__TOS_AIX__) && !defined(__64BIT__)) # define SQLITE_PTRSIZE 4 # else # define SQLITE_PTRSIZE 8 # endif @@ -1596,14 +1596,10 @@ struct FuncDefHash { FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */ }; #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) -#if defined(SQLITE_USER_AUTHENTICATION) -# warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \ - See ext/userauth/user-auth.txt for details." -#endif #ifdef SQLITE_USER_AUTHENTICATION /* ** Information held in the "sqlite3" database connection object and used ** to manage user authentication. */ @@ -2476,11 +2472,12 @@ #define TF_HasStat1 0x00000010 /* nRowLogEst set from sqlite_stat1 */ #define TF_HasVirtual 0x00000020 /* Has one or more VIRTUAL columns */ #define TF_HasStored 0x00000040 /* Has one or more STORED columns */ #define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ #define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ -#define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */ +#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by + ** Index.aiRowLogEst[] values */ #define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ #define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ #define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ #define TF_Shadow 0x00001000 /* True for a shadow table */ #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ @@ -4795,11 +4792,11 @@ void sqlite3ErrorMsg(Parse*, const char*, ...); int sqlite3ErrorToParser(sqlite3*,int); void sqlite3Dequote(char*); void sqlite3DequoteExpr(Expr*); void sqlite3DequoteToken(Token*); -void sqlite3DequoteNumber(Parse*, Expr*); +void sqlite3DequoteNumber(Expr*); void sqlite3TokenInit(Token*,char*); int sqlite3KeywordCode(const unsigned char*, int); int sqlite3RunParser(Parse*, const char*); void sqlite3FinishCoding(Parse*); int sqlite3GetTempReg(Parse*); Index: src/test_tclsh.c ================================================================== --- src/test_tclsh.c +++ src/test_tclsh.c @@ -106,11 +106,10 @@ #endif extern int TestExpert_Init(Tcl_Interp*); extern int Sqlitetest_window_Init(Tcl_Interp *); extern int Sqlitetestvdbecov_Init(Tcl_Interp *); extern int TestRecover_Init(Tcl_Interp*); - extern int Sqlitetestintck_Init(Tcl_Interp*); Tcl_CmdInfo cmdInfo; /* Since the primary use case for this binary is testing of SQLite, ** be sure to generate core files if we crash */ @@ -174,11 +173,10 @@ #endif TestExpert_Init(interp); Sqlitetest_window_Init(interp); Sqlitetestvdbecov_Init(interp); TestRecover_Init(interp); - Sqlitetestintck_Init(interp); Tcl_CreateObjCommand( interp, "load_testfixture_extensions", load_testfixture_extensions,0,0 ); return 0; Index: src/tokenize.c ================================================================== --- src/tokenize.c +++ src/tokenize.c @@ -435,62 +435,44 @@ testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); testcase( z[0]=='9' ); testcase( z[0]=='.' ); *tokenType = TK_INTEGER; #ifndef SQLITE_OMIT_HEX_INTEGER if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ - for(i=3; 1; i++){ - if( sqlite3Isxdigit(z[i])==0 ){ - if( z[i]==SQLITE_DIGIT_SEPARATOR ){ - *tokenType = TK_QNUMBER; - }else{ - break; - } - } - } - }else -#endif - { - for(i=0; 1; i++){ - if( sqlite3Isdigit(z[i])==0 ){ - if( z[i]==SQLITE_DIGIT_SEPARATOR ){ - *tokenType = TK_QNUMBER; - }else{ - break; - } - } - } -#ifndef SQLITE_OMIT_FLOATING_POINT - if( z[i]=='.' ){ - if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; - for(i++; 1; i++){ - if( sqlite3Isdigit(z[i])==0 ){ - if( z[i]==SQLITE_DIGIT_SEPARATOR ){ - *tokenType = TK_QNUMBER; - }else{ - break; - } - } - } - } - if( (z[i]=='e' || z[i]=='E') && - ( sqlite3Isdigit(z[i+1]) - || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) - ) - ){ - if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; - for(i+=2; 1; i++){ - if( sqlite3Isdigit(z[i])==0 ){ - if( z[i]==SQLITE_DIGIT_SEPARATOR ){ - *tokenType = TK_QNUMBER; - }else{ - break; - } - } - } - } -#endif - } + for(i=3; sqlite3Isxdigit(z[i]); i++){} + return i; + } +#endif + for(i=0; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ *tokenType = TK_QNUMBER; } + else{ break; } + } + } +#ifndef SQLITE_OMIT_FLOATING_POINT + if( z[i]=='.' ){ + if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; + for(i++; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ *tokenType = TK_QNUMBER; } + else{ break; } + } + } + } + if( (z[i]=='e' || z[i]=='E') && + ( sqlite3Isdigit(z[i+1]) + || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) + ) + ){ + if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; + for(i+=2; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ *tokenType = TK_QNUMBER; } + else{ break; } + } + } + } +#endif while( IdChar(z[i]) ){ *tokenType = TK_ILLEGAL; i++; } return i; Index: src/util.c ================================================================== --- src/util.c +++ src/util.c @@ -310,34 +310,27 @@ p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; sqlite3Dequote(p->u.zToken); } /* -** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken -** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those -** that contain '_' characters that must be removed before further processing. +** Expression p is a QINTEGER or QFLOAT (quoted integer or float). Dequote +** the value in p->u.zToken and set the type to INTEGER or FLOAT. "Quoted" +** integers or floats are those that contain '_' characters that must +** be removed before further processing. */ -void sqlite3DequoteNumber(Parse *pParse, Expr *p){ +void sqlite3DequoteNumber(Expr *p){ if( p ){ const char *pIn = p->u.zToken; char *pOut = p->u.zToken; - int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X')); assert( p->op==TK_QNUMBER ); p->op = TK_INTEGER; do { if( *pIn!=SQLITE_DIGIT_SEPARATOR ){ *pOut++ = *pIn; if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT; - }else{ - if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1]))) - || (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1]))) - ){ - sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken); - } } }while( *pIn++ ); - if( bHex ) p->op = TK_INTEGER; } } /* ** If the input token p is quoted, try to adjust the token to remove @@ -1095,11 +1088,11 @@ assert( i>=0 && izBuf)-1 ); p->n = sizeof(p->zBuf) - 1 - i; assert( p->n>0 ); assert( p->nzBuf) ); p->iDP = p->n + exp; - if( iRound<=0 ){ + if( iRound<0 ){ iRound = p->iDP - iRound; if( iRound==0 && p->zBuf[i+1]>='5' ){ iRound = 1; p->zBuf[i--] = '0'; p->n++; Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -2084,11 +2084,11 @@ } break; } #endif -#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE) +#ifndef SQLITE_OMIT_CAST /* Opcode: Cast P1 P2 * * * ** Synopsis: affinity(r[P1]) ** ** Force the value in register P1 to be the type defined by P2. ** @@ -6190,41 +6190,32 @@ if( res ) goto jump_to_p2; } break; } -/* Opcode: IfSizeBetween P1 P2 P3 P4 * +/* Opcode: IfSmaller P1 P2 P3 * * ** -** Let N be the approximate number of rows in the table or index -** with cursor P1 and let X be 10*log2(N) if N is positive or -1 -** if N is zero. Thus X will be within the range of -1 to 640, inclusive -** Jump to P2 if X is in between P3 and P4, inclusive. +** Estimate the number of rows in the table P1. Jump to P2 if that +** estimate is less than approximately 2**(0.1*P3). */ -case OP_IfSizeBetween: { /* jump */ +case OP_IfSmaller: { /* jump */ VdbeCursor *pC; BtCursor *pCrsr; int res; i64 sz; assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p4type==P4_INT32 ); - assert( pOp->p3>=-1 && pOp->p3<=640 ); - assert( pOp->p4.i>=-1 && pOp->p4.i<=640 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); if( rc ) goto abort_due_to_error; - if( res!=0 ){ - sz = -1; /* -Infinity encoding */ - }else{ + if( res==0 ){ sz = sqlite3BtreeRowCountEst(pCrsr); - assert( sz>0 ); - sz = sqlite3LogEst((u64)sz); + if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)p3 ) res = 1; } - res = sz>=pOp->p3 && sz<=pOp->p4.i; VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; break; } @@ -6920,55 +6911,42 @@ if( rc ) goto abort_due_to_error; pOut->u.i = pgno; break; } -/* Opcode: SqlExec P1 P2 * P4 * +/* Opcode: SqlExec * * * P4 * ** ** Run the SQL statement or statements specified in the P4 string. -** -** The P1 parameter is a bitmask of options: -** -** 0x0001 Disable Auth and Trace callbacks while the statements -** in P4 are running. -** -** 0x0002 Set db->nAnalysisLimit to P2 while the statements in -** P4 are running. -** +** Disable Auth and Trace callbacks while those statements are running if +** P1 is true. */ case OP_SqlExec: { char *zErr; #ifndef SQLITE_OMIT_AUTHORIZATION sqlite3_xauth xAuth; #endif u8 mTrace; - int savedAnalysisLimit; sqlite3VdbeIncrWriteCounter(p, 0); db->nSqlExec++; zErr = 0; #ifndef SQLITE_OMIT_AUTHORIZATION xAuth = db->xAuth; #endif mTrace = db->mTrace; - savedAnalysisLimit = db->nAnalysisLimit; - if( pOp->p1 & 0x0001 ){ + if( pOp->p1 ){ #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = 0; #endif db->mTrace = 0; } - if( pOp->p1 & 0x0002 ){ - db->nAnalysisLimit = pOp->p2; - } rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr); db->nSqlExec--; #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = xAuth; #endif db->mTrace = mTrace; - db->nAnalysisLimit = savedAnalysisLimit; if( zErr || rc ){ sqlite3VdbeError(p, "%s", zErr); sqlite3_free(zErr); if( rc==SQLITE_NOMEM ) goto no_mem; goto abort_due_to_error; @@ -7121,12 +7099,12 @@ ** register P1 the text of an error message describing any problems. ** If no problems are found, store a NULL in register P1. ** ** The register P3 contains one less than the maximum number of allowed errors. ** At most reg(P3) errors will be reported. -** In other words, the analysis stops as soon as reg(P3) errors are -** seen. Reg(P3) is updated with the number of errors remaining. +** In other words, the analysis stops as soon as reg(P1) errors are +** seen. Reg(P1) is updated with the number of errors remaining. ** ** The root page numbers of all tables in the database are integers ** stored in P4_INTARRAY argument. ** ** If P5 is not zero, the check is done on the auxiliary database Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -4058,27 +4058,10 @@ swapMixedEndianFloat(x); memcpy(&pMem->u.r, &x, sizeof(x)); pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; } } -static int serialGet7( - const unsigned char *buf, /* Buffer to deserialize from */ - Mem *pMem /* Memory cell to write value into */ -){ - u64 x = FOUR_BYTE_UINT(buf); - u32 y = FOUR_BYTE_UINT(buf+4); - x = (x<<32) + y; - assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); - swapMixedEndianFloat(x); - memcpy(&pMem->u.r, &x, sizeof(x)); - if( IsNaN(x) ){ - pMem->flags = MEM_Null; - return 1; - } - pMem->flags = MEM_Real; - return 0; -} void sqlite3VdbeSerialGet( const unsigned char *buf, /* Buffer to deserialize from */ u32 serial_type, /* Serial type to deserialize */ Mem *pMem /* Memory cell to write value into */ ){ @@ -4514,19 +4497,21 @@ testcase( x>r ); testcase( x==r ); return (xr); }else{ i64 y; + double s; if( r<-9223372036854775808.0 ) return +1; if( r>=9223372036854775808.0 ) return -1; y = (i64)r; if( iy ) return +1; - testcase( doubleLt(((double)i),r) ); - testcase( doubleLt(r,((double)i)) ); - testcase( doubleEq(r,((double)i)) ); - return (((double)i)r); + s = (double)i; + testcase( doubleLt(s,r) ); + testcase( doubleLt(r,s) ); + testcase( doubleEq(r,s) ); + return (sr); } } /* ** Compare the values contained by the two memory cells, returning @@ -4752,11 +4737,11 @@ if( serial_type>=10 ){ rc = serial_type==10 ? -1 : +1; }else if( serial_type==0 ){ rc = -1; }else if( serial_type==7 ){ - serialGet7(&aKey1[d1], &mem1); + sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r); }else{ i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); i64 rhs = pRhs->u.i; if( lhsu.r ){ + if( mem1.u.ru.r ){ rc = -1; }else if( mem1.u.r>pRhs->u.r ){ rc = +1; - }else{ - assert( rc==0 ); } }else{ - sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r); } } } @@ -4858,18 +4839,11 @@ } /* RHS is null */ else{ serial_type = aKey1[idx1]; - if( serial_type==0 - || serial_type==10 - || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0) - ){ - assert( rc==0 ); - }else{ - rc = 1; - } + rc = (serial_type!=0 && serial_type!=10); } if( rc!=0 ){ int sortFlags = pPKey2->pKeyInfo->aSortFlags[i]; if( sortFlags ){ Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -1660,21 +1660,12 @@ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); if( zVal==0 ) goto no_mem; sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); } } - if( affinity==SQLITE_AFF_BLOB ){ - if( op==TK_FLOAT ){ - assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) ); - sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8); - pVal->flags = MEM_Real; - }else if( op==TK_INTEGER ){ - /* This case is required by -9223372036854775808 and other strings - ** that look like integers but cannot be handled by the - ** sqlite3DecOrHexToI64() call above. */ - sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); - } + if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){ + sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); }else{ sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); } assert( (pVal->flags & MEM_IntReal)==0 ); if( pVal->flags & (MEM_Int|MEM_IntReal|MEM_Real) ){ Index: src/vtab.c ================================================================== --- src/vtab.c +++ src/vtab.c @@ -609,12 +609,10 @@ sCtx.pPrior = db->pVtabCtx; sCtx.bDeclared = 0; db->pVtabCtx = &sCtx; pTab->nTabRef++; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); - assert( pTab!=0 ); - assert( pTab->nTabRef>1 || rc!=SQLITE_OK ); sqlite3DeleteTable(db, pTab); db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); assert( sCtx.pTab==pTab ); @@ -633,11 +631,11 @@ pVTable->pVtab->pModule = pMod->pModule; pMod->nRefModule++; pVTable->nRef = 1; if( sCtx.bDeclared==0 ){ const char *zFormat = "vtable constructor did not declare schema: %s"; - *pzErr = sqlite3MPrintf(db, zFormat, zModuleName); + *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); sqlite3VtabUnlock(pVTable); rc = SQLITE_ERROR; }else{ int iCol; u16 oooHidden = 0; Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -2973,13 +2973,11 @@ assert( pNew->u.btree.nBtm==0 ); opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; } if( pProbe->bUnordered || pProbe->bLowQual ){ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); - if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){ - opMask &= ~(WO_EQ|WO_IN|WO_IS); - } + if( pProbe->bLowQual ) opMask &= ~(WO_EQ|WO_IN|WO_IS); } assert( pNew->u.btree.nEqnColumn ); assert( pNew->u.btree.nEqnKeyCol || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY ); @@ -3362,13 +3360,11 @@ if( pIndex->bUnordered ) return 0; if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; for(ii=0; iinExpr; ii++){ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); if( NEVER(pExpr==0) ) continue; - if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) - && pExpr->iTable==iCursor - ){ + if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ if( pExpr->iColumn<0 ) return 1; for(jj=0; jjnKeyCol; jj++){ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; } }else if( (aColExpr = pIndex->aColExpr)!=0 ){ @@ -3970,11 +3966,11 @@ if( pBuilder->bldFlags1==SQLITE_BLDF1_INDEXED ){ /* If a non-unique index is used, or if a prefix of the key for ** unique index is used (making the index functionally non-unique) ** then the sqlite_stat1 data becomes important for scoring the ** plan */ - pTab->tabFlags |= TF_MaybeReanalyze; + pTab->tabFlags |= TF_StatsUsed; } #ifdef SQLITE_ENABLE_STAT4 sqlite3Stat4ProbeFree(pBuilder->pRec); pBuilder->nRecValid = 0; pBuilder->pRec = 0; @@ -5464,13 +5460,14 @@ pWInfo->nOBSat = pFrom->isOrdered; if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } - /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */ - assert( pWInfo->pSelect->pOrderBy==0 - || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr ); + if( pWInfo->pSelect->pOrderBy + && pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){ + pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr; + } }else{ pWInfo->revMask = pFrom->revLoop; if( pWInfo->nOBSat<=0 ){ pWInfo->nOBSat = 0; if( nLoop>0 ){ @@ -5805,11 +5802,11 @@ WhereLoop *pLoop = pWInfo->a[i].pWLoop; const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; Table *pTab = pItem->pTab; if( (pTab->tabFlags & TF_HasStat1)==0 ) break; - pTab->tabFlags |= TF_MaybeReanalyze; + pTab->tabFlags |= TF_StatsUsed; if( i>=1 && (pLoop->wsFlags & reqFlags)==reqFlags /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0) ){ @@ -6057,14 +6054,11 @@ db = pParse->db; memset(&sWLB, 0, sizeof(sWLB)); /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); - if( pOrderBy && pOrderBy->nExpr>=BMS ){ - pOrderBy = 0; - wctrlFlags &= ~WHERE_WANT_DISTINCT; - } + if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0; /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask */ testcase( pTabList->nSrc==BMS ); Index: test/alter2.test ================================================================== --- test/alter2.test +++ test/alter2.test @@ -369,11 +369,11 @@ set sql {CREATE TABLE t1(a, b DEFAULT -123.0, c VARCHAR(10) default 5)} alter_table t1 $sql 3 execsql { SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1; } -} {1 integer -123.0 real 5 text} +} {1 integer -123 integer 5 text} #----------------------------------------------------------------------- # Test that UPDATE trigger tables work with default values, and that when # a row is updated the default values are correctly transfered to the # new row. @@ -395,15 +395,15 @@ do_test alter2-8.2 { execsql { UPDATE t1 SET c = 10 WHERE a = 1; SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1; } -} {1 integer -123.0 real 10 text} +} {1 integer -123 integer 10 text} ifcapable trigger { do_test alter2-8.3 { set ::val - } {-123.0 real 5 text -123.0 real 10 text} + } {-123 integer 5 text -123 integer 10 text} } #----------------------------------------------------------------------- # Test that DELETE trigger tables work with default values, and that when # a row is updated the default values are correctly transfered to the @@ -423,11 +423,11 @@ do_test alter2-9.2 { execsql { DELETE FROM t1 WHERE a = 2; } set ::val - } {-123.0 real 5 text} + } {-123 integer 5 text} } #----------------------------------------------------------------------- # Test creating an index on a column added with a default value. # Index: test/busy.test ================================================================== --- test/busy.test +++ test/busy.test @@ -104,11 +104,11 @@ } {6} proc busy_handler {n} { return 1 } do_test 3.5 { catchsql { PRAGMA optimize } -} {1 {database is locked}} +} {0 {}} do_test 3.6 { execsql { COMMIT } db2 execsql { WITH s(i) AS ( Index: test/date4.test ================================================================== --- test/date4.test +++ test/date4.test @@ -26,12 +26,12 @@ if {$tcl_platform(os)=="Linux"} { set FMT {%d,%e,%F,%H,%k,%I,%l,%j,%m,%M,%u,%w,%W,%Y,%%,%P,%p,%U,%V,%G,%g} } else { set FMT {%d,%e,%F,%H,%I,%j,%p,%R,%u,%w,%W,%%} } -for {set i 0} {$i<=24858} {incr i} { - set TS [expr {$i*86390}] +for {set i 0} {$i<=24854} {incr i} { + set TS [expr {$i*86401}] do_execsql_test date4-$i { SELECT strftime($::FMT,$::TS,'unixepoch'); } [list [strftime $FMT $TS]] } Index: test/distinctagg.test ================================================================== --- test/distinctagg.test +++ test/distinctagg.test @@ -93,11 +93,11 @@ 5 0 "SELECT count(DISTINCT rowid) FROM t1" 10 6 0 "SELECT count(DISTINCT a) FROM t1, t2" 5 7 0 "SELECT count(DISTINCT a) FROM t2, t1" 5 8 1 "SELECT count(DISTINCT a+b) FROM t1, t2, t2, t2" 6 9 0 "SELECT count(DISTINCT c) FROM t1 WHERE c=2" 1 - 10 0 "SELECT count(DISTINCT t1.rowid) FROM t1, t2" 10 + 10 1 "SELECT count(DISTINCT t1.rowid) FROM t1, t2" 10 } { do_test 3.$tn.1 { set prg [db eval "EXPLAIN $sql"] set idx [lsearch $prg OpenEphemeral] expr {$idx>=0} @@ -146,14 +146,10 @@ INSERT INTO t2 VALUES(2, 3, 'z'); CREATE TABLE t3(x, y, z); INSERT INTO t3 VALUES(1,1,1); INSERT INTO t3 VALUES(2,2,2); - - CREATE TABLE t4(a); - CREATE INDEX t4a ON t4(a); - INSERT INTO t4 VALUES(1), (2), (2), (3), (1); } foreach {tn use_eph sql res} { 1 0 "SELECT count(DISTINCT c) FROM t1 GROUP BY b" {2 3 0 1} 2 1 "SELECT count(DISTINCT a) FROM t1 GROUP BY b" {2 3 0 1} @@ -160,13 +156,10 @@ 3 1 "SELECT count(DISTINCT a) FROM t1 GROUP BY b+c" {0 1 1 1 1} 4 0 "SELECT count(DISTINCT f) FROM t2 GROUP BY d, e" {1 2 2 3} 5 1 "SELECT count(DISTINCT f) FROM t2 GROUP BY d" {2 3} 6 0 "SELECT count(DISTINCT f) FROM t2 WHERE d IS 1 GROUP BY e" {1 2 2} - - 7 0 "SELECT count(DISTINCT a) FROM t1" {4} - 8 0 "SELECT count(DISTINCT a) FROM t4" {3} } { do_test 4.$tn.1 { set prg [db eval "EXPLAIN $sql"] set idx [lsearch $prg OpenEphemeral] expr {$idx>=0} Index: test/fts3fault3.test ================================================================== --- test/fts3fault3.test +++ test/fts3fault3.test @@ -48,35 +48,7 @@ catchsql { COMMIT } faultsim_integrity_check faultsim_test_result {0 {}} } -#------------------------------------------------------------------- -reset_db - -do_execsql_test 2.0 { - BEGIN; - CREATE VIRTUAL TABLE t1 USING fts3(a); - WITH s(i) AS ( - SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50 - ) - INSERT INTO t1 SELECT 'abc def ghi jkl mno pqr' FROM s; - COMMIT; -} - -faultsim_save_and_close -do_faultsim_test 2 -faults oom-t* -prep { - faultsim_restore_and_reopen - execsql { - BEGIN; - CREATE TABLE x1(a PRIMARY KEY); - } -} -body { - execsql { - PRAGMA integrity_check; - } -} -test { - faultsim_test_result {0 ok} $::TMPDBERROR -} - finish_test Index: test/func.test ================================================================== --- test/func.test +++ test/func.test @@ -784,15 +784,10 @@ execsql { SELECT quote(a), quote(b) FROM tbl2; } } {X'616263' NULL} -# Test the quote function for +Inf and -Inf -do_execsql_test func-16.2 { - SELECT quote(4.2e+859), quote(-7.8e+904); -} {9.0e+999 -9.0e+999} - # Correctly handle function error messages that include %. Ticket #1354 # do_test func-17.1 { proc testfunc1 args {error "Error %d with %s percents %p"} db function testfunc1 ::testfunc1 Index: test/json101.test ================================================================== --- test/json101.test +++ test/json101.test @@ -376,24 +376,10 @@ SELECT j2.rowid, jx.rowid, fullkey, path, key FROM j2, json_tree(j2.json) AS jx WHERE jx.value<>jx.atom AND type NOT IN ('array','object'); } {} -# 2024-02-16 https://sqlite.org/forum/forumpost/ecb94cd210 -# Regression in json_tree()/json_each(). The value column -# should have the "J" subtype if the value is an array or -# object. -# -do_execsql_test json101-5.10 { - SELECT json_insert('{}','$.a',value) FROM json_tree('[1,2,3]') WHERE atom IS NULL; -} {{{"a":[1,2,3]}}} -# ^^^^^^^--- In double-quotes, a string literal, prior to bug fix - -do_execsql_test json101-5.11 { - SELECT json_insert('{}','$.a',value) FROM json_tree('"[1,2,3]"'); -} {{{"a":"[1,2,3]"}}} - do_execsql_test json101-6.1 { SELECT json_valid('{"a":55,"b":72,}'); } {0} do_execsql_test json101-6.2 { SELECT json_error_position('{"a":55,"b":72,}'); DELETED test/json107.test Index: test/json107.test ================================================================== --- test/json107.test +++ /dev/null @@ -1,86 +0,0 @@ -# 2024-01-23 -# -# 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. -# -#*********************************************************************** -# -# Legacy JSON bug: If the input is a BLOB that when cast into TEXT looks -# like valid JSON, then treat it as valid JSON. -# -# The original intent of the JSON functions was to raise an error on any -# BLOB input. That intent was clearly documented, but the code failed to -# to implement it. Subsequently, many applications began to depend on the -# incorrect behavior, especially apps that used readfile() to read JSON -# content, since readfile() returns a BLOB. So we need to support the -# bug moving forward. -# -# The tests in this fail verify that the original buggy behavior is -# preserved. -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl -set testprefix json107 - -if {[db one {PRAGMA encoding}]!="UTF-8"} { - # These tests only work for a UTF-8 encoding. - finish_test - return -} - -do_execsql_test 1.1 { - SELECT json_valid( CAST('{"a":1}' AS BLOB) ); -} 1 -do_execsql_test 1.1.1 { - SELECT json_valid( CAST('{"a":1}' AS BLOB), 1); -} 1 -do_execsql_test 1.1.2 { - SELECT json_valid( CAST('{"a":1}' AS BLOB), 2); -} 1 -do_execsql_test 1.1.4 { - SELECT json_valid( CAST('{"a":1}' AS BLOB), 4); -} 0 -do_execsql_test 1.1.8 { - SELECT json_valid( CAST('{"a":1}' AS BLOB), 8); -} 0 - -do_execsql_test 1.2.1 { - SELECT CAST('{"a":123}' AS blob) -> 'a'; -} 123 -do_execsql_test 1.2.2 { - SELECT CAST('{"a":123}' AS blob) ->> 'a'; -} 123 -do_execsql_test 1.2.3 { - SELECT json_extract(CAST('{"a":123}' AS blob), '$.a'); -} 123 -do_execsql_test 1.3 { - SELECT json_insert(CAST('{"a":123}' AS blob),'$.b',456); -} {{{"a":123,"b":456}}} -do_execsql_test 1.4 { - SELECT json_remove(CAST('{"a":123,"b":456}' AS blob),'$.a'); -} {{{"b":456}}} -do_execsql_test 1.5 { - SELECT json_set(CAST('{"a":123,"b":456}' AS blob),'$.a',789); -} {{{"a":789,"b":456}}} -do_execsql_test 1.6 { - SELECT json_replace(CAST('{"a":123,"b":456}' AS blob),'$.a',789); -} {{{"a":789,"b":456}}} -do_execsql_test 1.7 { - SELECT json_type(CAST('{"a":123,"b":456}' AS blob)); -} object -do_execsql_test 1.8 { - SELECT json(CAST('{"a":123,"b":456}' AS blob)); -} {{{"a":123,"b":456}}} - -ifcapable vtab { - do_execsql_test 2.1 { - SELECT key, value FROM json_tree( CAST('{"a":123,"b":456}' AS blob) ) - WHERE atom; - } {a 123 b 456} -} -finish_test Index: test/json501.test ================================================================== --- test/json501.test +++ test/json501.test @@ -303,34 +303,7 @@ # 2023-11-08 forum/forumpost/ddcad3e884 # do_execsql_test 13.1 { SELECT json('{x:''a "b" c''}'); } {{{"x":"a \"b\" c"}}} - -# 2024-01-31 -# Allow control characters within JSON5 string literals. -# -for {set c 1} {$c<=0x1f} {incr c} { - do_execsql_test 14.$c.1 { - SELECT json_valid('"abc' || char($c) || 'xyz"'); - } {0} - do_execsql_test 14.$c.2 { - SELECT json_valid('"abc' || char($c) || 'xyz"', 2); - } {1} - switch $c { - 8 {set e "\\b"} - 9 {set e "\\t"} - 10 {set e "\\n"} - 12 {set e "\\f"} - 13 {set e "\\r"} - default {set e [format "\\u00%02x" $c]} - } - do_execsql_test 14.$c.3 { - SELECT json('{label:"abc' || char($c) || 'xyz"}'); - } "{{\"label\":\"abc${e}xyz\"}}" - do_execsql_test 14.$c.4 { - SELECT jsonb('{label:"abc' || char($c) || 'xyz"}') -> '$'; - } "{{\"label\":\"abc${e}xyz\"}}" -} - finish_test Index: test/jsonb01.test ================================================================== --- test/jsonb01.test +++ test/jsonb01.test @@ -44,10 +44,6 @@ do_execsql_test jsonb01-1.2.$id.2 { SELECT json_remove(x,$path) FROM t1; } $res } -do_catchsql_test jsonb01-2.0 { - SELECT x'8ce6ffffffff171333' -> '$'; -} {1 {malformed JSON}} - finish_test Index: test/literal.test ================================================================== --- test/literal.test +++ test/literal.test @@ -17,30 +17,17 @@ set ::testprefix literal proc test_literal {tn lit type val} { do_execsql_test $tn.1 "SELECT typeof( $lit ), $lit" [list $type $val] - ifcapable altertable { - do_execsql_test $tn.2 " - DROP TABLE IF EXISTS x1; - CREATE TABLE x1(a); - INSERT INTO x1 VALUES(123); - ALTER TABLE x1 ADD COLUMN b DEFAULT $lit ; - SELECT typeof(b), b FROM x1; - " [list $type $val] - } - - do_execsql_test $tn.3 " - DROP TABLE IF EXISTS x1; - CREATE TABLE x1(a DEFAULT $lit); - INSERT INTO x1 DEFAULT VALUES; - SELECT typeof(a), a FROM x1; - " [list $type $val] -} - -proc test_literal_error {tn lit unrec} { - do_catchsql_test $tn "SELECT $lit" "1 {unrecognized token: \"$unrec\"}" + do_execsql_test $tn.2 " + DROP TABLE IF EXISTS x1; + CREATE TABLE x1(a); + INSERT INTO x1 VALUES(123); + ALTER TABLE x1 ADD COLUMN b DEFAULT $lit ; + SELECT typeof(b), b FROM x1; + " [list $type $val] } test_literal 1.0 45 integer 45 test_literal 1.1 0xFF integer 255 @@ -53,44 +40,18 @@ test_literal 1.9 +0x7FFFFFFFFFFFFFFF integer [expr +0x7FFFFFFFFFFFFFFF] test_literal 1.10 -45 integer -45 test_literal 1.11 '0xFF' text 0xFF test_literal 1.12 '-0xFF' text -0xFF test_literal 1.13 -'0xFF' integer 0 -test_literal 1.14 -9223372036854775808 integer -9223372036854775808 - -test_literal 2.1 1e12 real 1000000000000.0 -test_literal 2.2 1.0 real 1.0 -test_literal 2.3 1e1000 real Inf -test_literal 2.4 -1e1000 real -Inf - -test_literal 3.1 1_000 integer 1000 -test_literal 3.2 1.1_1 real 1.11 -test_literal 3.3 1_0.1_1 real 10.11 -test_literal 3.4 1e1_000 real Inf -test_literal 3.5 12_3_456.7_8_9 real 123456.789 -test_literal 3.6 9_223_372_036_854_775_807 integer 9223372036854775807 -test_literal 3.7 9_223_372_036_854_775_808 real 9.22337203685478e+18 -test_literal 3.8 -9_223_372_036_854_775_808 integer -9223372036854775808 - -foreach {tn lit unrec} { - 0 123a456 123a456 - 1 1_ 1_ - 2 1_.4 1_.4 - 3 1e_4 1e_4 - 4 1_e4 1_e4 - 5 1.4_e4 1.4_e4 - 6 1.4e+_4 1.4e - 7 1.4e-_4 1.4e - 8 1.4e4_ 1.4e4_ - 9 1.4_e4 1.4_e4 - 10 1.4e_4 1.4e_4 - 11 12__34 12__34 - 12 1234_ 1234_ - 13 12._34 12._34 - 14 12_.34 12_.34 - 15 12.34_ 12.34_ - 16 1.0e1_______2 1.0e1_______2 -} { - test_literal_error 4.$tn $lit $unrec -} + +test_literal 2.1 1_000 integer 1000 +test_literal 2.2 1.1_1 real 1.11 +test_literal 2.3 1_0.1_1 real 10.11 +test_literal 2.4 1e1_000 real Inf +test_literal 2.5 123______456.7_8__9_ real 123456.789 +test_literal 2.6 9_223_372_036_854_775_807 integer 9223372036854775807 +test_literal 2.7 9_223_372_036_854_775_808 real 9.22337203685478e+18 +test_literal 2.8 -9_223_372_036_854_775_808 integer -9223372036854775808 + +test_literal 3.3 1e12 real 1000000000000.0 finish_test DELETED test/literal2.tcl Index: test/literal2.tcl ================================================================== --- test/literal2.tcl +++ /dev/null @@ -1,40 +0,0 @@ -# 2018 May 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. -# -#*********************************************************************** -# - -source [file join [file dirname $argv0] pg_common.tcl] - -#========================================================================= - - -start_test literal2 "2024 Jan 23" - -execsql_test 1.0 { SELECT 123_456 } -errorsql_test 1.1 { SELECT 123__456 } - -execsql_float_test 2.1 { SELECT 1.0e1_2 } - - -execsql_test 3.0.0 { SELECT 0xFF_FF } -execsql_test 3.0.1 { SELECT 0xFF_EF } -errorsql_test 3.0.2 { SELECT 0xFF__EF } -# errorsql_test 3.0.3 { SELECT 0x_FFEF } -errorsql_test 3.0.4 { SELECT 0xFFEF_ } - -execsql_test 3.1.0 { SELECT 0XFF_FF } -execsql_test 3.1.1 { SELECT 0XFF_EF } -errorsql_test 3.1.2 { SELECT 0XFF__EF } -# errorsql_test 3.1.3 { SELECT 0X_FFEF } -errorsql_test 3.1.4 { SELECT 0XFFEF_ } - -finish_test - - DELETED test/literal2.test Index: test/literal2.test ================================================================== --- test/literal2.test +++ /dev/null @@ -1,84 +0,0 @@ -# 2024 Jan 23 -# -# 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. -# - -#################################################### -# DO NOT EDIT! THIS FILE IS AUTOMATICALLY GENERATED! -#################################################### - -set testdir [file dirname $argv0] -source $testdir/tester.tcl -set testprefix literal2 - -do_execsql_test 1.0 { - SELECT 123_456 -} {123456} - -# PG says ERROR: trailing junk after numeric literal at or near "123_" -do_test 1.1 { catch { execsql { - SELECT 123__456 -} } } 1 - - -do_test 2.1 { - set myres {} - foreach r [db eval {SELECT 1.0e1_2}] { - lappend myres [format %.4f [set r]] - } - set res2 {1000000000000.0000} - set i 0 - foreach r [set myres] r2 [set res2] { - if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} { - error "list element [set i] does not match: got=[set r] expected=[set r2]" - } - incr i - } - set {} {} -} {} - -do_execsql_test 3.0.0 { - SELECT 0xFF_FF -} {65535} - -do_execsql_test 3.0.1 { - SELECT 0xFF_EF -} {65519} - -# PG says ERROR: trailing junk after numeric literal at or near "0xFF_" -do_test 3.0.2 { catch { execsql { - SELECT 0xFF__EF -} } } 1 - -# PG says ERROR: trailing junk after numeric literal at or near "0xFFEF_" -do_test 3.0.4 { catch { execsql { - SELECT 0xFFEF_ -} } } 1 - -do_execsql_test 3.1.0 { - SELECT 0XFF_FF -} {65535} - -do_execsql_test 3.1.1 { - SELECT 0XFF_EF -} {65519} - -# PG says ERROR: trailing junk after numeric literal at or near "0XFF_" -do_test 3.1.2 { catch { execsql { - SELECT 0XFF__EF -} } } 1 - -# PG says ERROR: trailing junk after numeric literal at or near "0XFFEF_" -do_test 3.1.4 { catch { execsql { - SELECT 0XFFEF_ -} } } 1 - -finish_test Index: test/memdb1.test ================================================================== --- test/memdb1.test +++ test/memdb1.test @@ -82,10 +82,11 @@ } {1 2} do_test 152 { catchsql {INSERT INTO t1 VALUES(3,4);} } {1 {attempt to write a readonly database}} +breakpoint do_test 160 { db deserialize -maxsize 32768 $db1 db eval {SELECT * FROM t1} } {1 2} do_test 161 { @@ -245,11 +246,10 @@ db close set fd [open test.db] fconfigure $fd -translation binary -encoding binary set data [read $fd [expr 20*1024]] - close $fd sqlite3 db "" db deserialize $data do_execsql_test 810 { @@ -265,19 +265,6 @@ do_catchsql_test 830 { PRAGMA wal_checkpoint; } {1 {database disk image is malformed}} } -# 2024-01-20 -# https://sqlite.org/forum/forumpost/498777780e16880a -# -# Make sure a database is initialized before serializing it. -# -reset_db -sqlite3 dbempty :memory: -do_test 900 { - set len [string length [dbempty serialize]] - expr {$len>0} -} 1 -dbempty close - finish_test Index: test/misc1.test ================================================================== --- test/misc1.test +++ test/misc1.test @@ -652,11 +652,11 @@ do_catchsql_test misc1-21.1 { select''like''like''like#0; } {1 {near "#0": syntax error}} do_catchsql_test misc1-21.2 { VALUES(0,0x0MATCH#0; -} {1 {unrecognized token: "0x0MATCH"}} +} {1 {near ";": syntax error}} # 2015-04-15 do_execsql_test misc1-22.1 { SELECT ''+3 FROM (SELECT ''+5); } {3} Index: test/misc5.test ================================================================== --- test/misc5.test +++ test/misc5.test @@ -567,37 +567,25 @@ } } {1 {no such table: logs_base}} } # Overflow the lemon parser stack by providing an overly complex -# expression. Make sure that the overflow is detected and the -# stack is grown automatically such that the application calling -# SQLite never notices. +# expression. Make sure that the overflow is detected and reported. +# +# This test fails when building with -DYYSTACKDEPTH=0 # -do_test misc5-7.1.1 { +do_test misc5-7.1 { execsql {CREATE TABLE t1(x)} set sql "INSERT INTO t1 VALUES(" set tail "" for {set i 0} {$i<200} {incr i} { append sql "(1+" append tail ")" } - append sql "0$tail); SELECT * FROM t1;" - catchsql $sql -} {0 200} -do_test misc5-7.1.2 { - execsql {DELETE FROM t1} - set sql "INSERT INTO t1 VALUES(" - set tail "" - for {set i 0} {$i<900} {incr i} { - append sql "(1+" - append tail ")" - } - append sql "0$tail); SELECT * FROM t1;" - catchsql $sql -} {0 900} - + append sql 2$tail + catchsql $sql +} {1 {parser stack overflow}} # Parser stack overflow is silently ignored when it occurs while parsing the # schema and PRAGMA writable_schema is turned on. # do_test misc5-7.2 { Index: test/mmap1.test ================================================================== --- test/mmap1.test +++ test/mmap1.test @@ -43,22 +43,22 @@ $dbname func rblob rblob }] } -# For cases 1.1 and 1.4, the number of pages read using xRead() is 8 on -# unix and 12 on windows. The difference is that windows only ever maps +# For cases 1.1 and 1.4, the number of pages read using xRead() is 4 on +# unix and 9 on windows. The difference is that windows only ever maps # an integer number of OS pages (i.e. creates mappings that are a multiple # of 4KB in size). Whereas on unix any sized mapping may be created. # foreach {t mmap_size nRead c2init} { - 1.1 { PRAGMA mmap_size = 67108864 } /8|12/ {PRAGMA mmap_size = 0} - 1.2 { PRAGMA mmap_size = 53248 } /15[34]/ {PRAGMA mmap_size = 0} - 1.3 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 0} - 1.4 { PRAGMA mmap_size = 67108864 } /12|8/ {PRAGMA mmap_size = 67108864 } - 1.5 { PRAGMA mmap_size = 53248 } /15[34]/ {PRAGMA mmap_size = 67108864 } - 1.6 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 67108864 } + 1.1 { PRAGMA mmap_size = 67108864 } /[49]/ {PRAGMA mmap_size = 0} + 1.2 { PRAGMA mmap_size = 53248 } 150 {PRAGMA mmap_size = 0} + 1.3 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 0} + 1.4 { PRAGMA mmap_size = 67108864 } /[49]/ {PRAGMA mmap_size = 67108864 } + 1.5 { PRAGMA mmap_size = 53248 } 150 {PRAGMA mmap_size = 67108864 } + 1.6 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 67108864 } } { do_multiclient_test tn { sql1 {PRAGMA cache_size=2000} sql2 {PRAGMA cache_size=2000} DELETED test/mmapcorrupt.test Index: test/mmapcorrupt.test ================================================================== --- test/mmapcorrupt.test +++ /dev/null @@ -1,51 +0,0 @@ -# 2024 January 23 -# -# 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 special cases of corrupt database handling in mmap-mode. -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl -set testprefix mmapcorrupt - -database_may_be_corrupt - -db close -sqlite3_shutdown -sqlite3_config_lookaside 0 0 -sqlite3_initialize - -reset_db -do_execsql_test 1.0 { - PRAGMA page_size = 16384; - CREATE TABLE tn1(a PRIMARY KEY) WITHOUT ROWID; - CREATE TABLE t0(a PRIMARY KEY) WITHOUT ROWID; - CREATE TABLE t1(a PRIMARY KEY) WITHOUT ROWID; - INSERT INTO t1 VALUES('B'); -} -db close - -set sz [file size test.db] -hexio_write test.db [expr $sz-3] 800380 - -sqlite3 db test.db -do_execsql_test 2.1 { - PRAGMA mmap_size = 1000000; - SELECT sql FROM sqlite_schema LIMIT 1; - SELECT * FROM t0; -} {1000000 {CREATE TABLE tn1(a PRIMARY KEY) WITHOUT ROWID}} - -do_execsql_test 2.2 { - INSERT INTO t0 SELECT * FROM t1; -} - -finish_test - Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -93,11 +93,10 @@ $testdir/../ext/fts5/test/*.test \ $testdir/../ext/expert/*.test \ $testdir/../ext/lsm1/test/*.test \ $testdir/../ext/recover/*.test \ $testdir/../ext/rbu/*.test \ - $testdir/../ext/intck/*.test \ ] { lappend alltests $f } foreach f [glob -nocomplain $testdir/../ext/session/*.test] { lappend alltests $f Index: test/pragma.test ================================================================== --- test/pragma.test +++ test/pragma.test @@ -554,25 +554,10 @@ 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}} - - # forum post https://sqlite.org/forum/forumpost/ee4f6fa5ab - do_execsql_test pragma-3.24 { - DROP TABLE IF EXISTS t1; - CREATE TABLE t1(a); - INSERT INTO t1 VALUES (1); - ALTER TABLE t1 ADD COLUMN b NOT NULL DEFAULT 0.25; - SELECT * FROM t1; - PRAGMA integrity_check(t1); - } {1 0.25 ok} - do_execsql_test pragma-3.25 { - ALTER TABLE t1 ADD COLUMN c CHECK (1); - SELECT * FROM t1; - PRAGMA integrity_check(t1); - } {1 0.25 {} ok} } # PRAGMA integrity check (or more specifically the sqlite3BtreeCount() # interface) used to leave index cursors in an inconsistent state # which could result in an assertion fault in sqlite3BtreeKey() Index: test/printf.test ================================================================== --- test/printf.test +++ test/printf.test @@ -3830,24 +3830,7 @@ sqlite3 db test.db sqlite3_db_config_lookaside db 0 0 0 do_execsql_test printf-18.1 { SELECT length( format('%,.249f', -5.0e-300) ); } {252} - -# 2024-02-16 -# https://sqlite.org/forum/info/393708f4a8 -# -# The problem introduced by on 2023-07-03 by -# https://sqlite.org/src/info/32befb224b254639 -# -do_execsql_test printf-19.1 { - SELECT format('%0.0f %0.0g %0.0g', 0.9, 0.09, 1.9); -} {{1 0.09 2}} -do_execsql_test printf-19.2 { - SELECT format('%0.0f %#0.0f',0.0, 0.0); -} {{0 0.}} -do_execsql_test printf-19.3 { - SELECT format('%,.0f %,.0f',12345e+10, 12345e+11); -} {{123,450,000,000,000 1,234,500,000,000,000}} - finish_test Index: test/quote.test ================================================================== --- test/quote.test +++ test/quote.test @@ -101,11 +101,11 @@ 1 { CREATE TABLE xyz(a, b, c CHECK (c!="null") ) } null 2 { CREATE INDEX i2 ON t1(x, y, z||"abc") } abc 3 { CREATE INDEX i3 ON t1("w") } w 4 { CREATE INDEX i4 ON t1(x) WHERE z="w" } w } { - do_catchsql_test 2.1.$tn $sql [list 1 "no such column: \"$errname\" - should this be a string literal in single-quotes?"] + do_catchsql_test 2.1.$tn $sql [list 1 "no such column: $errname"] } do_execsql_test 2.2 { PRAGMA writable_schema = 1; CREATE TABLE xyz(a, b, c CHECK (c!="null") ); @@ -145,23 +145,23 @@ 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" - should this be a string literal in single-quotes?}} + } {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" - should this be a string literal in single-quotes?}} + } {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" - should this be a string literal in single-quotes?}} + } {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; @@ -170,11 +170,11 @@ 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" - should this be a string literal in single-quotes?}} + } {1 {error in index x1 after drop column: no such column: b}} sqlite3_db_config db SQLITE_DBCONFIG_DQS_DDL 1 do_catchsql_test 3.5 { DROP TABLE t1; CREATE TABLE t1(a, b, c); CREATE INDEX x1 ON t1("a"||"x"); Index: test/sqllimits1.test ================================================================== --- test/sqllimits1.test +++ test/sqllimits1.test @@ -705,11 +705,10 @@ catchsql [subst { SELECT $expr }] } "1 {Expression tree is too large (maximum depth $::SQLITE_MAX_EXPR_DEPTH)}" -if 0 { # Attempting to beat the expression depth limit using nested SELECT # queries causes a parser stack overflow. do_test sqllimits1-9.2 { set max $::SQLITE_MAX_EXPR_DEPTH set expr "SELECT 1" @@ -717,10 +716,11 @@ set expr "SELECT ($expr)" } catchsql [subst { $expr }] } "1 {parser stack overflow}" +if 0 { do_test sqllimits1-9.3 { execsql { PRAGMA max_page_count = 1000000; -- 1 GB CREATE TABLE v0(a); INSERT INTO v0 VALUES(1); Index: test/tkt-8454a207b9.test ================================================================== --- test/tkt-8454a207b9.test +++ test/tkt-8454a207b9.test @@ -47,11 +47,11 @@ do_test tkt-8454a207b9.4 { db eval { ALTER TABLE t1 ADD COLUMN e DEFAULT -123.0; SELECT e, typeof(e) FROM t1; } -} {-123.0 real} +} {-123 integer} do_test tkt-8454a207b9.5 { db eval { ALTER TABLE t1 ADD COLUMN f DEFAULT -123.5; SELECT f, typeof(f) FROM t1; } Index: tool/lemon.c ================================================================== --- tool/lemon.c +++ tool/lemon.c @@ -416,12 +416,10 @@ char *tokendest; /* Code to execute to destroy token data */ char *vardest; /* Code for the default non-terminal destructor */ char *filename; /* Name of the input file */ char *outname; /* Name of the current output file */ char *tokenprefix; /* A prefix added to token names in the .h file */ - char *reallocFunc; /* Function to use to allocate stack space */ - char *freeFunc; /* Function to use to free stack space */ int nconflict; /* Number of parsing conflicts */ int nactiontab; /* Number of entries in the yy_action[] table */ int nlookaheadtab; /* Number of entries in yy_lookahead[] */ int tablesize; /* Total table size of all tables in bytes */ int basisflag; /* Print only basis configurations */ @@ -2531,16 +2529,10 @@ psp->declargslot = &(psp->gp->tokentype); psp->insertLineMacro = 0; }else if( strcmp(x,"default_type")==0 ){ psp->declargslot = &(psp->gp->vartype); psp->insertLineMacro = 0; - }else if( strcmp(x,"realloc")==0 ){ - psp->declargslot = &(psp->gp->reallocFunc); - psp->insertLineMacro = 0; - }else if( strcmp(x,"free")==0 ){ - psp->declargslot = &(psp->gp->freeFunc); - psp->insertLineMacro = 0; }else if( strcmp(x,"stack_size")==0 ){ psp->declargslot = &(psp->gp->stacksize); psp->insertLineMacro = 0; }else if( strcmp(x,"start_symbol")==0 ){ psp->declargslot = &(psp->gp->start); @@ -4315,11 +4307,11 @@ int lineno; struct state *stp; struct action *ap; struct rule *rp; struct acttab *pActtab; - int i, j, n, sz, mn, mx; + int i, j, n, sz; int nLookAhead; int szActionType; /* sizeof(YYACTIONTYPE) */ int szCodeType; /* sizeof(YYCODETYPE) */ const char *name; int mnTknOfst, mxTknOfst; @@ -4507,25 +4499,10 @@ fprintf(out,"#define %sARG_PDECL\n",name); lineno++; fprintf(out,"#define %sARG_PARAM\n",name); lineno++; fprintf(out,"#define %sARG_FETCH\n",name); lineno++; fprintf(out,"#define %sARG_STORE\n",name); lineno++; } - if( lemp->reallocFunc ){ - fprintf(out,"#define YYREALLOC %s\n", lemp->reallocFunc); lineno++; - }else{ - fprintf(out,"#define YYREALLOC realloc\n"); lineno++; - } - if( lemp->freeFunc ){ - fprintf(out,"#define YYFREE %s\n", lemp->freeFunc); lineno++; - }else{ - fprintf(out,"#define YYFREE free\n"); lineno++; - } - if( lemp->reallocFunc && lemp->freeFunc ){ - fprintf(out,"#define YYDYNSTACK 1\n"); lineno++; - }else{ - fprintf(out,"#define YYDYNSTACK 0\n"); lineno++; - } if( lemp->ctx && lemp->ctx[0] ){ i = lemonStrlen(lemp->ctx); while( i>=1 && ISSPACE(lemp->ctx[i-1]) ) i--; while( i>=1 && (ISALNUM(lemp->ctx[i-1]) || lemp->ctx[i-1]=='_') ) i--; fprintf(out,"#define %sCTX_SDECL %s;\n",name,lemp->ctx); lineno++; @@ -4645,26 +4622,10 @@ fprintf(out,"#define YY_ACCEPT_ACTION %d\n", lemp->accAction); lineno++; fprintf(out,"#define YY_NO_ACTION %d\n", lemp->noAction); lineno++; fprintf(out,"#define YY_MIN_REDUCE %d\n", lemp->minReduce); lineno++; i = lemp->minReduce + lemp->nrule; fprintf(out,"#define YY_MAX_REDUCE %d\n", i-1); lineno++; - - /* Minimum and maximum token values that have a destructor */ - mn = mx = 0; - for(i=0; insymbol; i++){ - struct symbol *sp = lemp->symbols[i]; - - if( sp && sp->type!=TERMINAL && sp->destructor ){ - if( mn==0 || sp->indexindex; - if( sp->index>mx ) mx = sp->index; - } - } - if( lemp->tokendest ) mn = 0; - if( lemp->vardest ) mx = lemp->nsymbol-1; - fprintf(out,"#define YY_MIN_DSTRCTR %d\n", mn); lineno++; - fprintf(out,"#define YY_MAX_DSTRCTR %d\n", mx); lineno++; - tplt_xfer(lemp->name,in,out,&lineno); /* Now output the action table and its associates: ** ** yy_action[] A single table containing all actions. @@ -4804,11 +4765,11 @@ tplt_xfer(lemp->name,in,out,&lineno); /* Generate the table of fallback tokens. */ if( lemp->has_fallback ){ - mx = lemp->nterminal - 1; + int mx = lemp->nterminal - 1; /* 2019-08-28: Generate fallback entries for every token to avoid ** having to do a range check on the index */ /* while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; } */ lemp->tablesize += (mx+1)*szCodeType; for(i=0; i<=mx; i++){ Index: tool/lempar.c ================================================================== --- tool/lempar.c +++ tool/lempar.c @@ -65,13 +65,10 @@ ** ParseARG_PDECL A parameter declaration for the %extra_argument ** ParseARG_PARAM Code to pass %extra_argument as a subroutine parameter ** ParseARG_STORE Code to store %extra_argument into yypParser ** ParseARG_FETCH Code to extract %extra_argument from yypParser ** ParseCTX_* As ParseARG_ except for %extra_context -** YYREALLOC Name of the realloc() function to use -** YYFREE Name of the free() function to use -** YYDYNSTACK True if stack space should be extended on heap ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. ** YYNRULE the number of rules in the grammar ** YYNTOKEN Number of terminal symbols @@ -81,12 +78,10 @@ ** YY_ERROR_ACTION The yy_action[] code for syntax error ** YY_ACCEPT_ACTION The yy_action[] code for accept ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions -** YY_MIN_DSTRCTR Minimum symbol value that has a destructor -** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ @@ -104,26 +99,10 @@ */ #ifndef yytestcase # define yytestcase(X) #endif -/* Macro to determine if stack space has the ability to grow using -** heap memory. -*/ -#if YYSTACKDEPTH<=0 || YYDYNSTACK -# define YYGROWABLESTACK 1 -#else -# define YYGROWABLESTACK 0 -#endif - -/* Guarantee a minimum number of initial stack slots. -*/ -#if YYSTACKDEPTH<=0 -# undef YYSTACKDEPTH -# define YYSTACKDEPTH 2 /* Need a minimum stack size */ -#endif - /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an ** action integer. @@ -231,13 +210,18 @@ #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ #endif ParseARG_SDECL /* A place to hold %extra_argument */ ParseCTX_SDECL /* A place to hold %extra_context */ - yyStackEntry *yystackEnd; /* Last entry in the stack */ - yyStackEntry *yystack; /* The parser stack */ - yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ + yyStackEntry yystk0; /* First stack entry */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ + yyStackEntry *yystackEnd; /* Last entry in the stack */ +#endif }; typedef struct yyParser yyParser; #include #ifndef NDEBUG @@ -287,49 +271,41 @@ %% }; #endif /* NDEBUG */ -#if YYGROWABLESTACK +#if YYSTACKDEPTH<=0 /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int yyGrowStack(yyParser *p){ - int oldSize = 1 + (int)(p->yystackEnd - p->yystack); int newSize; int idx; yyStackEntry *pNew; - newSize = oldSize*2 + 100; - idx = (int)(p->yytos - p->yystack); - if( p->yystack==p->yystk0 ){ - pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); - if( pNew==0 ) return 1; - memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); + newSize = p->yystksz*2 + 100; + idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; + if( p->yystack==&p->yystk0 ){ + pNew = malloc(newSize*sizeof(pNew[0])); + if( pNew ) pNew[0] = p->yystk0; }else{ - pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); - if( pNew==0 ) return 1; - } - p->yystack = pNew; - p->yytos = &p->yystack[idx]; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", - yyTracePrompt, oldSize, newSize); - } -#endif - p->yystackEnd = &p->yystack[newSize-1]; - return 0; -} -#endif /* YYGROWABLESTACK */ - -#if !YYGROWABLESTACK -/* For builds that do no have a growable stack, yyGrowStack always -** returns an error. -*/ -# define yyGrowStack(X) 1 + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + } + if( pNew ){ + p->yystack = pNew; + p->yytos = &p->yystack[idx]; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", + yyTracePrompt, p->yystksz, newSize); + } +#endif + p->yystksz = newSize; + } + return pNew==0; +} #endif /* Datatype of the argument to the memory allocated passed as the ** second argument to ParseAlloc() below. This can be changed by ** putting an appropriate #define in the %include section of the input @@ -345,18 +321,28 @@ yyParser *yypParser = (yyParser*)yypRawParser; ParseCTX_STORE #ifdef YYTRACKMAXSTACKDEPTH yypParser->yyhwm = 0; #endif - yypParser->yystack = yypParser->yystk0; - yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; +#if YYSTACKDEPTH<=0 + yypParser->yytos = NULL; + yypParser->yystack = NULL; + yypParser->yystksz = 0; + if( yyGrowStack(yypParser) ){ + yypParser->yystack = &yypParser->yystk0; + yypParser->yystksz = 1; + } +#endif #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yypParser->yytos = yypParser->yystack; yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; +#if YYSTACKDEPTH>0 + yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; +#endif } #ifndef Parse_ENGINEALWAYSONSTACK /* ** This function allocates a new parser. @@ -438,30 +424,13 @@ /* ** Clear all secondary memory allocations from the parser */ void ParseFinalize(void *p){ yyParser *pParser = (yyParser*)p; - - /* In-lined version of calling yy_pop_parser_stack() for each - ** element left in the stack */ - yyStackEntry *yytos = pParser->yytos; - while( yytos>pParser->yystack ){ -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sPopping %s\n", - yyTracePrompt, - yyTokenName[yytos->major]); - } -#endif - if( yytos->major>=YY_MIN_DSTRCTR ){ - yy_destructor(pParser, yytos->major, &yytos->minor); - } - yytos--; - } - -#if YYGROWABLESTACK - if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); + while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); #endif } #ifndef Parse_ENGINEALWAYSONSTACK /* @@ -683,23 +652,29 @@ if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif - yytos = yypParser->yytos; - if( yytos>yypParser->yystackEnd ){ +#if YYSTACKDEPTH>0 + if( yypParser->yytos>yypParser->yystackEnd ){ + yypParser->yytos--; + yyStackOverflow(yypParser); + return; + } +#else + if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ if( yyGrowStack(yypParser) ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } - yytos = yypParser->yytos; - assert( yytos <= yypParser->yystackEnd ); } +#endif if( yyNewState > YY_MAX_SHIFT ){ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; } + yytos = yypParser->yytos; yytos->stateno = yyNewState; yytos->major = yyMajor; yytos->minor.yy0 = yyMinor; yyTraceShift(yypParser, yyNewState, "Shift"); } @@ -934,16 +909,23 @@ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); } #endif +#if YYSTACKDEPTH>0 if( yypParser->yytos>=yypParser->yystackEnd ){ + yyStackOverflow(yypParser); + break; + } +#else + if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); break; } } +#endif } yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor ParseCTX_PARAM); }else if( yyact <= YY_MAX_SHIFTREDUCE ){ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); #ifndef YYNOERRORRECOVERY