Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -815,11 +815,12 @@ mv vdbe.new tsrc/vdbe.c cp fts5.c fts5.h tsrc touch .target_source sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify has_tclsh84 - $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC) + $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS) \ + $(EXTRA_SRC) cp tsrc/sqlite3ext.h . cp $(TOP)/ext/session/sqlite3session.h . sqlite3r.h: sqlite3.h has_tclsh84 $(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h @@ -826,11 +827,12 @@ sqlite3r.c: sqlite3.c sqlite3r.h has_tclsh84 cp $(TOP)/ext/recover/sqlite3recover.c tsrc/ cp $(TOP)/ext/recover/sqlite3recover.h tsrc/ cp $(TOP)/ext/recover/dbdata.c tsrc/ - $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC) + $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover \ + $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC) sqlite3ext.h: .target_source cp tsrc/sqlite3ext.h . tclsqlite3.c: sqlite3.c @@ -1310,13 +1312,13 @@ testfixture$(TEXE): has_tclsh85 $(TESTFIXTURE_SRC) $(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \ -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS) -coretestprogs: testfixture$(BEXE) sqlite3$(BEXE) +coretestprogs: $(TESTPROGS) -testprogs: $(TESTPROGS) srcck1$(BEXE) fuzzcheck$(TEXE) sessionfuzz$(TEXE) +testprogs: coretestprogs srcck1$(BEXE) fuzzcheck$(TEXE) sessionfuzz$(TEXE) # A very detailed test running most or all test cases fulltest: alltest fuzztest # Run most or all tcl test cases Index: Makefile.msc ================================================================== --- Makefile.msc +++ Makefile.msc @@ -2494,13 +2494,13 @@ .\testfixture.exe $(TOP)\test\loadext.test $(TESTOPTS) tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe $(TOP)\tool\mktoolzip.tcl .\testfixture.exe $(TOP)\tool\mktoolzip.tcl -coretestprogs: testfixture.exe sqlite3.exe +coretestprogs: $(TESTPROGS) -testprogs: $(TESTPROGS) srcck1.exe fuzzcheck.exe sessionfuzz.exe +testprogs: coretestprogs srcck1.exe fuzzcheck.exe sessionfuzz.exe fulltest: alltest fuzztest alltest: $(TESTPROGS) @set PATH=$(LIBTCLPATH);$(PATH) @@ -2551,11 +2551,11 @@ mdevtest: $(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest # Testing for a release # -releasetest: testfixture.exe +releasetest: testfixture.exe fuzztest testfixture.exe $(TOP)\test\testrunner.tcl release smoketest: $(TESTPROGS) @set PATH=$(LIBTCLPATH);$(PATH) Index: VERSION ================================================================== --- VERSION +++ VERSION @@ -1,1 +1,1 @@ -3.46.1 +3.46.0 Index: autoconf/tea/configure.ac ================================================================== --- autoconf/tea/configure.ac +++ autoconf/tea/configure.ac @@ -17,11 +17,11 @@ # so you can encode the package version directly into the source files. # This will also define a special symbol for Windows (BUILD_ # so that we create the export library with the dll. #----------------------------------------------------------------------- -AC_INIT([sqlite],[3.46.1]) +AC_INIT([sqlite],[3.46.0]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. # This will define a ${TEA_PLATFORM} variable == "unix" or "windows" # as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE. Index: configure ================================================================== --- configure +++ configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sqlite 3.46.1. +# Generated by GNU Autoconf 2.69 for sqlite 3.46.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # @@ -724,12 +724,12 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.46.1' -PACKAGE_STRING='sqlite 3.46.1' +PACKAGE_VERSION='3.46.0' +PACKAGE_STRING='sqlite 3.46.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ @@ -1470,11 +1470,11 @@ # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.46.1 to adapt to many kinds of systems. +\`configure' configures sqlite 3.46.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. @@ -1535,11 +1535,11 @@ _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.46.1:";; + short | recursive ) echo "Configuration of sqlite 3.46.0:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options @@ -1666,11 +1666,11 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.46.1 +sqlite configure 3.46.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. @@ -2085,11 +2085,11 @@ } # ac_fn_c_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.46.1, which was +It was created by sqlite $as_me 3.46.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF @@ -12479,11 +12479,11 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.46.1, which was +This file was extended by sqlite $as_me 3.46.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS @@ -12545,11 +12545,11 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sqlite config.status 3.46.1 +sqlite config.status 3.46.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation Index: doc/testrunner.md ================================================================== --- doc/testrunner.md +++ doc/testrunner.md @@ -15,10 +15,11 @@
  • 3.1. Commands to Run SQLite Tests
  • 3.2. Running ZipVFS Tests
  • 3.3. Investigating Source Code Test Failures
  • 4. Extra testrunner.tcl Options +# 4. Extra testrunner.tcl Options
  • 5. Controlling CPU Core Utilization # 1. Overview @@ -26,22 +27,16 @@ testrunner.tcl is a Tcl script used to run multiple SQLite tests using multiple jobs. It supports the following types of tests: * Tcl test scripts. - * Tests run with `make` commands. Examples: - - `make mdevtest` - - `make releasetest` - - `make sdevtest` - - `make testrunner` + * Tests run with [make] commands. Specifically, at time of writing, + [make fuzztest], [make mptest], [make sourcetest] and [make threadtest]. testrunner.tcl pipes the output of all tests and builds run into log file -**testrunner.log**, created in the current working directory. Search this -file to find details of errors. Suggested search commands: - - * `grep "^!" testrunner.log` - * `grep failed testrunner.log` +**testrunner.log**, created in the cwd directory. Searching this file for +"failed" is a good way to find the output of a failed test. testrunner.tcl also populates SQLite database **testrunner.db**. This database contains details of all tests run, running and to be run. A useful query might be: @@ -63,21 +58,21 @@ watch ./testfixture $(TESTDIR)/testrunner.tcl status ``` in another terminal is a good way to keep an eye on a long running test. -Sometimes testrunner.tcl uses the `testfixture` binary that it is run with +Sometimes testrunner.tcl uses the [testfixture] binary that it is run with to run tests (see "Binary Tests" below). Sometimes it builds testfixture and other binaries in specific configurations to test (see "Source Tests"). # 2. Binary Tests The commands described in this section all run various combinations of the Tcl -test scripts using the `testfixture` binary used to run the testrunner.tcl +test scripts using the [testfixture] binary used to run the testrunner.tcl script (i.e. they do not invoke the compiler to build new binaries, or the -`make` command to run tests that are not Tcl scripts). The procedure to run +[make] command to run tests that are not Tcl scripts). The procedure to run these tests is therefore: 1. Build the "testfixture" (or "testfixture.exe" for windows) binary using whatever method seems convenient. @@ -196,11 +191,11 @@ ## 3.1. Commands to Run SQLite Tests The **mdevtest** command is equivalent to running the veryquick tests and -the `make fuzztest` target once for each of two --enable-all builds - one +the [make fuzztest] target once for each of two --enable-all builds - one with debugging enabled and one without: ``` tclsh $TESTDIR/testrunner.tcl mdevtest ``` @@ -286,11 +281,11 @@ # Create a script that recreates build configuration "Have-Not" on Windows: tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat ``` The generated bash or \*.bat file script accepts a single argument - a makefile -target to build. This may be used either to run a `make` command test directly, +target to build. This may be used either to run a [make] command test directly, or else to build a testfixture (or testfixture.exe) binary with which to run a Tcl test script, as described above. # 4. Extra testrunner.tcl Options @@ -313,20 +308,10 @@ ``` # Log the shell commmands that make up the mdevtest test. tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest" ``` -The **--explain** option is similar to --dryrun in that it prevents testrunner.tcl -from building any binaries or running any tests. The difference is that --explain -prints on standard output a human-readable summary of all the builds and tests that -would have been run. - -``` - # Show what builds and tests would have been run - tclsh $TESTDIR/testrunner.tcl --explain mdevtest -``` - # 5. Controlling CPU Core Utilization When running either binary or source code tests, testrunner.tcl reports the number of jobs it intends to use to stdout. e.g. @@ -352,5 +337,8 @@ testrunner.log and testrunner.db files: ``` $ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS ``` + + + Index: ext/expert/expert1.test ================================================================== --- ext/expert/expert1.test +++ ext/expert/expert1.test @@ -6,11 +6,10 @@ # 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. # #*********************************************************************** -# TESTRUNNER: shell # # The focus of this file is testing the CLI shell tool. Specifically, # the ".recommend" command. # # Index: ext/expert/sqlite3expert.c ================================================================== --- ext/expert/sqlite3expert.c +++ ext/expert/sqlite3expert.c @@ -1946,11 +1946,11 @@ /* Copy the entire schema of database [db] into [dbm]. */ if( rc==SQLITE_OK ){ sqlite3_stmt *pSql = 0; rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg, "SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'" - " AND sql NOT LIKE 'CREATE VIRTUAL %%' ORDER BY rowid" + " AND sql NOT LIKE 'CREATE VIRTUAL %%'" ); while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ const char *zSql = (const char*)sqlite3_column_text(pSql, 0); if( zSql ) rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg); } Index: ext/fts3/fts3_snippet.c ================================================================== --- ext/fts3/fts3_snippet.c +++ ext/fts3/fts3_snippet.c @@ -444,11 +444,11 @@ }else{ iScore += 1000; } mCover |= mPhrase; - for(j=0; jnToken && jnSnippet; j++){ + for(j=0; jnToken; j++){ mHighlight |= (mPos>>j); } if( 0==(*pCsr & 0x0FE) ) break; fts3GetDeltaPosition(&pCsr, &iCsr); Index: ext/fts5/fts5.h ================================================================== --- ext/fts5/fts5.h +++ ext/fts5/fts5.h @@ -53,12 +53,12 @@ /* ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): -** Return a copy of the pUserData pointer passed to the xCreateFunction() -** API when the extension function was registered. +** Return a copy of the context pointer the extension function was +** registered with. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken ** to the total number of tokens in the FTS5 table. Or, if iCol is ** non-negative but less than the number of columns in the table, return Index: ext/fts5/fts5_main.c ================================================================== --- ext/fts5/fts5_main.c +++ ext/fts5/fts5_main.c @@ -1698,11 +1698,10 @@ "'delete' may not be used with a contentless_delete=1 table" ); rc = SQLITE_ERROR; }else{ rc = fts5SpecialDelete(pTab, apVal); - bUpdateOrDelete = 1; } }else{ rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); } }else{ Index: ext/fts5/test/fts5secure8.test ================================================================== --- ext/fts5/test/fts5secure8.test +++ ext/fts5/test/fts5secure8.test @@ -40,26 +40,10 @@ do_execsql_test 1.2 { PRAGMA integrity_check; } {ok} -do_execsql_test 2.0 { - CREATE VIRTUAL TABLE xyz USING fts5 ( - name, - content='' - ); - - INSERT INTO xyz(xyz, rank) VALUES('secure-delete', 1); - INSERT INTO xyz (rowid, name) VALUES(1, 'A'); - INSERT INTO xyz (rowid, name) VALUES(2, 'A'); - INSERT INTO xyz(xyz, rowid, name) VALUES('delete', 2, 'A'); -} - -do_execsql_test 2.1 { - pragma quick_check; -} {ok} - finish_test Index: ext/icu/icu.c ================================================================== --- ext/icu/icu.c +++ ext/icu/icu.c @@ -469,11 +469,11 @@ const char *zLocale; /* Locale identifier - (eg. "jp_JP") */ const char *zName; /* SQL Collation sequence name (eg. "japanese") */ UCollator *pUCollator; /* ICU library collation object */ int rc; /* Return code from sqlite3_create_collation_x() */ - assert(nArg==2 || nArg==3); + assert(nArg==2); (void)nArg; /* Unused parameter */ zLocale = (const char *)sqlite3_value_text(apArg[0]); zName = (const char *)sqlite3_value_text(apArg[1]); if( !zLocale || !zName ){ @@ -484,43 +484,11 @@ if( !U_SUCCESS(status) ){ icuFunctionError(p, "ucol_open", status); return; } assert(p); - if(nArg==3){ - const char *zOption = (const char*)sqlite3_value_text(apArg[2]); - static const struct { - const char *zName; - UColAttributeValue val; - } aStrength[] = { - { "PRIMARY", UCOL_PRIMARY }, - { "SECONDARY", UCOL_SECONDARY }, - { "TERTIARY", UCOL_TERTIARY }, - { "DEFAULT", UCOL_DEFAULT_STRENGTH }, - { "QUARTERNARY", UCOL_QUATERNARY }, - { "IDENTICAL", UCOL_IDENTICAL }, - }; - unsigned int i; - for(i=0; i=sizeof(aStrength)/sizeof(aStrength[0]) ){ - sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p)); - sqlite3_str_appendf(pStr, - "unknown collation strength \"%s\" - should be one of:", - zOption); - for(i=0; iintck); + 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) ); { @@ -90,11 +90,11 @@ break; } case 3: assert( 0==strcmp("error", aCmd[iIdx].zName) ); { const char *zErr = 0; - rc = sqlite3_intck_error(p->intck, 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); @@ -104,11 +104,11 @@ Tcl_SetObjResult(interp, pRes); break; } case 4: assert( 0==strcmp("unlock", aCmd[iIdx].zName) ); { - rc = sqlite3_intck_unlock(p->intck); + 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) ); { Index: ext/misc/cksumvfs.c ================================================================== --- ext/misc/cksumvfs.c +++ ext/misc/cksumvfs.c @@ -444,13 +444,13 @@ ** (1) the size indicates that we are dealing with a complete ** database page ** (2) checksum verification is enabled ** (3) we are not in the middle of checkpoint */ - if( iAmt>=512 && (iAmt & (iAmt-1))==0 /* (1) */ - && p->verifyCksm /* (2) */ - && !p->inCkpt /* (3) */ + if( iAmt>=512 /* (1) */ + && p->verifyCksm /* (2) */ + && !p->inCkpt /* (3) */ ){ u8 cksum[8]; cksmCompute((u8*)zBuf, iAmt-8, cksum); if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){ sqlite3_log(SQLITE_IOERR_DATA, Index: ext/misc/fileio.c ================================================================== --- ext/misc/fileio.c +++ ext/misc/fileio.c @@ -370,13 +370,11 @@ ){ if( zFile==0 ) return 1; #if !defined(_WIN32) && !defined(WIN32) if( S_ISLNK(mode) ){ const char *zTo = (const char*)sqlite3_value_text(pData); - if( zTo==0 ) return 1; - unlink(zFile); - if( symlink(zTo, zFile)<0 ) return 1; + if( zTo==0 || symlink(zTo, zFile)<0 ) return 1; }else #endif { if( S_ISDIR(mode) ){ if( mkdir(zFile, mode) ){ @@ -458,23 +456,17 @@ times[1].tv_sec = mtime; if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){ return 1; } #else - /* Legacy unix. - ** - ** Do not use utimes() on a symbolic link - it sees through the link and - ** modifies the timestamps on the target. Or fails if the target does - ** not exist. */ - if( 0==S_ISLNK(mode) ){ - struct timeval times[2]; - times[0].tv_usec = times[1].tv_usec = 0; - times[0].tv_sec = time(0); - times[1].tv_sec = mtime; - if( utimes(zFile, times) ){ - return 1; - } + /* Legacy unix */ + struct timeval times[2]; + times[0].tv_usec = times[1].tv_usec = 0; + times[0].tv_sec = time(0); + times[1].tv_sec = mtime; + if( utimes(zFile, times) ){ + return 1; } #endif } return 0; Index: ext/misc/series.c ================================================================== --- ext/misc/series.c +++ ext/misc/series.c @@ -374,17 +374,17 @@ ** ** The query plan selected by seriesBestIndex is passed in the idxNum ** parameter. (idxStr is not used in this implementation.) idxNum ** is a bitmask showing which constraints are available: ** -** 0x01: start=VALUE -** 0x02: stop=VALUE -** 0x04: step=VALUE -** 0x08: descending order -** 0x10: ascending order -** 0x20: LIMIT VALUE -** 0x40: OFFSET VALUE +** 1: start=VALUE +** 2: stop=VALUE +** 4: step=VALUE +** +** Also, if bit 8 is set, that means that the series should be output +** in descending order rather than in ascending order. If bit 16 is +** set, then output must appear in ascending order. ** ** This routine should initialize the cursor and position it so that it ** is pointing at the first row, or pointing off the end of the table ** (so that seriesEof() will return true) if the table is empty. */ @@ -394,48 +394,30 @@ int argc, sqlite3_value **argv ){ series_cursor *pCur = (series_cursor *)pVtabCursor; int i = 0; (void)idxStrUnused; - if( idxNum & 0x01 ){ + if( idxNum & 1 ){ pCur->ss.iBase = sqlite3_value_int64(argv[i++]); }else{ pCur->ss.iBase = 0; } - if( idxNum & 0x02 ){ + if( idxNum & 2 ){ pCur->ss.iTerm = sqlite3_value_int64(argv[i++]); }else{ pCur->ss.iTerm = 0xffffffff; } - if( idxNum & 0x04 ){ + if( idxNum & 4 ){ pCur->ss.iStep = sqlite3_value_int64(argv[i++]); if( pCur->ss.iStep==0 ){ pCur->ss.iStep = 1; }else if( pCur->ss.iStep<0 ){ - if( (idxNum & 0x10)==0 ) idxNum |= 0x08; + if( (idxNum & 16)==0 ) idxNum |= 8; } }else{ pCur->ss.iStep = 1; } - if( idxNum & 0x20 ){ - sqlite3_int64 iLimit = sqlite3_value_int64(argv[i++]); - sqlite3_int64 iTerm; - if( idxNum & 0x40 ){ - sqlite3_int64 iOffset = sqlite3_value_int64(argv[i++]); - if( iOffset>0 ){ - pCur->ss.iBase += pCur->ss.iStep*iOffset; - } - } - if( iLimit>=0 ){ - iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep; - if( pCur->ss.iStep<0 ){ - if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm; - }else{ - if( iTermss.iTerm ) pCur->ss.iTerm = iTerm; - } - } - } for(i=0; iss.iBase = 1; @@ -442,11 +424,11 @@ pCur->ss.iTerm = 0; pCur->ss.iStep = 1; break; } } - if( idxNum & 0x08 ){ + if( idxNum & 8 ){ pCur->ss.isReversing = pCur->ss.iStep > 0; }else{ pCur->ss.isReversing = pCur->ss.iStep < 0; } setupSequence( &pCur->ss ); @@ -462,85 +444,54 @@ ** In this implementation idxNum is used to represent the ** query plan. idxStr is unused. ** ** The query plan is represented by bits in idxNum: ** -** 0x01 start = $value -- constraint exists -** 0x02 stop = $value -- constraint exists -** 0x04 step = $value -- constraint exists -** 0x08 output is in descending order -** 0x10 output is in ascending order -** 0x20 LIMIT $value -- constraint exists -** 0x40 OFFSET $value -- constraint exists +** (1) start = $value -- constraint exists +** (2) stop = $value -- constraint exists +** (4) step = $value -- constraint exists +** (8) output in descending order */ static int seriesBestIndex( sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo ){ int i, j; /* Loop over constraints */ int idxNum = 0; /* The query plan bitmask */ -#ifndef ZERO_ARGUMENT_GENERATE_SERIES int bStartSeen = 0; /* EQ constraint seen on the START column */ -#endif int unusableMask = 0; /* Mask of unusable constraints */ int nArg = 0; /* Number of arguments that seriesFilter() expects */ - int aIdx[5]; /* Constraints on start, stop, step, LIMIT, OFFSET */ + int aIdx[3]; /* Constraints on start, stop, and step */ const struct sqlite3_index_constraint *pConstraint; /* This implementation assumes that the start, stop, and step columns ** are the last three columns in the virtual table. */ assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); - aIdx[0] = aIdx[1] = aIdx[2] = aIdx[3] = aIdx[4] = -1; + aIdx[0] = aIdx[1] = aIdx[2] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; inConstraint; i++, pConstraint++){ int iCol; /* 0 for start, 1 for stop, 2 for step */ int iMask; /* bitmask for those column */ - int op = pConstraint->op; - if( op>=SQLITE_INDEX_CONSTRAINT_LIMIT - && op<=SQLITE_INDEX_CONSTRAINT_OFFSET - ){ - if( pConstraint->usable==0 ){ - /* do nothing */ - }else if( op==SQLITE_INDEX_CONSTRAINT_LIMIT ){ - aIdx[3] = i; - idxNum |= 0x20; - }else{ - assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET ); - aIdx[4] = i; - idxNum |= 0x40; - } - continue; - } if( pConstraint->iColumniColumn - SERIES_COLUMN_START; assert( iCol>=0 && iCol<=2 ); iMask = 1 << iCol; -#ifndef ZERO_ARGUMENT_GENERATE_SERIES - if( iCol==0 && op==SQLITE_INDEX_CONSTRAINT_EQ ){ - bStartSeen = 1; - } -#endif + if( iCol==0 ) bStartSeen = 1; if( pConstraint->usable==0 ){ unusableMask |= iMask; continue; - }else if( op==SQLITE_INDEX_CONSTRAINT_EQ ){ + }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ idxNum |= iMask; aIdx[iCol] = i; } } - if( aIdx[3]==0 ){ - /* Ignore OFFSET if LIMIT is omitted */ - idxNum &= ~0x60; - aIdx[4] = 0; - } - for(i=0; i<5; i++){ + for(i=0; i<3; i++){ if( (j = aIdx[i])>=0 ){ pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[j].omit = - !SQLITE_SERIES_CONSTRAINT_VERIFY || i>=3; + pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY; } } /* The current generate_column() implementation requires at least one ** argument (the START value). Legacy versions assumed START=0 if the ** first argument was omitted. Compile with -DZERO_ARGUMENT_GENERATE_SERIES @@ -557,26 +508,23 @@ /* The start, stop, and step columns are inputs. Therefore if there ** are unusable constraints on any of start, stop, or step then ** this plan is unusable */ return SQLITE_CONSTRAINT; } - if( (idxNum & 0x03)==0x03 ){ + if( (idxNum & 3)==3 ){ /* Both start= and stop= boundaries are available. This is the ** the preferred case */ pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0)); pIdxInfo->estimatedRows = 1000; if( pIdxInfo->nOrderBy>=1 && pIdxInfo->aOrderBy[0].iColumn==0 ){ if( pIdxInfo->aOrderBy[0].desc ){ - idxNum |= 0x08; + idxNum |= 8; }else{ - idxNum |= 0x10; + idxNum |= 16; } pIdxInfo->orderByConsumed = 1; } - }else if( (idxNum & 0x21)==0x21 ){ - /* We have start= and LIMIT */ - pIdxInfo->estimatedRows = 2500; }else{ /* If either boundary is missing, we have to generate a huge span ** of numbers. Make this case very expensive so that the query ** planner will work hard to avoid it. */ pIdxInfo->estimatedRows = 2147483647; Index: ext/misc/sqlar.c ================================================================== --- ext/misc/sqlar.c +++ ext/misc/sqlar.c @@ -79,27 +79,26 @@ sqlite3_context *context, int argc, sqlite3_value **argv ){ uLong nData; - sqlite3_int64 sz; + uLongf sz; assert( argc==2 ); sz = sqlite3_value_int(argv[1]); if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){ sqlite3_result_value(context, argv[0]); }else{ - uLongf szf = sz; const Bytef *pData= sqlite3_value_blob(argv[0]); Bytef *pOut = sqlite3_malloc(sz); if( pOut==0 ){ sqlite3_result_error_nomem(context); - }else if( Z_OK!=uncompress(pOut, &szf, pData, nData) ){ + }else if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){ sqlite3_result_error(context, "error in uncompress()", -1); }else{ - sqlite3_result_blob(context, pOut, szf, SQLITE_TRANSIENT); + sqlite3_result_blob(context, pOut, sz, SQLITE_TRANSIENT); } sqlite3_free(pOut); } } Index: ext/misc/vtablog.c ================================================================== --- ext/misc/vtablog.c +++ ext/misc/vtablog.c @@ -36,13 +36,12 @@ ** serve as the underlying representation of a vtablog virtual table */ typedef struct vtablog_vtab vtablog_vtab; struct vtablog_vtab { sqlite3_vtab base; /* Base class - must be first */ - char *zDb; /* Schema name. argv[1] of xConnect/xCreate */ - char *zName; /* Table name. argv[2] of xConnect/xCreate */ int nRow; /* Number of rows in the table */ + int iInst; /* Instance number for this vtablog table */ int nCursor; /* Number of cursors created */ }; /* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans @@ -166,18 +165,19 @@ int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr, int isCreate ){ + static int nInst = 0; vtablog_vtab *pNew; int i; int rc; + int iInst = ++nInst; char *zSchema = 0; char *zNRow = 0; - printf("%s.%s.%s():\n", argv[1], argv[2], - isCreate ? "xCreate" : "xConnect"); + printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst); printf(" argc=%d\n", argc); for(i=0; inRow = 10; if( zNRow ) pNew->nRow = atoi(zNRow); - printf(" nrow = %d\n", pNew->nRow); - pNew->zDb = sqlite3_mprintf("%s", argv[1]); - pNew->zName = sqlite3_mprintf("%s", argv[2]); - } - -vtablog_end_connect: - sqlite3_free(zSchema); - sqlite3_free(zNRow); + pNew->iInst = iInst; + } return rc; } static int vtablogCreate( sqlite3 *db, void *pAux, @@ -242,25 +235,21 @@ /* ** This method is the destructor for vtablog_cursor objects. */ static int vtablogDisconnect(sqlite3_vtab *pVtab){ vtablog_vtab *pTab = (vtablog_vtab*)pVtab; - printf("%s.%s.xDisconnect()\n", pTab->zDb, pTab->zName); - sqlite3_free(pTab->zDb); - sqlite3_free(pTab->zName); + printf("vtablogDisconnect(%d)\n", pTab->iInst); sqlite3_free(pVtab); return SQLITE_OK; } /* ** This method is the destructor for vtablog_cursor objects. */ static int vtablogDestroy(sqlite3_vtab *pVtab){ vtablog_vtab *pTab = (vtablog_vtab*)pVtab; - printf("%s.%s.xDestroy()\n", pTab->zDb, pTab->zName); - sqlite3_free(pTab->zDb); - sqlite3_free(pTab->zName); + printf("vtablogDestroy(%d)\n", pTab->iInst); sqlite3_free(pVtab); return SQLITE_OK; } /* @@ -267,12 +256,11 @@ ** Constructor for a new vtablog_cursor object. */ static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ vtablog_vtab *pTab = (vtablog_vtab*)p; vtablog_cursor *pCur; - printf("%s.%s.xOpen(cursor=%d)\n", pTab->zDb, pTab->zName, - ++pTab->nCursor); + printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor); pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->iCursor = pTab->nCursor; *ppCursor = &pCur->base; @@ -283,11 +271,11 @@ ** Destructor for a vtablog_cursor. */ static int vtablogClose(sqlite3_vtab_cursor *cur){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; - printf("%s.%s.xClose(cursor=%d)\n", pTab->zDb, pTab->zName, pCur->iCursor); + printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor); sqlite3_free(cur); return SQLITE_OK; } @@ -295,13 +283,12 @@ ** Advance a vtablog_cursor to its next row of output. */ static int vtablogNext(sqlite3_vtab_cursor *cur){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; - printf("%s.%s.xNext(cursor=%d) rowid %d -> %d\n", - pTab->zDb, pTab->zName, pCur->iCursor, - (int)pCur->iRowid, (int)pCur->iRowid+1); + printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n", + pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1); pCur->iRowid++; return SQLITE_OK; } /* @@ -321,12 +308,12 @@ sqlite3_snprintf(sizeof(zVal),zVal,"%c%d", "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid); }else{ sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid); } - printf("%s.%s.xColumn(cursor=%d, i=%d): [%s]\n", - pTab->zDb, pTab->zName, pCur->iCursor, i, zVal); + printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n", + pTab->iInst, pCur->iCursor, i, zVal); sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT); return SQLITE_OK; } /* @@ -334,12 +321,12 @@ ** rowid is the same as the output value. */ static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; - printf("%s.%s.xRowid(cursor=%d): %d\n", - pTab->zDb, pTab->zName, pCur->iCursor, (int)pCur->iRowid); + printf("vtablogRowid(tab=%d, cursor=%d): %d\n", + pTab->iInst, pCur->iCursor, (int)pCur->iRowid); *pRowid = pCur->iRowid; return SQLITE_OK; } /* @@ -348,12 +335,12 @@ */ static int vtablogEof(sqlite3_vtab_cursor *cur){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; int rc = pCur->iRowid >= pTab->nRow; - printf("%s.%s.xEof(cursor=%d): %d\n", - pTab->zDb, pTab->zName, pCur->iCursor, rc); + printf("vtablogEof(tab=%d, cursor=%d): %d\n", + pTab->iInst, pCur->iCursor, rc); return rc; } /* ** Output an sqlite3_value object's value as an SQL literal. @@ -428,11 +415,11 @@ int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ vtablog_cursor *pCur = (vtablog_cursor *)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; - printf("%s.%s.xFilter(cursor=%d):\n", pTab->zDb, pTab->zName, pCur->iCursor); + printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor); pCur->iRowid = 0; return SQLITE_OK; } /* @@ -441,41 +428,16 @@ ** a query plan for each invocation and compute an estimated cost for that ** plan. */ static int vtablogBestIndex( sqlite3_vtab *tab, - sqlite3_index_info *p + sqlite3_index_info *pIdxInfo ){ vtablog_vtab *pTab = (vtablog_vtab*)tab; - int i; - printf("%s.%s.xBestIndex():\n", pTab->zDb, pTab->zName); - printf(" colUsed: 0x%016llx\n", p->colUsed); - printf(" nConstraint: %d\n", p->nConstraint); - for(i=0; inConstraint; i++){ - printf( - " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n", - i, - p->aConstraint[i].iColumn, - p->aConstraint[i].iTermOffset, - p->aConstraint[i].op, - p->aConstraint[i].usable, - sqlite3_vtab_collation(p,i)); - } - printf(" nOrderBy: %d\n", p->nOrderBy); - for(i=0; inOrderBy; i++){ - printf(" orderby[%d]: col=%d desc=%d\n", - i, - p->aOrderBy[i].iColumn, - p->aOrderBy[i].desc); - } - p->estimatedCost = (double)500; - p->estimatedRows = 500; - printf(" idxNum=%d\n", p->idxNum); - printf(" idxStr=NULL\n"); - printf(" orderByConsumed=%d\n", p->orderByConsumed); - printf(" estimatedCost=%g\n", p->estimatedCost); - printf(" estimatedRows=%lld\n", p->estimatedRows); + printf("vtablogBestIndex(tab=%d):\n", pTab->iInst); + pIdxInfo->estimatedCost = (double)500; + pIdxInfo->estimatedRows = 500; return SQLITE_OK; } /* ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from @@ -490,102 +452,26 @@ sqlite3_value **argv, sqlite_int64 *pRowid ){ vtablog_vtab *pTab = (vtablog_vtab*)tab; int i; - printf("%s.%s.xUpdate():\n", pTab->zDb, pTab->zName); + printf("vtablogUpdate(tab=%d):\n", pTab->iInst); printf(" argc=%d\n", argc); for(i=0; izDb, pTab->zName); - return SQLITE_OK; -} -static int vtablogSync(sqlite3_vtab *tab){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xSync()\n", pTab->zDb, pTab->zName); - return SQLITE_OK; -} -static int vtablogCommit(sqlite3_vtab *tab){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xCommit()\n", pTab->zDb, pTab->zName); - return SQLITE_OK; -} -static int vtablogRollback(sqlite3_vtab *tab){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xRollback()\n", pTab->zDb, pTab->zName); - return SQLITE_OK; -} -static int vtablogSavepoint(sqlite3_vtab *tab, int N){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xSavepoint(%d)\n", pTab->zDb, pTab->zName, N); - return SQLITE_OK; -} -static int vtablogRelease(sqlite3_vtab *tab, int N){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xRelease(%d)\n", pTab->zDb, pTab->zName, N); - return SQLITE_OK; -} -static int vtablogRollbackTo(sqlite3_vtab *tab, int N){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xRollbackTo(%d)\n", pTab->zDb, pTab->zName, N); - return SQLITE_OK; -} - -static int vtablogFindMethod( - sqlite3_vtab *tab, - int nArg, - const char *zName, - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), - void **ppArg -){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xFindMethod(nArg=%d, zName=%s)\n", - pTab->zDb, pTab->zName, nArg, zName); - return SQLITE_OK; -} -static int vtablogRename(sqlite3_vtab *tab, const char *zNew){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xRename('%s')\n", pTab->zDb, pTab->zName, zNew); - sqlite3_free(pTab->zName); - pTab->zName = sqlite3_mprintf("%s", zNew); - return SQLITE_OK; -} - -/* Any table name that contains the text "shadow" is seen as a -** shadow table. Nothing else is. -*/ -static int vtablogShadowName(const char *zName){ - printf("vtablog.xShadowName('%s')\n", zName); - return sqlite3_strglob("*shadow*", zName)==0; -} - -static int vtablogIntegrity( - sqlite3_vtab *tab, - const char *zSchema, - const char *zTabName, - int mFlags, - char **pzErr -){ - vtablog_vtab *pTab = (vtablog_vtab*)tab; - printf("%s.%s.xIntegrity(mFlags=0x%x)\n", pTab->zDb, pTab->zName, mFlags); - return 0; -} - /* ** This following structure defines all the methods for the ** vtablog virtual table. */ static sqlite3_module vtablogModule = { - 4, /* iVersion */ + 0, /* iVersion */ vtablogCreate, /* xCreate */ vtablogConnect, /* xConnect */ vtablogBestIndex, /* xBestIndex */ vtablogDisconnect, /* xDisconnect */ vtablogDestroy, /* xDestroy */ @@ -595,21 +481,21 @@ vtablogNext, /* xNext - advance a cursor */ vtablogEof, /* xEof - check for end of scan */ vtablogColumn, /* xColumn - read data */ vtablogRowid, /* xRowid - read data */ vtablogUpdate, /* xUpdate */ - vtablogBegin, /* xBegin */ - vtablogSync, /* xSync */ - vtablogCommit, /* xCommit */ - vtablogRollback, /* xRollback */ - vtablogFindMethod, /* xFindMethod */ - vtablogRename, /* xRename */ - vtablogSavepoint, /* xSavepoint */ - vtablogRelease, /* xRelease */ - vtablogRollbackTo, /* xRollbackTo */ - vtablogShadowName, /* xShadowName */ - vtablogIntegrity /* xIntegrity */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; #ifdef _WIN32 __declspec(dllexport) #endif Index: ext/rbu/sqlite3rbu.c ================================================================== --- ext/rbu/sqlite3rbu.c +++ ext/rbu/sqlite3rbu.c @@ -197,11 +197,10 @@ #if !defined(SQLITE_AMALGAMATION) typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; typedef sqlite3_int64 i64; -typedef sqlite3_uint64 u64; #endif /* ** These values must match the values defined in wal.c for the equivalent ** locks. These are not magic numbers as they are part of the SQLite file @@ -884,11 +883,10 @@ pIter->bCleanup = 0; rc = sqlite3_step(pIter->pTblIter); if( rc!=SQLITE_ROW ){ rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg); pIter->zTbl = 0; - pIter->zDataTbl = 0; }else{ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0); pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1); rc = (pIter->zDataTbl && pIter->zTbl) ? SQLITE_OK : SQLITE_NOMEM; } @@ -2979,11 +2977,11 @@ if( p->rc==SQLITE_OK ){ sqlite3_file *pDb = p->pTargetFd->pReal; u32 volatile *ptr; p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr); if( p->rc==SQLITE_OK ){ - iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]); + iRet = ((i64)ptr[10] << 32) + ptr[11]; } } return iRet; } Index: ext/recover/dbdata.c ================================================================== --- ext/recover/dbdata.c +++ ext/recover/dbdata.c @@ -86,19 +86,10 @@ #define DBDATA_PADDING_BYTES 100 typedef struct DbdataTable DbdataTable; typedef struct DbdataCursor DbdataCursor; -typedef struct DbdataBuffer DbdataBuffer; - -/* -** Buffer type. -*/ -struct DbdataBuffer { - u8 *aBuf; - sqlite3_int64 nBuf; -}; /* Cursor object */ struct DbdataCursor { sqlite3_vtab_cursor base; /* Base class. Must be first */ sqlite3_stmt *pStmt; /* For fetching database pages */ @@ -111,11 +102,11 @@ int bOnePage; /* True to stop after one page */ int szDb; sqlite3_int64 iRowid; /* Only for the sqlite_dbdata table */ - DbdataBuffer rec; + u8 *pRec; /* Buffer containing current record */ sqlite3_int64 nRec; /* Size of pRec[] in bytes */ sqlite3_int64 nHdr; /* Size of header in bytes */ int iField; /* Current field number */ u8 *pHdrPtr; u8 *pPtr; @@ -156,35 +147,10 @@ " pgno INTEGER," \ " child INTEGER," \ " schema TEXT HIDDEN" \ ")" -/* -** Ensure the buffer passed as the first argument is at least nMin bytes -** in size. If an error occurs while attempting to resize the buffer, -** SQLITE_NOMEM is returned. Otherwise, SQLITE_OK. -*/ -static int dbdataBufferSize(DbdataBuffer *pBuf, sqlite3_int64 nMin){ - if( nMin>pBuf->nBuf ){ - sqlite3_int64 nNew = nMin+16384; - u8 *aNew = (u8*)sqlite3_realloc64(pBuf->aBuf, nNew); - - if( aNew==0 ) return SQLITE_NOMEM; - pBuf->aBuf = aNew; - pBuf->nBuf = nNew; - } - return SQLITE_OK; -} - -/* -** Release the allocation managed by buffer pBuf. -*/ -static void dbdataBufferFree(DbdataBuffer *pBuf){ - sqlite3_free(pBuf->aBuf); - memset(pBuf, 0, sizeof(*pBuf)); -} - /* ** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual ** table. */ static int dbdataConnect( @@ -321,13 +287,13 @@ pCsr->iPgno = 1; pCsr->iCell = 0; pCsr->iField = 0; pCsr->bOnePage = 0; sqlite3_free(pCsr->aPage); - dbdataBufferFree(&pCsr->rec); + sqlite3_free(pCsr->pRec); + pCsr->pRec = 0; pCsr->aPage = 0; - pCsr->nRec = 0; } /* ** Close an sqlite_dbdata or sqlite_dbptr cursor. */ @@ -465,91 +431,70 @@ u32 enc, int eType, u8 *pData, sqlite3_int64 nData ){ - if( eType>=0 ){ - if( dbdataValueBytes(eType)<=nData ){ - switch( eType ){ - case 0: - case 10: - case 11: - sqlite3_result_null(pCtx); - break; - - case 8: - sqlite3_result_int(pCtx, 0); - break; - case 9: - sqlite3_result_int(pCtx, 1); - break; - - case 1: case 2: case 3: case 4: case 5: case 6: case 7: { - sqlite3_uint64 v = (signed char)pData[0]; - pData++; - switch( eType ){ - case 7: - case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; - case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; - case 4: v = (v<<8) + pData[0]; pData++; - case 3: v = (v<<8) + pData[0]; pData++; - case 2: v = (v<<8) + pData[0]; pData++; - } - - if( eType==7 ){ - double r; - memcpy(&r, &v, sizeof(r)); - sqlite3_result_double(pCtx, r); - }else{ - sqlite3_result_int64(pCtx, (sqlite3_int64)v); - } - break; - } - - default: { - int n = ((eType-12) / 2); - if( eType % 2 ){ - switch( enc ){ - #ifndef SQLITE_OMIT_UTF16 - case SQLITE_UTF16BE: - sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT); - break; - case SQLITE_UTF16LE: - sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT); - break; - #endif - default: - sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT); - break; - } - }else{ - sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT); - } - } - } - }else{ - if( eType==7 ){ - sqlite3_result_double(pCtx, 0.0); - }else if( eType<7 ){ - sqlite3_result_int(pCtx, 0); - }else if( eType%2 ){ - sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); - }else{ - sqlite3_result_blob(pCtx, "", 0, SQLITE_STATIC); - } - } - } -} - -/* This macro is a copy of the MX_CELL() macro in the SQLite core. Given -** a page-size, it returns the maximum number of cells that may be present -** on the page. */ -#define DBDATA_MX_CELL(pgsz) ((pgsz-8)/6) - -/* Maximum number of fields that may appear in a single record. This is -** the "hard-limit", according to comments in sqliteLimit.h. */ -#define DBDATA_MX_FIELD 32676 + if( eType>=0 && dbdataValueBytes(eType)<=nData ){ + switch( eType ){ + case 0: + case 10: + case 11: + sqlite3_result_null(pCtx); + break; + + case 8: + sqlite3_result_int(pCtx, 0); + break; + case 9: + sqlite3_result_int(pCtx, 1); + break; + + case 1: case 2: case 3: case 4: case 5: case 6: case 7: { + sqlite3_uint64 v = (signed char)pData[0]; + pData++; + switch( eType ){ + case 7: + case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; + case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; + case 4: v = (v<<8) + pData[0]; pData++; + case 3: v = (v<<8) + pData[0]; pData++; + case 2: v = (v<<8) + pData[0]; pData++; + } + + if( eType==7 ){ + double r; + memcpy(&r, &v, sizeof(r)); + sqlite3_result_double(pCtx, r); + }else{ + sqlite3_result_int64(pCtx, (sqlite3_int64)v); + } + break; + } + + default: { + int n = ((eType-12) / 2); + if( eType % 2 ){ + switch( enc ){ +#ifndef SQLITE_OMIT_UTF16 + case SQLITE_UTF16BE: + sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT); + break; + case SQLITE_UTF16LE: + sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT); + break; +#endif + default: + sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT); + break; + } + }else{ + sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT); + } + } + } + } +} /* ** Move an sqlite_dbdata or sqlite_dbptr cursor to the next entry. */ static int dbdataNext(sqlite3_vtab_cursor *pCursor){ @@ -575,13 +520,10 @@ } assert( iOff+3+2<=pCsr->nPage ); pCsr->iCell = pTab->bPtr ? -2 : 0; pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]); - if( pCsr->nCell>DBDATA_MX_CELL(pCsr->nPage) ){ - pCsr->nCell = DBDATA_MX_CELL(pCsr->nPage); - } } if( pTab->bPtr ){ if( pCsr->aPage[iOff]!=0x02 && pCsr->aPage[iOff]!=0x05 ){ pCsr->iCell = pCsr->nCell; @@ -595,12 +537,11 @@ }else{ return SQLITE_OK; } }else{ /* If there is no record loaded, load it now. */ - assert( pCsr->rec.aBuf!=0 || pCsr->nRec==0 ); - if( pCsr->nRec==0 ){ + if( pCsr->pRec==0 ){ int bHasRowid = 0; int nPointer = 0; sqlite3_int64 nPayload = 0; sqlite3_int64 nHdr = 0; int iHdr; @@ -623,28 +564,27 @@ } if( pCsr->iCell>=pCsr->nCell ){ bNextPage = 1; }else{ - int iCellPtr = iOff + 8 + nPointer + pCsr->iCell*2; - if( iCellPtr>pCsr->nPage ){ + iOff += 8 + nPointer + pCsr->iCell*2; + if( iOff>pCsr->nPage ){ bNextPage = 1; }else{ - iOff = get_uint16(&pCsr->aPage[iCellPtr]); + iOff = get_uint16(&pCsr->aPage[iOff]); } /* For an interior node cell, skip past the child-page number */ iOff += nPointer; /* Load the "byte of payload including overflow" field */ - if( bNextPage || iOff>pCsr->nPage || iOff<=iCellPtr ){ + if( bNextPage || iOff>pCsr->nPage ){ bNextPage = 1; }else{ iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload); if( nPayload>0x7fffff00 ) nPayload &= 0x3fff; - if( nPayload==0 ) nPayload = 1; } /* If this is a leaf intkey cell, load the rowid */ if( bHasRowid && !bNextPage && iOffnPage ){ iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey); @@ -675,16 +615,17 @@ }else{ /* Allocate space for payload. And a bit more to catch small buffer ** overruns caused by attempting to read a varint or similar from ** near the end of a corrupt record. */ - rc = dbdataBufferSize(&pCsr->rec, nPayload+DBDATA_PADDING_BYTES); - if( rc!=SQLITE_OK ) return rc; - assert( nPayload!=0 ); + pCsr->pRec = (u8*)sqlite3_malloc64(nPayload+DBDATA_PADDING_BYTES); + if( pCsr->pRec==0 ) return SQLITE_NOMEM; + memset(pCsr->pRec, 0, nPayload+DBDATA_PADDING_BYTES); + pCsr->nRec = nPayload; /* Load the nLocal bytes of payload */ - memcpy(pCsr->rec.aBuf, &pCsr->aPage[iOff], nLocal); + memcpy(pCsr->pRec, &pCsr->aPage[iOff], nLocal); iOff += nLocal; /* Load content from overflow pages */ if( nPayload>nLocal ){ sqlite3_int64 nRem = nPayload - nLocal; @@ -698,64 +639,61 @@ if( rc!=SQLITE_OK ) return rc; if( aOvfl==0 ) break; nCopy = U-4; if( nCopy>nRem ) nCopy = nRem; - memcpy(&pCsr->rec.aBuf[nPayload-nRem], &aOvfl[4], nCopy); + memcpy(&pCsr->pRec[nPayload-nRem], &aOvfl[4], nCopy); nRem -= nCopy; pgnoOvfl = get_uint32(aOvfl); sqlite3_free(aOvfl); } - nPayload -= nRem; } - memset(&pCsr->rec.aBuf[nPayload], 0, DBDATA_PADDING_BYTES); - pCsr->nRec = nPayload; - iHdr = dbdataGetVarintU32(pCsr->rec.aBuf, &nHdr); + iHdr = dbdataGetVarintU32(pCsr->pRec, &nHdr); if( nHdr>nPayload ) nHdr = 0; pCsr->nHdr = nHdr; - pCsr->pHdrPtr = &pCsr->rec.aBuf[iHdr]; - pCsr->pPtr = &pCsr->rec.aBuf[pCsr->nHdr]; + pCsr->pHdrPtr = &pCsr->pRec[iHdr]; + pCsr->pPtr = &pCsr->pRec[pCsr->nHdr]; pCsr->iField = (bHasRowid ? -1 : 0); } } }else{ pCsr->iField++; if( pCsr->iField>0 ){ sqlite3_int64 iType; - if( pCsr->pHdrPtr>=&pCsr->rec.aBuf[pCsr->nRec] - || pCsr->iField>=DBDATA_MX_FIELD - ){ + if( pCsr->pHdrPtr>&pCsr->pRec[pCsr->nRec] ){ bNextPage = 1; }else{ int szField = 0; pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType); szField = dbdataValueBytes(iType); - if( (pCsr->nRec - (pCsr->pPtr - pCsr->rec.aBuf))pPtr = &pCsr->rec.aBuf[pCsr->nRec]; + if( (pCsr->nRec - (pCsr->pPtr - pCsr->pRec))pPtr = &pCsr->pRec[pCsr->nRec]; }else{ pCsr->pPtr += szField; } } } } if( bNextPage ){ sqlite3_free(pCsr->aPage); + sqlite3_free(pCsr->pRec); pCsr->aPage = 0; - pCsr->nRec = 0; + pCsr->pRec = 0; if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; }else{ - if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->rec.aBuf[pCsr->nHdr] ){ + if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->pRec[pCsr->nHdr] ){ return SQLITE_OK; } /* Advance to the next cell. The next iteration of the loop will load ** the record and so on. */ - pCsr->nRec = 0; + sqlite3_free(pCsr->pRec); + pCsr->pRec = 0; pCsr->iCell++; } } } @@ -941,16 +879,16 @@ sqlite3_result_int(ctx, pCsr->iField); break; case DBDATA_COLUMN_VALUE: { if( pCsr->iField<0 ){ sqlite3_result_int64(ctx, pCsr->iIntkey); - }else if( &pCsr->rec.aBuf[pCsr->nRec] >= pCsr->pPtr ){ + }else if( &pCsr->pRec[pCsr->nRec] >= pCsr->pPtr ){ sqlite3_int64 iType; dbdataGetVarintU32(pCsr->pHdrPtr, &iType); dbdataValue( ctx, pCsr->enc, iType, pCsr->pPtr, - &pCsr->rec.aBuf[pCsr->nRec] - pCsr->pPtr + &pCsr->pRec[pCsr->nRec] - pCsr->pPtr ); } break; } } Index: ext/recover/recover1.test ================================================================== --- ext/recover/recover1.test +++ ext/recover/recover1.test @@ -340,38 +340,27 @@ INSERT INTO t1 VALUES('abc', 'def'); PRAGMA writable_schema = 1; DELETE FROM sqlite_schema WHERE name='t1'; } - proc my_sql_hook2 {sql} { + proc my_sql_hook {sql} { if {[string match "INSERT INTO lostandfound*" $sql]} { lappend ::script $sql } return 0 } do_test 18.$enc.2 { set ::script [list] - set R [sqlite3_recover_init_sql db main my_sql_hook2] + set R [sqlite3_recover_init_sql db main my_sql_hook] $R config lostandfound lostandfound $R run $R finish set ::script } {{INSERT INTO lostandfound VALUES(2, 2, 2, 1, 'abc', 'def')}} } -#------------------------------------------------------------------------- -reset_db -do_execsql_test 19.0 { - CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); - INSERT INTO t1 VALUES(1, 'one'); - INSERT INTO t1 VALUES(2, 'two'); - - ALTER TABLE t1 ADD COLUMN c NOT NULL DEFAULT 13; - INSERT INTO t1 VALUES(3, 'three', 'hello world'); -} - -do_recover_test 19.1 + finish_test Index: ext/recover/recovercorrupt2.test ================================================================== --- ext/recover/recovercorrupt2.test +++ ext/recover/recovercorrupt2.test @@ -522,35 +522,7 @@ set R [sqlite3_recover_init db main test.db2] catch { $R run } list [catch { $R finish } msg] $msg } {1 {file is not a database}} -reset_db -breakpoint -do_test 8.0 { - sqlite3 db {} - db deserialize [decode_hexdb { -| size 8192 pagesize 4096 filename db.sqlite -| page 1 offset 0 -| 0: ac ae b3 76 74 65 20 66 6f 72 6d 61 74 20 33 00 ...vte format 3. -| 16: 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 .....@ ........ -| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................ -| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................ -| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ................ -| 96: 00 2e 76 8a 0d ff ff ff 1e 0f cb 00 0f cb 00 00 ..v............. -| 4032: 00 00 00 00 00 00 00 00 00 00 00 33 01 06 17 19 ...........3.... -| 4048: 19 01 43 74 61 62 6c 65 54 61 62 6c 65 30 54 61 ..CtableTable0Ta -| 4064: 62 6c 65 30 02 43 52 45 41 54 45 20 54 41 42 4c ble0.CREATE TABL -| 4080: 45 20 54 61 62 6c 65 30 20 28 43 6f 6c 30 20 29 E Table0 (Col0 ) -| page 2 offset 4096 -| 0: 0d 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 ................ -| end db.sqlite -}]} {} - -do_test 8.1 { - set R [sqlite3_recover_init db main test.db2] - catch { $R run } - list [catch { $R finish } msg] $msg -} {0 {}} - finish_test DELETED ext/recover/recovercorrupt3.test Index: ext/recover/recovercorrupt3.test ================================================================== --- ext/recover/recovercorrupt3.test +++ /dev/null @@ -1,549 +0,0 @@ -# 2024 May 1 -# -# 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]] recover_common.tcl] -set testprefix recovercorrupt3 - -#| 0: d5 d5 9b d5 d5 d5 d5 d5 d5 d5 d5 d5 d5 d5 d5 d5 ................ -#| 16: 04 00 00 00 1d 00 00 00 00 00 00 00 5f 5f 5f 5f ............____ -#| 32: 5f 5f 5f 5f 5f 5f 5f 5f 71 5f 5f 5f 02 02 02 02 ________q___.... -#| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -#| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -#| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -#| 96: 02 02 02 02 - -#------------------------------------------------------------------------- -reset_db -do_test 1.0 { - sqlite3 db {} - db deserialize [decode_hexdb { -| size 3821 pagesize 1024 filename clusterfuzz-testcase-sql_recovery_fuzzer-5803962339885056 -| 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 01 00 00 00 02 .....@ ........ -| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................ -| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................ -| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ................ -| 96: 00 2e 7a 70 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 352: 02 02 02 02 a0 02 02 02 02 02 02 02 02 02 02 02 ................ -| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 29 29 ..............)) -| 464: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 480: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 496: 29 29 29 dd dd dd dd dd dd dd dd dd dd dd dd dd )))............. -| 512: dd dd dd dd dd dd dd dd dd 6e 69 d2 e9 e9 e9 d2 .........ni..... -| 528: d2 d2 d2 d2 dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 544: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 560: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 576: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 592: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 608: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 624: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 640: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 656: dd dd dd dd dd dd dd da dd dd dd dd dd dd dd dd ................ -| 672: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 688: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 704: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 720: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 736: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 752: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 768: dd dd dd dd dd dd dd dd dd dd dd dd dd 29 29 29 .............))) -| 784: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 800: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 816: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 832: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 848: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 864: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 880: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 896: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 912: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 928: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 944: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 960: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 976: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 992: 29 29 29 29 29 29 29 29 29 29 dd dd dd dd dd dd ))))))))))...... -| 1008: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| page 2 offset 1024 -| 0: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 16: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 32: dd dd 6e 69 d2 e9 e9 e9 d2 d2 d2 d2 d2 dd dd dd ..ni............ -| 48: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 64: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 80: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 96: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 112: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 128: dd dd dd dd dd dd 29 29 29 29 29 29 29 29 29 29 ......)))))))))) -| 144: 29 29 29 29 29 29 29 29 29 ad a5 29 29 29 29 00 )))))))))..)))). -| 160: 75 9c 11 00 5b e5 64 28 7c ca 09 69 28 2d 69 00 u...[.d(|..i(-i. -| 176: 85 88 6c 81 48 83 a0 93 c0 c0 82 8b 81 84 85 f9 ..l.H........... -| 192: 88 7a 00 7f 00 96 40 7b 12 4b 84 75 a0 00 99 a0 .z....@..K.u.... -| 208: df a0 7e 81 c6 90 8f 7f 84 85 cc 84 82 90 88 60 ..~............` -| 224: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 240: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 256: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 272: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 288: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 02 02 ................ -| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 736: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 752: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 768: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 784: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 800: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 816: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 832: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 848: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 864: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 880: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 896: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 912: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 928: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 944: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 960: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 976: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 992: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 1008: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| page 3 offset 2048 -| 0: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 16: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 32: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 96: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 736: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 752: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 768: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 784: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 800: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 816: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 832: 02 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 848: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 864: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 880: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 896: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 912: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 928: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 944: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 960: 80 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 976: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 992: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 1008: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| page 4 offset 3072 -| 0: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 16: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 32: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 96: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 736: 5f 5f 5f 5f 5f 5f 5f 00 d5 fe fe fe 08 00 00 00 _______......... -| end clusterfuzz-testcase-sql_recovery_fuzzer-5803962339885056 -}]} {} - -sqlite3_dbdata_init db -do_execsql_test 1.1 { - PRAGMA writable_schema = 1; -} - -do_test 1.2 { - set R [sqlite3_recover_init db main test.db2] - $R run - $R finish -} {} - -#------------------------------------------------------------------------- -reset_db -do_test 2.0 { - sqlite3 db {} - db deserialize [decode_hexdb { -| size 3821 pagesize 1024 filename clusterfuzz-testcase-sql_recovery_fuzzer-5803962339885056 -| 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: 04 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 .....@ ........ -| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................ -| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................ -| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ................ -| 96: 00 2e 7a 70 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 352: 02 02 02 02 a0 02 02 02 02 02 02 02 02 02 02 02 ................ -| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 29 29 ..............)) -| 464: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 480: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 496: 29 29 29 dd dd dd dd dd dd dd dd dd dd dd dd dd )))............. -| 512: dd dd dd dd dd dd dd dd dd 6e 69 d2 e9 e9 e9 d2 .........ni..... -| 528: d2 d2 d2 d2 dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 544: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 560: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 576: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 592: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 608: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 624: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 640: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 656: dd dd dd dd dd dd dd da dd dd dd dd dd dd dd dd ................ -| 672: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 688: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 704: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 720: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 736: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 752: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 768: dd dd dd dd dd dd dd dd dd dd dd dd dd 29 29 29 .............))) -| 784: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 800: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 816: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 832: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 848: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 864: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 880: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 896: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 912: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 928: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 944: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 960: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 976: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 )))))))))))))))) -| 992: 29 29 29 29 29 29 29 29 29 29 dd dd dd dd dd dd ))))))))))...... -| 1008: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| page 2 offset 1024 -| 0: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 16: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 32: dd dd 6e 69 d2 e9 e9 e9 d2 d2 d2 d2 d2 dd dd dd ..ni............ -| 48: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 64: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 80: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 96: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 112: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................ -| 128: dd dd dd dd dd dd 29 29 29 29 29 29 29 29 29 29 ......)))))))))) -| 144: 29 29 29 29 29 29 29 29 29 ad a5 29 29 29 29 00 )))))))))..)))). -| 160: 75 9c 11 00 5b e5 64 28 7c ca 09 69 28 2d 69 00 u...[.d(|..i(-i. -| 176: 85 88 6c 81 48 83 a0 93 c0 c0 82 8b 81 84 85 f9 ..l.H........... -| 192: 88 7a 00 7f 00 96 40 7b 12 4b 84 75 a0 00 99 a0 .z....@..K.u.... -| 208: df a0 7e 81 c6 90 8f 7f 84 85 cc 84 82 90 88 60 ..~............` -| 224: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 240: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 256: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 272: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 288: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 02 02 ................ -| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 736: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 752: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 768: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 784: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 800: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 816: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 832: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 848: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 864: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 880: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 896: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 912: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 928: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 944: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 960: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 976: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 992: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 1008: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| page 3 offset 2048 -| 0: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 16: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 32: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 96: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 736: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 752: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 768: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 784: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 800: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 816: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 832: 02 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 848: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 864: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 880: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 896: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 912: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 928: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 944: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................ -| 960: 80 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 976: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 992: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 1008: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| page 4 offset 3072 -| 0: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 16: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 32: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 96: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................ -| 736: 5f 5f 5f 5f 5f 5f 5f 00 d5 fe fe fe 08 00 00 00 _______......... -| end clusterfuzz-testcase-sql_recovery_fuzzer-5803962339885056 -}]} {} - -sqlite3_dbdata_init db -do_execsql_test 2.1 { - PRAGMA writable_schema = 1; -} - -do_test 2.2 { - set R [sqlite3_recover_init db main test.db2] - $R run - $R finish -} {} - -finish_test - DELETED ext/recover/recovercorrupt4.test Index: ext/recover/recovercorrupt4.test ================================================================== --- ext/recover/recovercorrupt4.test +++ /dev/null @@ -1,64 +0,0 @@ -# 2024 May 15 -# -# 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]] recover_common.tcl] -set testprefix recovercorrupt4 - -database_may_be_corrupt - -if {[permutation]!="inmemory_journal"} { - # This test cannot be run with the inmemory_journal permutation, as it - # must open a truncated, corrupt, database file. With the inmemory_journal - # permutation, this fails (SQLITE_CORRUPT error) when the [sqlite3] wrapper - # executes "PRAGMA journal_mode = memory". - do_execsql_test 1.0 { - CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL, filler BLOB NOT NULL DEFAULT 13); - -- CREATE UNIQUE INDEX rows_index ON rows(indexed); - INSERT INTO rows(indexed, unindexed, filler) VALUES(1, 1, x'31'); - INSERT INTO rows(indexed, unindexed, filler) VALUES(2, 2, x'32'); - INSERT INTO rows(indexed, unindexed, filler) VALUES(4, 4, x'34'); - INSERT INTO rows(indexed, unindexed, filler) VALUES(8, 8, randomblob(2048)); - } - - db close - - do_test 1.1 { - set sz [expr [file size test.db] - 1024] - set fd [open test.db] - fconfigure $fd -encoding binary -translation binary - - set data [read $fd $sz] - set fd2 [open test.db2 w] - fconfigure $fd2 -encoding binary -translation binary - puts -nonewline $fd2 $data - close $fd2 - set {} {} - } {} - - do_test 1.2 { - forcedelete test.db3 - sqlite3 db test.db2 - set R [sqlite3_recover_init db main test.db3] - $R run - $R finish - } {} - - do_test 1.3 { - sqlite3 db test.db3 - execsql { - SELECT indexed, unindexed FROM rows - } - } {1 1 2 2 4 4 8 8} -} - -finish_test - Index: ext/recover/sqlite3recover.c ================================================================== --- ext/recover/sqlite3recover.c +++ ext/recover/sqlite3recover.c @@ -1187,11 +1187,11 @@ if( rc==SQLITE_OK ){ recoverSqlCallback(p, zSql); if( bTable && !bVirtual ){ if( SQLITE_ROW==sqlite3_step(pTblname) ){ const char *zTbl = (const char*)sqlite3_column_text(pTblname, 0); - if( zTbl ) recoverAddTable(p, zTbl, iRoot); + recoverAddTable(p, zTbl, iRoot); } recoverReset(p, pTblname); } }else if( rc!=SQLITE_ERROR ){ recoverDbError(p, p->dbOut); Index: ext/rtree/rtree.c ================================================================== --- ext/rtree/rtree.c +++ ext/rtree/rtree.c @@ -1839,12 +1839,10 @@ } pCons->pInfo = pInfo; return SQLITE_OK; } -int sqlite3IntFloatCompare(i64,double); - /* ** Rtree virtual table module xFilter method. */ static int rtreeFilter( sqlite3_vtab_cursor *pVtabCursor, @@ -1870,12 +1868,11 @@ RtreeSearchPoint *p; /* Search point for the leaf */ i64 iRowid = sqlite3_value_int64(argv[0]); i64 iNode = 0; int eType = sqlite3_value_numeric_type(argv[0]); if( eType==SQLITE_INTEGER - || (eType==SQLITE_FLOAT - && 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0]))) + || (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid) ){ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode); }else{ rc = SQLITE_OK; pLeaf = 0; Index: ext/rtree/rtree1.test ================================================================== --- ext/rtree/rtree1.test +++ ext/rtree/rtree1.test @@ -795,24 +795,6 @@ db eval {CREATE TABLE t1(a,b,c);} catch {db eval {CREATE TABLE t2 AS SELECT rtreecheck('t1') AS y;}} db eval {PRAGMA integrity_check;} } {ok} -reset_db -do_execsql_test 24.0 { - CREATE VIRTUAL TABLE rt1 USING rtree_i32(rid, c1, c2); - INSERT INTO rt1(rid, c1, c2) VALUES (9223372036854775807, 10, 18); -} - -do_execsql_test 24.1 { - SELECT (rid = (CAST (9223372036854775807 AS REAL))) - FROM rt1 WHERE - (rid = (CAST (9223372036854775807 AS REAL))); -} - -do_execsql_test 24.2 { - DELETE FROM rt1; - INSERT INTO rt1(rid, c1, c2) VALUES(1,2,3); - SELECT * FROM rt1 WHERE rid=1.005; -} {} - finish_test DELETED ext/session/sessionchange.test Index: ext/session/sessionchange.test ================================================================== --- ext/session/sessionchange.test +++ /dev/null @@ -1,101 +0,0 @@ -# 2011 March 07 -# -# 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. -# - -if {![info exists testdir]} { - set testdir [file join [file dirname [info script]] .. .. test] -} -source [file join [file dirname [info script]] session_common.tcl] -source $testdir/tester.tcl -ifcapable !session {finish_test; return} - -set testprefix sessionchange - -do_execsql_test 1.0 { - CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); -} - -do_test 1.1 { - set C [changeset_from_sql { - INSERT INTO t1 VALUES(1, 2, 3); - INSERT INTO t1 VALUES(10, 20, 30); - }] - - set res [list] - set iter [sqlite3changeset_start $C] - while {[$iter next]=="SQLITE_ROW"} { - lappend res [$iter data] - } - $iter finalize - - set res -} [list \ - {INSERT t1 0 X.. {} {i 10 i 20 i 30}} \ - {INSERT t1 0 X.. {} {i 1 i 2 i 3}} \ -] - - -do_test 1.2 { - sqlite3changegroup grp - set iter [sqlite3changeset_start $C] - while {[$iter next]=="SQLITE_ROW"} { - grp add_change $iter - } - $iter finalize - - set res [list] - grp output - sqlite3session_foreach c [grp output] { lappend res $c } - grp delete - set res -} [list \ - {INSERT t1 0 X.. {} {i 10 i 20 i 30}} \ - {INSERT t1 0 X.. {} {i 1 i 2 i 3}} \ -] - -do_test 1.3.1 { - set iter [sqlite3changeset_start $C] - sqlite3changegroup grp - list [catch { grp add_change $iter } msg] $msg -} {1 SQLITE_ERROR} -do_test 1.3.2 { - while {[$iter next]=="SQLITE_ROW"} { } - list [catch { grp add_change $iter } msg] $msg -} {1 SQLITE_ERROR} -grp delete -$iter finalize - -do_test 1.4 { - set res [list] - set iter [sqlite3changeset_start -invert $C] - while {[$iter next]=="SQLITE_ROW"} { - lappend res [$iter data] - } - $iter finalize - set res -} [list \ - {DELETE t1 0 X.. {i 10 i 20 i 30} {}} \ - {DELETE t1 0 X.. {i 1 i 2 i 3} {}} \ -] - -do_test 1.5 { - sqlite3changegroup grp - set iter [sqlite3changeset_start -invert $C] - $iter next - list [catch { grp add_change $iter } msg] $msg -} {1 SQLITE_ERROR} -$iter finalize -grp delete - - - -finish_test DELETED ext/session/sessionconflict.test Index: ext/session/sessionconflict.test ================================================================== --- ext/session/sessionconflict.test +++ /dev/null @@ -1,76 +0,0 @@ -# 2011 March 07 -# -# 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. -# - -if {![info exists testdir]} { - set testdir [file join [file dirname [info script]] .. .. test] -} -source [file join [file dirname [info script]] session_common.tcl] -source $testdir/tester.tcl -ifcapable !session {finish_test; return} - -set testprefix sessionconflict - -db close -sqlite3_shutdown -test_sqlite3_log log -proc log {code msg} { puts "LOG $code $msg" } -sqlite3 db test.db - -forcedelete test.db2 -sqlite3 db2 test.db2 - -do_test 1.0 { - do_common_sql { - CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE); - INSERT INTO t1 VALUES(1, 1, 1); - INSERT INTO t1 VALUES(2, 2, 2); - INSERT INTO t1 VALUES(3, 3, 3); - } -} {} - -do_execsql_test -db db2 1.1 { - INSERT INTO t1 VALUES(6, 6, 6); -} - -proc xConflict {args} { - return "ABORT" -} - - -do_test 1.2 { - set chng [changeset_from_sql { - UPDATE t1 SET b=10, c=10 WHERE a=1; - UPDATE t1 SET b=444 WHERE a=2; - INSERT INTO t1 VALUES(4, 4, 4); - INSERT INTO t1 VALUES(5, 5, 5); - INSERT INTO t1 VALUES(6, 6, 6); - }] - - execsql BEGIN db2 - set res [list [catch { sqlite3changeset_apply db2 $chng xConflict } msg] $msg] - execsql ROLLBACK db2 - set res -} {1 SQLITE_ABORT} - -do_execsql_test -db db2 1.3 { - SELECT * FROM t1; -} { - 1 1 1 - 2 2 2 - 3 3 3 - 6 6 6 -} - - - -finish_test Index: ext/session/sqlite3session.c ================================================================== --- ext/session/sqlite3session.c +++ ext/session/sqlite3session.c @@ -3683,18 +3683,18 @@ ** sufficient either for the 'T' or 'P' byte and the varint that follows ** it, or for the two single byte values otherwise. */ p->rc = sessionInputBuffer(&p->in, 2); if( p->rc!=SQLITE_OK ) return p->rc; - sessionDiscardData(&p->in); - p->in.iCurrent = p->in.iNext; - /* If the iterator is already at the end of the changeset, return DONE. */ if( p->in.iNext>=p->in.nData ){ return SQLITE_DONE; } + sessionDiscardData(&p->in); + p->in.iCurrent = p->in.iNext; + op = p->in.aData[p->in.iNext++]; while( op=='T' || op=='P' ){ if( pbNew ) *pbNew = 1; p->bPatchset = (op=='P'); if( sessionChangesetReadTblhdr(p) ) return p->rc; @@ -5425,11 +5425,10 @@ */ struct sqlite3_changegroup { int rc; /* Error code */ int bPatch; /* True to accumulate patchsets */ SessionTable *pList; /* List of tables in current patch */ - SessionBuffer rec; sqlite3 *db; /* Configured by changegroup_schema() */ char *zDb; /* Configured by changegroup_schema() */ }; @@ -5724,132 +5723,112 @@ return rc; } /* -** Locate or create a SessionTable object that may be used to add the -** change currently pointed to by iterator pIter to changegroup pGrp. -** If successful, set output variable (*ppTab) to point to the table -** object and return SQLITE_OK. Otherwise, if some error occurs, return -** an SQLite error code and leave (*ppTab) set to NULL. -*/ -static int sessionChangesetFindTable( - sqlite3_changegroup *pGrp, - const char *zTab, - sqlite3_changeset_iter *pIter, - SessionTable **ppTab -){ - int rc = SQLITE_OK; - SessionTable *pTab = 0; - int nTab = (int)strlen(zTab); - u8 *abPK = 0; - int nCol = 0; - - *ppTab = 0; - sqlite3changeset_pk(pIter, &abPK, &nCol); - - /* Search the list for an existing table */ - for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ - if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break; - } - - /* If one was not found above, create a new table now */ - if( !pTab ){ - SessionTable **ppNew; - - pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1); - if( !pTab ){ - return SQLITE_NOMEM; - } - memset(pTab, 0, sizeof(SessionTable)); - pTab->nCol = nCol; - pTab->abPK = (u8*)&pTab[1]; - memcpy(pTab->abPK, abPK, nCol); - pTab->zName = (char*)&pTab->abPK[nCol]; - memcpy(pTab->zName, zTab, nTab+1); - - if( pGrp->db ){ - pTab->nCol = 0; - rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); - if( rc ){ - assert( pTab->azCol==0 ); - sqlite3_free(pTab); - return rc; - } - } - - /* The new object must be linked on to the end of the list, not - ** simply added to the start of it. This is to ensure that the - ** tables within the output of sqlite3changegroup_output() are in - ** the right order. */ - for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext); - *ppNew = pTab; - } - - /* Check that the table is compatible. */ - if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ - rc = SQLITE_SCHEMA; - } - - *ppTab = pTab; - return rc; -} - -/* -** Add the change currently indicated by iterator pIter to the hash table -** belonging to changegroup pGrp. -*/ -static int sessionOneChangeToHash( - sqlite3_changegroup *pGrp, - sqlite3_changeset_iter *pIter, - int bRebase -){ - int rc = SQLITE_OK; - int nCol = 0; - int op = 0; - int iHash = 0; - int bIndirect = 0; - SessionChange *pChange = 0; - SessionChange *pExist = 0; - SessionChange **pp = 0; - SessionTable *pTab = 0; - u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; - int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; - - /* Ensure that only changesets, or only patchsets, but not a mixture - ** of both, are being combined. It is an error to try to combine a - ** changeset and a patchset. */ - if( pGrp->pList==0 ){ - pGrp->bPatch = pIter->bPatchset; - }else if( pIter->bPatchset!=pGrp->bPatch ){ - rc = SQLITE_ERROR; - } - - if( rc==SQLITE_OK ){ - const char *zTab = 0; - sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); - rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); - } - - if( rc==SQLITE_OK && nColnCol ){ - SessionBuffer *pBuf = &pGrp->rec; - rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf); - aRec = pBuf->aBuf; - nRec = pBuf->nBuf; - assert( pGrp->db ); - } - - if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){ - rc = SQLITE_NOMEM; - } - - if( rc==SQLITE_OK ){ - /* Search for existing entry. If found, remove it from the hash table. - ** Code below may link it back in. */ +** Add all changes in the changeset traversed by the iterator passed as +** the first argument to the changegroup hash tables. +*/ +static int sessionChangesetToHash( + sqlite3_changeset_iter *pIter, /* Iterator to read from */ + sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ + int bRebase /* True if hash table is for rebasing */ +){ + u8 *aRec; + int nRec; + int rc = SQLITE_OK; + SessionTable *pTab = 0; + SessionBuffer rec = {0, 0, 0}; + + while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ + const char *zNew; + int nCol; + int op; + int iHash; + int bIndirect; + SessionChange *pChange; + SessionChange *pExist = 0; + SessionChange **pp; + + /* Ensure that only changesets, or only patchsets, but not a mixture + ** of both, are being combined. It is an error to try to combine a + ** changeset and a patchset. */ + if( pGrp->pList==0 ){ + pGrp->bPatch = pIter->bPatchset; + }else if( pIter->bPatchset!=pGrp->bPatch ){ + rc = SQLITE_ERROR; + break; + } + + sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect); + if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){ + /* Search the list for a matching table */ + int nNew = (int)strlen(zNew); + u8 *abPK; + + sqlite3changeset_pk(pIter, &abPK, 0); + for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ + if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break; + } + if( !pTab ){ + SessionTable **ppTab; + + pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1); + if( !pTab ){ + rc = SQLITE_NOMEM; + break; + } + memset(pTab, 0, sizeof(SessionTable)); + pTab->nCol = nCol; + pTab->abPK = (u8*)&pTab[1]; + memcpy(pTab->abPK, abPK, nCol); + pTab->zName = (char*)&pTab->abPK[nCol]; + memcpy(pTab->zName, zNew, nNew+1); + + if( pGrp->db ){ + pTab->nCol = 0; + rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); + if( rc ){ + assert( pTab->azCol==0 ); + sqlite3_free(pTab); + break; + } + } + + /* The new object must be linked on to the end of the list, not + ** simply added to the start of it. This is to ensure that the + ** tables within the output of sqlite3changegroup_output() are in + ** the right order. */ + for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext); + *ppTab = pTab; + } + + if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ + rc = SQLITE_SCHEMA; + break; + } + } + + if( nColnCol ){ + assert( pGrp->db ); + rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec); + if( rc ) break; + aRec = rec.aBuf; + nRec = rec.nBuf; + } + + if( sessionGrowHash(0, pIter->bPatchset, pTab) ){ + rc = SQLITE_NOMEM; + break; + } iHash = sessionChangeHash( pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange ); + + /* Search for existing entry. If found, remove it from the hash table. + ** Code below may link it back in. + */ for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ int bPkOnly1 = 0; int bPkOnly2 = 0; if( pIter->bPatchset ){ bPkOnly1 = (*pp)->op==SQLITE_DELETE; @@ -5860,45 +5839,23 @@ *pp = (*pp)->pNext; pTab->nEntry--; break; } } - } - if( rc==SQLITE_OK ){ rc = sessionChangeMerge(pTab, bRebase, pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange ); - } - if( rc==SQLITE_OK && pChange ){ - pChange->pNext = pTab->apChange[iHash]; - pTab->apChange[iHash] = pChange; - pTab->nEntry++; - } - - if( rc==SQLITE_OK ) rc = pIter->rc; - return rc; -} - -/* -** Add all changes in the changeset traversed by the iterator passed as -** the first argument to the changegroup hash tables. -*/ -static int sessionChangesetToHash( - sqlite3_changeset_iter *pIter, /* Iterator to read from */ - sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ - int bRebase /* True if hash table is for rebasing */ -){ - u8 *aRec; - int nRec; - int rc = SQLITE_OK; - - while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ - rc = sessionOneChangeToHash(pGrp, pIter, bRebase); - if( rc!=SQLITE_OK ) break; - } - + if( rc ) break; + if( pChange ){ + pChange->pNext = pTab->apChange[iHash]; + pTab->apChange[iHash] = pChange; + pTab->nEntry++; + } + } + + sqlite3_free(rec.aBuf); if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } /* @@ -6022,27 +5979,10 @@ } sqlite3changeset_finalize(pIter); return rc; } -/* -** Add a single change to a changeset-group. -*/ -int sqlite3changegroup_add_change( - sqlite3_changegroup *pGrp, - sqlite3_changeset_iter *pIter -){ - if( pIter->in.iCurrent==pIter->in.iNext - || pIter->rc!=SQLITE_OK - || pIter->bInvert - ){ - /* Iterator does not point to any valid entry or is an INVERT iterator. */ - return SQLITE_ERROR; - } - return sessionOneChangeToHash(pGrp, pIter, 0); -} - /* ** Obtain a buffer containing a changeset representing the concatenation ** of all changesets added to the group so far. */ int sqlite3changegroup_output( @@ -6088,11 +6028,10 @@ */ void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); - sqlite3_free(pGrp->rec.aBuf); sqlite3_free(pGrp); } } /* @@ -6490,11 +6429,10 @@ ** Destroy a rebaser object */ void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ sessionDeleteTable(0, p->grp.pList); - sqlite3_free(p->grp.rec.aBuf); sqlite3_free(p); } } /* Index: ext/session/sqlite3session.h ================================================================== --- ext/session/sqlite3session.h +++ ext/session/sqlite3session.h @@ -1054,34 +1054,10 @@ ** ** In all cases, if an error occurs the state of the final contents of the ** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); - -/* -** CAPI3REF: Add A Single Change To A Changegroup -** METHOD: sqlite3_changegroup -** -** This function adds the single change currently indicated by the iterator -** passed as the second argument to the changegroup object. The rules for -** adding the change are just as described for [sqlite3changegroup_add()]. -** -** If the change is successfully added to the changegroup, SQLITE_OK is -** returned. Otherwise, an SQLite error code is returned. -** -** The iterator must point to a valid entry when this function is called. -** If it does not, SQLITE_ERROR is returned and no change is added to the -** changegroup. Additionally, the iterator must not have been opened with -** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also -** returned. -*/ -int sqlite3changegroup_add_change( - sqlite3_changegroup*, - sqlite3_changeset_iter* -); - - /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup ** Index: ext/session/test_session.c ================================================================== --- ext/session/test_session.c +++ ext/session/test_session.c @@ -1036,68 +1036,10 @@ } sqlite3_free(sOut.p); return rc; } -static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){ - Tcl_Obj *pVar = 0; - int nCol; /* Number of columns in table */ - int nCol2; /* Number of columns in table */ - int op; /* SQLITE_INSERT, UPDATE or DELETE */ - const char *zTab; /* Name of table change applies to */ - Tcl_Obj *pOld; /* Vector of old.* values */ - Tcl_Obj *pNew; /* Vector of new.* values */ - int bIndirect; - - char *zPK; - unsigned char *abPK; - int i; - - sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); - pVar = Tcl_NewObj(); - - Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj( - op==SQLITE_INSERT ? "INSERT" : - op==SQLITE_UPDATE ? "UPDATE" : - "DELETE", -1 - )); - - Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1)); - Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect)); - - zPK = ckalloc(nCol+1); - memset(zPK, 0, nCol+1); - sqlite3changeset_pk(pIter, &abPK, &nCol2); - assert( nCol==nCol2 ); - for(i=0; ipGrp, pIter->pIter); - if( rc!=SQLITE_OK ){ - rc = test_session_error(interp, rc, 0); - } - break; - }; - default: { /* delete */ assert( iSub==3 ); Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); break; } @@ -1607,127 +1583,17 @@ ); Tcl_SetObjResult(interp, objv[1]); return TCL_OK; } -extern const char *sqlite3ErrName(int); - -/* -** Destructor for Tcl iterator command object. -*/ -static void test_iter_del(void *clientData){ - TestChangeIter *p = (TestChangeIter*)clientData; - sqlite3changeset_finalize(p->pIter); - ckfree(p); -} - -static int SQLITE_TCLAPI test_iter_cmd( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - static const char *aSub[] = { - "next", /* 0 */ - "data", /* 1 */ - "finalize", /* 2 */ - 0 - }; - int iSub = 0; - - TestChangeIter *p = (TestChangeIter*)clientData; - int rc = SQLITE_OK; - - if( objc<2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "CMD"); - return TCL_ERROR; - } - - if( Tcl_GetIndexFromObj(interp, objv[1], aSub, "sub-command", 0, &iSub) ){ - return TCL_ERROR; - } - switch( iSub ){ - case 0: - rc = sqlite3changeset_next(p->pIter); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); - break; - case 1: - Tcl_SetObjResult(interp, testIterData(p->pIter)); - break; - case 2: - rc = sqlite3changeset_finalize(p->pIter); - p->pIter = 0; - Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); - break; - default: - assert( 0 ); - break; - } - - return TCL_OK; -} - -/* -** Tclcmd: sqlite3changeset_start ?-invert? CHANGESET -*/ -static int SQLITE_TCLAPI test_sqlite3changeset_start( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - int isInvert = 0; - void *pChangeset = 0; /* Buffer containing changeset */ - int nChangeset = 0; /* Size of buffer aChangeset in bytes */ - TestChangeIter *pNew = 0; - sqlite3_changeset_iter *pIter = 0; - int flags = 0; - int rc = SQLITE_OK; - - static int iCmd = 1; - char zCmd[64]; - - if( objc==3 ){ - int n = 0; - const char *z = Tcl_GetStringFromObj(objv[1], &n); - isInvert = (n>=2 && sqlite3_strnicmp(z, "-invert", n)==0); - } - - if( objc!=2 && (objc!=3 || !isInvert) ){ - Tcl_WrongNumArgs(interp, 1, objv, "?-invert? CHANGESET"); - return TCL_ERROR; - } - - flags = isInvert ? SQLITE_CHANGESETSTART_INVERT : 0; - pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[objc-1], &nChangeset); - rc = sqlite3changeset_start_v2(&pIter, nChangeset, pChangeset, flags); - if( rc!=SQLITE_OK ){ - char *zErr = sqlite3_mprintf( - "error in sqlite3changeset_start_v2() - %d", rc - ); - Tcl_AppendResult(interp, zErr, (char*)0); - return TCL_ERROR; - } - - pNew = (TestChangeIter*)ckalloc(sizeof(TestChangeIter)); - pNew->pIter = pIter; - - sprintf(zCmd, "csiter%d", iCmd++); - Tcl_CreateObjCommand(interp, zCmd, test_iter_cmd, (void*)pNew, test_iter_del); - Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1)); - return TCL_OK; -} - int TestSession_Init(Tcl_Interp *interp){ struct Cmd { const char *zCmd; Tcl_ObjCmdProc *xProc; } aCmd[] = { { "sqlite3session", test_sqlite3session }, { "sqlite3changegroup", test_sqlite3changegroup }, - { "sqlite3changeset_start", test_sqlite3changeset_start }, { "sqlite3session_foreach", test_sqlite3session_foreach }, { "sqlite3changeset_invert", test_sqlite3changeset_invert }, { "sqlite3changeset_concat", test_sqlite3changeset_concat }, { "sqlite3changeset_apply", test_sqlite3changeset_apply }, { "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 }, Index: ext/wasm/GNUmakefile ================================================================== --- ext/wasm/GNUmakefile +++ ext/wasm/GNUmakefile @@ -287,16 +287,10 @@ ######################################################################### # bin.version-info = binary to output various sqlite3 version info for # embedding in the JS files and in building the distribution zip file. # It must NOT be in $(dir.tmp) because we need it to survive the # cleanup process for the dist build to work properly. -# -# Slight caveat: this uses the version info from the in-tree -# sqlite3.c/h, which may diff from a user-provided $(sqlite3.c). The -# end result is that the generated JS files may have static version -# info from $(bin.version-info) which differ from their runtime-emited -# version info (e.g. from sqlite3_libversion()). bin.version-info := $(dir.top)/version-info .NOTPARALLEL: $(bin.version-info) $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile $(MAKE) -C $(dir.top) version-info @@ -311,13 +305,12 @@ $(CC) -o $@ $< DISTCLEAN_FILES += $(bin.stripccomments) ######################################################################## -# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via: -# -# ./c-pp -f $(1) -o $(2) $(3) +# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via ./c-pp -f +# $(1) ... # # Historical notes: # # - We first attempted to use gcc and/or clang to preprocess JS files # in the same way we would normally do C files, but C-specific quirks @@ -339,30 +332,23 @@ # builds but is maintained as a standalone project: # https://fossil.wanderinghorse.net/r/c-pp # # Note that the SQLITE_... build flags used here have NO EFFECT on the # JS/WASM build. They are solely for use with $(bin.c-pp) itself. -# -# -D... flags which should be included in all invocations should be -# appended to $(C-PP.FILTER.global). bin.c-pp := ./c-pp $(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE) $(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \ -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_UTF16 \ -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \ -DSQLITE_TEMP_STORE=3 -C-PP.FILTER.global ?= -ifeq (1,$(SQLITE_C_IS_SEE)) - C-PP.FILTER.global += -Denable-see -endif define C-PP.FILTER # Create $2 from $1 using $(bin.c-pp) # $1 = Input file: c-pp -f $(1).js # $2 = Output file: c-pp -o $(2).js # $3 = optional c-pp -D... flags $(2): $(1) $$(MAKEFILE) $$(bin.c-pp) - $$(bin.c-pp) -f $(1) -o $$@ $(3) $(C-PP.FILTER.global) + $$(bin.c-pp) -f $(1) -o $$@ $(3) CLEAN_FILES += $(2) endef # /end C-PP.FILTER ######################################################################## @@ -464,17 +450,16 @@ # SOAP.js is an external API file which is part of our distribution # but not part of the sqlite3-api.js amalgamation. It's a component of # the first OPFS VFS and necessarily an external file. SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js)) -# -# $(sqlite3-api.ext.jses) = API-related files which are standalone files, -# not part of the amalgamation. -# -sqlite3-api.ext.jses := $(SOAP.js.bld) +sqlite3-api.ext.jses += $(SOAP.js.bld) $(SOAP.js.bld): $(SOAP.js) cp $< $@ + +all quick: $(sqlite3-api.ext.jses) +q: quick ######################################################################## # $(sqlite3-api*.*js) contain the core library code but not the # Emscripten-related glue which deals with loading sqlite3.wasm. In # theory they can be used by arbitrary build environments and WASM @@ -838,17 +823,17 @@ # $2 = build mode name: one of $(JS_BUILD_MODES) # $3 = 1 for ESM build mode, else 0 # $4 = resulting sqlite-api JS/MJS file # $5 = resulting JS/MJS file # $6 = -D... flags for $(bin.c-pp) -# $7 = optional extra flags for emcc +# $7 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out) # # Maintenance reminder: be careful not to introduce spaces around args # ($1, $2), otherwise string concatenation will malfunction. # -# Before calling this, emcc.environment.$(2) must be set to a value -# for emcc's -sENVIRONMENT flag. +# emcc.environment.$(2) must be set to a value for emcc's +# -sENVIRONMENT flag. # # $(cflags.$(1)) and $(cflags.$(1).$(2)) may be defined to append # CFLAGS to a given build mode. # # $(emcc.flags.$(1)) and $(emcc.flags.$(1).$(2)) may be defined to @@ -951,43 +936,22 @@ # copy. sqlite3-worker1.js.in := $(dir.api)/sqlite3-worker1.c-pp.js sqlite3-worker1-promiser.js.in := $(dir.api)/sqlite3-worker1-promiser.c-pp.js sqlite3-worker1.js := $(dir.dout)/sqlite3-worker1.js sqlite3-worker1-promiser.js := $(dir.dout)/sqlite3-worker1-promiser.js -sqlite3-worker1-promiser.mjs := $(dir.dout)/sqlite3-worker1-promiser.mjs -sqlite3-worker1-bundler-friendly.mjs := $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs +sqlite3-worker1-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs sqlite3-worker1-promiser-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js $(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1.js))) -$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.mjs),\ +$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.js),\ $(c-pp.D.sqlite3-bundler-friendly))) $(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.js))) $(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),\ $(sqlite3-worker1-promiser-bundler-friendly.js),\ $(c-pp.D.sqlite3-bundler-friendly))) -$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.mjs),\ - -Dtarget=es6-module -Dtarget=es6-bundler-friendly)) -$(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.mjs) \ +$(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.js) \ $(sqlite3-worker1-promiser-bundler-friendly.js) -$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js)) -$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\ - -Dtarget=es6-module)) -$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html)) -$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\ - -Dtarget=es6-module)) -all: $(sqlite3-worker1.js) \ - $(sqlite3-worker1-promiser.js) $(sqlite3-worker1-promiser.mjs) - -demo-worker1-promiser.html: $(sqlite3-worker1-promiser.js) demo-worker1-promiser.js -demo-worker1-promiser-esm.html: $(sqlite3-worker1-promiser.mjs) demo-worker1-promiser.mjs -all: demo-worker1-promiser.html demo-worker1-promiser-esm.html - -sqlite3-api.ext.jses += \ - $(sqlite3-worker1-promiser.mjs) \ - $(sqlite3-worker1-bundler-friendly.mjs) \ - $(sqlite3-worker1.js) -all quick: $(sqlite3-api.ext.jses) -q: quick +$(sqlite3.js) $(sqlite3.mjs): $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js) ######################################################################## # batch-runner.js is part of one of the test apps which reads in SQL # dumps generated by $(speedtest1) and executes them. dir.sql := sql Index: ext/wasm/api/sqlite3-api-glue.js ================================================================== --- ext/wasm/api/sqlite3-api-glue.js +++ ext/wasm/api/sqlite3-api-glue.js @@ -327,12 +327,11 @@ build-time function-export list does not currently take optional features into account. */ wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); } -//#if enable-see - if(wasm.exports.sqlite3_key_v2 instanceof Function){ + if(wasm.exports.sqlite3_activate_see instanceof Function){ /** This code is capable of using an SEE build but note that an SEE WASM build is generally incompatible with SEE's license conditions. It is permitted for use internally in organizations which have licensed SEE, but not for public sites because @@ -345,12 +344,10 @@ ["sqlite3_rekey", "int", "sqlite3*", "string", "int"], ["sqlite3_rekey_v2", "int", "sqlite3*", "string", "*", "int"], ["sqlite3_activate_see", undefined, "string"] ); } -//#endif enable-see - /** Functions which require BigInt (int64) support are separated from the others because we need to conditionally bind them or apply dummy impls, depending on the capabilities of the environment. (That said: we never actually build without BigInt support, @@ -625,18 +622,14 @@ no clients "should" be depending on the old names. */ wasm.bindingSignatures.wasmInternal = [ ["sqlite3__wasm_db_reset", "int", "sqlite3*"], ["sqlite3__wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"], - [/* DO NOT USE. This is deprecated since 2023-08-11 because it can - trigger assert() in debug builds when used with file sizes - which are not sizes to a multiple of a valid db page size. */ - "sqlite3__wasm_vfs_create_file", "int", "sqlite3_vfs*","string","*", "int" - ], + ["sqlite3__wasm_vfs_create_file", "int", + "sqlite3_vfs*","string","*", "int"], ["sqlite3__wasm_posix_create_file", "int", "string","*", "int"], - ["sqlite3__wasm_vfs_unlink", "int", "sqlite3_vfs*","string"], - ["sqlite3__wasm_qfmt_token","string:dealloc", "string","int"] + ["sqlite3__wasm_vfs_unlink", "int", "sqlite3_vfs*","string"] ]; /** Install JS<->C struct bindings for the non-opaque struct types we need... */ @@ -1552,14 +1545,10 @@ case capi.SQLITE_CONFIG_SERIALIZED: // 3 /* nil */ case capi.SQLITE_CONFIG_SINGLETHREAD: // 1 /* nil */: case capi.SQLITE_CONFIG_SQLLOG: // 21 /* xSqllog, void* */ case capi.SQLITE_CONFIG_WIN32_HEAPSIZE: // 23 /* int nByte */ default: - /* maintenance note: we specifically do not include - SQLITE_CONFIG_ROWID_IN_VIEW here, on the grounds that - it's only for legacy support and no apps written with - this API require that. */ return capi.SQLITE_NOTFOUND; } }; }/* sqlite3_config() */ Index: ext/wasm/api/sqlite3-api-oo1.js ================================================================== --- ext/wasm/api/sqlite3-api-oo1.js +++ ext/wasm/api/sqlite3-api-oo1.js @@ -85,108 +85,10 @@ VFS. In the latter case, the call signature is (theDbObject,sqlite3Namespace) and the callback is expected to throw on error. */ const __vfsPostOpenSql = Object.create(null); -//#if enable-see - /** - Converts ArrayBuffer or Uint8Array ba into a string of hex - digits. - */ - const byteArrayToHex = function(ba){ - if( ba instanceof ArrayBuffer ){ - ba = new Uint8Array(ba); - } - const li = []; - const digits = "0123456789abcdef"; - for( const d of ba ){ - li.push( digits[(d & 0xf0) >> 4], digits[d & 0x0f] ); - } - return li.join(''); - }; - - /** - Internal helper to apply an SEE key to a just-opened - database. Requires that db be-a DB object which has just been - opened, opt be the options object processed by its ctor, and opt - must have either the key, hexkey, or textkey properties, either - as a string, an ArrayBuffer, or a Uint8Array. - - This is a no-op in non-SEE builds. It throws on error and returns - without side effects if none of the key/textkey/hexkey options - are set. It throws if more than one is set or if any are set to - values of an invalid type. - - Returns true if it applies the key, else an unspecified falsy value. - */ - const dbCtorApplySEEKey = function(db,opt){ - if( !capi.sqlite3_key_v2 ) return; - let keytype; - let key; - const check = (opt.key ? 1 : 0) + (opt.hexkey ? 1 : 0) + (opt.textkey ? 1 : 0); - if( !check ) return; - else if( check>1 ){ - toss3(capi.SQLITE_MISUSE, - "Only ONE of (key, hexkey, textkey) may be provided."); - } - if( opt.key ){ - /* It is not legal to bind an argument to PRAGMA key=?, so we - convert it to a hexkey... */ - keytype = 'key'; - key = opt.key; - if('string'===typeof key){ - key = new TextEncoder('utf-8').encode(key); - } - if((key instanceof ArrayBuffer) || (key instanceof Uint8Array)){ - key = byteArrayToHex(key); - keytype = 'hexkey'; - }else{ - toss3(capi.SQLITE_MISUSE, - "Invalid value for the 'key' option. Expecting a string,", - "ArrayBuffer, or Uint8Array."); - return; - } - }else if( opt.textkey ){ - /* For textkey we need it to be in string form, so convert it to - a string if it's a byte array... */ - keytype = 'textkey'; - key = opt.textkey; - if(key instanceof ArrayBuffer){ - key = new Uint8Array(key); - } - if(key instanceof Uint8Array){ - key = new TextDecoder('utf-8').decode(key); - }else if('string'!==typeof key){ - toss3(capi.SQLITE_MISUSE, - "Invalid value for the 'textkey' option. Expecting a string,", - "ArrayBuffer, or Uint8Array."); - } - }else if( opt.hexkey ){ - keytype = 'hexkey'; - key = opt.hexkey; - if((key instanceof ArrayBuffer) || (key instanceof Uint8Array)){ - key = byteArrayToHex(key); - }else if('string'!==typeof key){ - toss3(capi.SQLITE_MISUSE, - "Invalid value for the 'hexkey' option. Expecting a string,", - "ArrayBuffer, or Uint8Array."); - } - /* else assume it's valid hex codes */ - }else{ - return; - } - let stmt; - try{ - stmt = db.prepare("PRAGMA "+keytype+"="+util.sqlite3__wasm_qfmt_token(key, 1)); - stmt.step(); - }finally{ - if(stmt) stmt.finalize(); - } - return true; - }; -//#endif enable-see - /** A proxy for DB class constructors. It must be called with the being-construct DB object as its "this". See the DB constructor for the argument docs. This is split into a separate function in order to enable simple creation of special-case DB constructors, @@ -271,32 +173,20 @@ } this.filename = fnJs; __ptrMap.set(this, pDb); __stmtMap.set(this, Object.create(null)); try{ -//#if enable-see - dbCtorApplySEEKey(this,opt); -//#endif // Check for per-VFS post-open SQL/callback... - const pVfs = capi.sqlite3_js_db_vfs(pDb) - || toss3("Internal error: cannot get VFS for new db handle."); + const pVfs = capi.sqlite3_js_db_vfs(pDb); + if(!pVfs) toss3("Internal error: cannot get VFS for new db handle."); const postInitSql = __vfsPostOpenSql[pVfs]; - if(postInitSql){ - /** - Reminder: if this db is encrypted and the client did _not_ pass - in the key, any init code will fail, causing the ctor to throw. - We don't actually know whether the db is encrypted, so we cannot - sensibly apply any heuristics which skip the init code only for - encrypted databases for which no key has yet been supplied. - */ - if(postInitSql instanceof Function){ - postInitSql(this, sqlite3); - }else{ - checkSqlite3Rc( - pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0) - ); - } + if(postInitSql instanceof Function){ + postInitSql(this, sqlite3); + }else if(postInitSql){ + checkSqlite3Rc( + pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0) + ); } }catch(e){ this.close(); throw e; } @@ -388,50 +278,19 @@ - `filename`: database file name - `flags`: open-mode flags - `vfs`: the VFS fname -//#if enable-see - - SEE-capable builds optionally support ONE of the following - additional options: - - - `key`, `hexkey`, or `textkey`: encryption key as a string, - ArrayBuffer, or Uint8Array. These flags function as documented - for the SEE pragmas of the same names. Using a byte array for - `hexkey` is equivalent to the same series of hex codes in - string form, so `'666f6f'` is equivalent to - `Uint8Array([0x66,0x6f,0x6f])`. A `textkey` byte array is - assumed to be UTF-8. A `key` string is transformed into a UTF-8 - byte array, and a `key` byte array is transformed into a - `hexkey` with the same bytes. - - In non-SEE builds, these options are ignored. In SEE builds, - `PRAGMA key/textkey/hexkey=X` is executed immediately after - opening the db. If more than one of the options is provided, - or any option has an invalid argument type, an exception is - thrown. - - Note that some DB subclasses may run post-initialization SQL - code, e.g. to set a busy-handler timeout or tweak the page cache - size. Such code is run _after_ the SEE key is applied. If no key - is supplied and the database is encrypted, execution of the - post-initialization SQL will fail, causing the constructor to - throw. - -//#endif enable-see - The `filename` and `vfs` arguments may be either JS strings or C-strings allocated via WASM. `flags` is required to be a JS string (because it's specific to this API, which is specific to JS). For purposes of passing a DB instance to C-style sqlite3 functions, the DB object's read-only `pointer` property holds its `sqlite3*` pointer value. That property can also be used to check - whether this DB instance is still open: it will evaluate to - `undefined` after the DB object's close() method is called. + whether this DB instance is still open. In the main window thread, the filenames `":localStorage:"` and `":sessionStorage:"` are special: they cause the db to use either localStorage or sessionStorage for storing the database using the kvvfs. If one of these names are used, they trump @@ -572,60 +431,44 @@ if(!opt.callback && !opt.returnValue && undefined!==opt.rowMode){ if(!opt.resultRows) opt.resultRows = []; out.returnVal = ()=>opt.resultRows; } if(opt.callback || opt.resultRows){ - switch((undefined===opt.rowMode) ? 'array' : opt.rowMode) { - case 'object': - out.cbArg = (stmt,cache)=>{ - if( !cache.columnNames ) cache.columnNames = stmt.getColumnNames([]); - /* https://sqlite.org/forum/forumpost/3632183d2470617d: - conversion of rows to objects (key/val pairs) is - somewhat expensive for large data sets because of the - native-to-JS conversion of the column names. If we - instead cache the names and build objects from that - list of strings, it can run twice as fast. The - difference is not noticeable for small data sets but - becomes human-perceivable when enough rows are - involved. */ - const row = stmt.get([]); - const rv = Object.create(null); - for( const i in cache.columnNames ) rv[cache.columnNames[i]] = row[i]; - return rv; - }; - break; - case 'array': out.cbArg = (stmt)=>stmt.get([]); break; - case 'stmt': - if(Array.isArray(opt.resultRows)){ - toss3("exec(): invalid rowMode for a resultRows array: must", - "be one of 'array', 'object',", - "a result column number, or column name reference."); - } - out.cbArg = (stmt)=>stmt; - break; - default: - if(util.isInt32(opt.rowMode)){ - out.cbArg = (stmt)=>stmt.get(opt.rowMode); - break; - }else if('string'===typeof opt.rowMode - && opt.rowMode.length>1 - && '$'===opt.rowMode[0]){ - /* "$X": fetch column named "X" (case-sensitive!). Prior - to 2022-12-14 ":X" and "@X" were also permitted, but - having so many options is unnecessary and likely to - cause confusion. */ - const $colName = opt.rowMode.substr(1); - out.cbArg = (stmt)=>{ - const rc = stmt.get(Object.create(null))[$colName]; - return (undefined===rc) - ? toss3(capi.SQLITE_NOTFOUND, - "exec(): unknown result column:",$colName) - : rc; - }; - break; - } - toss3("Invalid rowMode:",opt.rowMode); + switch((undefined===opt.rowMode) + ? 'array' : opt.rowMode) { + case 'object': out.cbArg = (stmt)=>stmt.get(Object.create(null)); break; + case 'array': out.cbArg = (stmt)=>stmt.get([]); break; + case 'stmt': + if(Array.isArray(opt.resultRows)){ + toss3("exec(): invalid rowMode for a resultRows array: must", + "be one of 'array', 'object',", + "a result column number, or column name reference."); + } + out.cbArg = (stmt)=>stmt; + break; + default: + if(util.isInt32(opt.rowMode)){ + out.cbArg = (stmt)=>stmt.get(opt.rowMode); + break; + }else if('string'===typeof opt.rowMode + && opt.rowMode.length>1 + && '$'===opt.rowMode[0]){ + /* "$X": fetch column named "X" (case-sensitive!). Prior + to 2022-12-14 ":X" and "@X" were also permitted, but + having so many options is unnecessary and likely to + cause confusion. */ + const $colName = opt.rowMode.substr(1); + out.cbArg = (stmt)=>{ + const rc = stmt.get(Object.create(null))[$colName]; + return (undefined===rc) + ? toss3(capi.SQLITE_NOTFOUND, + "exec(): unknown result column:",$colName) + : rc; + }; + break; + } + toss3("Invalid rowMode:",opt.rowMode); } } return out; }; @@ -1039,19 +882,14 @@ a schema change between the prepare() and step(), via another connection, may invalidate the column count and names. */) ? 0 : 1; evalFirstResult = false; if(arg.cbArg || resultRows){ - const cbArgCache = Object.create(null) - /* 2nd arg for arg.cbArg, used by (at least) row-to-object - converter */; for(; stmt.step(); stmt._lockedByExec = false){ - if(0===gotColNames++){ - stmt.getColumnNames(cbArgCache.columnNames = (opt.columnNames || [])); - } + if(0===gotColNames++) stmt.getColumnNames(opt.columnNames); stmt._lockedByExec = true; - const row = arg.cbArg(stmt,cbArgCache); + const row = arg.cbArg(stmt); if(resultRows) resultRows.push(row); if(callback && false === callback.call(opt, row, stmt)){ break; } } @@ -1682,11 +1520,11 @@ - Numbers are bound as either doubles or integers: doubles if they are larger than 32 bits, else double or int32, depending on whether they have a fractional part. Booleans are bound as integer 0 or 1. It is not expected the distinction of binding - doubles which have no fractional parts and integers is + doubles which have no fractional parts is integers is significant for the majority of clients due to sqlite3's data typing model. If [BigInt] support is enabled then this routine will bind BigInt values as 64-bit integers if they'll fit in 64 bits. If that support disabled, it will store the BigInt as an int32 or a double if it can do so without loss @@ -1866,11 +1704,11 @@ Requires that step() has just returned a truthy value, else an exception is thrown. By default it will determine the data type of the result - automatically. If passed a second argument, it must be one + automatically. If passed a second arugment, it must be one of the enumeration values for sqlite3 types, which are defined as members of the sqlite3 module: SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB. Any other value, except for undefined, will trigger an exception. Passing undefined is the same as not passing a value. It is legal @@ -2066,30 +1904,20 @@ if(util.isUIThread()){ /** Functionally equivalent to DB(storageName,'c','kvvfs') except that it throws if the given storage name is not one of 'local' or 'session'. - - As of version 3.46, the argument may optionally be an options - object in the form: - - { - filename: 'session'|'local', - ... etc. (all options supported by the DB ctor) - } - - noting that the 'vfs' option supported by main DB - constructor is ignored here: the vfs is always 'kvvfs'. */ sqlite3.oo1.JsStorageDb = function(storageName='session'){ - const opt = dbCtorHelper.normalizeArgs(...arguments); - storageName = opt.filename; if('session'!==storageName && 'local'!==storageName){ toss3("JsStorageDb db name must be one of 'session' or 'local'."); } - opt.vfs = 'kvvfs'; - dbCtorHelper.call(this, opt); + dbCtorHelper.call(this, { + filename: storageName, + flags: 'c', + vfs: "kvvfs" + }); }; const jdb = sqlite3.oo1.JsStorageDb; jdb.prototype = Object.create(DB.prototype); /** Equivalent to sqlite3_js_kvvfs_clear(). */ jdb.clearStorage = capi.sqlite3_js_kvvfs_clear; Index: ext/wasm/api/sqlite3-api-prologue.js ================================================================== --- ext/wasm/api/sqlite3-api-prologue.js +++ ext/wasm/api/sqlite3-api-prologue.js @@ -243,11 +243,11 @@ that object is treated as the 2nd argument to the parent constructor. The exception's message is created by concatenating its arguments with a space between each, except for the - two-args-with-an-object form and that the first argument will + two-args-with-an-objec form and that the first argument will get coerced to a string, as described above, if it's an integer. If passed an integer first argument, the error object's `resultCode` member will be set to the given integer value, @@ -1836,11 +1836,11 @@ }; /** Calls either sqlite3_result_error_nomem(), if e is-a WasmAllocError, or sqlite3_result_error(). In the latter case, - the second argument is coerced to a string to create the error + the second arugment is coerced to a string to create the error message. The first argument is a (sqlite3_context*). Returns void. Does not throw. */ Index: ext/wasm/api/sqlite3-api-worker1.js ================================================================== --- ext/wasm/api/sqlite3-api-worker1.js +++ ext/wasm/api/sqlite3-api-worker1.js @@ -456,10 +456,15 @@ }; const getDefaultDbId = function(){ return wState.dbList[0] && getDbId(wState.dbList[0]); }; + + const guessVfs = function(filename){ + const m = /^file:.+(vfs=(\w+))/.exec(filename); + return sqlite3.capi.sqlite3_vfs_find(m ? m[2] : 0); + }; const isSpecialDbFilename = (n)=>{ return ""===n || ':'===n[0]; }; @@ -478,12 +483,40 @@ const oargs = Object.create(null), args = (ev.args || Object.create(null)); if(args.simulateError){ // undocumented internal testing option toss("Throwing because of simulateError flag."); } const rc = Object.create(null); + let byteArray, pVfs; oargs.vfs = args.vfs; - oargs.filename = args.filename || ""; + if(isSpecialDbFilename(args.filename)){ + oargs.filename = args.filename || ""; + }else{ + oargs.filename = args.filename; + byteArray = args.byteArray; + if(byteArray) pVfs = guessVfs(args.filename); + } + if(pVfs){ + /* 2022-11-02: this feature is as-yet untested except that + sqlite3__wasm_vfs_create_file() has been tested from the + browser dev console. */ + let pMem; + try{ + pMem = sqlite3.wasm.allocFromTypedArray(byteArray); + const rc = util.sqlite3__wasm_vfs_create_file( + pVfs, oargs.filename, pMem, byteArray.byteLength + ); + if(rc) sqlite3.SQLite3Error.toss(rc); + }catch(e){ + throw new sqlite3.SQLite3Error( + e.name+' creating '+args.filename+": "+e.message, { + cause: e + } + ); + }finally{ + if(pMem) sqlite3.wasm.dealloc(pMem); + } + } const db = wState.open(oargs); rc.filename = db.filename; rc.persistent = !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs"); rc.dbId = getDbId(db); rc.vfs = db.dbVfsName(); Index: ext/wasm/api/sqlite3-opfs-async-proxy.js ================================================================== --- ext/wasm/api/sqlite3-opfs-async-proxy.js +++ ext/wasm/api/sqlite3-opfs-async-proxy.js @@ -49,11 +49,11 @@ versions (approximately) 104-107 are extinct) should change our usage of those methods to remove the "await". */ "use strict"; const wPost = (type,...args)=>postMessage({type, payload:args}); -const installAsyncProxy = function(){ +const installAsyncProxy = function(self){ const toss = function(...args){throw new Error(args.join(' '))}; if(globalThis.window === globalThis){ toss("This code cannot run from the main thread.", "Load it as a Worker from a separate Worker."); }else if(!navigator?.storage?.getDirectory){ @@ -560,18 +560,10 @@ storeAndNotify(opName, state.sq3Codes.SQLITE_NOTFOUND); mTimeEnd(); wTimeEnd(); return; } - if( state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN & opfsFlags ){ - try{ - await hDir.removeEntry(filenamePart); - }catch(e){ - /* ignoring */ - //warn("Ignoring failed Unlink of",filename,":",e); - } - } const hFile = await hDir.getFileHandle(filenamePart, {create}); wTimeEnd(); const fh = Object.assign(Object.create(null),{ fid: fid, filenameAbs: filename, @@ -917,7 +909,7 @@ !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory){ wPost('opfs-unavailable',"Missing required OPFS APIs."); }else{ - installAsyncProxy(); + installAsyncProxy(self); } 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 @@ -1270,11 +1270,11 @@ }/*extend sqlite3.oo1*/ thePool.log("VFS initialized."); return poolUtil; }).catch(async (e)=>{ await thePool.removeVfs().catch(()=>{}); - throw e; + return e; }); }).catch((err)=>{ //error("rejecting promise:",err); return initPromises[vfsName] = Promise.reject(err); }); 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 @@ -421,30 +421,15 @@ toss("Maintenance required: not found:",k); } }); state.opfsFlags = Object.assign(Object.create(null),{ /** - Flag for use with xOpen(). URI flag "opfs-unlock-asap=1" - enables this. See defaultUnlockAsap, below. + Flag for use with xOpen(). "opfs-unlock-asap=1" enables + this. See defaultUnlockAsap, below. */ OPFS_UNLOCK_ASAP: 0x01, /** - Flag for use with xOpen(). URI flag "delete-before-open=1" - tells the VFS to delete the db file before attempting to open - it. This can be used, e.g., to replace a db which has been - corrupted (without forcing us to expose a delete/unlink() - function in the public API). - - Failure to unlink the file is ignored but may lead to - downstream errors. An unlink can fail if, e.g., another tab - has the handle open. - - It goes without saying that deleting a file out from under another - instance results in Undefined Behavior. - */ - OPFS_UNLINK_BEFORE_OPEN: 0x02, - /** If true, any async routine which implicitly acquires a sync access handle (i.e. an OPFS lock) will release that locks at the end of the call which acquires it. If false, such "autolocks" are not released until the VFS is idle for some brief amount of time. @@ -888,21 +873,17 @@ xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){ mTimeStart('xOpen'); let opfsFlags = 0; if(0===zName){ zName = randomFilename(); - }else if(wasm.isPtr(zName)){ + }else if('number'===typeof zName){ if(capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)){ /* -----------------------^^^^^ MUST pass the untranslated C-string here. */ opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP; } - if(capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)){ - opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN; - } zName = wasm.cstrToJs(zName); - //warn("xOpen zName =",zName, "opfsFlags =",opfsFlags); } const fh = Object.create(null); fh.fid = pFile; fh.filename = zName; fh.sab = new SharedArrayBuffer(state.fileBufferSize); Index: ext/wasm/api/sqlite3-wasm.c ================================================================== --- ext/wasm/api/sqlite3-wasm.c +++ ext/wasm/api/sqlite3-wasm.c @@ -543,14 +543,10 @@ DefInt(SQLITE_CONFIG_PMASZ); DefInt(SQLITE_CONFIG_STMTJRNL_SPILL); DefInt(SQLITE_CONFIG_SMALL_MALLOC); DefInt(SQLITE_CONFIG_SORTERREF_SIZE); DefInt(SQLITE_CONFIG_MEMDB_MAXSIZE); - /* maintenance note: we specifically do not include - SQLITE_CONFIG_ROWID_IN_VIEW here, on the grounds that - it's only for legacy support and no apps written with - this API require that. */ } _DefGroup; DefGroup(dataTypes) { DefInt(SQLITE_INTEGER); DefInt(SQLITE_FLOAT); @@ -1676,28 +1672,39 @@ SQLITE_WASM_EXPORT int sqlite3__wasm_config_j(int op, sqlite3_int64 arg){ return sqlite3_config(op, arg); } +#if 0 +// Pending removal after verification of a workaround discussed in the +// forum post linked to below. /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** -** If z is not NULL, returns the result of passing z to -** sqlite3_mprintf()'s %Q modifier (if addQuotes is true) or %q (if -** addQuotes is 0). Returns NULL if z is NULL or on OOM. -*/ -SQLITE_WASM_EXPORT -char * sqlite3__wasm_qfmt_token(char *z, int addQuotes){ - char * rc = 0; - if( z ){ - rc = addQuotes - ? sqlite3_mprintf("%Q", z) - : sqlite3_mprintf("%q", z); - } - return rc; -} +** Returns a pointer to sqlite3_free(). In compliant browsers the +** return value, when passed to sqlite3.wasm.exports.functionEntry(), +** must resolve to the same function as +** sqlite3.wasm.exports.sqlite3_free. i.e. from a dev console where +** sqlite3 is exported globally, the following must be true: +** +** ``` +** sqlite3.wasm.functionEntry( +** sqlite3.wasm.exports.sqlite3__wasm_ptr_to_sqlite3_free() +** ) === sqlite3.wasm.exports.sqlite3_free +** ``` +** +** Using a function to return this pointer, as opposed to exporting it +** via sqlite3__wasm_enum_json(), is an attempt to work around a +** Safari-specific quirk covered at +** https://sqlite.org/forum/info/e5b20e1feb37a19a. +**/ +SQLITE_WASM_EXPORT +void * sqlite3__wasm_ptr_to_sqlite3_free(void){ + return (void*)sqlite3_free; +} +#endif #if defined(__EMSCRIPTEN__) && defined(SQLITE_ENABLE_WASMFS) #include /* 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 @@ -40,17 +40,13 @@ function, enabling delayed instantiation of a Worker. - `onready` (optional, but...): this callback is called with no arguments when the worker fires its initial 'sqlite3-api'/'worker1-ready' message, which it does when - sqlite3.initWorker1API() completes its initialization. This is the - simplest way to tell the worker to kick off work at the earliest - opportunity, and the only way to know when the worker module has - completed loading. The irony of using a callback for this, instead - of returning a promise from sqlite3Worker1Promiser() is not lost on - the developers: see sqlite3Worker1Promiser.v2() which uses a - Promise instead. + sqlite3.initWorker1API() completes its initialization. This is + the simplest way to tell the worker to kick off work at the + earliest opportunity. - `onunhandled` (optional): a callback which gets passed the message event object for any worker.onmessage() events which are not handled by this proxy. Ideally that "should" never happen, as this proxy aims to handle all known message types. @@ -205,11 +201,11 @@ msg = Object.create(null); msg.type = arguments[0]; msg.args = arguments[1]; msg.dbId = msg.args.dbId; }else{ - toss("Invalid arguments for sqlite3Worker1Promiser()-created factory."); + toss("Invalid arugments for sqlite3Worker1Promiser()-created factory."); } if(!msg.dbId && msg.type!=='open') msg.dbId = dbId; msg.messageId = genMsgId(msg); msg.departureTime = performance.now(); const proxy = Object.create(null); @@ -249,14 +245,13 @@ }); if(rowCallbackId) p = p.finally(()=>delete handlerMap[rowCallbackId]); return p; }; }/*sqlite3Worker1Promiser()*/; - globalThis.sqlite3Worker1Promiser.defaultConfig = { worker: function(){ -//#if target=es6-module +//#if target=es6-bundler-friendly return new Worker(new URL("sqlite3-worker1-bundler-friendly.mjs", import.meta.url),{ type: 'module' }); //#else let theJs = "sqlite3-worker1.js"; @@ -273,74 +268,16 @@ } } return new Worker(theJs + globalThis.location.search); //#endif } -//#ifnot target=es6-module +//#ifnot target=es6-bundler-friendly .bind({ currentScript: globalThis?.document?.currentScript }) //#endif , onerror: (...args)=>console.error('worker1 promiser error',...args) -}/*defaultConfig*/; - -/** - sqlite3Worker1Promiser.v2(), added in 3.46, works identically to - sqlite3Worker1Promiser() except that it returns a Promise instead - of relying an an onready callback in the config object. The Promise - resolves to the same factory function which - sqlite3Worker1Promiser() returns. - - If config is-a function or is an object which contains an onready - function, that function is replaced by a proxy which will resolve - after calling the original function and will reject if that - function throws. -*/ -sqlite3Worker1Promiser.v2 = function(config){ - let oldFunc; - if( 'function' == typeof config ){ - oldFunc = config; - config = {}; - }else if('function'===typeof config?.onready){ - oldFunc = config.onready; - delete config.onready; - } - const promiseProxy = Object.create(null); - config = Object.assign((config || Object.create(null)),{ - onready: async function(func){ - try { - if( oldFunc ) await oldFunc(func); - promiseProxy.resolve(func); - } - catch(e){promiseProxy.reject(e)} - } - }); - const p = new Promise(function(resolve,reject){ - promiseProxy.resolve = resolve; - promiseProxy.reject = reject; - }); - try{ - this.original(config); - }catch(e){ - promiseProxy.reject(e); - } - return p; -}.bind({ - /* We do this because clients are - recommended to delete globalThis.sqlite3Worker1Promiser. */ - original: sqlite3Worker1Promiser -}); - -//#if target=es6-module -/** - When built as a module, we export sqlite3Worker1Promiser.v2() - instead of sqlite3Worker1Promise() because (A) its interface is more - conventional for ESM usage and (B) the ESM option export option for - this API did not exist until v2 was created, so there's no backwards - incompatibility. -*/ -export default sqlite3Worker1Promiser.v2; -//#endif /* target=es6-module */ +}; //#else /* Built with the omit-oo1 flag. */ //#endif ifnot omit-oo1 Index: ext/wasm/common/whwasmutil.js ================================================================== --- ext/wasm/common/whwasmutil.js +++ ext/wasm/common/whwasmutil.js @@ -1380,23 +1380,19 @@ arguments, and its return value is returned to xCall's caller. If not found, an exception is thrown. This function does no conversion of argument or return types, but see xWrap() and xCallWrapped() for variants which do. - If the first argument is a function is is assumed to be - a WASM-bound function and is used as-is instead of looking up - the function via xGet(). - As a special case, if passed only 1 argument after the name and that argument in an Array, that array's entries become the function arguments. (This is not an ambiguous case because it's not legal to pass an Array object to a WASM function.) */ target.xCall = function(fname, ...args){ - const f = (fname instanceof Function) ? fname : target.xGet(fname); + const f = target.xGet(fname); if(!(f instanceof Function)) toss("Exported symbol",fname,"is not a function."); - if(f.length!==args.length) __argcMismatch(((f===fname) ? f.name : fname),f.length) + if(f.length!==args.length) __argcMismatch(fname,f.length) /* This is arguably over-pedantic but we want to help clients keep from shooting themselves in the foot when calling C APIs. */; return (2===arguments.length && Array.isArray(arguments[1])) ? f.apply(null, arguments[1]) : f.apply(null, args); @@ -1539,11 +1535,11 @@ - signature: a function signature string compatible with jsFuncToWasm(). - bindScope (string): one of ('transient', 'context', - 'singleton', 'permanent'). Bind scopes are: + 'singleton'). Bind scopes are: - 'transient': it will convert JS functions to WASM only for the duration of the xWrap()'d function call, using scopedInstallFunction(). Before that call returns, the WASM-side binding will be uninstalled. @@ -1789,33 +1785,15 @@ (t)=>xArg.get(t) || toss("Argument adapter not found:",t); const __xResultAdapterCheck = (t)=>xResult.get(t) || toss("Result adapter not found:",t); - /** - Fetches the xWrap() argument adapter mapped to t, calls it, - passing in all remaining arguments, and returns the result. - Throws if t is not mapped to an argument converter. - */ cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args); - /** - Identical to convertArg() except that it does not perform - an is-defined check on the mapping to t before invoking it. - */ cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args); - /** - Fetches the xWrap() result adapter mapped to t, calls it, passing - it v, and returns the result. Throws if t is not mapped to an - argument converter. - */ cache.xWrap.convertResult = (t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined)); - /** - Identical to convertResult() except that it does not perform an - is-defined check on the mapping to t before invoking it. - */ cache.xWrap.convertResultNoCheck = (t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined)); /** Creates a wrapper for another function which converts the arguments @@ -1923,19 +1901,19 @@ - `string` or `utf8` (results): treats the result value as a const C-string, encoded as UTF-8, copies it to a JS string, and returns that JS string. - - `string:dealloc` or `utf8:dealloc` (results): treats the result - value as a non-const UTF-8 C-string, ownership of which has - just been transfered to the caller. It copies the C-string to a - JS string, frees the C-string, and returns the JS string. If - such a result value is NULL, the JS result is `null`. Achtung: - when using an API which returns results from a specific - allocator, e.g. `my_malloc()`, this conversion _is not - legal_. Instead, an equivalent conversion which uses the - appropriate deallocator is required. For example: + - `string:dealloc` or `utf8:dealloc) (results): treats the result value + as a non-const UTF-8 C-string, ownership of which has just been + transfered to the caller. It copies the C-string to a JS + string, frees the C-string, and returns the JS string. If such + a result value is NULL, the JS result is `null`. Achtung: when + using an API which returns results from a specific allocator, + e.g. `my_malloc()`, this conversion _is not legal_. Instead, an + equivalent conversion which uses the appropriate deallocator is + required. For example: ```js target.xWrap.resultAdapter('string:my_free',(i)=>{ try { return i ? target.cstrToJs(i) : null } finally{ target.exports.my_free(i) } @@ -2032,16 +2010,12 @@ an implementation detail and subject to change. i.e. the public interface of 1 argument is stable. The fact that any arguments may be passed in after that one, and what those arguments are, is _not_ part of the public interface and is _not_ stable. - - Maintenance reminder: the Ember framework modifies the core - Array type, breaking for-in loops. */ - let i = 0; - for(; i < args.length; ++i) args[i] = cxw.convertArgNoCheck( + for(const i in args) args[i] = cxw.convertArgNoCheck( argTypes[i], args[i], args, i ); return cxw.convertResultNoCheck(resultType, xf.apply(null,args)); }finally{ target.scopedAllocPop(scope); DELETED ext/wasm/demo-worker1-promiser.c-pp.html Index: ext/wasm/demo-worker1-promiser.c-pp.html ================================================================== --- ext/wasm/demo-worker1-promiser.c-pp.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - -//#if target=es6-module - worker-promise (via ESM) tests -//#else - worker-promise tests -//#endif - - -
    worker-promise tests
    - -
    -
    -
    Initializing app...
    -
    - On a slow internet connection this may take a moment. If this - message displays for "a long time", intialization may have - failed and the JavaScript console may contain clues as to why. -
    -
    -
    Downloading...
    -
    - -
    -
    Most stuff on this page happens in the dev console.
    -
    -
    - -//#if target=es6-module - -//#else - - -//#endif - - DELETED ext/wasm/demo-worker1-promiser.c-pp.js Index: ext/wasm/demo-worker1-promiser.c-pp.js ================================================================== --- ext/wasm/demo-worker1-promiser.c-pp.js +++ /dev/null @@ -1,285 +0,0 @@ -/* - 2022-08-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. - - *********************************************************************** - - Demonstration of the sqlite3 Worker API #1 Promiser: a Promise-based - proxy for for the sqlite3 Worker #1 API. -*/ -//#if target=es6-module -import {default as promiserFactory} from "./jswasm/sqlite3-worker1-promiser.mjs"; -//#else -"use strict"; -const promiserFactory = globalThis.sqlite3Worker1Promiser.v2; -delete globalThis.sqlite3Worker1Promiser; -//#endif -(async function(){ - const T = globalThis.SqliteTestUtil; - const eOutput = document.querySelector('#test-output'); - const warn = console.warn.bind(console); - const error = console.error.bind(console); - const log = console.log.bind(console); - const logHtml = async function(cssClass,...args){ - log.apply(this, args); - const ln = document.createElement('div'); - if(cssClass) ln.classList.add(cssClass); - ln.append(document.createTextNode(args.join(' '))); - eOutput.append(ln); - }; - - let startTime; - const testCount = async ()=>{ - logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms"); - }; - - const promiserConfig = { -//#ifnot target=es6-module - /** - The v1 interfaces uses an onready function. The v2 interface optionally - accepts one but does not require it. If provided, it is called _before_ - the promise is resolved, and the promise is rejected if onready() throws. - */ - onready: function(f){ - /* f === the function returned by promiserFactory(). - Ostensibly (f === workerPromise) but this function is - called before the promiserFactory() Promise resolves, so - before workerPromise is set. */ - console.warn("This is the v2 interface - you don't need an onready() function."); - }, -//#endif - debug: 1 ? undefined : (...args)=>console.debug('worker debug',...args), - onunhandled: function(ev){ - error("Unhandled worker message:",ev.data); - }, - onerror: function(ev){ - error("worker1 error:",ev); - } - }; - const workerPromise = await promiserFactory(promiserConfig) - .then((func)=>{ - console.log("Init complete. Starting tests momentarily."); - globalThis.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/; - return func; - }); - - const wtest = async function(msgType, msgArgs, callback){ - if(2===arguments.length && 'function'===typeof msgArgs){ - callback = msgArgs; - msgArgs = undefined; - } - const p = 1 - ? workerPromise({type: msgType, args:msgArgs}) - : workerPromise(msgType, msgArgs); - return callback ? p.then(callback).finally(testCount) : p; - }; - - let sqConfig; - const runTests = async function(){ - const dbFilename = '/testing2.sqlite3'; - startTime = performance.now(); - - await wtest('config-get', (ev)=>{ - const r = ev.result; - log('sqlite3.config subset:', r); - T.assert('boolean' === typeof r.bigIntEnabled); - sqConfig = r; - }); - logHtml('', - "Sending 'open' message and waiting for its response before continuing..."); - - await wtest('open', { - filename: dbFilename, - simulateError: 0 /* if true, fail the 'open' */, - }, function(ev){ - const r = ev.result; - log("then open result",r); - T.assert(ev.dbId === r.dbId) - .assert(ev.messageId) - .assert('string' === typeof r.vfs); - promiserConfig.dbId = ev.dbId; - }).then(runTests2); - }; - - const runTests2 = async function(){ - const mustNotReach = ()=>toss("This is not supposed to be reached."); - - await wtest('exec',{ - sql: ["create table t(a,b)", - "insert into t(a,b) values(1,2),(3,4),(5,6)" - ].join(';'), - resultRows: [], columnNames: [], - countChanges: sqConfig.bigIntEnabled ? 64 : true - }, function(ev){ - ev = ev.result; - T.assert(0===ev.resultRows.length) - .assert(0===ev.columnNames.length) - .assert(sqConfig.bigIntEnabled - ? (3n===ev.changeCount) - : (3===ev.changeCount)); - }); - - await wtest('exec',{ - sql: 'select a a, b b from t order by a', - resultRows: [], columnNames: [], - }, function(ev){ - ev = ev.result; - T.assert(3===ev.resultRows.length) - .assert(1===ev.resultRows[0][0]) - .assert(6===ev.resultRows[2][1]) - .assert(2===ev.columnNames.length) - .assert('b'===ev.columnNames[1]); - }); - - await wtest('exec',{ - sql: 'select a a, b b from t order by a', - resultRows: [], columnNames: [], - rowMode: 'object', - countChanges: true - }, function(ev){ - ev = ev.result; - T.assert(3===ev.resultRows.length) - .assert(1===ev.resultRows[0].a) - .assert(6===ev.resultRows[2].b) - .assert(0===ev.changeCount); - }); - - await wtest( - 'exec', - {sql:'intentional_error'}, - mustNotReach - ).catch((e)=>{ - warn("Intentional error:",e); - }); - - await wtest('exec',{ - sql:'select 1 union all select 3', - resultRows: [] - }, function(ev){ - ev = ev.result; - T.assert(2 === ev.resultRows.length) - .assert(1 === ev.resultRows[0][0]) - .assert(3 === ev.resultRows[1][0]) - .assert(undefined === ev.changeCount); - }); - - const resultRowTest1 = function f(ev){ - if(undefined === f.counter) f.counter = 0; - if(null === ev.rowNumber){ - /* End of result set. */ - T.assert(undefined === ev.row) - .assert(2===ev.columnNames.length) - .assert('a'===ev.columnNames[0]) - .assert('B'===ev.columnNames[1]); - }else{ - T.assert(ev.rowNumber > 0); - ++f.counter; - } - log("exec() result row:",ev); - T.assert(null === ev.rowNumber || 'number' === typeof ev.row.B); - }; - await wtest('exec',{ - sql: 'select a a, b B from t order by a limit 3', - callback: resultRowTest1, - rowMode: 'object' - }, function(ev){ - T.assert(3===resultRowTest1.counter); - resultRowTest1.counter = 0; - }); - - const resultRowTest2 = function f(ev){ - if(null === ev.rowNumber){ - /* End of result set. */ - T.assert(undefined === ev.row) - .assert(1===ev.columnNames.length) - .assert('a'===ev.columnNames[0]) - }else{ - T.assert(ev.rowNumber > 0); - f.counter = ev.rowNumber; - } - log("exec() result row:",ev); - T.assert(null === ev.rowNumber || 'number' === typeof ev.row); - }; - await wtest('exec',{ - sql: 'select a a from t limit 3', - callback: resultRowTest2, - rowMode: 0 - }, function(ev){ - T.assert(3===resultRowTest2.counter); - }); - - const resultRowTest3 = function f(ev){ - if(null === ev.rowNumber){ - T.assert(3===ev.columnNames.length) - .assert('foo'===ev.columnNames[0]) - .assert('bar'===ev.columnNames[1]) - .assert('baz'===ev.columnNames[2]); - }else{ - f.counter = ev.rowNumber; - T.assert('number' === typeof ev.row); - } - }; - await wtest('exec',{ - sql: "select 'foo' foo, a bar, 'baz' baz from t limit 2", - callback: resultRowTest3, - columnNames: [], - rowMode: '$bar' - }, function(ev){ - log("exec() result row:",ev); - T.assert(2===resultRowTest3.counter); - }); - - await wtest('exec',{ - sql:[ - 'pragma foreign_keys=0;', - // ^^^ arbitrary query with no result columns - 'select a, b from t order by a desc; select a from t;' - // exec() only honors SELECT results from the first - // statement with result columns (regardless of whether - // it has any rows). - ], - rowMode: 1, - resultRows: [] - },function(ev){ - const rows = ev.result.resultRows; - T.assert(3===rows.length). - assert(6===rows[0]); - }); - - await wtest('exec',{sql: 'delete from t where a>3'}); - - await wtest('exec',{ - sql: 'select count(a) from t', - resultRows: [] - },function(ev){ - ev = ev.result; - T.assert(1===ev.resultRows.length) - .assert(2===ev.resultRows[0][0]); - }); - - await wtest('export', function(ev){ - ev = ev.result; - T.assert('string' === typeof ev.filename) - .assert(ev.byteArray instanceof Uint8Array) - .assert(ev.byteArray.length > 1024) - .assert('application/x-sqlite3' === ev.mimetype); - }); - - /***** close() tests must come last. *****/ - await wtest('close',{},function(ev){ - T.assert('string' === typeof ev.result.filename); - }); - - await wtest('close', (ev)=>{ - T.assert(undefined === ev.result.filename); - }).finally(()=>logHtml('',"That's all, folks!")); - }/*runTests2()*/; - - runTests(); -})(); ADDED ext/wasm/demo-worker1-promiser.html Index: ext/wasm/demo-worker1-promiser.html ================================================================== --- /dev/null +++ ext/wasm/demo-worker1-promiser.html @@ -0,0 +1,34 @@ + + + + + + + + + worker-promise tests + + +
    worker-promise tests
    + +
    +
    +
    Initializing app...
    +
    + On a slow internet connection this may take a moment. If this + message displays for "a long time", intialization may have + failed and the JavaScript console may contain clues as to why. +
    +
    +
    Downloading...
    +
    + +
    +
    Most stuff on this page happens in the dev console.
    +
    +
    + + + + + ADDED ext/wasm/demo-worker1-promiser.js Index: ext/wasm/demo-worker1-promiser.js ================================================================== --- /dev/null +++ ext/wasm/demo-worker1-promiser.js @@ -0,0 +1,277 @@ +/* + 2022-08-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. + + *********************************************************************** + + Demonstration of the sqlite3 Worker API #1 Promiser: a Promise-based + proxy for for the sqlite3 Worker #1 API. +*/ +'use strict'; +(function(){ + const T = self.SqliteTestUtil; + const eOutput = document.querySelector('#test-output'); + const warn = console.warn.bind(console); + const error = console.error.bind(console); + const log = console.log.bind(console); + const logHtml = async function(cssClass,...args){ + log.apply(this, args); + const ln = document.createElement('div'); + if(cssClass) ln.classList.add(cssClass); + ln.append(document.createTextNode(args.join(' '))); + eOutput.append(ln); + }; + + let startTime; + const testCount = async ()=>{ + logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms"); + }; + + //why is this triggered even when we catch() a Promise? + //window.addEventListener('unhandledrejection', function(event) { + // warn('unhandledrejection',event); + //}); + + const promiserConfig = { + worker: ()=>{ + const w = new Worker("jswasm/sqlite3-worker1.js"); + w.onerror = (event)=>error("worker.onerror",event); + return w; + }, + 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); + } + }; + const workerPromise = self.sqlite3Worker1Promiser(promiserConfig); + delete self.sqlite3Worker1Promiser; + + const wtest = async function(msgType, msgArgs, callback){ + if(2===arguments.length && 'function'===typeof msgArgs){ + callback = msgArgs; + msgArgs = undefined; + } + const p = 1 + ? workerPromise({type: msgType, args:msgArgs}) + : workerPromise(msgType, msgArgs); + return callback ? p.then(callback).finally(testCount) : p; + }; + + let sqConfig; + const runTests = async function(){ + const dbFilename = '/testing2.sqlite3'; + startTime = performance.now(); + + await wtest('config-get', (ev)=>{ + const r = ev.result; + log('sqlite3.config subset:', r); + T.assert('boolean' === typeof r.bigIntEnabled); + sqConfig = r; + }); + logHtml('', + "Sending 'open' message and waiting for its response before continuing..."); + + await wtest('open', { + filename: dbFilename, + simulateError: 0 /* if true, fail the 'open' */, + }, function(ev){ + const r = ev.result; + log("then open result",r); + T.assert(ev.dbId === r.dbId) + .assert(ev.messageId) + .assert('string' === typeof r.vfs); + promiserConfig.dbId = ev.dbId; + }).then(runTests2); + }; + + const runTests2 = async function(){ + const mustNotReach = ()=>toss("This is not supposed to be reached."); + + await wtest('exec',{ + sql: ["create table t(a,b)", + "insert into t(a,b) values(1,2),(3,4),(5,6)" + ].join(';'), + resultRows: [], columnNames: [], + countChanges: sqConfig.bigIntEnabled ? 64 : true + }, function(ev){ + ev = ev.result; + T.assert(0===ev.resultRows.length) + .assert(0===ev.columnNames.length) + .assert(sqConfig.bigIntEnabled + ? (3n===ev.changeCount) + : (3===ev.changeCount)); + }); + + await wtest('exec',{ + sql: 'select a a, b b from t order by a', + resultRows: [], columnNames: [], + }, function(ev){ + ev = ev.result; + T.assert(3===ev.resultRows.length) + .assert(1===ev.resultRows[0][0]) + .assert(6===ev.resultRows[2][1]) + .assert(2===ev.columnNames.length) + .assert('b'===ev.columnNames[1]); + }); + + await wtest('exec',{ + sql: 'select a a, b b from t order by a', + resultRows: [], columnNames: [], + rowMode: 'object', + countChanges: true + }, function(ev){ + ev = ev.result; + T.assert(3===ev.resultRows.length) + .assert(1===ev.resultRows[0].a) + .assert(6===ev.resultRows[2].b) + .assert(0===ev.changeCount); + }); + + await wtest( + 'exec', + {sql:'intentional_error'}, + mustNotReach + ).catch((e)=>{ + warn("Intentional error:",e); + }); + + await wtest('exec',{ + sql:'select 1 union all select 3', + resultRows: [] + }, function(ev){ + ev = ev.result; + T.assert(2 === ev.resultRows.length) + .assert(1 === ev.resultRows[0][0]) + .assert(3 === ev.resultRows[1][0]) + .assert(undefined === ev.changeCount); + }); + + const resultRowTest1 = function f(ev){ + if(undefined === f.counter) f.counter = 0; + if(null === ev.rowNumber){ + /* End of result set. */ + T.assert(undefined === ev.row) + .assert(2===ev.columnNames.length) + .assert('a'===ev.columnNames[0]) + .assert('B'===ev.columnNames[1]); + }else{ + T.assert(ev.rowNumber > 0); + ++f.counter; + } + log("exec() result row:",ev); + T.assert(null === ev.rowNumber || 'number' === typeof ev.row.B); + }; + await wtest('exec',{ + sql: 'select a a, b B from t order by a limit 3', + callback: resultRowTest1, + rowMode: 'object' + }, function(ev){ + T.assert(3===resultRowTest1.counter); + resultRowTest1.counter = 0; + }); + + const resultRowTest2 = function f(ev){ + if(null === ev.rowNumber){ + /* End of result set. */ + T.assert(undefined === ev.row) + .assert(1===ev.columnNames.length) + .assert('a'===ev.columnNames[0]) + }else{ + T.assert(ev.rowNumber > 0); + f.counter = ev.rowNumber; + } + log("exec() result row:",ev); + T.assert(null === ev.rowNumber || 'number' === typeof ev.row); + }; + await wtest('exec',{ + sql: 'select a a from t limit 3', + callback: resultRowTest2, + rowMode: 0 + }, function(ev){ + T.assert(3===resultRowTest2.counter); + }); + + const resultRowTest3 = function f(ev){ + if(null === ev.rowNumber){ + T.assert(3===ev.columnNames.length) + .assert('foo'===ev.columnNames[0]) + .assert('bar'===ev.columnNames[1]) + .assert('baz'===ev.columnNames[2]); + }else{ + f.counter = ev.rowNumber; + T.assert('number' === typeof ev.row); + } + }; + await wtest('exec',{ + sql: "select 'foo' foo, a bar, 'baz' baz from t limit 2", + callback: resultRowTest3, + columnNames: [], + rowMode: '$bar' + }, function(ev){ + log("exec() result row:",ev); + T.assert(2===resultRowTest3.counter); + }); + + await wtest('exec',{ + sql:[ + 'pragma foreign_keys=0;', + // ^^^ arbitrary query with no result columns + 'select a, b from t order by a desc; select a from t;' + // exec() only honors SELECT results from the first + // statement with result columns (regardless of whether + // it has any rows). + ], + rowMode: 1, + resultRows: [] + },function(ev){ + const rows = ev.result.resultRows; + T.assert(3===rows.length). + assert(6===rows[0]); + }); + + await wtest('exec',{sql: 'delete from t where a>3'}); + + await wtest('exec',{ + sql: 'select count(a) from t', + resultRows: [] + },function(ev){ + ev = ev.result; + T.assert(1===ev.resultRows.length) + .assert(2===ev.resultRows[0][0]); + }); + + await wtest('export', function(ev){ + ev = ev.result; + T.assert('string' === typeof ev.filename) + .assert(ev.byteArray instanceof Uint8Array) + .assert(ev.byteArray.length > 1024) + .assert('application/x-sqlite3' === ev.mimetype); + }); + + /***** close() tests must come last. *****/ + await wtest('close',{},function(ev){ + T.assert('string' === typeof ev.result.filename); + }); + + await wtest('close', (ev)=>{ + T.assert(undefined === ev.result.filename); + }).finally(()=>logHtml('',"That's all, folks!")); + }/*runTests2()*/; + + log("Init complete, but async init bits may still be running."); +})(); Index: ext/wasm/dist.make ================================================================== --- ext/wasm/dist.make +++ ext/wasm/dist.make @@ -17,19 +17,14 @@ # Chicken/egg situation: we need $(bin.version-info) to get the # version info for the archive name, but that binary may not yet be # built, and won't be built until we expand the dependencies. Thus we # have to use a temporary name for the archive until we can get # that binary built. -ifeq (1,$(SQLITE_C_IS_SEE)) -dist-name-extra := -see -else -dist-name-extra := -endif -ifeq (,$(filter snapshot,$(MAKECMDGOALS))) -dist-name-prefix := sqlite-wasm$(dist-name-extra) -else -dist-name-prefix := sqlite-wasm$(dist-name-extra)-snapshot-$(shell /usr/bin/date +%Y%m%d) +ifeq (,$(filter snapshot,$(MAKECMDGOALS))) +dist-name-prefix := sqlite-wasm +else +dist-name-prefix := sqlite-wasm-snapshot-$(shell /usr/bin/date +%Y%m%d) endif dist-name := $(dist-name-prefix)-TEMP ######################################################################## # dist.build must be the name of a target which triggers the build of @@ -52,22 +47,16 @@ demo-123.html demo-123-worker.html demo-123.js \ tester1.html tester1-worker.html tester1-esm.html \ tester1.js tester1.mjs \ demo-jsstorage.html demo-jsstorage.js \ demo-worker1.html demo-worker1.js \ - demo-worker1-promiser.html demo-worker1-promiser.js \ - demo-worker1-promiser-esm.html demo-worker1-promiser.mjs -dist.jswasm.extras := $(sqlite3.wasm) \ - $(sqlite3-api.ext.jses) + demo-worker1-promiser.html demo-worker1-promiser.js +dist.jswasm.extras := $(sqlite3-api.ext.jses) $(sqlite3.wasm) dist.common.extras := \ $(wildcard $(dir.common)/*.css) \ $(dir.common)/SqliteTestUtil.js -#$(info sqlite3-worker1-promiser.mjs = $(sqlite3-worker1-promiser.mjs)) -#$(info sqlite3-worker1.js = $(sqlite3-worker1.js)) -#$(info sqlite3-api.ext.jses = $(sqlite3-api.ext.jses)) -#$(info dist.jswasm.extras = $(dist.jswasm.extras)) .PHONY: dist snapshot # DIST_STRIP_COMMENTS $(call)able to be used in stripping C-style # from the dist copies of certain files. # # $1 = source js file @@ -76,12 +65,11 @@ $(bin.stripccomments) $(2) < $(1) > $(dist-dir.jswasm)/$(notdir $(1)) || exit; endef # STRIP_K1.js = list of JS files which need to be passed through # $(bin.stripcomments) with a single -k flag. STRIP_K1.js := $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js) \ - $(sqlite3-worker1-bundler-friendly.js) \ - $(sqlite3-api.ext.jses) + $(sqlite3-worker1-bundler-friendly.js) $(sqlite3-worker1-promiser-bundler-friendly.js) # STRIP_K2.js = list of JS files which need to be passed through # $(bin.stripcomments) with two -k flags. STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \ $(sqlite3-bundler-friendly.mjs) $(sqlite3-node.mjs) ######################################################################## @@ -98,11 +86,10 @@ # dist file's name, so cannot (without a recursive make) have the # target name equal to the archive name. dist: \ $(bin.stripccomments) $(bin.version-info) \ $(dist.build) $(STRIP_K1.js) $(STRIP_K2.js) \ - $(dist.jswasm.extras) $(dist.common.extras) \ $(MAKEFILE) $(MAKEFILE.dist) @echo "Making end-user deliverables..." @rm -fr $(dist-dir.top) @mkdir -p $(dist-dir.jswasm) $(dist-dir.common) @cp -p $(dist.top.extras) $(dist-dir.top) Index: ext/wasm/fiddle/fiddle.js ================================================================== --- ext/wasm/fiddle/fiddle.js +++ ext/wasm/fiddle/fiddle.js @@ -759,17 +759,11 @@ " a(t) AS (\n", " SELECT group_concat( substr(' .+*#', 1+min(iter/7,4), 1), '') \n", " FROM m2 GROUP BY cy\n", " )\n", "SELECT group_concat(rtrim(t),x'0a') as Mandelbrot FROM a;\n", - ]}, - {name: "JSON pretty-print", - sql: "select json_pretty(json_object('ex',json('[52,3.14159]')))" - }, - {name: "JSON pretty-print (with tabs)", - sql: "select json_pretty(json_object('ex',json('[52,3.14159]')),char(0x09))" - } + ]} ]; const newOpt = function(lbl,val){ const o = document.createElement('option'); if(Array.isArray(val)) val = val.join(''); o.value = val; Index: ext/wasm/index-dist.html ================================================================== --- ext/wasm/index-dist.html +++ ext/wasm/index-dist.html @@ -95,12 +95,10 @@
  • demo-worker1: Worker-based wrapper of the OO API #1. Its Promise-based wrapper is significantly easier to use, however.
  • demo-worker1-promiser: a demo of the Promise-based wrapper of the Worker1 API.
  • -
  • demo-worker1-promiser-esm: - same as the previous demo except loads the promiser from an ESM module.