Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -416,12 +416,10 @@ $(TOP)/ext/fts3/fts3_test.c \ $(TOP)/ext/session/test_session.c \ $(TOP)/ext/recover/sqlite3recover.c \ $(TOP)/ext/recover/dbdata.c \ $(TOP)/ext/recover/test_recover.c \ - $(TOP)/ext/intck/test_intck.c \ - $(TOP)/ext/intck/sqlite3intck.c \ $(TOP)/ext/rbu/test_rbu.c # Statically linked extensions # TESTSRC += \ @@ -815,11 +813,11 @@ 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) 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 +824,11 @@ 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) sqlite3ext.h: .target_source cp tsrc/sqlite3ext.h . tclsqlite3.c: sqlite3.c @@ -1152,41 +1150,39 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash$(BEXE) >keywordhash.h -# Source and header files that shell.c depends on -SHELL_DEP = \ - $(TOP)/src/shell.c.in \ - $(TOP)/ext/consio/console_io.c \ - $(TOP)/ext/consio/console_io.h \ - $(TOP)/ext/expert/sqlite3expert.c \ - $(TOP)/ext/expert/sqlite3expert.h \ - $(TOP)/ext/intck/sqlite3intck.c \ - $(TOP)/ext/intck/sqlite3intck.h \ - $(TOP)/ext/misc/appendvfs.c \ - $(TOP)/ext/misc/base64.c \ - $(TOP)/ext/misc/base85.c \ - $(TOP)/ext/misc/completion.c \ - $(TOP)/ext/misc/decimal.c \ - $(TOP)/ext/misc/fileio.c \ - $(TOP)/ext/misc/ieee754.c \ - $(TOP)/ext/misc/memtrace.c \ - $(TOP)/ext/misc/pcachetrace.c \ - $(TOP)/ext/misc/regexp.c \ - $(TOP)/ext/misc/series.c \ - $(TOP)/ext/misc/shathree.c \ - $(TOP)/ext/misc/sqlar.c \ - $(TOP)/ext/misc/uint.c \ - $(TOP)/ext/misc/zipfile.c \ - $(TOP)/ext/recover/dbdata.c \ - $(TOP)/ext/recover/sqlite3recover.c \ - $(TOP)/ext/recover/sqlite3recover.h \ - $(TOP)/src/test_windirent.c \ - $(TOP)/src/test_windirent.h - -shell.c: $(SHELL_DEP) $(TOP)/tool/mkshellc.tcl has_tclsh84 +# Source files that go into making shell.c +SHELL_SRC = \ + $(TOP)/src/shell.c.in \ + $(TOP)/ext/consio/console_io.c \ + $(TOP)/ext/consio/console_io.h \ + $(TOP)/ext/misc/appendvfs.c \ + $(TOP)/ext/misc/completion.c \ + $(TOP)/ext/misc/decimal.c \ + $(TOP)/ext/misc/basexx.c \ + $(TOP)/ext/misc/base64.c \ + $(TOP)/ext/misc/base85.c \ + $(TOP)/ext/misc/fileio.c \ + $(TOP)/ext/misc/ieee754.c \ + $(TOP)/ext/misc/regexp.c \ + $(TOP)/ext/misc/series.c \ + $(TOP)/ext/misc/shathree.c \ + $(TOP)/ext/misc/sqlar.c \ + $(TOP)/ext/misc/uint.c \ + $(TOP)/ext/expert/sqlite3expert.c \ + $(TOP)/ext/expert/sqlite3expert.h \ + $(TOP)/ext/misc/zipfile.c \ + $(TOP)/ext/misc/memtrace.c \ + $(TOP)/ext/misc/pcachetrace.c \ + $(TOP)/ext/recover/dbdata.c \ + $(TOP)/ext/recover/sqlite3recover.c \ + $(TOP)/ext/recover/sqlite3recover.h \ + $(TOP)/src/test_windirent.c + +shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl has_tclsh84 $(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c @@ -1310,13 +1306,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 @@ -16,17 +16,10 @@ !IFNDEF USE_AMALGAMATION USE_AMALGAMATION = 1 !ENDIF # <> -# Optionally set EXTRA_SRC to a list of C files to append to -# the generated sqlite3.c. -# -!IFNDEF EXTRA_SRC -EXTRA_SRC = -!ENDIF - # Set this non-0 to enable full warnings (-W4, etc) when compiling. # !IFNDEF USE_FULLWARN USE_FULLWARN = 1 !ENDIF @@ -1600,12 +1593,10 @@ $(TOP)\ext\misc\unionvtab.c \ $(TOP)\ext\misc\wholenumber.c \ $(TOP)\ext\rtree\test_rtreedoc.c \ $(TOP)\ext\recover\sqlite3recover.c \ $(TOP)\ext\recover\test_recover.c \ - $(TOP)\ext\intck\test_intck.c \ - $(TOP)\ext\intck\sqlite3intck.c \ $(TOP)\ext\recover\dbdata.c # If use of zlib is enabled, add the "zipfile.c" source file. # !IF $(USE_ZLIB)!=0 @@ -1920,11 +1911,11 @@ $(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl $(OPTS) < tsrc\vdbe.c > vdbe.new move vdbe.new tsrc\vdbe.c echo > .target_source sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) src-verify.exe - $(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS) $(EXTRA_SRC) + $(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS) sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl $(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl # <> @@ -2270,48 +2261,43 @@ $(TOP)\tool\mkkeywordhash.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS) keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe .\mkkeywordhash.exe > keywordhash.h -# Source and header files that shell.c depends on -SHELL_DEP = \ - $(TOP)\src\shell.c.in \ - $(TOP)\ext\consio\console_io.c \ - $(TOP)\ext\consio\console_io.h \ - $(TOP)\ext\expert\sqlite3expert.c \ - $(TOP)\ext\expert\sqlite3expert.h \ - $(TOP)\ext\intck\sqlite3intck.c \ - $(TOP)\ext\intck\sqlite3intck.h \ - $(TOP)\ext\misc\appendvfs.c \ - $(TOP)\ext\misc\base64.c \ - $(TOP)\ext\misc\base85.c \ - $(TOP)\ext\misc\completion.c \ - $(TOP)\ext\misc\decimal.c \ - $(TOP)\ext\misc\fileio.c \ - $(TOP)\ext\misc\ieee754.c \ - $(TOP)\ext\misc\memtrace.c \ - $(TOP)\ext\misc\pcachetrace.c \ - $(TOP)\ext\misc\regexp.c \ - $(TOP)\ext\misc\series.c \ - $(TOP)\ext\misc\shathree.c \ - $(TOP)\ext\misc\sqlar.c \ - $(TOP)\ext\misc\uint.c \ - $(TOP)\ext\misc\zipfile.c \ - $(TOP)\ext\recover\dbdata.c \ - $(TOP)\ext\recover\sqlite3recover.c \ - $(TOP)\ext\recover\sqlite3recover.h \ - $(TOP)\src\test_windirent.c \ - $(TOP)\src\test_windirent.h +# Source files that go into making shell.c +SHELL_SRC = \ + $(TOP)\src\shell.c.in \ + $(TOP)\ext\consio\console_io.c \ + $(TOP)\ext\consio\console_io.h \ + $(TOP)\ext\misc\appendvfs.c \ + $(TOP)\ext\misc\completion.c \ + $(TOP)\ext\misc\base64.c \ + $(TOP)\ext\misc\base85.c \ + $(TOP)\ext\misc\decimal.c \ + $(TOP)\ext\misc\fileio.c \ + $(TOP)\ext\misc\ieee754.c \ + $(TOP)\ext\misc\regexp.c \ + $(TOP)\ext\misc\series.c \ + $(TOP)\ext\misc\shathree.c \ + $(TOP)\ext\misc\uint.c \ + $(TOP)\ext\expert\sqlite3expert.c \ + $(TOP)\ext\expert\sqlite3expert.h \ + $(TOP)\ext\misc\memtrace.c \ + $(TOP)\ext\misc\pcachetrace.c \ + $(TOP)\ext\recover\dbdata.c \ + $(TOP)\ext\recover\sqlite3recover.c \ + $(TOP)\ext\recover\sqlite3recover.h \ + $(TOP)\src\test_windirent.c # If use of zlib is enabled, add the "zipfile.c" source file. # !IF $(USE_ZLIB)!=0 -SHELL_DEP = $(SHELL_DEP) $(TOP)\ext\misc\sqlar.c -SHELL_DEP = $(SHELL_DEP) $(TOP)\ext\misc\zipfile.c +SHELL_SRC = $(SHELL_SRC) $(TOP)\ext\misc\sqlar.c +SHELL_SRC = $(SHELL_SRC) $(TOP)\ext\misc\zipfile.c !ENDIF -shell.c: $(SHELL_DEP) $(TOP)\tool\mkshellc.tcl +shell.c: $(SHELL_SRC) $(TOP)\tool\mkshellc.tcl $(TCLSH_CMD) $(TOP)\tool\mkshellc.tcl > shell.c zlib: pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) && popd @@ -2494,13 +2480,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 +2537,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.0 +3.45.1 DELETED art/icon-243x273.gif Index: art/icon-243x273.gif ================================================================== --- art/icon-243x273.gif +++ /dev/null cannot compute difference between binary files DELETED art/icon-80x90.gif Index: art/icon-80x90.gif ================================================================== --- art/icon-80x90.gif +++ /dev/null cannot compute difference between binary files Index: autoconf/Makefile.msc ================================================================== --- autoconf/Makefile.msc +++ autoconf/Makefile.msc @@ -16,17 +16,10 @@ # that contains this "Makefile.msc". # TOP = . -# Optionally set EXTRA_SRC to a list of C files to append to -# the generated sqlite3.c. -# -!IFNDEF EXTRA_SRC -EXTRA_SRC = -!ENDIF - # Set this non-0 to enable full warnings (-W4, etc) when compiling. # !IFNDEF USE_FULLWARN USE_FULLWARN = 1 !ENDIF 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.0]) +AC_INIT([sqlite],[3.45.1]) #-------------------------------------------------------------------- # 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.0. +# Generated by GNU Autoconf 2.69 for sqlite 3.45.1. # # # 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.0' -PACKAGE_STRING='sqlite 3.46.0' +PACKAGE_VERSION='3.45.1' +PACKAGE_STRING='sqlite 3.45.1' 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.0 to adapt to many kinds of systems. +\`configure' configures sqlite 3.45.1 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.0:";; + short | recursive ) echo "Configuration of sqlite 3.45.1:";; 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.0 +sqlite configure 3.45.1 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.0, which was +It was created by sqlite $as_me 3.45.1, 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.0, which was +This file was extended by sqlite $as_me 3.45.1, 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.0 +sqlite config.status 3.45.1 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/lemon.html ================================================================== --- doc/lemon.html +++ doc/lemon.html @@ -681,11 +681,10 @@
  • %destructor
  • %else
  • %endif
  • %extra_argument
  • %fallback -
  • %free
  • %if
  • %ifdef
  • %ifndef
  • %include
  • %left @@ -692,11 +691,10 @@
  • %name
  • %nonassoc
  • %parse_accept
  • %parse_failure
  • %right -
  • %realloc
  • %stack_overflow
  • %stack_size
  • %start_symbol
  • %syntax_error
  • %token @@ -1200,25 +1198,10 @@

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

    - -

    4.4.26 The %realloc and %free directives

    - -

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

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

    5.0 Error Processing

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

    -

    6.0 History of Lemon

    Lemon was originally written by Richard Hipp sometime in the late Index: 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/consio/console_io.c ================================================================== --- ext/consio/console_io.c +++ ext/consio/console_io.c @@ -27,13 +27,10 @@ # include "sqlite3.h" #endif #ifndef HAVE_CONSOLE_IO_H # include "console_io.h" #endif -#if defined(_MSC_VER) -# pragma warning(disable : 4204) -#endif #ifndef SQLITE_CIO_NO_TRANSLATE # if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT # ifndef SHELL_NO_SYSINC # include @@ -128,14 +125,10 @@ ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); return ppst->reachesConsole; # endif } -# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING -# define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4) -# endif - # if CIO_WIN_WC_XLATE /* Define console modes for use with the Windows Console API. */ # define SHELL_CONI_MODE \ (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) @@ -682,10 +675,6 @@ } # endif } #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ -#if defined(_MSC_VER) -# pragma warning(default : 4204) -#endif - #undef SHELL_INVALID_FILE_PTR Index: ext/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.c ================================================================== --- ext/fts3/fts3.c +++ ext/fts3/fts3.c @@ -4012,28 +4012,26 @@ const char *zTabname, /* Name of the pVTab table */ int isQuick, /* True if this is a quick_check */ char **pzErr /* Write error message here */ ){ Fts3Table *p = (Fts3Table*)pVtab; - int rc = SQLITE_OK; + int rc; int bOk = 0; UNUSED_PARAMETER(isQuick); rc = sqlite3Fts3IntegrityCheck(p, &bOk); - assert( rc!=SQLITE_CORRUPT_VTAB ); - if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ + assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 ); + if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" " FTS%d table %s.%s: %s", p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); - if( *pzErr ) rc = SQLITE_OK; - }else if( rc==SQLITE_OK && bOk==0 ){ + }else if( bOk==0 ){ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s", p->bFts4 ? 4 : 3, zSchema, zTabname); - if( *pzErr==0 ) rc = SQLITE_NOMEM; } sqlite3Fts3SegmentsClose(p); - return rc; + return SQLITE_OK; } static const sqlite3_module fts3Module = { Index: ext/fts3/fts3_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/fts3/fts3_write.c ================================================================== --- ext/fts3/fts3_write.c +++ ext/fts3/fts3_write.c @@ -5370,16 +5370,11 @@ } sqlite3_finalize(pStmt); } - if( rc==SQLITE_CORRUPT_VTAB ){ - rc = SQLITE_OK; - *pbOk = 0; - }else{ - *pbOk = (rc==SQLITE_OK && cksum1==cksum2); - } + *pbOk = (rc==SQLITE_OK && cksum1==cksum2); return rc; } /* ** Run the integrity-check. If no error occurs and the current contents of Index: ext/fts5/fts5.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_index.c ================================================================== --- ext/fts5/fts5_index.c +++ ext/fts5/fts5_index.c @@ -6835,30 +6835,27 @@ ** a rowid of iFrom or greater. */ static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ int ii; Fts5TokenDataIter *pT = pIter->pTokenDataIter; - Fts5Index *pIndex = pIter->pIndex; for(ii=0; iinIter; ii++){ Fts5Iter *p = pT->apIter[ii]; if( p->base.bEof==0 && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowidpIndex, p, bFrom, iFrom); while( bFrom && p->base.bEof==0 && p->base.iRowidrc==SQLITE_OK + && p->pIndex->rc==SQLITE_OK ){ - fts5MultiIterNext(pIndex, p, 0, 0); + fts5MultiIterNext(p->pIndex, p, 0, 0); } } } - if( pIndex->rc==SQLITE_OK ){ - fts5IterSetOutputsTokendata(pIter); - } + fts5IterSetOutputsTokendata(pIter); } /* ** If the segment-iterator passed as the first argument is at EOF, then ** set pIter->term to a copy of buffer pTerm. Index: ext/fts5/fts5_main.c ================================================================== --- ext/fts5/fts5_main.c +++ ext/fts5/fts5_main.c @@ -2977,19 +2977,18 @@ UNUSED_PARAM(isQuick); rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s", zSchema, zTabname); - rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM; }else if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" " FTS5 table %s.%s: %s", zSchema, zTabname, sqlite3_errstr(rc)); } sqlite3Fts5IndexCloseReader(pTab->p.pIndex); - return rc; + return SQLITE_OK; } static int fts5Init(sqlite3 *db){ static const sqlite3_module fts5Mod = { /* iVersion */ 4, Index: ext/fts5/fts5_tcl.c ================================================================== --- ext/fts5/fts5_tcl.c +++ ext/fts5/fts5_tcl.c @@ -1167,11 +1167,11 @@ /* ** Delete the OriginTextCtx object indicated by the only argument. */ static void f5tOrigintextTokenizerDelete(void *pCtx){ OriginTextCtx *p = (OriginTextCtx*)pCtx; - ckfree((char*)p); + ckfree(p); } static int f5tOrigintextCreate( void *pCtx, const char **azArg, Index: ext/fts5/test/fts5fault8.test ================================================================== --- ext/fts5/test/fts5fault8.test +++ ext/fts5/test/fts5fault8.test @@ -54,10 +54,11 @@ faultsim_test_result {0 {1 3}} {1 SQLITE_NOMEM} } } } ;# foreach_detail_mode... + do_execsql_test 4.0 { CREATE VIRTUAL TABLE x2 USING fts5(a); INSERT INTO x2(x2, rank) VALUES('crisismerge', 2); INSERT INTO x2(x2, rank) VALUES('pgsz', 32); @@ -77,20 +78,7 @@ execsql { INSERT INTO x2(x2) VALUES('optimize') } } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} } -set TMPDBERROR {1 {unable to open a temporary database file for storing temporary tables}} - -do_faultsim_test 5 -faults oom-t* -prep { - faultsim_restore_and_reopen - execsql { PRAGMA temp_store = memory } -} -body { - execsql { PRAGMA integrity_check } -} -test { - if {[string match {*error code=7*} $testresult]==0} { - faultsim_test_result {0 ok} {1 SQLITE_NOMEM} $::TMPDBERROR - } -} - finish_test Index: ext/fts5/test/fts5faultH.test ================================================================== --- ext/fts5/test/fts5faultH.test +++ ext/fts5/test/fts5faultH.test @@ -125,26 +125,17 @@ INSERT INTO t1(rowid, x) VALUES(34, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(35, 'aaa bbb BBB'); COMMIT; } -do_faultsim_test 3.1 -faults oom* -prep { +do_faultsim_test 3 -faults oom* -prep { } -body { execsql { SELECT rowid FROM t1('BBB AND AAA'); } } -test { faultsim_integrity_check faultsim_test_result {0 {10 35}} } -do_faultsim_test 3.2 -faults oom* -prep { -} -body { - execsql { - SELECT count(*) FROM t1('BBB'); - } -} -test { - faultsim_integrity_check - faultsim_test_result {0 27} -} finish_test 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; i -#include - -#include -#include - -/* -** nKeyVal: -** The number of values that make up the 'key' for the current pCheck -** statement. -** -** rc: -** Error code returned by most recent sqlite3_intck_step() or -** sqlite3_intck_unlock() call. This is set to SQLITE_DONE when -** the integrity-check operation is finished. -** -** zErr: -** If the object has entered the error state, this is the error message. -** Is freed using sqlite3_free() when the object is deleted. -** -** zTestSql: -** The value returned by the most recent call to sqlite3_intck_testsql(). -** Each call to testsql() frees the previous zTestSql value (using -** sqlite3_free()) and replaces it with the new value it will return. -*/ -struct sqlite3_intck { - sqlite3 *db; - const char *zDb; /* Copy of zDb parameter to _open() */ - char *zObj; /* Current object. Or NULL. */ - - sqlite3_stmt *pCheck; /* Current check statement */ - char *zKey; - int nKeyVal; - - char *zMessage; - int bCorruptSchema; - - int rc; /* Error code */ - char *zErr; /* Error message */ - char *zTestSql; /* Returned by sqlite3_intck_test_sql() */ -}; - - -/* -** Some error has occurred while using database p->db. Save the error message -** and error code currently held by the database handle in p->rc and p->zErr. -*/ -static void intckSaveErrmsg(sqlite3_intck *p){ - p->rc = sqlite3_errcode(p->db); - sqlite3_free(p->zErr); - p->zErr = sqlite3_mprintf("%s", sqlite3_errmsg(p->db)); -} - -/* -** If the handle passed as the first argument is already in the error state, -** then this function is a no-op (returns NULL immediately). Otherwise, if an -** error occurs within this function, it leaves an error in said handle. -** -** Otherwise, this function attempts to prepare SQL statement zSql and -** return the resulting statement handle to the user. -*/ -static sqlite3_stmt *intckPrepare(sqlite3_intck *p, const char *zSql){ - sqlite3_stmt *pRet = 0; - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3_prepare_v2(p->db, zSql, -1, &pRet, 0); - if( p->rc!=SQLITE_OK ){ - intckSaveErrmsg(p); - assert( pRet==0 ); - } - } - return pRet; -} - -/* -** If the handle passed as the first argument is already in the error state, -** then this function is a no-op (returns NULL immediately). Otherwise, if an -** error occurs within this function, it leaves an error in said handle. -** -** Otherwise, this function treats argument zFmt as a printf() style format -** string. It formats it according to the trailing arguments and then -** attempts to prepare the results and return the resulting prepared -** statement. -*/ -static sqlite3_stmt *intckPrepareFmt(sqlite3_intck *p, const char *zFmt, ...){ - sqlite3_stmt *pRet = 0; - va_list ap; - char *zSql = 0; - va_start(ap, zFmt); - zSql = sqlite3_vmprintf(zFmt, ap); - if( p->rc==SQLITE_OK && zSql==0 ){ - p->rc = SQLITE_NOMEM; - } - pRet = intckPrepare(p, zSql); - sqlite3_free(zSql); - va_end(ap); - return pRet; -} - -/* -** Finalize SQL statement pStmt. If an error occurs and the handle passed -** as the first argument does not already contain an error, store the -** error in the handle. -*/ -static void intckFinalize(sqlite3_intck *p, sqlite3_stmt *pStmt){ - int rc = sqlite3_finalize(pStmt); - if( p->rc==SQLITE_OK && rc!=SQLITE_OK ){ - intckSaveErrmsg(p); - } -} - -/* -** If there is already an error in handle p, return it. Otherwise, call -** sqlite3_step() on the statement handle and return that value. -*/ -static int intckStep(sqlite3_intck *p, sqlite3_stmt *pStmt){ - if( p->rc ) return p->rc; - return sqlite3_step(pStmt); -} - -/* -** Execute SQL statement zSql. There is no way to obtain any results -** returned by the statement. This function uses the sqlite3_intck error -** code convention. -*/ -static void intckExec(sqlite3_intck *p, const char *zSql){ - sqlite3_stmt *pStmt = 0; - pStmt = intckPrepare(p, zSql); - intckStep(p, pStmt); - intckFinalize(p, pStmt); -} - -/* -** A wrapper around sqlite3_mprintf() that uses the sqlite3_intck error -** code convention. -*/ -static char *intckMprintf(sqlite3_intck *p, const char *zFmt, ...){ - va_list ap; - char *zRet = 0; - va_start(ap, zFmt); - zRet = sqlite3_vmprintf(zFmt, ap); - if( p->rc==SQLITE_OK ){ - if( zRet==0 ){ - p->rc = SQLITE_NOMEM; - } - }else{ - sqlite3_free(zRet); - zRet = 0; - } - return zRet; -} - -/* -** This is used by sqlite3_intck_unlock() to save the vector key value -** required to restart the current pCheck query as a nul-terminated string -** in p->zKey. -*/ -static void intckSaveKey(sqlite3_intck *p){ - int ii; - char *zSql = 0; - sqlite3_stmt *pStmt = 0; - sqlite3_stmt *pXinfo = 0; - const char *zDir = 0; - - assert( p->pCheck ); - assert( p->zKey==0 ); - - pXinfo = intckPrepareFmt(p, - "SELECT group_concat(desc, '') FROM %Q.sqlite_schema s, " - "pragma_index_xinfo(%Q, %Q) " - "WHERE s.type='index' AND s.name=%Q", - p->zDb, p->zObj, p->zDb, p->zObj - ); - if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXinfo) ){ - zDir = (const char*)sqlite3_column_text(pXinfo, 0); - } - - if( zDir==0 ){ - /* Object is a table, not an index. This is the easy case,as there are - ** no DESC columns or NULL values in a primary key. */ - const char *zSep = "SELECT '(' || "; - for(ii=0; iinKeyVal; ii++){ - zSql = intckMprintf(p, "%z%squote(?)", zSql, zSep); - zSep = " || ', ' || "; - } - zSql = intckMprintf(p, "%z || ')'", zSql); - }else{ - - /* Object is an index. */ - assert( p->nKeyVal>1 ); - for(ii=p->nKeyVal; ii>0; ii--){ - int bLastIsDesc = zDir[ii-1]=='1'; - int bLastIsNull = sqlite3_column_type(p->pCheck, ii)==SQLITE_NULL; - const char *zLast = sqlite3_column_name(p->pCheck, ii); - char *zLhs = 0; - char *zRhs = 0; - char *zWhere = 0; - - if( bLastIsNull ){ - if( bLastIsDesc ) continue; - zWhere = intckMprintf(p, "'%s IS NOT NULL'", zLast); - }else{ - const char *zOp = bLastIsDesc ? "<" : ">"; - zWhere = intckMprintf(p, "'%s %s ' || quote(?%d)", zLast, zOp, ii); - } - - if( ii>1 ){ - const char *zLhsSep = ""; - const char *zRhsSep = ""; - int jj; - for(jj=0; jjpCheck,jj+1); - zLhs = intckMprintf(p, "%z%s%s", zLhs, zLhsSep, zAlias); - zRhs = intckMprintf(p, "%z%squote(?%d)", zRhs, zRhsSep, jj+1); - zLhsSep = ","; - zRhsSep = " || ',' || "; - } - - zWhere = intckMprintf(p, - "'(%z) IS (' || %z || ') AND ' || %z", - zLhs, zRhs, zWhere); - } - zWhere = intckMprintf(p, "'WHERE ' || %z", zWhere); - - zSql = intckMprintf(p, "%z%s(quote( %z ) )", - zSql, - (zSql==0 ? "VALUES" : ",\n "), - zWhere - ); - } - zSql = intckMprintf(p, - "WITH wc(q) AS (\n%z\n)" - "SELECT 'VALUES' || group_concat('(' || q || ')', ',\n ') FROM wc" - , zSql - ); - } - - pStmt = intckPrepare(p, zSql); - if( p->rc==SQLITE_OK ){ - for(ii=0; iinKeyVal; ii++){ - sqlite3_bind_value(pStmt, ii+1, sqlite3_column_value(p->pCheck, ii+1)); - } - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - p->zKey = intckMprintf(p,"%s",(const char*)sqlite3_column_text(pStmt, 0)); - } - intckFinalize(p, pStmt); - } - - sqlite3_free(zSql); - intckFinalize(p, pXinfo); -} - -/* -** Find the next database object (table or index) to check. If successful, -** set sqlite3_intck.zObj to point to a nul-terminated buffer containing -** the object's name before returning. -*/ -static void intckFindObject(sqlite3_intck *p){ - sqlite3_stmt *pStmt = 0; - char *zPrev = p->zObj; - p->zObj = 0; - - assert( p->rc==SQLITE_OK ); - assert( p->pCheck==0 ); - - pStmt = intckPrepareFmt(p, - "WITH tables(table_name) AS (" - " SELECT name" - " FROM %Q.sqlite_schema WHERE (type='table' OR type='index') AND rootpage" - " UNION ALL " - " SELECT 'sqlite_schema'" - ")" - "SELECT table_name FROM tables " - "WHERE ?1 IS NULL OR table_name%s?1 " - "ORDER BY 1" - , p->zDb, (p->zKey ? ">=" : ">") - ); - - if( p->rc==SQLITE_OK ){ - sqlite3_bind_text(pStmt, 1, zPrev, -1, SQLITE_TRANSIENT); - if( sqlite3_step(pStmt)==SQLITE_ROW ){ - p->zObj = intckMprintf(p,"%s",(const char*)sqlite3_column_text(pStmt, 0)); - } - } - intckFinalize(p, pStmt); - - /* If this is a new object, ensure the previous key value is cleared. */ - if( sqlite3_stricmp(p->zObj, zPrev) ){ - sqlite3_free(p->zKey); - p->zKey = 0; - } - - sqlite3_free(zPrev); -} - -/* -** Return the size in bytes of the first token in nul-terminated buffer z. -** For the purposes of this call, a token is either: -** -** * a quoted SQL string, -* * a contiguous series of ascii alphabet characters, or -* * any other single byte. -*/ -static int intckGetToken(const char *z){ - char c = z[0]; - int iRet = 1; - if( c=='\'' || c=='"' || c=='`' ){ - while( 1 ){ - if( z[iRet]==c ){ - iRet++; - if( z[iRet]!=c ) break; - } - iRet++; - } - } - else if( c=='[' ){ - while( z[iRet++]!=']' && z[iRet] ); - } - else if( (c>='A' && c<='Z') || (c>='a' && c<='z') ){ - while( (z[iRet]>='A' && z[iRet]<='Z') || (z[iRet]>='a' && z[iRet]<='z') ){ - iRet++; - } - } - - return iRet; -} - -/* -** Return true if argument c is an ascii whitespace character. -*/ -static int intckIsSpace(char c){ - return (c==' ' || c=='\t' || c=='\n' || c=='\r'); -} - -/* -** Argument z points to the text of a CREATE INDEX statement. This function -** identifies the part of the text that contains either the index WHERE -** clause (if iCol<0) or the iCol'th column of the index. -** -** If (iCol<0), the identified fragment does not include the "WHERE" keyword, -** only the expression that follows it. If (iCol>=0) then the identified -** fragment does not include any trailing sort-order keywords - "ASC" or -** "DESC". -** -** If the CREATE INDEX statement does not contain the requested field or -** clause, NULL is returned and (*pnByte) is set to 0. Otherwise, a pointer to -** the identified fragment is returned and output parameter (*pnByte) set -** to its size in bytes. -*/ -static const char *intckParseCreateIndex(const char *z, int iCol, int *pnByte){ - int iOff = 0; - int iThisCol = 0; - int iStart = 0; - int nOpen = 0; - - const char *zRet = 0; - int nRet = 0; - - int iEndOfCol = 0; - - /* Skip forward until the first "(" token */ - while( z[iOff]!='(' ){ - iOff += intckGetToken(&z[iOff]); - if( z[iOff]=='\0' ) return 0; - } - assert( z[iOff]=='(' ); - - nOpen = 1; - iOff++; - iStart = iOff; - while( z[iOff] ){ - const char *zToken = &z[iOff]; - int nToken = 0; - - /* Check if this is the end of the current column - either a "," or ")" - ** when nOpen==1. */ - if( nOpen==1 ){ - if( z[iOff]==',' || z[iOff]==')' ){ - if( iCol==iThisCol ){ - int iEnd = iEndOfCol ? iEndOfCol : iOff; - nRet = (iEnd - iStart); - zRet = &z[iStart]; - break; - } - iStart = iOff+1; - while( intckIsSpace(z[iStart]) ) iStart++; - iThisCol++; - } - if( z[iOff]==')' ) break; - } - if( z[iOff]=='(' ) nOpen++; - if( z[iOff]==')' ) nOpen--; - nToken = intckGetToken(zToken); - - if( (nToken==3 && 0==sqlite3_strnicmp(zToken, "ASC", nToken)) - || (nToken==4 && 0==sqlite3_strnicmp(zToken, "DESC", nToken)) - ){ - iEndOfCol = iOff; - }else if( 0==intckIsSpace(zToken[0]) ){ - iEndOfCol = 0; - } - - iOff += nToken; - } - - /* iStart is now the byte offset of 1 byte passed the final ')' in the - ** CREATE INDEX statement. Try to find a WHERE clause to return. */ - while( zRet==0 && z[iOff] ){ - int n = intckGetToken(&z[iOff]); - if( n==5 && 0==sqlite3_strnicmp(&z[iOff], "where", 5) ){ - zRet = &z[iOff+5]; - nRet = (int)strlen(zRet); - } - iOff += n; - } - - /* Trim any whitespace from the start and end of the returned string. */ - if( zRet ){ - while( intckIsSpace(zRet[0]) ){ - nRet--; - zRet++; - } - while( nRet>0 && intckIsSpace(zRet[nRet-1]) ) nRet--; - } - - *pnByte = nRet; - return zRet; -} - -/* -** User-defined SQL function wrapper for intckParseCreateIndex(): -** -** SELECT parse_create_index(, ); -*/ -static void intckParseCreateIndexFunc( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - const char *zSql = (const char*)sqlite3_value_text(apVal[0]); - int idx = sqlite3_value_int(apVal[1]); - const char *zRes = 0; - int nRes = 0; - - assert( nVal==2 ); - if( zSql ){ - zRes = intckParseCreateIndex(zSql, idx, &nRes); - } - sqlite3_result_text(pCtx, zRes, nRes, SQLITE_TRANSIENT); -} - -/* -** Return true if sqlite3_intck.db has automatic indexes enabled, false -** otherwise. -*/ -static int intckGetAutoIndex(sqlite3_intck *p){ - int bRet = 0; - sqlite3_stmt *pStmt = 0; - pStmt = intckPrepare(p, "PRAGMA automatic_index"); - if( SQLITE_ROW==intckStep(p, pStmt) ){ - bRet = sqlite3_column_int(pStmt, 0); - } - intckFinalize(p, pStmt); - return bRet; -} - -/* -** Return true if zObj is an index, or false otherwise. -*/ -static int intckIsIndex(sqlite3_intck *p, const char *zObj){ - int bRet = 0; - sqlite3_stmt *pStmt = 0; - pStmt = intckPrepareFmt(p, - "SELECT 1 FROM %Q.sqlite_schema WHERE name=%Q AND type='index'", - p->zDb, zObj - ); - if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - bRet = 1; - } - intckFinalize(p, pStmt); - return bRet; -} - -/* -** Return a pointer to a nul-terminated buffer containing the SQL statement -** used to check database object zObj (a table or index) for corruption. -** If parameter zPrev is not NULL, then it must be a string containing the -** vector key required to restart the check where it left off last time. -** If pnKeyVal is not NULL, then (*pnKeyVal) is set to the number of -** columns in the vector key value for the specified object. -** -** This function uses the sqlite3_intck error code convention. -*/ -static char *intckCheckObjectSql( - sqlite3_intck *p, /* Integrity check object */ - const char *zObj, /* Object (table or index) to scan */ - const char *zPrev, /* Restart key vector, if any */ - int *pnKeyVal /* OUT: Number of key-values for this scan */ -){ - char *zRet = 0; - sqlite3_stmt *pStmt = 0; - int bAutoIndex = 0; - int bIsIndex = 0; - - const char *zCommon = - /* Relation without_rowid also contains just one row. Column "b" is - ** set to true if the table being examined is a WITHOUT ROWID table, - ** or false otherwise. */ - ", without_rowid(b) AS (" - " SELECT EXISTS (" - " SELECT 1 FROM tabname, pragma_index_list(tab, db) AS l" - " WHERE origin='pk' " - " AND NOT EXISTS (SELECT 1 FROM sqlite_schema WHERE name=l.name)" - " )" - ")" - "" - /* Table idx_cols contains 1 row for each column in each index on the - ** table being checked. Columns are: - ** - ** idx_name: Name of the index. - ** idx_ispk: True if this index is the PK of a WITHOUT ROWID table. - ** col_name: Name of indexed column, or NULL for index on expression. - ** col_expr: Indexed expression, including COLLATE clause. - ** col_alias: Alias used for column in 'intck_wrapper' table. - */ - ", idx_cols(idx_name, idx_ispk, col_name, col_expr, col_alias) AS (" - " SELECT l.name, (l.origin=='pk' AND w.b), i.name, COALESCE((" - " SELECT parse_create_index(sql, i.seqno) FROM " - " sqlite_schema WHERE name = l.name" - " ), format('\"%w\"', i.name) || ' COLLATE ' || quote(i.coll))," - " 'c' || row_number() OVER ()" - " FROM " - " tabname t," - " without_rowid w," - " pragma_index_list(t.tab, t.db) l," - " pragma_index_xinfo(l.name) i" - " WHERE i.key" - " UNION ALL" - " SELECT '', 1, '_rowid_', '_rowid_', 'r1' FROM without_rowid WHERE b=0" - ")" - "" - "" - /* - ** For a PK declared as "PRIMARY KEY(a, b) ... WITHOUT ROWID", where - ** the intck_wrapper aliases of "a" and "b" are "c1" and "c2": - ** - ** o_pk: "o.c1, o.c2" - ** i_pk: "i.'a', i.'b'" - ** ... - ** n_pk: 2 - */ - ", tabpk(db, tab, idx, o_pk, i_pk, q_pk, eq_pk, ps_pk, pk_pk, n_pk) AS (" - " WITH pkfields(f, a) AS (" - " SELECT i.col_name, i.col_alias FROM idx_cols i WHERE i.idx_ispk" - " )" - " SELECT t.db, t.tab, t.idx, " - " group_concat(a, ', '), " - " group_concat('i.'||quote(f), ', '), " - " group_concat('quote(o.'||a||')', ' || '','' || '), " - " format('(%s)==(%s)'," - " group_concat('o.'||a, ', '), " - " group_concat(format('\"%w\"', f), ', ')" - " )," - " group_concat('%s', ',')," - " group_concat('quote('||a||')', ', '), " - " count(*)" - " FROM tabname t, pkfields" - ")" - "" - ", idx(name, match_expr, partial, partial_alias, idx_ps, idx_idx) AS (" - " SELECT idx_name," - " format('(%s,%s) IS (%s,%s)', " - " group_concat(i.col_expr, ', '), i_pk," - " group_concat('o.'||i.col_alias, ', '), o_pk" - " ), " - " parse_create_index(" - " (SELECT sql FROM sqlite_schema WHERE name=idx_name), -1" - " )," - " 'cond' || row_number() OVER ()" - " , group_concat('%s', ',')" - " , group_concat('quote('||i.col_alias||')', ', ')" - " FROM tabpk t, " - " without_rowid w," - " idx_cols i" - " WHERE i.idx_ispk==0 " - " GROUP BY idx_name" - ")" - "" - ", wrapper_with(s) AS (" - " SELECT 'intck_wrapper AS (\n SELECT\n ' || (" - " WITH f(a, b) AS (" - " SELECT col_expr, col_alias FROM idx_cols" - " UNION ALL " - " SELECT partial, partial_alias FROM idx WHERE partial IS NOT NULL" - " )" - " SELECT group_concat(format('%s AS %s', a, b), ',\n ') FROM f" - " )" - " || format('\n FROM %Q.%Q ', t.db, t.tab)" - /* If the object being checked is a table, append "NOT INDEXED". - ** Otherwise, append "INDEXED BY ", and then, if the index - ** is a partial index " WHERE ". */ - " || CASE WHEN t.idx IS NULL THEN " - " 'NOT INDEXED'" - " ELSE" - " format('INDEXED BY %Q%s', t.idx, ' WHERE '||i.partial)" - " END" - " || '\n)'" - " FROM tabname t LEFT JOIN idx i ON (i.name=t.idx)" - ")" - "" - ; - - bAutoIndex = intckGetAutoIndex(p); - if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 0"); - - bIsIndex = intckIsIndex(p, zObj); - if( bIsIndex ){ - pStmt = intckPrepareFmt(p, - /* Table idxname contains a single row. The first column, "db", contains - ** the name of the db containing the table (e.g. "main") and the second, - ** "tab", the name of the table itself. */ - "WITH tabname(db, tab, idx) AS (" - " SELECT %Q, (SELECT tbl_name FROM %Q.sqlite_schema WHERE name=%Q), %Q " - ")" - "" - ", whereclause(w_c) AS (%s)" - "" - "%s" /* zCommon */ - "" - ", case_statement(c) AS (" - " SELECT " - " 'CASE WHEN (' || group_concat(col_alias, ', ') || ', 1) IS (\n' " - " || ' SELECT ' || group_concat(col_expr, ', ') || ', 1 FROM '" - " || format('%%Q.%%Q NOT INDEXED WHERE %%s\n', t.db, t.tab, p.eq_pk)" - " || ' )\n THEN NULL\n '" - " || 'ELSE format(''surplus entry ('" - " || group_concat('%%s', ',') || ',' || p.ps_pk" - " || ') in index ' || t.idx || ''', ' " - " || group_concat('quote('||i.col_alias||')', ', ') || ', ' || p.pk_pk" - " || ')'" - " || '\n END AS error_message'" - " FROM tabname t, tabpk p, idx_cols i WHERE i.idx_name=t.idx" - ")" - "" - ", thiskey(k, n) AS (" - " SELECT group_concat(i.col_alias, ', ') || ', ' || p.o_pk, " - " count(*) + p.n_pk " - " FROM tabpk p, idx_cols i WHERE i.idx_name=p.idx" - ")" - "" - ", main_select(m, n) AS (" - " SELECT format(" - " 'WITH %%s\n' ||" - " ', idx_checker AS (\n' ||" - " ' SELECT %%s,\n' ||" - " ' %%s\n' || " - " ' FROM intck_wrapper AS o\n' ||" - " ')\n'," - " ww.s, c, t.k" - " ), t.n" - " FROM case_statement, wrapper_with ww, thiskey t" - ")" - - "SELECT m || " - " group_concat('SELECT * FROM idx_checker ' || w_c, ' UNION ALL '), n" - " FROM " - "main_select, whereclause " - , p->zDb, p->zDb, zObj, zObj - , zPrev ? zPrev : "VALUES('')", zCommon - ); - }else{ - pStmt = intckPrepareFmt(p, - /* Table tabname contains a single row. The first column, "db", contains - ** the name of the db containing the table (e.g. "main") and the second, - ** "tab", the name of the table itself. */ - "WITH tabname(db, tab, idx, prev) AS (SELECT %Q, %Q, NULL, %Q)" - "" - "%s" /* zCommon */ - - /* expr(e) contains one row for each index on table zObj. Value e - ** is set to an expression that evaluates to NULL if the required - ** entry is present in the index, or an error message otherwise. */ - ", expr(e, p) AS (" - " SELECT format('CASE WHEN EXISTS \n" - " (SELECT 1 FROM %%Q.%%Q AS i INDEXED BY %%Q WHERE %%s%%s)\n" - " THEN NULL\n" - " ELSE format(''entry (%%s,%%s) missing from index %%s'', %%s, %%s)\n" - " END\n'" - " , t.db, t.tab, i.name, i.match_expr, ' AND (' || partial || ')'," - " i.idx_ps, t.ps_pk, i.name, i.idx_idx, t.pk_pk)," - " CASE WHEN partial IS NULL THEN NULL ELSE i.partial_alias END" - " FROM tabpk t, idx i" - ")" - - ", numbered(ii, cond, e) AS (" - " SELECT 0, 'n.ii=0', 'NULL'" - " UNION ALL " - " SELECT row_number() OVER ()," - " '(n.ii='||row_number() OVER ()||COALESCE(' AND '||p||')', ')'), e" - " FROM expr" - ")" - - ", counter_with(w) AS (" - " SELECT 'WITH intck_counter(ii) AS (\n ' || " - " group_concat('SELECT '||ii, ' UNION ALL\n ') " - " || '\n)' FROM numbered" - ")" - "" - ", case_statement(c) AS (" - " SELECT 'CASE ' || " - " group_concat(format('\n WHEN %%s THEN (%%s)', cond, e), '') ||" - " '\nEND AS error_message'" - " FROM numbered" - ")" - "" - - /* This table contains a single row consisting of a single value - - ** the text of an SQL expression that may be used by the main SQL - ** statement to output an SQL literal that can be used to resume - ** the scan if it is suspended. e.g. for a rowid table, an expression - ** like: - ** - ** format('(%d,%d)', _rowid_, n.ii) - */ - ", thiskey(k, n) AS (" - " SELECT o_pk || ', ii', n_pk+1 FROM tabpk" - ")" - "" - ", whereclause(w_c) AS (" - " SELECT CASE WHEN prev!='' THEN " - " '\nWHERE (' || o_pk ||', n.ii) > ' || prev" - " ELSE ''" - " END" - " FROM tabpk, tabname" - ")" - "" - ", main_select(m, n) AS (" - " SELECT format(" - " '%%s, %%s\nSELECT %%s,\n%%s\nFROM intck_wrapper AS o" - ", intck_counter AS n%%s\nORDER BY %%s', " - " w, ww.s, c, thiskey.k, whereclause.w_c, t.o_pk" - " ), thiskey.n" - " FROM case_statement, tabpk t, counter_with, " - " wrapper_with ww, thiskey, whereclause" - ")" - - "SELECT m, n FROM main_select", - p->zDb, zObj, zPrev, zCommon - ); - } - - while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - zRet = intckMprintf(p, "%s", (const char*)sqlite3_column_text(pStmt, 0)); - if( pnKeyVal ){ - *pnKeyVal = sqlite3_column_int(pStmt, 1); - } - } - intckFinalize(p, pStmt); - - if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 1"); - return zRet; -} - -/* -** Open a new integrity-check object. -*/ -int sqlite3_intck_open( - sqlite3 *db, /* Database handle to operate on */ - const char *zDbArg, /* "main", "temp" etc. */ - sqlite3_intck **ppOut /* OUT: New integrity-check handle */ -){ - sqlite3_intck *pNew = 0; - int rc = SQLITE_OK; - const char *zDb = zDbArg ? zDbArg : "main"; - int nDb = (int)strlen(zDb); - - pNew = (sqlite3_intck*)sqlite3_malloc(sizeof(*pNew) + nDb + 1); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pNew, 0, sizeof(*pNew)); - pNew->db = db; - pNew->zDb = (const char*)&pNew[1]; - memcpy(&pNew[1], zDb, nDb+1); - rc = sqlite3_create_function(db, "parse_create_index", - 2, SQLITE_UTF8, 0, intckParseCreateIndexFunc, 0, 0 - ); - if( rc!=SQLITE_OK ){ - sqlite3_intck_close(pNew); - pNew = 0; - } - } - - *ppOut = pNew; - return rc; -} - -/* -** Free the integrity-check object. -*/ -void sqlite3_intck_close(sqlite3_intck *p){ - if( p ){ - sqlite3_finalize(p->pCheck); - sqlite3_create_function( - p->db, "parse_create_index", 1, SQLITE_UTF8, 0, 0, 0, 0 - ); - sqlite3_free(p->zObj); - sqlite3_free(p->zKey); - sqlite3_free(p->zTestSql); - sqlite3_free(p->zErr); - sqlite3_free(p->zMessage); - sqlite3_free(p); - } -} - -/* -** Step the integrity-check object. -*/ -int sqlite3_intck_step(sqlite3_intck *p){ - if( p->rc==SQLITE_OK ){ - - if( p->zMessage ){ - sqlite3_free(p->zMessage); - p->zMessage = 0; - } - - if( p->bCorruptSchema ){ - p->rc = SQLITE_DONE; - }else - if( p->pCheck==0 ){ - intckFindObject(p); - if( p->rc==SQLITE_OK ){ - if( p->zObj ){ - char *zSql = 0; - zSql = intckCheckObjectSql(p, p->zObj, p->zKey, &p->nKeyVal); - p->pCheck = intckPrepare(p, zSql); - sqlite3_free(zSql); - sqlite3_free(p->zKey); - p->zKey = 0; - }else{ - p->rc = SQLITE_DONE; - } - }else if( p->rc==SQLITE_CORRUPT ){ - p->rc = SQLITE_OK; - p->zMessage = intckMprintf(p, "%s", - "corruption found while reading database schema" - ); - p->bCorruptSchema = 1; - } - } - - if( p->pCheck ){ - assert( p->rc==SQLITE_OK ); - if( sqlite3_step(p->pCheck)==SQLITE_ROW ){ - /* Normal case, do nothing. */ - }else{ - intckFinalize(p, p->pCheck); - p->pCheck = 0; - p->nKeyVal = 0; - if( p->rc==SQLITE_CORRUPT ){ - p->rc = SQLITE_OK; - p->zMessage = intckMprintf(p, - "corruption found while scanning database object %s", p->zObj - ); - } - } - } - } - - return p->rc; -} - -/* -** Return a message describing the corruption encountered by the most recent -** call to sqlite3_intck_step(), or NULL if no corruption was encountered. -*/ -const char *sqlite3_intck_message(sqlite3_intck *p){ - assert( p->pCheck==0 || p->zMessage==0 ); - if( p->zMessage ){ - return p->zMessage; - } - if( p->pCheck ){ - return (const char*)sqlite3_column_text(p->pCheck, 0); - } - return 0; -} - -/* -** Return the error code and message. -*/ -int sqlite3_intck_error(sqlite3_intck *p, const char **pzErr){ - if( pzErr ) *pzErr = p->zErr; - return (p->rc==SQLITE_DONE ? SQLITE_OK : p->rc); -} - -/* -** Close any read transaction the integrity-check object is holding open -** on the database. -*/ -int sqlite3_intck_unlock(sqlite3_intck *p){ - if( p->rc==SQLITE_OK && p->pCheck ){ - assert( p->zKey==0 && p->nKeyVal>0 ); - intckSaveKey(p); - intckFinalize(p, p->pCheck); - p->pCheck = 0; - } - return p->rc; -} - -/* -** Return the SQL statement used to check object zObj. Or, if zObj is -** NULL, the current SQL statement. -*/ -const char *sqlite3_intck_test_sql(sqlite3_intck *p, const char *zObj){ - sqlite3_free(p->zTestSql); - if( zObj ){ - p->zTestSql = intckCheckObjectSql(p, zObj, 0, 0); - }else{ - if( p->zObj ){ - p->zTestSql = intckCheckObjectSql(p, p->zObj, p->zKey, 0); - }else{ - sqlite3_free(p->zTestSql); - p->zTestSql = 0; - } - } - return p->zTestSql; -} DELETED ext/intck/sqlite3intck.h Index: ext/intck/sqlite3intck.h ================================================================== --- ext/intck/sqlite3intck.h +++ /dev/null @@ -1,171 +0,0 @@ -/* -** 2024-02-08 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -*/ - -/* -** Incremental Integrity-Check Extension -** ------------------------------------- -** -** This module contains code to check whether or not an SQLite database -** is well-formed or corrupt. This is the same task as performed by SQLite's -** built-in "PRAGMA integrity_check" command. This module differs from -** "PRAGMA integrity_check" in that: -** -** + It is less thorough - this module does not detect certain types -** of corruption that are detected by the PRAGMA command. However, -** it does detect all kinds of corruption that are likely to cause -** errors in SQLite applications. -** -** + It is slower. Sometimes up to three times slower. -** -** + It allows integrity-check operations to be split into multiple -** transactions, so that the database does not need to be read-locked -** for the duration of the integrity-check. -** -** One way to use the API to run integrity-check on the "main" database -** of handle db is: -** -** int rc = SQLITE_OK; -** sqlite3_intck *p = 0; -** -** sqlite3_intck_open(db, "main", &p); -** while( SQLITE_OK==sqlite3_intck_step(p) ){ -** const char *zMsg = sqlite3_intck_message(p); -** if( zMsg ) printf("corruption: %s\n", zMsg); -** } -** rc = sqlite3_intck_error(p, &zErr); -** if( rc!=SQLITE_OK ){ -** printf("error occured (rc=%d), (errmsg=%s)\n", rc, zErr); -** } -** sqlite3_intck_close(p); -** -** Usually, the sqlite3_intck object opens a read transaction within the -** first call to sqlite3_intck_step() and holds it open until the -** integrity-check is complete. However, if sqlite3_intck_unlock() is -** called, the read transaction is ended and a new read transaction opened -** by the subsequent call to sqlite3_intck_step(). -*/ - -#ifndef _SQLITE_INTCK_H -#define _SQLITE_INTCK_H - -#include "sqlite3.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* -** An ongoing incremental integrity-check operation is represented by an -** opaque pointer of the following type. -*/ -typedef struct sqlite3_intck sqlite3_intck; - -/* -** Open a new incremental integrity-check object. If successful, populate -** output variable (*ppOut) with the new object handle and return SQLITE_OK. -** Or, if an error occurs, set (*ppOut) to NULL and return an SQLite error -** code (e.g. SQLITE_NOMEM). -** -** The integrity-check will be conducted on database zDb (which must be "main", -** "temp", or the name of an attached database) of database handle db. Once -** this function has been called successfully, the caller should not use -** database handle db until the integrity-check object has been destroyed -** using sqlite3_intck_close(). -*/ -int sqlite3_intck_open( - sqlite3 *db, /* Database handle */ - const char *zDb, /* Database name ("main", "temp" etc.) */ - sqlite3_intck **ppOut /* OUT: New sqlite3_intck handle */ -); - -/* -** Close and release all resources associated with a handle opened by an -** earlier call to sqlite3_intck_open(). The results of using an -** integrity-check handle after it has been passed to this function are -** undefined. -*/ -void sqlite3_intck_close(sqlite3_intck *pCk); - -/* -** Do the next step of the integrity-check operation specified by the handle -** passed as the only argument. This function returns SQLITE_DONE if the -** integrity-check operation is finished, or an SQLite error code if -** an error occurs, or SQLITE_OK if no error occurs but the integrity-check -** is not finished. It is not considered an error if database corruption -** is encountered. -** -** Following a successful call to sqlite3_intck_step() (one that returns -** SQLITE_OK), sqlite3_intck_message() returns a non-NULL value if -** corruption was detected in the db. -** -** If an error occurs and a value other than SQLITE_OK or SQLITE_DONE is -** returned, then the integrity-check handle is placed in an error state. -** In this state all subsequent calls to sqlite3_intck_step() or -** sqlite3_intck_unlock() will immediately return the same error. The -** sqlite3_intck_error() method may be used to obtain an English language -** error message in this case. -*/ -int sqlite3_intck_step(sqlite3_intck *pCk); - -/* -** If the previous call to sqlite3_intck_step() encountered corruption -** within the database, then this function returns a pointer to a buffer -** containing a nul-terminated string describing the corruption in -** English. If the previous call to sqlite3_intck_step() did not encounter -** corruption, or if there was no previous call, this function returns -** NULL. -*/ -const char *sqlite3_intck_message(sqlite3_intck *pCk); - -/* -** Close any read-transaction opened by an earlier call to -** sqlite3_intck_step(). Any subsequent call to sqlite3_intck_step() will -** open a new transaction. Return SQLITE_OK if successful, or an SQLite error -** code otherwise. -** -** If an error occurs, then the integrity-check handle is placed in an error -** state. In this state all subsequent calls to sqlite3_intck_step() or -** sqlite3_intck_unlock() will immediately return the same error. The -** sqlite3_intck_error() method may be used to obtain an English language -** error message in this case. -*/ -int sqlite3_intck_unlock(sqlite3_intck *pCk); - -/* -** If an error has occurred in an earlier call to sqlite3_intck_step() -** or sqlite3_intck_unlock(), then this method returns the associated -** SQLite error code. Additionally, if pzErr is not NULL, then (*pzErr) -** may be set to point to a nul-terminated string containing an English -** language error message. Or, if no error message is available, to -** NULL. -** -** If no error has occurred within sqlite3_intck_step() or -** sqlite_intck_unlock() calls on the handle passed as the first argument, -** then SQLITE_OK is returned and (*pzErr) set to NULL. -*/ -int sqlite3_intck_error(sqlite3_intck *pCk, const char **pzErr); - -/* -** This API is used for testing only. It returns the full-text of an SQL -** statement used to test object zObj, which may be a table or index. -** The returned buffer is valid until the next call to either this function -** or sqlite3_intck_close() on the same sqlite3_intck handle. -*/ -const char *sqlite3_intck_test_sql(sqlite3_intck *pCk, const char *zObj); - - -#ifdef __cplusplus -} /* end of the 'extern "C"' block */ -#endif - -#endif /* ifndef _SQLITE_INTCK_H */ DELETED ext/intck/test_intck.c Index: ext/intck/test_intck.c ================================================================== --- ext/intck/test_intck.c +++ /dev/null @@ -1,238 +0,0 @@ -/* -** 2010 August 28 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Code for testing all sorts of SQLite interfaces. This code -** is not included in the SQLite library. -*/ - -#include "sqlite3.h" -#include "sqlite3intck.h" - -#if defined(INCLUDE_SQLITE_TCL_H) -# include "sqlite_tcl.h" -#else -# include "tcl.h" -#endif - -#include -#include - -/* In test1.c */ -int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); -const char *sqlite3ErrName(int); - -typedef struct TestIntck TestIntck; -struct TestIntck { - sqlite3_intck *intck; -}; - -static int testIntckCmd( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - struct Subcmd { - const char *zName; - int nArg; - const char *zExpect; - } aCmd[] = { - {"close", 0, ""}, /* 0 */ - {"step", 0, ""}, /* 1 */ - {"message", 0, ""}, /* 2 */ - {"error", 0, ""}, /* 3 */ - {"unlock", 0, ""}, /* 4 */ - {"test_sql", 1, ""}, /* 5 */ - {0 , 0} - }; - int rc = TCL_OK; - int iIdx = -1; - TestIntck *p = (TestIntck*)clientData; - - if( objc<2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); - return TCL_ERROR; - } - - rc = Tcl_GetIndexFromObjStruct( - interp, objv[1], aCmd, sizeof(aCmd[0]), "SUB-COMMAND", 0, &iIdx - ); - if( rc ) return rc; - - if( objc!=2+aCmd[iIdx].nArg ){ - Tcl_WrongNumArgs(interp, 2, objv, aCmd[iIdx].zExpect); - return TCL_ERROR; - } - - switch( iIdx ){ - case 0: assert( 0==strcmp("close", aCmd[iIdx].zName) ); { - Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0)); - break; - } - - case 1: assert( 0==strcmp("step", aCmd[iIdx].zName) ); { - rc = sqlite3_intck_step(p->intck); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); - break; - } - - case 2: assert( 0==strcmp("message", aCmd[iIdx].zName) ); { - const char *z = sqlite3_intck_message(p->intck); - Tcl_SetObjResult(interp, Tcl_NewStringObj(z ? z : "", -1)); - break; - } - - case 3: assert( 0==strcmp("error", aCmd[iIdx].zName) ); { - const char *zErr = 0; - rc = sqlite3_intck_error(p->intck, 0); - Tcl_Obj *pRes = Tcl_NewObj(); - Tcl_ListObjAppendElement( - interp, pRes, Tcl_NewStringObj(sqlite3ErrName(rc), -1) - ); - sqlite3_intck_error(p->intck, &zErr); - Tcl_ListObjAppendElement( - interp, pRes, Tcl_NewStringObj(zErr ? zErr : 0, -1) - ); - Tcl_SetObjResult(interp, pRes); - break; - } - - case 4: assert( 0==strcmp("unlock", aCmd[iIdx].zName) ); { - rc = sqlite3_intck_unlock(p->intck); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); - break; - } - - case 5: assert( 0==strcmp("test_sql", aCmd[iIdx].zName) ); { - const char *zObj = Tcl_GetString(objv[2]); - const char *zSql = sqlite3_intck_test_sql(p->intck, zObj[0] ? zObj : 0); - Tcl_SetObjResult(interp, Tcl_NewStringObj(zSql, -1)); - break; - } - } - - return TCL_OK; -} - -/* -** Destructor for commands created by test_sqlite3_intck(). -*/ -static void testIntckFree(void *clientData){ - TestIntck *p = (TestIntck*)clientData; - sqlite3_intck_close(p->intck); - ckfree(p); -} - -/* -** tclcmd: sqlite3_intck DB DBNAME -*/ -static int test_sqlite3_intck( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - char zName[64]; - int iName = 0; - Tcl_CmdInfo info; - TestIntck *p = 0; - sqlite3 *db = 0; - const char *zDb = 0; - int rc = SQLITE_OK; - - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME"); - return TCL_ERROR; - } - - p = (TestIntck*)ckalloc(sizeof(TestIntck)); - memset(p, 0, sizeof(TestIntck)); - - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ - return TCL_ERROR; - } - zDb = Tcl_GetString(objv[2]); - if( zDb[0]=='\0' ) zDb = 0; - - rc = sqlite3_intck_open(db, zDb, &p->intck); - if( rc!=SQLITE_OK ){ - ckfree(p); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errstr(rc), -1)); - return TCL_ERROR; - } - - do { - sprintf(zName, "intck%d", iName++); - }while( Tcl_GetCommandInfo(interp, zName, &info)!=0 ); - Tcl_CreateObjCommand(interp, zName, testIntckCmd, (void*)p, testIntckFree); - Tcl_SetObjResult(interp, Tcl_NewStringObj(zName, -1)); - - return TCL_OK; -} - -/* -** tclcmd: test_do_intck DB DBNAME -*/ -static int test_do_intck( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite3 *db = 0; - const char *zDb = 0; - int rc = SQLITE_OK; - sqlite3_intck *pCk = 0; - Tcl_Obj *pRet = 0; - const char *zErr = 0; - - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME"); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ - return TCL_ERROR; - } - zDb = Tcl_GetString(objv[2]); - - pRet = Tcl_NewObj(); - Tcl_IncrRefCount(pRet); - - rc = sqlite3_intck_open(db, zDb, &pCk); - if( rc==SQLITE_OK ){ - while( sqlite3_intck_step(pCk)==SQLITE_OK ){ - const char *zMsg = sqlite3_intck_message(pCk); - if( zMsg ){ - Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zMsg, -1)); - } - } - rc = sqlite3_intck_error(pCk, &zErr); - } - if( rc!=SQLITE_OK ){ - if( zErr ){ - Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1)); - }else{ - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); - } - }else{ - Tcl_SetObjResult(interp, pRet); - } - Tcl_DecrRefCount(pRet); - sqlite3_intck_close(pCk); - sqlite3_intck_close(0); - return rc ? TCL_ERROR : TCL_OK; -} - -int Sqlitetestintck_Init(Tcl_Interp *interp){ - Tcl_CreateObjCommand(interp, "sqlite3_intck", test_sqlite3_intck, 0, 0); - Tcl_CreateObjCommand(interp, "test_do_intck", test_do_intck, 0, 0); - return TCL_OK; -} Index: ext/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/noop.c ================================================================== --- ext/misc/noop.c +++ ext/misc/noop.c @@ -36,28 +36,10 @@ ){ assert( argc==1 ); sqlite3_result_value(context, argv[0]); } -/* -** Implementation of the multitype_text() function. -** -** The function returns its argument. The result will always have a -** TEXT value. But if the original input is numeric, it will also -** have that numeric value. -*/ -static void multitypeTextFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - assert( argc==1 ); - (void)argc; - (void)sqlite3_value_text(argv[0]); - sqlite3_result_value(context, argv[0]); -} - #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_noop_init( sqlite3 *db, @@ -80,11 +62,7 @@ 0, noopfunc, 0, 0); if( rc ) return rc; rc = sqlite3_create_function(db, "noop_nd", 1, SQLITE_UTF8, 0, noopfunc, 0, 0); - if( rc ) return rc; - rc = sqlite3_create_function(db, "multitype_text", 1, - SQLITE_UTF8, - 0, multitypeTextFunc, 0, 0); return rc; } Index: ext/misc/series.c ================================================================== --- ext/misc/series.c +++ ext/misc/series.c @@ -101,24 +101,20 @@ /* ** Return that member of a generate_series(...) sequence whose 0-based ** index is ix. The 0th member is given by smBase. The sequence members ** progress per ix increment by smStep. */ -static sqlite3_int64 genSeqMember( - sqlite3_int64 smBase, - sqlite3_int64 smStep, - sqlite3_uint64 ix -){ - static const sqlite3_uint64 mxI64 = - ((sqlite3_uint64)0x7fffffff)<<32 | 0xffffffff; - if( ix>=mxI64 ){ +static sqlite3_int64 genSeqMember(sqlite3_int64 smBase, + sqlite3_int64 smStep, + sqlite3_uint64 ix){ + if( ix>=(sqlite3_uint64)LLONG_MAX ){ /* Get ix into signed i64 range. */ - ix -= mxI64; + ix -= (sqlite3_uint64)LLONG_MAX; /* With 2's complement ALU, this next can be 1 step, but is split into * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */ - smBase += (mxI64/2) * smStep; - smBase += (mxI64 - mxI64/2) * smStep; + smBase += (LLONG_MAX/2) * smStep; + smBase += (LLONG_MAX - LLONG_MAX/2) * smStep; } /* Under UBSAN (or on 1's complement machines), must do this last term * in steps to avoid the dreaded (and harmless) signed multiply overlow. */ if( ix>=2 ){ sqlite3_int64 ix2 = (sqlite3_int64)ix/2; @@ -374,17 +370,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 +390,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 +420,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 +440,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 +504,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/recover/test_recover.c ================================================================== --- ext/recover/test_recover.c +++ ext/recover/test_recover.c @@ -234,11 +234,11 @@ } if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR; zDb = Tcl_GetString(objv[2]); if( zDb[0]=='\0' ) zDb = 0; - pNew = (TestRecover*)ckalloc(sizeof(TestRecover)); + pNew = ckalloc(sizeof(TestRecover)); if( bSql==0 ){ zUri = Tcl_GetString(objv[3]); pNew->p = sqlite3_recover_init(db, zDb, zUri); }else{ pNew->interp = interp; Index: ext/rtree/rtree.c ================================================================== --- ext/rtree/rtree.c +++ ext/rtree/rtree.c @@ -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; @@ -3226,11 +3223,10 @@ /* ** Called when a transaction starts. */ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; - assert( pRtree->inWrTrans==0 ); pRtree->inWrTrans = 1; return SQLITE_OK; } /* 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 Index: ext/rtree/rtreeJ.test ================================================================== --- ext/rtree/rtreeJ.test +++ ext/rtree/rtreeJ.test @@ -14,11 +14,10 @@ if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl set testprefix rtreeJ -ifcapable !rtree { finish_test ; return } do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2); INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2); } {} 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/sessionstat1.test ================================================================== --- ext/session/sessionstat1.test +++ ext/session/sessionstat1.test @@ -90,11 +90,11 @@ ANALYZE; } } {} do_execsql_test -db db2 2.2 { - SELECT * FROM sqlite_stat1 ORDER BY tbl, idx + SELECT * FROM sqlite_stat1 } { t1 sqlite_autoindex_t1_1 {32 1} t1 t1b {32 4} t1 t1c {32 16} } @@ -102,11 +102,11 @@ do_test 2.3 { do_then_apply_sql -ignorenoop { DROP INDEX t1c } } {} do_execsql_test -db db2 2.4 { - SELECT * FROM sqlite_stat1 ORDER BY tbl, idx; + SELECT * FROM sqlite_stat1 } { t1 sqlite_autoindex_t1_1 {32 1} t1 t1b {32 4} } Index: ext/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/userauth/user-auth.txt ================================================================== --- ext/userauth/user-auth.txt +++ ext/userauth/user-auth.txt @@ -1,17 +1,5 @@ -*********************************** NOTICE ************************************ -* This extension is deprecated. The SQLite developers do not maintain this * -* extension. At some point in the future, it might disappear from the source * -* tree. * -* * -* If you are using this extension and think it should be supported moving * -* forward, visit the SQLite Forum (https://sqlite.org/forum) and argue your * -* case there. * -* * -* This deprecation notice was added on 2024-01-22. * -******************************************************************************* - Activate the user authentication logic by including the ext/userauth/userauth.c source code file in the build and adding the -DSQLITE_USER_AUTHENTICATION compile-time option. The ext/userauth/sqlite3userauth.h header file is available to applications to define the interface. Index: ext/wasm/GNUmakefile ================================================================== --- ext/wasm/GNUmakefile +++ ext/wasm/GNUmakefile @@ -41,13 +41,12 @@ # # 1) Consolidate the code generation for sqlite3*.*js into a script # which generates the makefile code, rather than using $(call) and # $(eval), or at least centralize the setup of the numerous vars # related to each build variant $(JS_BUILD_MODES). (Update: an -# external script was attempted but generating properly-escaped -# makefile code from within a shell script is even less legible -# than the $(eval) indirection going on in this file.) +# external script was attempted but it's even less legible than the +# $(eval) indirection going on in this file. # default: all #default: quick SHELL := $(shell which bash 2>/dev/null) MAKEFILE := $(lastword $(MAKEFILE_LIST)) @@ -287,16 +286,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 +304,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 +331,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 ######################################################################## @@ -445,14 +430,12 @@ sqlite3-api.jses += $(sqlite3-api-build-version.js) # sqlite3-api-oo1.js = the oo1 API: sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js # sqlite3-api-worker.js = the Worker1 API: sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js -# sqlite3-vfs-helper = helper APIs for VFSes: -sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js -# sqlite3-vtab-helper = helper APIs for VTABLEs: -sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js +# sqlite3-v-helper = helper APIs for VFSes and VTABLEs: +sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js # sqlite3-vfs-opfs.c-pp.js = the first OPFS VFS: sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js # sqlite3-vfs-opfs-sahpool.c-pp.js = the second OPFS VFS: sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js # sqlite3-api-cleanup.js = "finalizes" the build and cleans up @@ -464,17 +447,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 @@ -544,14 +526,10 @@ # https://github.com/emscripten-core/emscripten/issues/18610 # TL;DR: does not work with MODULARIZE or EXPORT_ES6 as of version # 3.1.31. The fix for that in newer emcc's is to throw a built-time # error if STRICT_JS is used together with those options. -# emcc.jsflags += -sSTRICT=1 -# -sSTRICT=1 Causes failures about unknown symbols which the build -# tools should be installing, e.g. __syscall_geteuid32 - # -sENVIRONMENT values for the various build modes: emcc.environment.vanilla := web,worker emcc.environment.bundler-friendly := $(emcc.environment.vanilla) emcc.environment.esm := $(emcc.environment.vanilla) emcc.environment.node := node @@ -568,15 +546,10 @@ # workloads MAY suffer considerably if we start small and have to grow # at runtime. e.g. OPFS-backed (speedtest1 --size 75) take MAY take X # time with 16mb+ memory and 3X time when starting with 8MB. However, # such test results are inconsistent due to browser internals which # are opaque to us. -# -# 2024-03-04: emsdk 3.1.55 replaces INITIAL_MEMORY with INITIAL_HEAP, -# but also says (in its changelog): "Note that it is currently not -# supported in all configurations (#21071)." -# https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md emcc.jsflags += -sALLOW_MEMORY_GROWTH emcc.INITIAL_MEMORY.128 := 134217728 emcc.INITIAL_MEMORY.96 := 100663296 emcc.INITIAL_MEMORY.64 := 67108864 emcc.INITIAL_MEMORY.32 := 33554432 @@ -838,17 +811,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 +924,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 @@ -1024,13 +976,10 @@ emcc.speedtest1.common += --no-entry emcc.speedtest1.common += -sABORTING_MALLOC emcc.speedtest1.common += -sSTRICT_JS=0 emcc.speedtest1.common += -sMODULARIZE emcc.speedtest1.common += -Wno-limited-postlink-optimizations -emcc.speedtest1.common += -Wno-unused-main -# ^^^^ -Wno-unused-main is for emcc 3.1.52+. speedtest1 has a wasm_main() which is -# exported and called by the JS code. EXPORTED_FUNCTIONS.speedtest1 := $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1) emcc.speedtest1.common += -sSTACK_SIZE=512KB emcc.speedtest1.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1) emcc.speedtest1.common += $(emcc.exportedRuntimeMethods) emcc.speedtest1.common += -sALLOW_TABLE_GROWTH Index: ext/wasm/SQLTester/SQLTester.mjs ================================================================== --- ext/wasm/SQLTester/SQLTester.mjs +++ ext/wasm/SQLTester/SQLTester.mjs @@ -172,21 +172,15 @@ //! "Special" characters - we have to escape output if it contains any. special: /[\x00-\x20\x22\x5c\x7b\x7d]/, squiggly: /[{}]/ }); - - const Util = newObj({ toss, - unlink: function f(fn){ - if(!f.unlink){ - f.unlink = sqlite3.wasm.xWrap('sqlite3__wasm_vfs_unlink','int', - ['*','string']); - } - return 0==f.unlink(0,fn); + unlink: function(fn){ + return 0==sqlite3.wasm.sqlite3_wasm_vfs_unlink(0,fn); }, argvToString: (list)=>{ const m = [...list]; m.shift() /* strip command name */; @@ -201,11 +195,11 @@ ); }, utf8Encode: (str)=>__utf8Encoder.encode(str), - strglob: sqlite3.wasm.xWrap('sqlite3__wasm_SQLTester_strglob','int', + strglob: sqlite3.wasm.xWrap('sqlite3_wasm_SQLTester_strglob','int', ['string','string']) })/*Util*/; class Outer { #lnBuf = []; Index: ext/wasm/api/README.md ================================================================== --- ext/wasm/api/README.md +++ ext/wasm/api/README.md @@ -76,16 +76,14 @@ - **`sqlite3-worker1-promiser.js`**\ Is likewise not part of the amalgamated sources and provides a Promise-based interface into the Worker #1 API. This is a far user-friendlier way to interface with databases running in a Worker thread. -- **`sqlite3-vfs-helper.js`**\ - Installs the `sqlite3.vfs` namespace, which contain helpers for use - by downstream code which creates `sqlite3_vfs` implementations. -- **`sqlite3-vtab-helper.js`**\ - Installs the `sqlite3.vtab` namespace, which contain helpers for use - by downstream code which creates `sqlite3_module` implementations. +- **`sqlite3-v-helper.js`**\ + Installs `sqlite3.vfs` and `sqlite3.vtab`, namespaces which contain + helpers for use by downstream code which creates `sqlite3_vfs` + and `sqlite3_module` implementations. - **`sqlite3-vfs-opfs.c-pp.js`**\ is an sqlite3 VFS implementation which supports the Origin-Private FileSystem (OPFS) as a storage layer to provide persistent storage for database files in a browser. It requires... - **`sqlite3-opfs-async-proxy.js`**\ Index: ext/wasm/api/post-js-header.js ================================================================== --- ext/wasm/api/post-js-header.js +++ ext/wasm/api/post-js-header.js @@ -17,12 +17,10 @@ - common/whwasmutil.js => Replacements for much of Emscripten's glue - jaccwaby/jaccwabyt.js => Jaccwabyt (C/JS struct binding) - sqlite3-api-glue.js => glues previous parts together - sqlite3-api-oo.js => SQLite3 OO API #1 - sqlite3-api-worker1.js => Worker-based API - - sqlite3-vfs-helper.c-pp.js => Utilities for VFS impls - - sqlite3-vtab-helper.c-pp.js => Utilities for virtual table impls - - sqlite3-vfs-opfs.c-pp.js => OPFS VFS - - sqlite3-vfs-opfs-sahpool.c-pp.js => OPFS SAHPool VFS + - sqlite3-vfs-helper.js => Internal-use utilities for... + - sqlite3-vfs-opfs.js => OPFS VFS - sqlite3-api-cleanup.js => final API cleanup - post-js-footer.js => closes this postRun() function */ Index: ext/wasm/api/sqlite3-api-glue.js ================================================================== --- ext/wasm/api/sqlite3-api-glue.js +++ ext/wasm/api/sqlite3-api-glue.js @@ -12,12 +12,11 @@ This file glues together disparate pieces of JS which are loaded in previous steps of the sqlite3-api.js bootstrapping process: sqlite3-api-prologue.js, whwasmutil.js, and jaccwabyt.js. It initializes the main API pieces so that the downstream components - (e.g. sqlite3-api-oo1.js) have all of the infrastructure that they - need. + (e.g. sqlite3-api-oo1.js) have all that they need. */ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'use strict'; const toss = (...args)=>{throw new Error(args.join(' '))}; const toss3 = sqlite3.SQLite3Error.toss; @@ -327,36 +326,23 @@ 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){ - /** - 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 - exposing an SEE build of sqlite3.wasm effectively provides all - clients with a working copy of the commercial SEE code. - */ + if(wasm.exports.sqlite3_activate_see instanceof Function){ wasm.bindingSignatures.push( ["sqlite3_key", "int", "sqlite3*", "string", "int"], ["sqlite3_key_v2","int","sqlite3*","string","*","int"], ["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, - and such builds are untested.) Note that not all of these functions directly require int64 but are only for use with APIs which require int64. For example, the vtab-related functions. */ @@ -371,14 +357,11 @@ ["sqlite3_declare_vtab", "int", ["sqlite3*", "string:flexible"]], ["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"] /* Careful! Short version: de/serialize() are problematic because they might use a different allocator than the user for managing the deserialized block. de/serialize() are ONLY safe to use with - sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. Because - of this, the canonical builds of sqlite3.wasm/js guarantee that - sqlite3.wasm.alloc() and friends use those allocators. Custom builds - may not guarantee that, however. */, + sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */, ["sqlite3_drop_modules", "int", ["sqlite3*", "**"]], ["sqlite3_last_insert_rowid", "i64", ["sqlite3*"]], ["sqlite3_malloc64", "*","i64"], ["sqlite3_msize", "i64", "*"], ["sqlite3_overload_function", "int", ["sqlite3*","string","int"]], @@ -437,10 +420,12 @@ ["sqlite3_vtab_rhs_value","int", "sqlite3_index_info*", "int", "**"] ]; // Add session/changeset APIs... if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){ + /* ACHTUNG: 2022-12-23: the session/changeset API bindings are + COMPLETELY UNTESTED. */ /** FuncPtrAdapter options for session-related callbacks with the native signature "i(ps)". This proxy converts the 2nd argument from a C string to a JS string before passing the arguments on to the client-provided JS callback. @@ -614,29 +599,20 @@ }/*session/changeset APIs*/ /** Functions which are intended solely for API-internal use by the WASM components, not client code. These get installed into - sqlite3.util. Some of them get exposed to clients via variants - in sqlite3_js_...(). - - 2024-01-11: these were renamed, with two underscores in the - prefix, to ensure that clients do not accidentally depend on - them. They have always been documented as internal-use-only, so - no clients "should" be depending on the old names. + sqlite3.wasm. Some of them get exposed to clients via variants + named sqlite3_js_...(). */ - 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_posix_create_file", "int", "string","*", "int"], - ["sqlite3__wasm_vfs_unlink", "int", "sqlite3_vfs*","string"], - ["sqlite3__wasm_qfmt_token","string:dealloc", "string","int"] + wasm.bindingSignatures.wasm = [ + ["sqlite3_wasm_db_reset", "int", "sqlite3*"], + ["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"], + ["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"] ]; /** Install JS<->C struct bindings for the non-opaque struct types we need... */ @@ -674,11 +650,11 @@ call and all future calls which are passed a string-equivalent argument. Use case: sqlite3_bind_pointer() and sqlite3_result_pointer() call for "a static string and preferably a string - literal." This converter is used to ensure that the string + literal". This converter is used to ensure that the string value seen by those functions is long-lived and behaves as they need it to. */ wasm.xWrap.argAdapter( 'string:static', @@ -696,19 +672,18 @@ wasm.bindingSignatures and (B) provide automatic conversion from higher-level representations, e.g. capi.sqlite3_vfs to `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. */ const __xArgPtr = wasm.xWrap.argAdapter('*'); - const nilType = function(){ - /*a class which no value can ever be an instance of*/ - }; + const nilType = function(){}/*a class no value can ever be an instance of*/; wasm.xWrap.argAdapter('sqlite3_filename', __xArgPtr) ('sqlite3_context*', __xArgPtr) ('sqlite3_value*', __xArgPtr) ('void*', __xArgPtr) ('sqlite3_changegroup*', __xArgPtr) ('sqlite3_changeset_iter*', __xArgPtr) + //('sqlite3_rebaser*', __xArgPtr) ('sqlite3_session*', __xArgPtr) ('sqlite3_stmt*', (v)=> __xArgPtr((v instanceof (sqlite3?.oo1?.Stmt || nilType)) ? v.pointer : v)) ('sqlite3*', (v)=> @@ -765,12 +740,12 @@ ); } for(const e of wasm.bindingSignatures){ capi[e[0]] = wasm.xWrap.apply(null, e); } - for(const e of wasm.bindingSignatures.wasmInternal){ - util[e[0]] = wasm.xWrap.apply(null, e); + for(const e of wasm.bindingSignatures.wasm){ + wasm[e[0]] = wasm.xWrap.apply(null, e); } /* For C API functions which cannot work properly unless wasm.bigIntEnabled is true, install a bogus impl which throws if called when bigIntEnabled is false. The alternative would be @@ -788,13 +763,13 @@ /* There's no need to expose bindingSignatures to clients, implicitly making it part of the public interface. */ delete wasm.bindingSignatures; - if(wasm.exports.sqlite3__wasm_db_error){ + if(wasm.exports.sqlite3_wasm_db_error){ const __db_err = wasm.xWrap( - 'sqlite3__wasm_db_error', 'int', 'sqlite3*', 'int', 'string' + 'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string' ); /** Sets the given db's error state. Accepts: - (sqlite3*, int code, string msg) @@ -808,11 +783,11 @@ exception, the message string defaults to theError.message. Returns the resulting code. Pass (pDb,0,0) to clear the error state. */ - util.sqlite3__wasm_db_error = function(pDb, resultCode, message){ + util.sqlite3_wasm_db_error = function(pDb, resultCode, message){ if(resultCode instanceof sqlite3.WasmAllocError){ resultCode = capi.SQLITE_NOMEM; message = 0 /*avoid allocating message string*/; }else if(resultCode instanceof Error){ message = message || ''+resultCode; @@ -819,21 +794,21 @@ resultCode = (resultCode.resultCode || capi.SQLITE_ERROR); } return pDb ? __db_err(pDb, resultCode, message) : resultCode; }; }else{ - util.sqlite3__wasm_db_error = function(pDb,errCode,msg){ - console.warn("sqlite3__wasm_db_error() is not exported.",arguments); + util.sqlite3_wasm_db_error = function(pDb,errCode,msg){ + console.warn("sqlite3_wasm_db_error() is not exported.",arguments); return errCode; }; } }/*xWrap() bindings*/ {/* Import C-level constants and structs... */ - const cJson = wasm.xCall('sqlite3__wasm_enum_json'); + const cJson = wasm.xCall('sqlite3_wasm_enum_json'); if(!cJson){ - toss("Maintenance required: increase sqlite3__wasm_enum_json()'s", + toss("Maintenance required: increase sqlite3_wasm_enum_json()'s", "static buffer size!"); } //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); // Groups of SQLITE_xyz macros... @@ -900,11 +875,11 @@ 'sqlite3_index_constraint_usage']){ capi.sqlite3_index_info[k] = capi[k]; delete capi[k]; } capi.sqlite3_vtab_config = wasm.xWrap( - 'sqlite3__wasm_vtab_config','int',[ + 'sqlite3_wasm_vtab_config','int',[ 'sqlite3*', 'int', 'int'] ); }/* end vtab-related setup */ }/*end C constant and struct imports*/ @@ -912,20 +887,20 @@ Internal helper to assist in validating call argument counts in the hand-written sqlite3_xyz() wrappers. We do this only for consistency with non-special-case wrappings. */ const __dbArgcMismatch = (pDb,f,n)=>{ - return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, + return util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE, f+"() requires "+n+" argument"+ (1===n?"":'s')+"."); }; /** Code duplication reducer for functions which take an encoding argument and require SQLITE_UTF8. Sets the db error code to SQLITE_FORMAT and returns that code. */ const __errEncoding = (pDb)=>{ - return util.sqlite3__wasm_db_error( + return util.sqlite3_wasm_db_error( pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding." ); }; /** @@ -1151,11 +1126,11 @@ if(0===rc && xCompare instanceof Function){ __dbCleanupMap.addCollation(pDb, zName); } return rc; }catch(e){ - return util.sqlite3__wasm_db_error(pDb, e); + return util.sqlite3_wasm_db_error(pDb, e); } }; capi.sqlite3_create_collation = (pDb,zName,eTextRep,pArg,xCompare)=>{ return (5===arguments.length) @@ -1277,11 +1252,11 @@ __dbCleanupMap.addFunction(pDb, funcName, nArg); } return rc; }catch(e){ console.error("sqlite3_create_function_v2() setup threw:",e); - return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: "+e); + return util.sqlite3_wasm_db_error(pDb, e, "Creation of UDF threw: "+e); } }; /* Documented in the api object's initializer. */ capi.sqlite3_create_function = function f( @@ -1322,11 +1297,11 @@ __dbCleanupMap.addWindowFunc(pDb, funcName, nArg); } return rc; }catch(e){ console.error("sqlite3_create_window_function() setup threw:",e); - return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: "+e); + return util.sqlite3_wasm_db_error(pDb, e, "Creation of UDF threw: "+e); } }; /** A _deprecated_ alias for capi.sqlite3_result_js() which predates the addition of that function in the public API. @@ -1417,11 +1392,11 @@ const [xSql, xSqlLen] = __flexiString(sql, sqlLen); switch(typeof xSql){ case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail); default: - return util.sqlite3__wasm_db_error( + return util.sqlite3_wasm_db_error( pDb, capi.SQLITE_MISUSE, "Invalid SQL argument type for sqlite3_prepare_v2/v3()." ); } }; @@ -1461,19 +1436,19 @@ p = wasm.allocFromTypedArray(text); n = text.byteLength; }else if('string'===typeof text){ [p, n] = wasm.allocCString(text); }else{ - return util.sqlite3__wasm_db_error( + return util.sqlite3_wasm_db_error( capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_text()." ); } return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); }catch(e){ wasm.dealloc(p); - return util.sqlite3__wasm_db_error( + return util.sqlite3_wasm_db_error( capi.sqlite3_db_handle(pStmt), e ); } }/*sqlite3_bind_text()*/; @@ -1495,19 +1470,19 @@ p = wasm.allocFromTypedArray(pMem); n = nMem>=0 ? nMem : pMem.byteLength; }else if('string'===typeof pMem){ [p, n] = wasm.allocCString(pMem); }else{ - return util.sqlite3__wasm_db_error( + return util.sqlite3_wasm_db_error( capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_blob()." ); } return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); }catch(e){ wasm.dealloc(p); - return util.sqlite3__wasm_db_error( + return util.sqlite3_wasm_db_error( capi.sqlite3_db_handle(pStmt), e ); } }/*sqlite3_bind_blob()*/; @@ -1527,15 +1502,15 @@ case capi.SQLITE_CONFIG_MEMSTATUS:// 9 /* boolean */ case capi.SQLITE_CONFIG_SMALL_MALLOC: // 27 /* boolean */ case capi.SQLITE_CONFIG_SORTERREF_SIZE: // 28 /* int nByte */ case capi.SQLITE_CONFIG_STMTJRNL_SPILL: // 26 /* int nByte */ case capi.SQLITE_CONFIG_URI:// 17 /* int */ - return wasm.exports.sqlite3__wasm_config_i(op, args[0]); + return wasm.exports.sqlite3_wasm_config_i(op, args[0]); case capi.SQLITE_CONFIG_LOOKASIDE: // 13 /* int int */ - return wasm.exports.sqlite3__wasm_config_ii(op, args[0], args[1]); + return wasm.exports.sqlite3_wasm_config_ii(op, args[0], args[1]); case capi.SQLITE_CONFIG_MEMDB_MAXSIZE: // 29 /* sqlite3_int64 */ - return wasm.exports.sqlite3__wasm_config_j(op, args[0]); + return wasm.exports.sqlite3_wasm_config_j(op, args[0]); case capi.SQLITE_CONFIG_GETMALLOC: // 5 /* sqlite3_mem_methods* */ case capi.SQLITE_CONFIG_GETMUTEX: // 11 /* sqlite3_mutex_methods* */ case capi.SQLITE_CONFIG_GETPCACHE2: // 19 /* sqlite3_pcache_methods2* */ case capi.SQLITE_CONFIG_GETPCACHE: // 15 /* no-op */ case capi.SQLITE_CONFIG_HEAP: // 8 /* void*, int nByte, int min */ @@ -1552,14 +1527,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() */ @@ -1601,24 +1572,24 @@ const pKvvfs = capi.sqlite3_vfs_find("kvvfs"); if( pKvvfs ){/* kvvfs-specific glue */ if(util.isUIThread()){ const kvvfsMethods = new capi.sqlite3_kvvfs_methods( - wasm.exports.sqlite3__wasm_kvvfs_methods() + wasm.exports.sqlite3_wasm_kvvfs_methods() ); delete capi.sqlite3_kvvfs_methods; - const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKeyOnPstack, + const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack, pstack = wasm.pstack; const kvvfsStorage = (zClass)=> ((115/*=='s'*/===wasm.peek(zClass)) ? sessionStorage : localStorage); /** Implementations for members of the object referred to by - sqlite3__wasm_kvvfs_methods(). We swap out the native + sqlite3_wasm_kvvfs_methods(). We swap out the native implementations with these, which use localStorage or sessionStorage for their backing store. */ const kvvfsImpls = { xRead: (zClass, zKey, zBuf, nBuf)=>{ @@ -1694,183 +1665,7 @@ be used that way but it's not really intended to be. */ capi.sqlite3_vfs_unregister(pKvvfs); } }/*pKvvfs*/ - /* Warn if client-level code makes use of FuncPtrAdapter. */ wasm.xWrap.FuncPtrAdapter.warnOnUse = true; - - const StructBinder = sqlite3.StructBinder - /* we require a local alias b/c StructBinder is removed from the sqlite3 - object during the final steps of the API cleanup. */; - /** - Installs a StructBinder-bound function pointer member of the - given name and function in the given StructBinder.StructType - target object. - - It creates a WASM proxy for the given function and arranges for - that proxy to be cleaned up when tgt.dispose() is called. Throws - on the slightest hint of error, e.g. tgt is-not-a StructType, - name does not map to a struct-bound member, etc. - - As a special case, if the given function is a pointer, then - `wasm.functionEntry()` is used to validate that it is a known - function. If so, it is used as-is with no extra level of proxying - or cleanup, else an exception is thrown. It is legal to pass a - value of 0, indicating a NULL pointer, with the caveat that 0 - _is_ a legal function pointer in WASM but it will not be accepted - as such _here_. (Justification: the function at address zero must - be one which initially came from the WASM module, not a method we - want to bind to a virtual table or VFS.) - - This function returns a proxy for itself which is bound to tgt - and takes 2 args (name,func). That function returns the same - thing as this one, permitting calls to be chained. - - If called with only 1 arg, it has no side effects but returns a - func with the same signature as described above. - - ACHTUNG: because we cannot generically know how to transform JS - exceptions into result codes, the installed functions do no - automatic catching of exceptions. It is critical, to avoid - undefined behavior in the C layer, that methods mapped via - this function do not throw. The exception, as it were, to that - rule is... - - If applyArgcCheck is true then each JS function (as opposed to - function pointers) gets wrapped in a proxy which asserts that it - is passed the expected number of arguments, throwing if the - argument count does not match expectations. That is only intended - for dev-time usage for sanity checking, and may leave the C - environment in an undefined state. - */ - const installMethod = function callee( - tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck - ){ - if(!(tgt instanceof StructBinder.StructType)){ - toss("Usage error: target object is-not-a StructType."); - }else if(!(func instanceof Function) && !wasm.isPtr(func)){ - toss("Usage errror: expecting a Function or WASM pointer to one."); - } - if(1===arguments.length){ - return (n,f)=>callee(tgt, n, f, applyArgcCheck); - } - if(!callee.argcProxy){ - callee.argcProxy = function(tgt, funcName, func,sig){ - return function(...args){ - if(func.length!==arguments.length){ - toss("Argument mismatch for", - tgt.structInfo.name+"::"+funcName - +": Native signature is:",sig); - } - return func.apply(this, args); - } - }; - /* An ondispose() callback for use with - StructBinder-created types. */ - callee.removeFuncList = function(){ - if(this.ondispose.__removeFuncList){ - this.ondispose.__removeFuncList.forEach( - (v,ndx)=>{ - if('number'===typeof v){ - try{wasm.uninstallFunction(v)} - catch(e){/*ignore*/} - } - /* else it's a descriptive label for the next number in - the list. */ - } - ); - delete this.ondispose.__removeFuncList; - } - }; - }/*static init*/ - const sigN = tgt.memberSignature(name); - if(sigN.length<2){ - toss("Member",name,"does not have a function pointer signature:",sigN); - } - const memKey = tgt.memberKey(name); - const fProxy = (applyArgcCheck && !wasm.isPtr(func)) - /** This middle-man proxy is only for use during development, to - confirm that we always pass the proper number of - arguments. We know that the C-level code will always use the - correct argument count. */ - ? callee.argcProxy(tgt, memKey, func, sigN) - : func; - if(wasm.isPtr(fProxy)){ - if(fProxy && !wasm.functionEntry(fProxy)){ - toss("Pointer",fProxy,"is not a WASM function table entry."); - } - tgt[memKey] = fProxy; - }else{ - const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); - tgt[memKey] = pFunc; - if(!tgt.ondispose || !tgt.ondispose.__removeFuncList){ - tgt.addOnDispose('ondispose.__removeFuncList handler', - callee.removeFuncList); - tgt.ondispose.__removeFuncList = []; - } - tgt.ondispose.__removeFuncList.push(memKey, pFunc); - } - return (n,f)=>callee(tgt, n, f, applyArgcCheck); - }/*installMethod*/; - installMethod.installMethodArgcCheck = false; - - /** - Installs methods into the given StructBinder.StructType-type - instance. Each entry in the given methods object must map to a - known member of the given StructType, else an exception will be - triggered. See installMethod() for more details, including the - semantics of the 3rd argument. - - As an exception to the above, if any two or more methods in the - 2nd argument are the exact same function, installMethod() is - _not_ called for the 2nd and subsequent instances, and instead - those instances get assigned the same method pointer which is - created for the first instance. This optimization is primarily to - accommodate special handling of sqlite3_module::xConnect and - xCreate methods. - - On success, returns its first argument. Throws on error. - */ - const installMethods = function( - structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck - ){ - const seen = new Map /* map of */; - for(const k of Object.keys(methods)){ - const m = methods[k]; - const prior = seen.get(m); - if(prior){ - const mkey = structInstance.memberKey(k); - structInstance[mkey] = structInstance[structInstance.memberKey(prior)]; - }else{ - installMethod(structInstance, k, m, applyArgcCheck); - seen.set(m, k); - } - } - return structInstance; - }; - - /** - Equivalent to calling installMethod(this,...arguments) with a - first argument of this object. If called with 1 or 2 arguments - and the first is an object, it's instead equivalent to calling - installMethods(this,...arguments). - */ - StructBinder.StructType.prototype.installMethod = function callee( - name, func, applyArgcCheck = installMethod.installMethodArgcCheck - ){ - return (arguments.length < 3 && name && 'object'===typeof name) - ? installMethods(this, ...arguments) - : installMethod(this, ...arguments); - }; - - /** - Equivalent to calling installMethods() with a first argument - of this object. - */ - StructBinder.StructType.prototype.installMethods = function( - methods, applyArgcCheck = installMethod.installMethodArgcCheck - ){ - return installMethods(this, methods, applyArgcCheck); - }; - }); 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 @@ -35,11 +35,11 @@ for use in creating bundles configured for specific WASM environments. This function expects a configuration object, intended to abstract away details specific to any given WASM environment, primarily so - that it can be used without any direct dependency on + that it can be used without any _direct_ dependency on Emscripten. (Note the default values for the config object!) The config object is only honored the first time this is called. Subsequent calls ignore the argument and return the same (configured) object which gets initialized by the first call. This function will throw if any of the required config options are @@ -96,59 +96,28 @@ this function calls that function to fetch the value, enabling delayed evaluation. The returned object is the top-level sqlite3 namespace object. - - Client code may optionally assign sqlite3ApiBootstrap.defaultConfig - an object-type value before calling sqlite3ApiBootstrap() (without - arguments) in order to tell that call to use this object as its - default config value. The intention of this is to provide - downstream clients with a reasonably flexible approach for plugging - in an environment-suitable configuration without having to define a - new global-scope symbol. - - However, because clients who access this library via an - Emscripten-hosted module will not have an opportunity to call - sqlite3ApiBootstrap() themselves, nor to access it before it is - called, an alternative option for setting the configuration is to - define globalThis.sqlite3ApiConfig to an object. If it is set, it - is used instead of sqlite3ApiBootstrap.defaultConfig if - sqlite3ApiBootstrap() is called without arguments. - - Both sqlite3ApiBootstrap.defaultConfig and - globalThis.sqlite3ApiConfig get deleted by sqlite3ApiBootstrap() - because any changes to them made after that point would have no - useful effect. */ 'use strict'; globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( apiConfig = (globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) ){ if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */ - (sqlite3ApiBootstrap.sqlite3.config || console).warn( - "sqlite3ApiBootstrap() called multiple times.", - "Config and external initializers are ignored on calls after the first." - ); + console.warn("sqlite3ApiBootstrap() called multiple times.", + "Config and external initializers are ignored on calls after the first."); return sqlite3ApiBootstrap.sqlite3; } const config = Object.assign(Object.create(null),{ exports: undefined, memory: undefined, bigIntEnabled: (()=>{ if('undefined'!==typeof Module){ /* Emscripten module will contain HEAPU64 when built with - -sWASM_BIGINT=1, else it will not. - - As of emsdk 3.1.55, when building in strict mode, HEAPxyz - are only available if _explicitly_ included in the exports, - else they are not. We do not (as of 2024-03-04) use -sSTRICT - for the canonical builds. - */ - if( !!Module.HEAPU64 ) return true; - /* Else fall through and hope for the best. Nobody _really_ - builds this without BigInt support, do they? */ + -sWASM_BIGINT=1, else it will not. */ + return !!Module.HEAPU64; } return !!globalThis.BigInt64Array; })(), debug: console.debug.bind(console), warn: console.warn.bind(console), @@ -178,19 +147,10 @@ ].forEach((k)=>{ if('function' === typeof config[k]){ config[k] = config[k](); } }); - - /** - Eliminate any confusion about whether these config objects may - be used after library initialization by eliminating the outward-facing - objects... - */ - delete globalThis.sqlite3ApiConfig; - delete sqlite3ApiBootstrap.defaultConfig; - /** The main sqlite3 binding API gets installed into this object, mimicking the C API as closely as we can. The numerous members names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as possible, identically to the C-native counterparts, as documented at: @@ -243,11 +203,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, @@ -1099,11 +1059,11 @@ /** Sets the current pstack position to the given pointer. Results are undefined if the passed-in value did not come from this.pointer. */ - restore: wasm.exports.sqlite3__wasm_pstack_restore, + restore: wasm.exports.sqlite3_wasm_pstack_restore, /** Attempts to allocate the given number of bytes from the pstack. On success, it zeroes out a block of memory of the given size, adjusts the pstack pointer, and returns a pointer to the memory. On error, throws a WasmAllocError. The @@ -1121,11 +1081,11 @@ */ alloc: function(n){ if('string'===typeof n && !(n = wasm.sizeofIR(n))){ WasmAllocError.toss("Invalid value for pstack.alloc(",arguments[0],")"); } - return wasm.exports.sqlite3__wasm_pstack_alloc(n) + return wasm.exports.sqlite3_wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate",n, "bytes from the pstack."); }, /** alloc()'s n chunks, each sz bytes, as a single memory block and @@ -1201,31 +1161,31 @@ first reserving it via wasm.pstack.alloc() and friends, leads to undefined results. */ pointer: { configurable: false, iterable: true, writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_ptr + get: wasm.exports.sqlite3_wasm_pstack_ptr //Whether or not a setter as an alternative to restore() is //clearer or would just lead to confusion is unclear. - //set: wasm.exports.sqlite3__wasm_pstack_restore + //set: wasm.exports.sqlite3_wasm_pstack_restore }, /** sqlite3.wasm.pstack.quota to the total number of bytes available in the pstack, including any space which is currently allocated. This value is a compile-time constant. */ quota: { configurable: false, iterable: true, writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_quota + get: wasm.exports.sqlite3_wasm_pstack_quota }, /** sqlite3.wasm.pstack.remaining resolves to the amount of space remaining in the pstack. */ remaining: { configurable: false, iterable: true, writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_remaining + get: wasm.exports.sqlite3_wasm_pstack_remaining } })/*wasm.pstack properties*/; capi.sqlite3_randomness = (...args)=>{ if(1===args.length && util.isTypedArray(args[0]) @@ -1294,18 +1254,18 @@ || !globalThis.FileSystemFileHandle){ return __wasmfsOpfsDir = ""; } try{ if(pdir && 0===wasm.xCallWrapped( - 'sqlite3__wasm_init_wasmfs', 'i32', ['string'], pdir + 'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir )){ return __wasmfsOpfsDir = pdir; }else{ return __wasmfsOpfsDir = ""; } }catch(e){ - // sqlite3__wasm_init_wasmfs() is not available + // sqlite3_wasm_init_wasmfs() is not available return __wasmfsOpfsDir = ""; } }; /** @@ -1403,11 +1363,11 @@ memory boundary! */ const zSchema = schema ? (wasm.isPtr(schema) ? schema : wasm.scopedAllocCString(''+schema)) : 0; - let rc = wasm.exports.sqlite3__wasm_db_serialize( + let rc = wasm.exports.sqlite3_wasm_db_serialize( pDb, zSchema, ppOut, pSize, 0 ); if(rc){ toss3("Database serialization failed with code", sqlite3.capi.sqlite3_js_rc_str(rc)); @@ -1429,11 +1389,11 @@ C-string pointer, which may be 0), returns a pointer to the sqlite3_vfs responsible for it. If the given db name is null/0, or not provided, then "main" is assumed. */ capi.sqlite3_js_db_vfs = - (dbPointer, dbName=0)=>util.sqlite3__wasm_db_vfs(dbPointer, dbName); + (dbPointer, dbName=0)=>wasm.sqlite3_wasm_db_vfs(dbPointer, dbName); /** A thin wrapper around capi.sqlite3_aggregate_context() which behaves the same except that it throws a WasmAllocError if that function returns 0. As a special case, if n is falsy it does @@ -1487,11 +1447,11 @@ } try{ if(!util.isInt32(dataLen) || dataLen<0){ SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file()."); } - const rc = util.sqlite3__wasm_posix_create_file(filename, pData, dataLen); + const rc = wasm.sqlite3_wasm_posix_create_file(filename, pData, dataLen); if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); }finally{ wasm.dealloc(pData); } @@ -1589,11 +1549,11 @@ if(!util.isInt32(dataLen) || dataLen<0){ wasm.dealloc(pData); SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file()."); } try{ - const rc = util.sqlite3__wasm_vfs_create_file(vfs, filename, pData, dataLen); + const rc = wasm.sqlite3_wasm_vfs_create_file(vfs, filename, pData, dataLen); if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); }finally{ wasm.dealloc(pData); } @@ -1710,16 +1670,16 @@ The variants which take `(int, int*)` arguments treat a missing or falsy pointer argument as 0. */ capi.sqlite3_db_config = function(pDb, op, ...args){ if(!this.s){ - this.s = wasm.xWrap('sqlite3__wasm_db_config_s','int', + this.s = wasm.xWrap('sqlite3_wasm_db_config_s','int', ['sqlite3*', 'int', 'string:static'] /* MAINDBNAME requires a static string */); - this.pii = wasm.xWrap('sqlite3__wasm_db_config_pii', 'int', + this.pii = wasm.xWrap('sqlite3_wasm_db_config_pii', 'int', ['sqlite3*', 'int', '*','int', 'int']); - this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int', + this.ip = wasm.xWrap('sqlite3_wasm_db_config_ip','int', ['sqlite3*', 'int', 'int','*']); } switch(op){ case capi.SQLITE_DBCONFIG_ENABLE_FKEY: case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER: @@ -1836,11 +1796,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 @@ -357,11 +357,10 @@ } ``` */ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ -const util = sqlite3.util; sqlite3.initWorker1API = function(){ 'use strict'; const toss = (...args)=>{throw new Error(args.join(' '))}; if(!(globalThis.WorkerGlobalScope instanceof Function)){ toss("initWorker1API() must be run from a Worker thread."); @@ -408,16 +407,16 @@ }, close: function(db,alsoUnlink){ if(db){ delete this.dbs[getDbId(db)]; const filename = db.filename; - const pVfs = util.sqlite3__wasm_db_vfs(db.pointer, 0); + const pVfs = sqlite3.wasm.sqlite3_wasm_db_vfs(db.pointer, 0); db.close(); const ddNdx = this.dbList.indexOf(db); if(ddNdx>=0) this.dbList.splice(ddNdx, 1); if(alsoUnlink && filename && pVfs){ - util.sqlite3__wasm_vfs_unlink(pVfs, filename); + sqlite3.wasm.sqlite3_wasm_vfs_unlink(pVfs, filename); } } }, /** Posts the given worker message value. If xferList is provided, @@ -456,10 +455,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 +482,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 = sqlite3.wasm.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); } ADDED ext/wasm/api/sqlite3-v-helper.js Index: ext/wasm/api/sqlite3-v-helper.js ================================================================== --- /dev/null +++ ext/wasm/api/sqlite3-v-helper.js @@ -0,0 +1,718 @@ +/* +** 2022-11-30 +** +** 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 installs sqlite3.vfs, and object which exists to assist + in the creation of JavaScript implementations of sqlite3_vfs, along + with its virtual table counterpart, sqlite3.vtab. +*/ +'use strict'; +globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ + const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; + const vfs = Object.create(null), vtab = Object.create(null); + + const StructBinder = sqlite3.StructBinder + /* we require a local alias b/c StructBinder is removed from the sqlite3 + object during the final steps of the API cleanup. */; + sqlite3.vfs = vfs; + sqlite3.vtab = vtab; + + const sii = capi.sqlite3_index_info; + /** + If n is >=0 and less than this.$nConstraint, this function + returns either a WASM pointer to the 0-based nth entry of + this.$aConstraint (if passed a truthy 2nd argument) or an + sqlite3_index_info.sqlite3_index_constraint object wrapping that + address (if passed a falsy value or no 2nd argument). Returns a + falsy value if n is out of range. + */ + sii.prototype.nthConstraint = function(n, asPtr=false){ + if(n<0 || n>=this.$nConstraint) return false; + const ptr = this.$aConstraint + ( + sii.sqlite3_index_constraint.structInfo.sizeof * n + ); + return asPtr ? ptr : new sii.sqlite3_index_constraint(ptr); + }; + + /** + Works identically to nthConstraint() but returns state from + this.$aConstraintUsage, so returns an + sqlite3_index_info.sqlite3_index_constraint_usage instance + if passed no 2nd argument or a falsy 2nd argument. + */ + sii.prototype.nthConstraintUsage = function(n, asPtr=false){ + if(n<0 || n>=this.$nConstraint) return false; + const ptr = this.$aConstraintUsage + ( + sii.sqlite3_index_constraint_usage.structInfo.sizeof * n + ); + return asPtr ? ptr : new sii.sqlite3_index_constraint_usage(ptr); + }; + + /** + If n is >=0 and less than this.$nOrderBy, this function + returns either a WASM pointer to the 0-based nth entry of + this.$aOrderBy (if passed a truthy 2nd argument) or an + sqlite3_index_info.sqlite3_index_orderby object wrapping that + address (if passed a falsy value or no 2nd argument). Returns a + falsy value if n is out of range. + */ + sii.prototype.nthOrderBy = function(n, asPtr=false){ + if(n<0 || n>=this.$nOrderBy) return false; + const ptr = this.$aOrderBy + ( + sii.sqlite3_index_orderby.structInfo.sizeof * n + ); + return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr); + }; + + /** + Installs a StructBinder-bound function pointer member of the + given name and function in the given StructType target object. + + It creates a WASM proxy for the given function and arranges for + that proxy to be cleaned up when tgt.dispose() is called. Throws + on the slightest hint of error, e.g. tgt is-not-a StructType, + name does not map to a struct-bound member, etc. + + As a special case, if the given function is a pointer, then + `wasm.functionEntry()` is used to validate that it is a known + function. If so, it is used as-is with no extra level of proxying + or cleanup, else an exception is thrown. It is legal to pass a + value of 0, indicating a NULL pointer, with the caveat that 0 + _is_ a legal function pointer in WASM but it will not be accepted + as such _here_. (Justification: the function at address zero must + be one which initially came from the WASM module, not a method we + want to bind to a virtual table or VFS.) + + This function returns a proxy for itself which is bound to tgt + and takes 2 args (name,func). That function returns the same + thing as this one, permitting calls to be chained. + + If called with only 1 arg, it has no side effects but returns a + func with the same signature as described above. + + ACHTUNG: because we cannot generically know how to transform JS + exceptions into result codes, the installed functions do no + automatic catching of exceptions. It is critical, to avoid + undefined behavior in the C layer, that methods mapped via + this function do not throw. The exception, as it were, to that + rule is... + + If applyArgcCheck is true then each JS function (as opposed to + function pointers) gets wrapped in a proxy which asserts that it + is passed the expected number of arguments, throwing if the + argument count does not match expectations. That is only intended + for dev-time usage for sanity checking, and will leave the C + environment in an undefined state. + */ + const installMethod = function callee( + tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck + ){ + if(!(tgt instanceof StructBinder.StructType)){ + toss("Usage error: target object is-not-a StructType."); + }else if(!(func instanceof Function) && !wasm.isPtr(func)){ + toss("Usage errror: expecting a Function or WASM pointer to one."); + } + if(1===arguments.length){ + return (n,f)=>callee(tgt, n, f, applyArgcCheck); + } + if(!callee.argcProxy){ + callee.argcProxy = function(tgt, funcName, func,sig){ + return function(...args){ + if(func.length!==arguments.length){ + toss("Argument mismatch for", + tgt.structInfo.name+"::"+funcName + +": Native signature is:",sig); + } + return func.apply(this, args); + } + }; + /* An ondispose() callback for use with + StructBinder-created types. */ + callee.removeFuncList = function(){ + if(this.ondispose.__removeFuncList){ + this.ondispose.__removeFuncList.forEach( + (v,ndx)=>{ + if('number'===typeof v){ + try{wasm.uninstallFunction(v)} + catch(e){/*ignore*/} + } + /* else it's a descriptive label for the next number in + the list. */ + } + ); + delete this.ondispose.__removeFuncList; + } + }; + }/*static init*/ + const sigN = tgt.memberSignature(name); + if(sigN.length<2){ + toss("Member",name,"does not have a function pointer signature:",sigN); + } + const memKey = tgt.memberKey(name); + const fProxy = (applyArgcCheck && !wasm.isPtr(func)) + /** This middle-man proxy is only for use during development, to + confirm that we always pass the proper number of + arguments. We know that the C-level code will always use the + correct argument count. */ + ? callee.argcProxy(tgt, memKey, func, sigN) + : func; + if(wasm.isPtr(fProxy)){ + if(fProxy && !wasm.functionEntry(fProxy)){ + toss("Pointer",fProxy,"is not a WASM function table entry."); + } + tgt[memKey] = fProxy; + }else{ + const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); + tgt[memKey] = pFunc; + if(!tgt.ondispose || !tgt.ondispose.__removeFuncList){ + tgt.addOnDispose('ondispose.__removeFuncList handler', + callee.removeFuncList); + tgt.ondispose.__removeFuncList = []; + } + tgt.ondispose.__removeFuncList.push(memKey, pFunc); + } + return (n,f)=>callee(tgt, n, f, applyArgcCheck); + }/*installMethod*/; + installMethod.installMethodArgcCheck = false; + + /** + Installs methods into the given StructType-type instance. Each + entry in the given methods object must map to a known member of + the given StructType, else an exception will be triggered. See + installMethod() for more details, including the semantics of the + 3rd argument. + + As an exception to the above, if any two or more methods in the + 2nd argument are the exact same function, installMethod() is + _not_ called for the 2nd and subsequent instances, and instead + those instances get assigned the same method pointer which is + created for the first instance. This optimization is primarily to + accommodate special handling of sqlite3_module::xConnect and + xCreate methods. + + On success, returns its first argument. Throws on error. + */ + const installMethods = function( + structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck + ){ + const seen = new Map /* map of */; + for(const k of Object.keys(methods)){ + const m = methods[k]; + const prior = seen.get(m); + if(prior){ + const mkey = structInstance.memberKey(k); + structInstance[mkey] = structInstance[structInstance.memberKey(prior)]; + }else{ + installMethod(structInstance, k, m, applyArgcCheck); + seen.set(m, k); + } + } + return structInstance; + }; + + /** + Equivalent to calling installMethod(this,...arguments) with a + first argument of this object. If called with 1 or 2 arguments + and the first is an object, it's instead equivalent to calling + installMethods(this,...arguments). + */ + StructBinder.StructType.prototype.installMethod = function callee( + name, func, applyArgcCheck = installMethod.installMethodArgcCheck + ){ + return (arguments.length < 3 && name && 'object'===typeof name) + ? installMethods(this, ...arguments) + : installMethod(this, ...arguments); + }; + + /** + Equivalent to calling installMethods() with a first argument + of this object. + */ + StructBinder.StructType.prototype.installMethods = function( + methods, applyArgcCheck = installMethod.installMethodArgcCheck + ){ + return installMethods(this, methods, applyArgcCheck); + }; + + /** + Uses sqlite3_vfs_register() to register this + sqlite3.capi.sqlite3_vfs. This object must have already been + filled out properly. If the first argument is truthy, the VFS is + registered as the default VFS, else it is not. + + On success, returns this object. Throws on error. + */ + capi.sqlite3_vfs.prototype.registerVfs = function(asDefault=false){ + if(!(this instanceof sqlite3.capi.sqlite3_vfs)){ + toss("Expecting a sqlite3_vfs-type argument."); + } + const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0); + if(rc){ + toss("sqlite3_vfs_register(",this,") failed with rc",rc); + } + if(this.pointer !== capi.sqlite3_vfs_find(this.$zName)){ + toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS", + this); + } + return this; + }; + + /** + A wrapper for installMethods() or registerVfs() to reduce + installation of a VFS and/or its I/O methods to a single + call. + + Accepts an object which contains the properties "io" and/or + "vfs", each of which is itself an object with following properties: + + - `struct`: an sqlite3.StructType-type struct. This must be a + populated (except for the methods) object of type + sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the + "vfs" entry). + + - `methods`: an object mapping sqlite3_io_methods method names + (e.g. 'xClose') to JS implementations of those methods. The JS + implementations must be call-compatible with their native + counterparts. + + For each of those object, this function passes its (`struct`, + `methods`, (optional) `applyArgcCheck`) properties to + installMethods(). + + If the `vfs` entry is set then: + + - Its `struct` property's registerVfs() is called. The + `vfs` entry may optionally have an `asDefault` property, which + gets passed as the argument to registerVfs(). + + - If `struct.$zName` is falsy and the entry has a string-type + `name` property, `struct.$zName` is set to the C-string form of + that `name` value before registerVfs() is called. That string + gets added to the on-dispose state of the struct. + + On success returns this object. Throws on error. + */ + vfs.installVfs = function(opt){ + let count = 0; + const propList = ['io','vfs']; + for(const key of propList){ + const o = opt[key]; + if(o){ + ++count; + installMethods(o.struct, o.methods, !!o.applyArgcCheck); + if('vfs'===key){ + if(!o.struct.$zName && 'string'===typeof o.name){ + o.struct.addOnDispose( + o.struct.$zName = wasm.allocCString(o.name) + ); + } + o.struct.registerVfs(!!o.asDefault); + } + } + } + if(!count) toss("Misuse: installVfs() options object requires at least", + "one of:", propList); + return this; + }; + + /** + Internal factory function for xVtab and xCursor impls. + */ + const __xWrapFactory = function(methodName,StructType){ + return function(ptr,removeMapping=false){ + if(0===arguments.length) ptr = new StructType; + if(ptr instanceof StructType){ + //T.assert(!this.has(ptr.pointer)); + this.set(ptr.pointer, ptr); + return ptr; + }else if(!wasm.isPtr(ptr)){ + sqlite3.SQLite3Error.toss("Invalid argument to",methodName+"()"); + } + let rc = this.get(ptr); + if(removeMapping) this.delete(ptr); + return rc; + }.bind(new Map); + }; + + /** + A factory function which implements a simple lifetime manager for + mappings between C struct pointers and their JS-level wrappers. + The first argument must be the logical name of the manager + (e.g. 'xVtab' or 'xCursor'), which is only used for error + reporting. The second must be the capi.XYZ struct-type value, + e.g. capi.sqlite3_vtab or capi.sqlite3_vtab_cursor. + + Returns an object with 4 methods: create(), get(), unget(), and + dispose(), plus a StructType member with the value of the 2nd + argument. The methods are documented in the body of this + function. + */ + const StructPtrMapper = function(name, StructType){ + const __xWrap = __xWrapFactory(name,StructType); + /** + This object houses a small API for managing mappings of (`T*`) + to StructType objects, specifically within the lifetime + requirements of sqlite3_module methods. + */ + return Object.assign(Object.create(null),{ + /** The StructType object for this object's API. */ + StructType, + /** + Creates a new StructType object, writes its `pointer` + value to the given output pointer, and returns that + object. Its intended usage depends on StructType: + + sqlite3_vtab: to be called from sqlite3_module::xConnect() + or xCreate() implementations. + + sqlite3_vtab_cursor: to be called from xOpen(). + + This will throw if allocation of the StructType instance + fails or if ppOut is not a pointer-type value. + */ + create: (ppOut)=>{ + const rc = __xWrap(); + wasm.pokePtr(ppOut, rc.pointer); + return rc; + }, + /** + Returns the StructType object previously mapped to the + given pointer using create(). Its intended usage depends + on StructType: + + sqlite3_vtab: to be called from sqlite3_module methods which + take a (sqlite3_vtab*) pointer _except_ for + xDestroy()/xDisconnect(), in which case unget() or dispose(). + + sqlite3_vtab_cursor: to be called from any sqlite3_module methods + which take a `sqlite3_vtab_cursor*` argument except xClose(), + in which case use unget() or dispose(). + + Rule to remember: _never_ call dispose() on an instance + returned by this function. + */ + get: (pCObj)=>__xWrap(pCObj), + /** + Identical to get() but also disconnects the mapping between the + given pointer and the returned StructType object, such that + future calls to this function or get() with the same pointer + will return the undefined value. Its intended usage depends + on StructType: + + sqlite3_vtab: to be called from sqlite3_module::xDisconnect() or + xDestroy() implementations or in error handling of a failed + xCreate() or xConnect(). + + sqlite3_vtab_cursor: to be called from xClose() or during + cleanup in a failed xOpen(). + + Calling this method obligates the caller to call dispose() on + the returned object when they're done with it. + */ + unget: (pCObj)=>__xWrap(pCObj,true), + /** + Works like unget() plus it calls dispose() on the + StructType object. + */ + dispose: (pCObj)=>{ + const o = __xWrap(pCObj,true); + if(o) o.dispose(); + } + }); + }; + + /** + A lifetime-management object for mapping `sqlite3_vtab*` + instances in sqlite3_module methods to capi.sqlite3_vtab + objects. + + The API docs are in the API-internal StructPtrMapper(). + */ + vtab.xVtab = StructPtrMapper('xVtab', capi.sqlite3_vtab); + + /** + A lifetime-management object for mapping `sqlite3_vtab_cursor*` + instances in sqlite3_module methods to capi.sqlite3_vtab_cursor + objects. + + The API docs are in the API-internal StructPtrMapper(). + */ + vtab.xCursor = StructPtrMapper('xCursor', capi.sqlite3_vtab_cursor); + + /** + Convenience form of creating an sqlite3_index_info wrapper, + intended for use in xBestIndex implementations. Note that the + caller is expected to call dispose() on the returned object + before returning. Though not _strictly_ required, as that object + does not own the pIdxInfo memory, it is nonetheless good form. + */ + vtab.xIndexInfo = (pIdxInfo)=>new capi.sqlite3_index_info(pIdxInfo); + + /** + Given an error object, this function returns + sqlite3.capi.SQLITE_NOMEM if (e instanceof + sqlite3.WasmAllocError), else it returns its + second argument. Its intended usage is in the methods + of a sqlite3_vfs or sqlite3_module: + + ``` + try{ + let rc = ... + return rc; + }catch(e){ + return sqlite3.vtab.exceptionToRc(e, sqlite3.capi.SQLITE_XYZ); + // where SQLITE_XYZ is some call-appropriate result code. + } + ``` + */ + /**vfs.exceptionToRc = vtab.exceptionToRc = + (e, defaultRc=capi.SQLITE_ERROR)=>( + (e instanceof sqlite3.WasmAllocError) + ? capi.SQLITE_NOMEM + : defaultRc + );*/ + + /** + Given an sqlite3_module method name and error object, this + function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof + sqlite3.WasmAllocError), else it returns its second argument. Its + intended usage is in the methods of a sqlite3_vfs or + sqlite3_module: + + ``` + try{ + let rc = ... + return rc; + }catch(e){ + return sqlite3.vtab.xError( + 'xColumn', e, sqlite3.capi.SQLITE_XYZ); + // where SQLITE_XYZ is some call-appropriate result code. + } + ``` + + If no 3rd argument is provided, its default depends on + the error type: + + - An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM. + + - If err is an SQLite3Error then its `resultCode` property + is used. + + - If all else fails, capi.SQLITE_ERROR is used. + + If xError.errorReporter is a function, it is called in + order to report the error, else the error is not reported. + If that function throws, that exception is ignored. + */ + vtab.xError = function f(methodName, err, defaultRc){ + if(f.errorReporter instanceof Function){ + try{f.errorReporter("sqlite3_module::"+methodName+"(): "+err.message);} + catch(e){/*ignored*/} + } + let rc; + if(err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM; + else if(arguments.length>2) rc = defaultRc; + else if(err instanceof sqlite3.SQLite3Error) rc = err.resultCode; + return rc || capi.SQLITE_ERROR; + }; + vtab.xError.errorReporter = 1 ? console.error.bind(console) : false; + + /** + "The problem" with this is that it introduces an outer function with + a different arity than the passed-in method callback. That means we + cannot do argc validation on these. Additionally, some methods (namely + xConnect) may have call-specific error handling. It would be a shame to + hard-coded that per-method support in this function. + */ + /** vtab.methodCatcher = function(methodName, method, defaultErrRc=capi.SQLITE_ERROR){ + return function(...args){ + try { method(...args); } + }catch(e){ return vtab.xError(methodName, e, defaultRc) } + }; + */ + + /** + A helper for sqlite3_vtab::xRowid() and xUpdate() + implementations. It must be passed the final argument to one of + those methods (an output pointer to an int64 row ID) and the + value to store at the output pointer's address. Returns the same + as wasm.poke() and will throw if the 1st or 2nd arguments + are invalid for that function. + + Example xRowid impl: + + ``` + const xRowid = (pCursor, ppRowid64)=>{ + const c = vtab.xCursor(pCursor); + vtab.xRowid(ppRowid64, c.myRowId); + return 0; + }; + ``` + */ + vtab.xRowid = (ppRowid64, value)=>wasm.poke(ppRowid64, value, 'i64'); + + /** + A helper to initialize and set up an sqlite3_module object for + later installation into individual databases using + sqlite3_create_module(). Requires an object with the following + properties: + + - `methods`: an object containing a mapping of properties with + the C-side names of the sqlite3_module methods, e.g. xCreate, + xBestIndex, etc., to JS implementations for those functions. + Certain special-case handling is performed, as described below. + + - `catchExceptions` (default=false): if truthy, the given methods + are not mapped as-is, but are instead wrapped inside wrappers + which translate exceptions into result codes of SQLITE_ERROR or + SQLITE_NOMEM, depending on whether the exception is an + sqlite3.WasmAllocError. In the case of the xConnect and xCreate + methods, the exception handler also sets the output error + string to the exception's error string. + + - OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If + not set, one will be created automatically. If the current + "this" is-a sqlite3_module then it is unconditionally used in + place of `struct`. + + - OPTIONAL `iVersion`: if set, it must be an integer value and it + gets assigned to the `$iVersion` member of the struct object. + If it's _not_ set, and the passed-in `struct` object's `$iVersion` + is 0 (the default) then this function attempts to define a value + for that property based on the list of methods it has. + + If `catchExceptions` is false, it is up to the client to ensure + that no exceptions escape the methods, as doing so would move + them through the C API, leading to undefined + behavior. (vtab.xError() is intended to assist in reporting + such exceptions.) + + Certain methods may refer to the same implementation. To simplify + the definition of such methods: + + - If `methods.xConnect` is `true` then the value of + `methods.xCreate` is used in its place, and vice versa. sqlite + treats xConnect/xCreate functions specially if they are exactly + the same function (same pointer value). + + - If `methods.xDisconnect` is true then the value of + `methods.xDestroy` is used in its place, and vice versa. + + This is to facilitate creation of those methods inline in the + passed-in object without requiring the client to explicitly get a + reference to one of them in order to assign it to the other + one. + + The `catchExceptions`-installed handlers will account for + identical references to the above functions and will install the + same wrapper function for both. + + The given methods are expected to return integer values, as + expected by the C API. If `catchExceptions` is truthy, the return + value of the wrapped function will be used as-is and will be + translated to 0 if the function returns a falsy value (e.g. if it + does not have an explicit return). If `catchExceptions` is _not_ + active, the method implementations must explicitly return integer + values. + + Throws on error. On success, returns the sqlite3_module object + (`this` or `opt.struct` or a new sqlite3_module instance, + depending on how it's called). + */ + vtab.setupModule = function(opt){ + let createdMod = false; + const mod = (this instanceof capi.sqlite3_module) + ? this : (opt.struct || (createdMod = new capi.sqlite3_module())); + try{ + const methods = opt.methods || toss("Missing 'methods' object."); + for(const e of Object.entries({ + // -----^ ==> [k,v] triggers a broken code transformation in + // some versions of the emsdk toolchain. + xConnect: 'xCreate', xDisconnect: 'xDestroy' + })){ + // Remap X=true to X=Y for certain X/Y combinations + const k = e[0], v = e[1]; + if(true === methods[k]) methods[k] = methods[v]; + else if(true === methods[v]) methods[v] = methods[k]; + } + if(opt.catchExceptions){ + const fwrap = function(methodName, func){ + if(['xConnect','xCreate'].indexOf(methodName) >= 0){ + return function(pDb, pAux, argc, argv, ppVtab, pzErr){ + try{return func(...arguments) || 0} + catch(e){ + if(!(e instanceof sqlite3.WasmAllocError)){ + wasm.dealloc(wasm.peekPtr(pzErr)); + wasm.pokePtr(pzErr, wasm.allocCString(e.message)); + } + return vtab.xError(methodName, e); + } + }; + }else{ + return function(...args){ + try{return func(...args) || 0} + catch(e){ + return vtab.xError(methodName, e); + } + }; + } + }; + const mnames = [ + 'xCreate', 'xConnect', 'xBestIndex', 'xDisconnect', + 'xDestroy', 'xOpen', 'xClose', 'xFilter', 'xNext', + 'xEof', 'xColumn', 'xRowid', 'xUpdate', + 'xBegin', 'xSync', 'xCommit', 'xRollback', + 'xFindFunction', 'xRename', 'xSavepoint', 'xRelease', + 'xRollbackTo', 'xShadowName' + ]; + const remethods = Object.create(null); + for(const k of mnames){ + const m = methods[k]; + if(!(m instanceof Function)) continue; + else if('xConnect'===k && methods.xCreate===m){ + remethods[k] = methods.xCreate; + }else if('xCreate'===k && methods.xConnect===m){ + remethods[k] = methods.xConnect; + }else{ + remethods[k] = fwrap(k, m); + } + } + installMethods(mod, remethods, false); + }else{ + // No automatic exception handling. Trust the client + // to not throw. + installMethods( + mod, methods, !!opt.applyArgcCheck/*undocumented option*/ + ); + } + if(0===mod.$iVersion){ + let v; + if('number'===typeof opt.iVersion) v = opt.iVersion; + else if(mod.$xShadowName) v = 3; + else if(mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2; + else v = 1; + mod.$iVersion = v; + } + }catch(e){ + if(createdMod) createdMod.dispose(); + throw e; + } + return mod; + }/*setupModule()*/; + + /** + Equivalent to calling vtab.setupModule() with this sqlite3_module + object as the call's `this`. + */ + capi.sqlite3_module.prototype.setupModule = function(opt){ + return vtab.setupModule.call(this, opt); + }; +}/*sqlite3ApiBootstrap.initializers.push()*/); DELETED ext/wasm/api/sqlite3-vfs-helper.c-pp.js Index: ext/wasm/api/sqlite3-vfs-helper.c-pp.js ================================================================== --- ext/wasm/api/sqlite3-vfs-helper.c-pp.js +++ /dev/null @@ -1,103 +0,0 @@ -/* -** 2022-11-30 -** -** 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 installs sqlite3.vfs, a namespace of helpers for use in - the creation of JavaScript implementations of sqlite3_vfs. -*/ -'use strict'; -globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; - const vfs = Object.create(null); - sqlite3.vfs = vfs; - - /** - Uses sqlite3_vfs_register() to register this - sqlite3.capi.sqlite3_vfs instance. This object must have already - been filled out properly. If the first argument is truthy, the - VFS is registered as the default VFS, else it is not. - - On success, returns this object. Throws on error. - */ - capi.sqlite3_vfs.prototype.registerVfs = function(asDefault=false){ - if(!(this instanceof sqlite3.capi.sqlite3_vfs)){ - toss("Expecting a sqlite3_vfs-type argument."); - } - const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0); - if(rc){ - toss("sqlite3_vfs_register(",this,") failed with rc",rc); - } - if(this.pointer !== capi.sqlite3_vfs_find(this.$zName)){ - toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS", - this); - } - return this; - }; - - /** - A wrapper for - sqlite3.StructBinder.StructType.prototype.installMethods() or - registerVfs() to reduce installation of a VFS and/or its I/O - methods to a single call. - - Accepts an object which contains the properties "io" and/or - "vfs", each of which is itself an object with following properties: - - - `struct`: an sqlite3.StructBinder.StructType-type struct. This - must be a populated (except for the methods) object of type - sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the - "vfs" entry). - - - `methods`: an object mapping sqlite3_io_methods method names - (e.g. 'xClose') to JS implementations of those methods. The JS - implementations must be call-compatible with their native - counterparts. - - For each of those object, this function passes its (`struct`, - `methods`, (optional) `applyArgcCheck`) properties to - installMethods(). - - If the `vfs` entry is set then: - - - Its `struct` property's registerVfs() is called. The - `vfs` entry may optionally have an `asDefault` property, which - gets passed as the argument to registerVfs(). - - - If `struct.$zName` is falsy and the entry has a string-type - `name` property, `struct.$zName` is set to the C-string form of - that `name` value before registerVfs() is called. That string - gets added to the on-dispose state of the struct. - - On success returns this object. Throws on error. - */ - vfs.installVfs = function(opt){ - let count = 0; - const propList = ['io','vfs']; - for(const key of propList){ - const o = opt[key]; - if(o){ - ++count; - o.struct.installMethods(o.methods, !!o.applyArgcCheck); - if('vfs'===key){ - if(!o.struct.$zName && 'string'===typeof o.name){ - o.struct.addOnDispose( - o.struct.$zName = wasm.allocCString(o.name) - ); - } - o.struct.registerVfs(!!o.asDefault); - } - } - } - if(!count) toss("Misuse: installVfs() options object requires at least", - "one of:", propList); - return this; - }; -}/*sqlite3ApiBootstrap.initializers.push()*/); Index: ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js ================================================================== --- ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js +++ ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js @@ -1135,13 +1135,12 @@ Imports the contents of an SQLite database, provided as a byte array or ArrayBuffer, under the given name, overwriting any existing content. Throws if the pool has no available file slots, on I/O error, or if the input does not appear to be a database. In the latter case, only a cursory examination is made. - Results are undefined if the given db name refers to an opened - db. Note that this routine is _only_ for importing database - files, not arbitrary files, the reason being that this VFS will + Note that this routine is _only_ for importing database files, + not arbitrary files, the reason being that this VFS will automatically clean up any non-database files so importing them is pointless. If passed a function for its second argument, its behavior changes to asynchronous and it imports its data in chunks fed to @@ -1270,11 +1269,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 @@ -243,12 +243,11 @@ : null /* dVfs will be null when sqlite3 is built with SQLITE_OS_OTHER. */; opfsIoMethods.$iVersion = 1; opfsVfs.$iVersion = 2/*yes, two*/; opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - opfsVfs.$mxPathname = 1024/* sure, why not? The OPFS name length limit - is undocumented/unspecified. */; + opfsVfs.$mxPathname = 1024/*sure, why not?*/; opfsVfs.$zName = wasm.allocCString("opfs"); // All C-side memory of opfsVfs is zeroed out, but just to be explicit: opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; opfsVfs.addOnDispose( '$zName', opfsVfs.$zName, @@ -421,30 +420,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 +872,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); @@ -1010,10 +990,31 @@ Generates a random ASCII string, intended for use as a temporary file name. Its argument is the length of the string, defaulting to 16. */ opfsUtil.randomFilename = randomFilename; + + /** + Re-registers the OPFS VFS. This is intended only for odd use + cases which have to call sqlite3_shutdown() as part of their + initialization process, which will unregister the VFS + registered by installOpfsVfs(). If passed a truthy value, the + OPFS VFS is registered as the default VFS, else it is not made + the default. Returns the result of the the + sqlite3_vfs_register() call. + + Design note: the problem of having to re-register things after + a shutdown/initialize pair is more general. How to best plug + that in to the library is unclear. In particular, we cannot + hook in to any C-side calls to sqlite3_initialize(), so we + cannot add an after-initialize callback mechanism. + */ + opfsUtil.registerVfs = (asDefault=false)=>{ + return wasm.exports.sqlite3_vfs_register( + opfsVfs.pointer, asDefault ? 1 : 0 + ); + }; /** Returns a promise which resolves to an object which represents all files and directories in the OPFS tree. The top-most object has two properties: `dirs` is an array of directory entries @@ -1210,22 +1211,20 @@ /** Asynchronously imports the given bytes (a byte array or ArrayBuffer) into the given database file. - Results are undefined if the given db name refers to an opened - db. - If passed a function for its second argument, its behaviour - changes: imports its data in chunks fed to it by the given - callback function. It calls the callback (which may be async) - repeatedly, expecting either a Uint8Array or ArrayBuffer (to - denote new input) or undefined (to denote EOF). For so long as - the callback continues to return non-undefined, it will append - incoming data to the given VFS-hosted database file. When - called this way, the resolved value of the returned Promise is - the number of bytes written to the target file. + changes to async and it imports its data in chunks fed to it by + the given callback function. It calls the callback (which may + be async) repeatedly, expecting either a Uint8Array or + ArrayBuffer (to denote new input) or undefined (to denote + EOF). For so long as the callback continues to return + non-undefined, it will append incoming data to the given + VFS-hosted database file. When called this way, the resolved + value of the returned Promise is the number of bytes written to + the target file. It very specifically requires the input to be an SQLite3 database and throws if that's not the case. It does so in order to prevent this function from taking on a larger scope than it is specifically intended to. i.e. we do not want it to DELETED ext/wasm/api/sqlite3-vtab-helper.c-pp.js Index: ext/wasm/api/sqlite3-vtab-helper.c-pp.js ================================================================== --- ext/wasm/api/sqlite3-vtab-helper.c-pp.js +++ /dev/null @@ -1,423 +0,0 @@ -/* -** 2022-11-30 -** -** 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 installs sqlite3.vtab, a namespace of helpers for use in - the creation of JavaScript implementations virtual tables. -*/ -'use strict'; -globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; - const vtab = Object.create(null); - sqlite3.vtab = vtab; - - const sii = capi.sqlite3_index_info; - /** - If n is >=0 and less than this.$nConstraint, this function - returns either a WASM pointer to the 0-based nth entry of - this.$aConstraint (if passed a truthy 2nd argument) or an - sqlite3_index_info.sqlite3_index_constraint object wrapping that - address (if passed a falsy value or no 2nd argument). Returns a - falsy value if n is out of range. - */ - sii.prototype.nthConstraint = function(n, asPtr=false){ - if(n<0 || n>=this.$nConstraint) return false; - const ptr = this.$aConstraint + ( - sii.sqlite3_index_constraint.structInfo.sizeof * n - ); - return asPtr ? ptr : new sii.sqlite3_index_constraint(ptr); - }; - - /** - Works identically to nthConstraint() but returns state from - this.$aConstraintUsage, so returns an - sqlite3_index_info.sqlite3_index_constraint_usage instance - if passed no 2nd argument or a falsy 2nd argument. - */ - sii.prototype.nthConstraintUsage = function(n, asPtr=false){ - if(n<0 || n>=this.$nConstraint) return false; - const ptr = this.$aConstraintUsage + ( - sii.sqlite3_index_constraint_usage.structInfo.sizeof * n - ); - return asPtr ? ptr : new sii.sqlite3_index_constraint_usage(ptr); - }; - - /** - If n is >=0 and less than this.$nOrderBy, this function - returns either a WASM pointer to the 0-based nth entry of - this.$aOrderBy (if passed a truthy 2nd argument) or an - sqlite3_index_info.sqlite3_index_orderby object wrapping that - address (if passed a falsy value or no 2nd argument). Returns a - falsy value if n is out of range. - */ - sii.prototype.nthOrderBy = function(n, asPtr=false){ - if(n<0 || n>=this.$nOrderBy) return false; - const ptr = this.$aOrderBy + ( - sii.sqlite3_index_orderby.structInfo.sizeof * n - ); - return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr); - }; - - /** - Internal factory function for xVtab and xCursor impls. - */ - const __xWrapFactory = function(methodName,StructType){ - return function(ptr,removeMapping=false){ - if(0===arguments.length) ptr = new StructType; - if(ptr instanceof StructType){ - //T.assert(!this.has(ptr.pointer)); - this.set(ptr.pointer, ptr); - return ptr; - }else if(!wasm.isPtr(ptr)){ - sqlite3.SQLite3Error.toss("Invalid argument to",methodName+"()"); - } - let rc = this.get(ptr); - if(removeMapping) this.delete(ptr); - return rc; - }.bind(new Map); - }; - - /** - A factory function which implements a simple lifetime manager for - mappings between C struct pointers and their JS-level wrappers. - The first argument must be the logical name of the manager - (e.g. 'xVtab' or 'xCursor'), which is only used for error - reporting. The second must be the capi.XYZ struct-type value, - e.g. capi.sqlite3_vtab or capi.sqlite3_vtab_cursor. - - Returns an object with 4 methods: create(), get(), unget(), and - dispose(), plus a StructType member with the value of the 2nd - argument. The methods are documented in the body of this - function. - */ - const StructPtrMapper = function(name, StructType){ - const __xWrap = __xWrapFactory(name,StructType); - /** - This object houses a small API for managing mappings of (`T*`) - to StructType objects, specifically within the lifetime - requirements of sqlite3_module methods. - */ - return Object.assign(Object.create(null),{ - /** The StructType object for this object's API. */ - StructType, - /** - Creates a new StructType object, writes its `pointer` - value to the given output pointer, and returns that - object. Its intended usage depends on StructType: - - sqlite3_vtab: to be called from sqlite3_module::xConnect() - or xCreate() implementations. - - sqlite3_vtab_cursor: to be called from xOpen(). - - This will throw if allocation of the StructType instance - fails or if ppOut is not a pointer-type value. - */ - create: (ppOut)=>{ - const rc = __xWrap(); - wasm.pokePtr(ppOut, rc.pointer); - return rc; - }, - /** - Returns the StructType object previously mapped to the - given pointer using create(). Its intended usage depends - on StructType: - - sqlite3_vtab: to be called from sqlite3_module methods which - take a (sqlite3_vtab*) pointer _except_ for - xDestroy()/xDisconnect(), in which case unget() or dispose(). - - sqlite3_vtab_cursor: to be called from any sqlite3_module methods - which take a `sqlite3_vtab_cursor*` argument except xClose(), - in which case use unget() or dispose(). - - Rule to remember: _never_ call dispose() on an instance - returned by this function. - */ - get: (pCObj)=>__xWrap(pCObj), - /** - Identical to get() but also disconnects the mapping between the - given pointer and the returned StructType object, such that - future calls to this function or get() with the same pointer - will return the undefined value. Its intended usage depends - on StructType: - - sqlite3_vtab: to be called from sqlite3_module::xDisconnect() or - xDestroy() implementations or in error handling of a failed - xCreate() or xConnect(). - - sqlite3_vtab_cursor: to be called from xClose() or during - cleanup in a failed xOpen(). - - Calling this method obligates the caller to call dispose() on - the returned object when they're done with it. - */ - unget: (pCObj)=>__xWrap(pCObj,true), - /** - Works like unget() plus it calls dispose() on the - StructType object. - */ - dispose: (pCObj)=>{ - const o = __xWrap(pCObj,true); - if(o) o.dispose(); - } - }); - }; - - /** - A lifetime-management object for mapping `sqlite3_vtab*` - instances in sqlite3_module methods to capi.sqlite3_vtab - objects. - - The API docs are in the API-internal StructPtrMapper(). - */ - vtab.xVtab = StructPtrMapper('xVtab', capi.sqlite3_vtab); - - /** - A lifetime-management object for mapping `sqlite3_vtab_cursor*` - instances in sqlite3_module methods to capi.sqlite3_vtab_cursor - objects. - - The API docs are in the API-internal StructPtrMapper(). - */ - vtab.xCursor = StructPtrMapper('xCursor', capi.sqlite3_vtab_cursor); - - /** - Convenience form of creating an sqlite3_index_info wrapper, - intended for use in xBestIndex implementations. Note that the - caller is expected to call dispose() on the returned object - before returning. Though not _strictly_ required, as that object - does not own the pIdxInfo memory, it is nonetheless good form. - */ - vtab.xIndexInfo = (pIdxInfo)=>new capi.sqlite3_index_info(pIdxInfo); - - /** - Given an sqlite3_module method name and error object, this - function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof - sqlite3.WasmAllocError), else it returns its second argument. Its - intended usage is in the methods of a sqlite3_vfs or - sqlite3_module: - - ``` - try{ - let rc = ... - return rc; - }catch(e){ - return sqlite3.vtab.xError( - 'xColumn', e, sqlite3.capi.SQLITE_XYZ); - // where SQLITE_XYZ is some call-appropriate result code. - } - ``` - - If no 3rd argument is provided, its default depends on - the error type: - - - An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM. - - - If err is an SQLite3Error then its `resultCode` property - is used. - - - If all else fails, capi.SQLITE_ERROR is used. - - If xError.errorReporter is a function, it is called in - order to report the error, else the error is not reported. - If that function throws, that exception is ignored. - */ - vtab.xError = function f(methodName, err, defaultRc){ - if(f.errorReporter instanceof Function){ - try{f.errorReporter("sqlite3_module::"+methodName+"(): "+err.message);} - catch(e){/*ignored*/} - } - let rc; - if(err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM; - else if(arguments.length>2) rc = defaultRc; - else if(err instanceof sqlite3.SQLite3Error) rc = err.resultCode; - return rc || capi.SQLITE_ERROR; - }; - vtab.xError.errorReporter = 1 ? console.error.bind(console) : false; - - /** - A helper for sqlite3_vtab::xRowid() and xUpdate() - implementations. It must be passed the final argument to one of - those methods (an output pointer to an int64 row ID) and the - value to store at the output pointer's address. Returns the same - as wasm.poke() and will throw if the 1st or 2nd arguments - are invalid for that function. - - Example xRowid impl: - - ``` - const xRowid = (pCursor, ppRowid64)=>{ - const c = vtab.xCursor(pCursor); - vtab.xRowid(ppRowid64, c.myRowId); - return 0; - }; - ``` - */ - vtab.xRowid = (ppRowid64, value)=>wasm.poke(ppRowid64, value, 'i64'); - - /** - A helper to initialize and set up an sqlite3_module object for - later installation into individual databases using - sqlite3_create_module(). Requires an object with the following - properties: - - - `methods`: an object containing a mapping of properties with - the C-side names of the sqlite3_module methods, e.g. xCreate, - xBestIndex, etc., to JS implementations for those functions. - Certain special-case handling is performed, as described below. - - - `catchExceptions` (default=false): if truthy, the given methods - are not mapped as-is, but are instead wrapped inside wrappers - which translate exceptions into result codes of SQLITE_ERROR or - SQLITE_NOMEM, depending on whether the exception is an - sqlite3.WasmAllocError. In the case of the xConnect and xCreate - methods, the exception handler also sets the output error - string to the exception's error string. - - - OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If - not set, one will be created automatically. If the current - "this" is-a sqlite3_module then it is unconditionally used in - place of `struct`. - - - OPTIONAL `iVersion`: if set, it must be an integer value and it - gets assigned to the `$iVersion` member of the struct object. - If it's _not_ set, and the passed-in `struct` object's `$iVersion` - is 0 (the default) then this function attempts to define a value - for that property based on the list of methods it has. - - If `catchExceptions` is false, it is up to the client to ensure - that no exceptions escape the methods, as doing so would move - them through the C API, leading to undefined - behavior. (vtab.xError() is intended to assist in reporting - such exceptions.) - - Certain methods may refer to the same implementation. To simplify - the definition of such methods: - - - If `methods.xConnect` is `true` then the value of - `methods.xCreate` is used in its place, and vice versa. sqlite - treats xConnect/xCreate functions specially if they are exactly - the same function (same pointer value). - - - If `methods.xDisconnect` is true then the value of - `methods.xDestroy` is used in its place, and vice versa. - - This is to facilitate creation of those methods inline in the - passed-in object without requiring the client to explicitly get a - reference to one of them in order to assign it to the other - one. - - The `catchExceptions`-installed handlers will account for - identical references to the above functions and will install the - same wrapper function for both. - - The given methods are expected to return integer values, as - expected by the C API. If `catchExceptions` is truthy, the return - value of the wrapped function will be used as-is and will be - translated to 0 if the function returns a falsy value (e.g. if it - does not have an explicit return). If `catchExceptions` is _not_ - active, the method implementations must explicitly return integer - values. - - Throws on error. On success, returns the sqlite3_module object - (`this` or `opt.struct` or a new sqlite3_module instance, - depending on how it's called). - */ - vtab.setupModule = function(opt){ - let createdMod = false; - const mod = (this instanceof capi.sqlite3_module) - ? this : (opt.struct || (createdMod = new capi.sqlite3_module())); - try{ - const methods = opt.methods || toss("Missing 'methods' object."); - for(const e of Object.entries({ - // -----^ ==> [k,v] triggers a broken code transformation in - // some versions of the emsdk toolchain. - xConnect: 'xCreate', xDisconnect: 'xDestroy' - })){ - // Remap X=true to X=Y for certain X/Y combinations - const k = e[0], v = e[1]; - if(true === methods[k]) methods[k] = methods[v]; - else if(true === methods[v]) methods[v] = methods[k]; - } - if(opt.catchExceptions){ - const fwrap = function(methodName, func){ - if(['xConnect','xCreate'].indexOf(methodName) >= 0){ - return function(pDb, pAux, argc, argv, ppVtab, pzErr){ - try{return func(...arguments) || 0} - catch(e){ - if(!(e instanceof sqlite3.WasmAllocError)){ - wasm.dealloc(wasm.peekPtr(pzErr)); - wasm.pokePtr(pzErr, wasm.allocCString(e.message)); - } - return vtab.xError(methodName, e); - } - }; - }else{ - return function(...args){ - try{return func(...args) || 0} - catch(e){ - return vtab.xError(methodName, e); - } - }; - } - }; - const mnames = [ - 'xCreate', 'xConnect', 'xBestIndex', 'xDisconnect', - 'xDestroy', 'xOpen', 'xClose', 'xFilter', 'xNext', - 'xEof', 'xColumn', 'xRowid', 'xUpdate', - 'xBegin', 'xSync', 'xCommit', 'xRollback', - 'xFindFunction', 'xRename', 'xSavepoint', 'xRelease', - 'xRollbackTo', 'xShadowName' - ]; - const remethods = Object.create(null); - for(const k of mnames){ - const m = methods[k]; - if(!(m instanceof Function)) continue; - else if('xConnect'===k && methods.xCreate===m){ - remethods[k] = methods.xCreate; - }else if('xCreate'===k && methods.xConnect===m){ - remethods[k] = methods.xConnect; - }else{ - remethods[k] = fwrap(k, m); - } - } - mod.installMethods(remethods, false); - }else{ - // No automatic exception handling. Trust the client - // to not throw. - mod.installMethods( - methods, !!opt.applyArgcCheck/*undocumented option*/ - ); - } - if(0===mod.$iVersion){ - let v; - if('number'===typeof opt.iVersion) v = opt.iVersion; - else if(mod.$xShadowName) v = 3; - else if(mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2; - else v = 1; - mod.$iVersion = v; - } - }catch(e){ - if(createdMod) createdMod.dispose(); - throw e; - } - return mod; - }/*setupModule()*/; - - /** - Equivalent to calling vtab.setupModule() with this sqlite3_module - object as the call's `this`. - */ - capi.sqlite3_module.prototype.setupModule = function(opt){ - return vtab.setupModule.call(this, opt); - }; -}/*sqlite3ApiBootstrap.initializers.push()*/); Index: ext/wasm/api/sqlite3-wasm.c ================================================================== --- ext/wasm/api/sqlite3-wasm.c +++ ext/wasm/api/sqlite3-wasm.c @@ -236,40 +236,40 @@ ** to work just fine. ** ** Another option is to malloc() a chunk of our own and call that our ** "stack". */ -SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_end(void){ +SQLITE_WASM_EXPORT void * sqlite3_wasm_stack_end(void){ extern void __heap_base /* see https://stackoverflow.com/questions/10038964 */; return &__heap_base; } -SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_begin(void){ +SQLITE_WASM_EXPORT void * sqlite3_wasm_stack_begin(void){ extern void __data_end; return &__data_end; } static void * pWasmStackPtr = 0; -SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_ptr(void){ - if(!pWasmStackPtr) pWasmStackPtr = sqlite3__wasm_stack_end(); +SQLITE_WASM_EXPORT void * sqlite3_wasm_stack_ptr(void){ + if(!pWasmStackPtr) pWasmStackPtr = sqlite3_wasm_stack_end(); return pWasmStackPtr; } -SQLITE_WASM_EXPORT void sqlite3__wasm_stack_restore(void * p){ +SQLITE_WASM_EXPORT void sqlite3_wasm_stack_restore(void * p){ pWasmStackPtr = p; } -SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_alloc(int n){ +SQLITE_WASM_EXPORT void * sqlite3_wasm_stack_alloc(int n){ if(n<=0) return 0; n = (n + 7) & ~7 /* align to 8-byte boundary */; - unsigned char * const p = (unsigned char *)sqlite3__wasm_stack_ptr(); - unsigned const char * const b = (unsigned const char *)sqlite3__wasm_stack_begin(); + unsigned char * const p = (unsigned char *)sqlite3_wasm_stack_ptr(); + unsigned const char * const b = (unsigned const char *)sqlite3_wasm_stack_begin(); if(b + n >= p || b + n < b/*overflow*/) return 0; return pWasmStackPtr = p - n; } #endif /* stack allocator experiment */ /* ** State for the "pseudo-stack" allocator implemented in -** sqlite3__wasm_pstack_xyz(). In order to avoid colliding with +** sqlite3_wasm_pstack_xyz(). In order to avoid colliding with ** Emscripten-controled stack space, it carves out a bit of stack ** memory to use for that purpose. This memory ends up in the ** WASM-managed memory, such that routines which manipulate the wasm ** heap can also be used to manipulate this memory. ** @@ -289,18 +289,18 @@ &PStack_mem[0] + sizeof(PStack_mem) }; /* ** Returns the current pstack position. */ -SQLITE_WASM_EXPORT void * sqlite3__wasm_pstack_ptr(void){ +SQLITE_WASM_EXPORT void * sqlite3_wasm_pstack_ptr(void){ return PStack.pPos; } /* ** Sets the pstack position poitner to p. Results are undefined if the -** given value did not come from sqlite3__wasm_pstack_ptr(). +** given value did not come from sqlite3_wasm_pstack_ptr(). */ -SQLITE_WASM_EXPORT void sqlite3__wasm_pstack_restore(unsigned char * p){ +SQLITE_WASM_EXPORT void sqlite3_wasm_pstack_restore(unsigned char * p){ assert(p>=PStack.pBegin && p<=PStack.pEnd && p>=PStack.pPos); assert(0==((unsigned long long)p & 0x7)); if(p>=PStack.pBegin && p<=PStack.pEnd /*&& p>=PStack.pPos*/){ PStack.pPos = p; } @@ -311,11 +311,11 @@ ** is always adjusted to be a multiple of 8 and returned memory is ** always zeroed out before returning (because this keeps the client ** JS code from having to do so, and most uses of the pstack will ** call for doing so). */ -SQLITE_WASM_EXPORT void * sqlite3__wasm_pstack_alloc(int n){ +SQLITE_WASM_EXPORT void * sqlite3_wasm_pstack_alloc(int n){ if( n<=0 ) return 0; //if( n & 0x7 ) n += 8 - (n & 0x7) /* align to 8-byte boundary */; n = (n + 7) & ~7 /* align to 8-byte boundary */; if( PStack.pBegin + n > PStack.pPos /*not enough space left*/ || PStack.pBegin + n <= PStack.pBegin /*overflow*/ ) return 0; @@ -322,13 +322,13 @@ memset((PStack.pPos = PStack.pPos - n), 0, (unsigned int)n); return PStack.pPos; } /* ** Return the number of bytes left which can be -** sqlite3__wasm_pstack_alloc()'d. +** sqlite3_wasm_pstack_alloc()'d. */ -SQLITE_WASM_EXPORT int sqlite3__wasm_pstack_remaining(void){ +SQLITE_WASM_EXPORT int sqlite3_wasm_pstack_remaining(void){ assert(PStack.pPos >= PStack.pBegin); assert(PStack.pPos <= PStack.pEnd); return (int)(PStack.pPos - PStack.pBegin); } @@ -335,11 +335,11 @@ /* ** Return the total number of bytes available in the pstack, including ** any space which is currently allocated. This value is a ** compile-time constant. */ -SQLITE_WASM_EXPORT int sqlite3__wasm_pstack_quota(void){ +SQLITE_WASM_EXPORT int sqlite3_wasm_pstack_quota(void){ return (int)(PStack.pEnd - PStack.pBegin); } /* ** This function is NOT part of the sqlite3 public API. It is strictly @@ -354,11 +354,11 @@ ** from client code. ** ** Returns err_code. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_db_error(sqlite3*db, int err_code, const char *zMsg){ +int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){ if( db!=0 ){ if( 0!=zMsg ){ const int nMsg = sqlite3Strlen30(zMsg); sqlite3_mutex_enter(sqlite3_db_mutex(db)); sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg); @@ -378,11 +378,11 @@ int64_t v8; void (*xFunc)(void*); }; typedef struct WasmTestStruct WasmTestStruct; SQLITE_WASM_EXPORT -void sqlite3__wasm_test_struct(WasmTestStruct * s){ +void sqlite3_wasm_test_struct(WasmTestStruct * s){ if(s){ s->v4 *= 2; s->v8 = s->v4 * 2; s->ppV = s; s->cstr = __FILE__; @@ -406,11 +406,11 @@ ** If this function returns NULL then it means that the internal ** buffer is not large enough for the generated JSON and needs to be ** increased. In debug builds that will trigger an assert(). */ SQLITE_WASM_EXPORT -const char * sqlite3__wasm_enum_json(void){ +const char * sqlite3_wasm_enum_json(void){ static char aBuffer[1024 * 20] = {0} /* where the JSON goes */; int n = 0, nChildren = 0, nStruct = 0 /* output counters for figuring out where commas go */; char * zPos = &aBuffer[1] /* skip first byte for now to help protect ** against a small race condition */; @@ -423,11 +423,11 @@ ** instance might return and use the string before the 1st instance ** is done filling it. */ /* Core output macros... */ #define lenCheck assert(zPos < zEnd - 128 \ - && "sqlite3__wasm_enum_json() buffer is too small."); \ + && "sqlite3_wasm_enum_json() buffer is too small."); \ if( zPos >= zEnd - 128 ) return 0 #define outf(format,...) \ zPos += snprintf(zPos, ((size_t)(zEnd - zPos)), format, __VA_ARGS__); \ lenCheck #define out(TXT) outf("%s",TXT) @@ -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); @@ -1222,11 +1218,11 @@ ** zName is NULL, no default VFS is found, or it has no xDelete ** method, SQLITE_MISUSE is returned, else the result of the xDelete() ** call is returned. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_vfs_unlink(sqlite3_vfs *pVfs, const char *zName){ +int sqlite3_wasm_vfs_unlink(sqlite3_vfs *pVfs, const char *zName){ int rc = SQLITE_MISUSE /* ??? */; if( 0==pVfs && 0!=zName ) pVfs = sqlite3_vfs_find(0); if( zName && pVfs && pVfs->xDelete ){ rc = pVfs->xDelete(pVfs, zName, 1); } @@ -1240,11 +1236,11 @@ ** Returns a pointer to the given DB's VFS for the given DB name, ** defaulting to "main" if zDbName is 0. Returns 0 if no db with the ** given name is open. */ SQLITE_WASM_EXPORT -sqlite3_vfs * sqlite3__wasm_db_vfs(sqlite3 *pDb, const char *zDbName){ +sqlite3_vfs * sqlite3_wasm_db_vfs(sqlite3 *pDb, const char *zDbName){ sqlite3_vfs * pVfs = 0; sqlite3_file_control(pDb, zDbName ? zDbName : "main", SQLITE_FCNTL_VFS_POINTER, &pVfs); return pVfs; } @@ -1263,11 +1259,11 @@ ** ** Returns 0 on success, an SQLITE_xxx code on error. Returns ** SQLITE_MISUSE if pDb is NULL. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_db_reset(sqlite3 *pDb){ +int sqlite3_wasm_db_reset(sqlite3 *pDb){ int rc = SQLITE_MISUSE; if( pDb ){ sqlite3_table_column_metadata(pDb, "main", 0, 0, 0, 0, 0, 0, 0); rc = sqlite3_db_config(pDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); if( 0==rc ){ @@ -1290,15 +1286,15 @@ ** code from the callback. Note that this is not thread-friendly: it ** expects that it will be the only thread reading the db file and ** takes no measures to ensure that is the case. ** ** This implementation appears to work fine, but -** sqlite3__wasm_db_serialize() is arguably the better way to achieve +** sqlite3_wasm_db_serialize() is arguably the better way to achieve ** this. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_db_export_chunked( sqlite3* pDb, +int sqlite3_wasm_db_export_chunked( sqlite3* pDb, int (*xCallback)(unsigned const char *zOut, int n) ){ sqlite3_int64 nSize = 0; sqlite3_int64 nPos = 0; sqlite3_file * pFile = 0; unsigned char buf[1024 * 8]; @@ -1345,11 +1341,11 @@ ** ** If `*pOut` is not NULL, the caller is responsible for passing it to ** sqlite3_free() to free it. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_db_serialize( sqlite3 *pDb, const char *zSchema, +int sqlite3_wasm_db_serialize( sqlite3 *pDb, const char *zSchema, unsigned char **pOut, sqlite3_int64 *nOut, unsigned int mFlags ){ unsigned char * z; if( !pDb || !pOut ) return SQLITE_MISUSE; if( nOut ) *nOut = 0; @@ -1368,11 +1364,11 @@ ** ** ACHTUNG: it was discovered on 2023-08-11 that, with SQLITE_DEBUG, ** this function's out-of-scope use of the sqlite3_vfs/file/io_methods ** APIs leads to triggering of assertions in the core library. Its use ** is now deprecated and VFS-specific APIs for importing files need to -** be found to replace it. sqlite3__wasm_posix_create_file() is +** be found to replace it. sqlite3_wasm_posix_create_file() is ** suitable for the "unix" family of VFSes. ** ** Creates a new file using the I/O API of the given VFS, containing ** the given number of bytes of the given data. If the file exists, it ** is truncated to the given length and populated with the given @@ -1409,11 +1405,11 @@ ** Design note: nData is an integer, instead of int64, for WASM ** portability, so that the API can still work in builds where BigInt ** support is disabled or unavailable. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_vfs_create_file( sqlite3_vfs *pVfs, +int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs, const char *zFilename, const unsigned char * pData, int nData ){ int rc; sqlite3_file *pFile = 0; @@ -1499,11 +1495,11 @@ ** i.e. Emscripten's virtual filesystem. Creates or truncates ** zFilename, appends pData bytes to it, and returns 0 on success or ** SQLITE_IOERR on error. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_posix_create_file( const char *zFilename, +int sqlite3_wasm_posix_create_file( const char *zFilename, const unsigned char * pData, int nData ){ int rc; FILE * pFile = 0; int fileExisted = 0; @@ -1522,21 +1518,21 @@ /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** Allocates sqlite3KvvfsMethods.nKeySize bytes from -** sqlite3__wasm_pstack_alloc() and returns 0 if that allocation fails, +** sqlite3_wasm_pstack_alloc() and returns 0 if that allocation fails, ** else it passes that string to kvstorageMakeKey() and returns a ** NUL-terminated pointer to that string. It is up to the caller to -** use sqlite3__wasm_pstack_restore() to free the returned pointer. +** use sqlite3_wasm_pstack_restore() to free the returned pointer. */ SQLITE_WASM_EXPORT -char * sqlite3__wasm_kvvfsMakeKeyOnPstack(const char *zClass, +char * sqlite3_wasm_kvvfsMakeKeyOnPstack(const char *zClass, const char *zKeyIn){ assert(sqlite3KvvfsMethods.nKeySize>24); char *zKeyOut = - (char *)sqlite3__wasm_pstack_alloc(sqlite3KvvfsMethods.nKeySize); + (char *)sqlite3_wasm_pstack_alloc(sqlite3KvvfsMethods.nKeySize); if(zKeyOut){ kvstorageMakeKey(zClass, zKeyIn, zKeyOut); } return zKeyOut; } @@ -1547,11 +1543,11 @@ ** ** Returns the pointer to the singleton object which holds the kvvfs ** I/O methods and associated state. */ SQLITE_WASM_EXPORT -sqlite3_kvvfs_methods * sqlite3__wasm_kvvfs_methods(void){ +sqlite3_kvvfs_methods * sqlite3_wasm_kvvfs_methods(void){ return &sqlite3KvvfsMethods; } /* ** This function is NOT part of the sqlite3 public API. It is strictly @@ -1562,11 +1558,11 @@ ** value of its 2nd argument. Returns the result of ** sqlite3_vtab_config(), or SQLITE_MISUSE if the 2nd arg is not a ** valid value. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_vtab_config(sqlite3 *pDb, int op, int arg){ +int sqlite3_wasm_vtab_config(sqlite3 *pDb, int op, int arg){ switch(op){ case SQLITE_VTAB_DIRECTONLY: case SQLITE_VTAB_INNOCUOUS: return sqlite3_vtab_config(pDb, op); case SQLITE_VTAB_CONSTRAINT_SUPPORT: @@ -1582,11 +1578,11 @@ ** ** Wrapper for the variants of sqlite3_db_config() which take ** (int,int*) variadic args. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){ +int sqlite3_wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){ switch(op){ case SQLITE_DBCONFIG_ENABLE_FKEY: case SQLITE_DBCONFIG_ENABLE_TRIGGER: case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: @@ -1615,11 +1611,11 @@ ** ** Wrapper for the variants of sqlite3_db_config() which take ** (void*,int,int) variadic args. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_db_config_pii(sqlite3 *pDb, int op, void * pArg1, int arg2, int arg3){ +int sqlite3_wasm_db_config_pii(sqlite3 *pDb, int op, void * pArg1, int arg2, int arg3){ switch(op){ case SQLITE_DBCONFIG_LOOKASIDE: return sqlite3_db_config(pDb, op, pArg1, arg2, arg3); default: return SQLITE_MISUSE; } @@ -1631,11 +1627,11 @@ ** ** Wrapper for the variants of sqlite3_db_config() which take ** (const char *) variadic args. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_db_config_s(sqlite3 *pDb, int op, const char *zArg){ +int sqlite3_wasm_db_config_s(sqlite3 *pDb, int op, const char *zArg){ switch(op){ case SQLITE_DBCONFIG_MAINDBNAME: return sqlite3_db_config(pDb, op, zArg); default: return SQLITE_MISUSE; } @@ -1648,11 +1644,11 @@ ** ** Binding for combinations of sqlite3_config() arguments which take ** a single integer argument. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_config_i(int op, int arg){ +int sqlite3_wasm_config_i(int op, int arg){ return sqlite3_config(op, arg); } /* ** This function is NOT part of the sqlite3 public API. It is strictly @@ -1660,11 +1656,11 @@ ** ** Binding for combinations of sqlite3_config() arguments which take ** two int arguments. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_config_ii(int op, int arg1, int arg2){ +int sqlite3_wasm_config_ii(int op, int arg1, int arg2){ return sqlite3_config(op, arg1, arg2); } /* ** This function is NOT part of the sqlite3 public API. It is strictly @@ -1672,32 +1668,43 @@ ** ** Binding for combinations of sqlite3_config() arguments which take ** a single i64 argument. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_config_j(int op, sqlite3_int64 arg){ +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 /* @@ -1720,11 +1727,11 @@ ** object fails, SQLITE_IOERR if mkdir() of the zMountPoint dir in ** the virtual FS fails. In builds compiled without SQLITE_ENABLE_WASMFS ** defined, SQLITE_NOTFOUND is returned without side effects. */ SQLITE_WASM_EXPORT -int sqlite3__wasm_init_wasmfs(const char *zMountPoint){ +int sqlite3_wasm_init_wasmfs(const char *zMountPoint){ static backend_t pOpfs = 0; if( !zMountPoint || !*zMountPoint ) zMountPoint = "/opfs"; if( !pOpfs ){ pOpfs = wasmfs_create_opfs_backend(); } @@ -1740,65 +1747,65 @@ } return pOpfs ? 0 : SQLITE_NOMEM; } #else SQLITE_WASM_EXPORT -int sqlite3__wasm_init_wasmfs(const char *zUnused){ +int sqlite3_wasm_init_wasmfs(const char *zUnused){ //emscripten_console_warn("WASMFS OPFS is not compiled in."); if(zUnused){/*unused*/} return SQLITE_NOTFOUND; } #endif /* __EMSCRIPTEN__ && SQLITE_ENABLE_WASMFS */ #if SQLITE_WASM_TESTS SQLITE_WASM_EXPORT -int sqlite3__wasm_test_intptr(int * p){ +int sqlite3_wasm_test_intptr(int * p){ return *p = *p * 2; } SQLITE_WASM_EXPORT -void * sqlite3__wasm_test_voidptr(void * p){ +void * sqlite3_wasm_test_voidptr(void * p){ return p; } SQLITE_WASM_EXPORT -int64_t sqlite3__wasm_test_int64_max(void){ +int64_t sqlite3_wasm_test_int64_max(void){ return (int64_t)0x7fffffffffffffff; } SQLITE_WASM_EXPORT -int64_t sqlite3__wasm_test_int64_min(void){ - return ~sqlite3__wasm_test_int64_max(); +int64_t sqlite3_wasm_test_int64_min(void){ + return ~sqlite3_wasm_test_int64_max(); } SQLITE_WASM_EXPORT -int64_t sqlite3__wasm_test_int64_times2(int64_t x){ +int64_t sqlite3_wasm_test_int64_times2(int64_t x){ return x * 2; } SQLITE_WASM_EXPORT -void sqlite3__wasm_test_int64_minmax(int64_t * min, int64_t *max){ - *max = sqlite3__wasm_test_int64_max(); - *min = sqlite3__wasm_test_int64_min(); +void sqlite3_wasm_test_int64_minmax(int64_t * min, int64_t *max){ + *max = sqlite3_wasm_test_int64_max(); + *min = sqlite3_wasm_test_int64_min(); /*printf("minmax: min=%lld, max=%lld\n", *min, *max);*/ } SQLITE_WASM_EXPORT -int64_t sqlite3__wasm_test_int64ptr(int64_t * p){ - /*printf("sqlite3__wasm_test_int64ptr( @%lld = 0x%llx )\n", (int64_t)p, *p);*/ +int64_t sqlite3_wasm_test_int64ptr(int64_t * p){ + /*printf("sqlite3_wasm_test_int64ptr( @%lld = 0x%llx )\n", (int64_t)p, *p);*/ return *p = *p * 2; } SQLITE_WASM_EXPORT -void sqlite3__wasm_test_stack_overflow(int recurse){ - if(recurse) sqlite3__wasm_test_stack_overflow(recurse); +void sqlite3_wasm_test_stack_overflow(int recurse){ + if(recurse) sqlite3_wasm_test_stack_overflow(recurse); } /* For testing the 'string:dealloc' whwasmutil.xWrap() conversion. */ SQLITE_WASM_EXPORT -char * sqlite3__wasm_test_str_hello(int fail){ +char * sqlite3_wasm_test_str_hello(int fail){ char * s = fail ? 0 : (char *)sqlite3_malloc(6); if(s){ memcpy(s, "hello", 5); s[5] = 0; } @@ -1829,16 +1836,16 @@ ** ** '#' Matches any sequence of one or more digits with an ** optional + or - sign in front, or a hexadecimal ** literal of the form 0x... */ -static int sqlite3__wasm_SQLTester_strnotglob(const char *zGlob, const char *z){ +static int sqlite3_wasm_SQLTester_strnotglob(const char *zGlob, const char *z){ int c, c2; int invert; int seen; typedef int (*recurse_f)(const char *,const char *); - static const recurse_f recurse = sqlite3__wasm_SQLTester_strnotglob; + static const recurse_f recurse = sqlite3_wasm_SQLTester_strnotglob; while( (c = (*(zGlob++)))!=0 ){ if( c=='*' ){ while( (c=(*(zGlob++))) == '*' || c=='?' ){ if( c=='?' && (*(z++))==0 ) return 0; @@ -1909,12 +1916,13 @@ } return *z==0; } SQLITE_WASM_EXPORT -int sqlite3__wasm_SQLTester_strglob(const char *zGlob, const char *z){ - return !sqlite3__wasm_SQLTester_strnotglob(zGlob, z); +int sqlite3_wasm_SQLTester_strglob(const char *zGlob, const char *z){ + return !sqlite3_wasm_SQLTester_strnotglob(zGlob, z); } + #endif /* SQLITE_WASM_TESTS */ #undef SQLITE_WASM_EXPORT 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. @@ -158,19 +154,18 @@ }; const toss = (...args)=>{throw new Error(args.join(' '))}; if(!config.worker) config.worker = callee.defaultConfig.worker; if('function'===typeof config.worker) config.worker = config.worker(); let dbId; - let promiserFunc; config.worker.onmessage = function(ev){ ev = ev.data; debug('worker1.onmessage',ev); let msgHandler = handlerMap[ev.messageId]; if(!msgHandler){ if(ev && 'sqlite3-api'===ev.type && 'worker1-ready'===ev.result) { /*fired one time when the Worker1 API initializes*/ - if(config.onready) config.onready(promiserFunc); + if(config.onready) config.onready(); return; } msgHandler = handlerMap[ev.type] /* check for exec per-row callback */; if(msgHandler && msgHandler.onrow){ msgHandler.onrow(ev); @@ -195,21 +190,21 @@ break; } try {msgHandler.resolve(ev)} catch(e){msgHandler.reject(e)} }/*worker.onmessage()*/; - return promiserFunc = function(/*(msgType, msgArgs) || (msgEnvelope)*/){ + return function(/*(msgType, msgArgs) || (msgEnvelope)*/){ let msg; if(1===arguments.length){ msg = arguments[0]; }else if(2===arguments.length){ msg = Object.create(null); 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 +244,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 +267,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,275 @@ +/* + 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(){ + 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.make ================================================================== --- ext/wasm/fiddle.make +++ ext/wasm/fiddle.make @@ -7,22 +7,20 @@ ######################################################################## # shell.c and its build flags... make-np-0 := make -C $(dir.top) -n -p make-np-1 := sed -e 's/(TOP)/(dir.top)/g' -# Extract SHELL_OPT and SHELL_DEP from the top-most makefile and import -# them as vars here... $(eval $(shell $(make-np-0) | grep -e '^SHELL_OPT ' | $(make-np-1))) -$(eval $(shell $(make-np-0) | grep -e '^SHELL_DEP ' | $(make-np-1))) +$(eval $(shell $(make-np-0) | grep -e '^SHELL_SRC ' | $(make-np-1))) # ^^^ can't do that in 1 invocation b/c newlines get stripped ifeq (,$(SHELL_OPT)) $(error Could not parse SHELL_OPT from $(dir.top)/Makefile.) endif -ifeq (,$(SHELL_DEP)) -$(error Could not parse SHELL_DEP from $(dir.top)/Makefile.) +ifeq (,$(SHELL_SRC)) +$(error Could not parse SHELL_SRC from $(dir.top)/Makefile.) endif -$(dir.top)/shell.c: $(SHELL_DEP) $(dir.top)/tool/mkshellc.tcl $(sqlite3.c) +$(dir.top)/shell.c: $(SHELL_SRC) $(dir.top)/tool/mkshellc.tcl $(sqlite3.c) $(MAKE) -C $(dir.top) shell.c # /shell.c ######################################################################## EXPORTED_FUNCTIONS.fiddle := $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle Index: ext/wasm/fiddle/fiddle-worker.js ================================================================== --- ext/wasm/fiddle/fiddle-worker.js +++ ext/wasm/fiddle/fiddle-worker.js @@ -164,14 +164,15 @@ return false; } stdout("SQLite version", capi.sqlite3_libversion(), capi.sqlite3_sourceid().substr(0,19)); stdout('Welcome to the "fiddle" shell.'); - if(capi.sqlite3_vfs_find("opfs")){ + if(sqlite3.opfs){ stdout("\nOPFS is available. To open a persistent db, use:\n\n", " .open file:name?vfs=opfs\n\nbut note that some", "features (e.g. upload) do not yet work with OPFS."); + sqlite3.opfs.registerVfs(); } stdout('\nEnter ".help" for usage hints.'); this.exec([ // initialization commands... '.nullvalue NULL', '.headers on' @@ -314,11 +315,11 @@ return; } }; console.warn("Unknown fiddle-worker message type:",ev); }; - + /** emscripten module for use with build mode -sMODULARIZE. */ const fiddleModule = { print: stdout, @@ -371,11 +372,13 @@ sqlite3 = _sqlite3; console.warn("Installing sqlite3 module globally (in Worker)", "for use in the dev console.", sqlite3); globalThis.sqlite3 = sqlite3; const dbVfs = sqlite3.wasm.xWrap('fiddle_db_vfs', "*", ['string']); - fiddleModule.fsUnlink = (fn)=>fiddleModule.FS.unlink(fn); + fiddleModule.fsUnlink = (fn)=>{ + return sqlite3.wasm.sqlite3_wasm_vfs_unlink(dbVfs(0), fn); + }; wMsg('fiddle-ready'); }).catch(e=>{ console.error("Fiddle worker init failed:",e); }); })(); Index: ext/wasm/fiddle/fiddle.js ================================================================== --- ext/wasm/fiddle/fiddle.js +++ ext/wasm/fiddle/fiddle.js @@ -401,14 +401,12 @@ // Unhide all elements which start out hidden EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden')); E('#btn-reset').addEventListener('click',()=>SF.resetDb()); const taInput = E('#input'); const btnClearIn = E('#btn-clear'); - const selectExamples = E('#select-examples'); btnClearIn.addEventListener('click',function(){ taInput.value = ''; - selectExamples.selectedIndex = 0; },false); // Ctrl-enter and shift-enter both run the current SQL. taInput.addEventListener('keydown',function(ev){ if((ev.ctrlKey || ev.shiftKey) && 13 === ev.keyCode){ ev.preventDefault(); @@ -733,19 +731,20 @@ "-- ================================================\n", ".help\n" ]}, //{name: "Timer on", sql: ".timer on"}, // ^^^ re-enable if emscripten re-enables getrusage() - {name: "Box Mode", sql: ".mode box"}, {name: "Setup table T", sql:[ ".nullvalue NULL\n", "CREATE TABLE t(a,b);\n", "INSERT INTO t(a,b) VALUES('abc',123),('def',456),(NULL,789),('ghi',012);\n", "SELECT * FROM t;\n" ]}, - {name: "sqlite_schema", sql: "select * from sqlite_schema"}, - {name: "Mandelbrot", sql:[ + {name: "Table list", sql: ".tables"}, + {name: "Box Mode", sql: ".mode box"}, + {name: "JSON Mode", sql: ".mode json"}, + {name: "Mandlebrot", sql:[ "WITH RECURSIVE", " xaxis(x) AS (VALUES(-2.0) UNION ALL SELECT x+0.05 FROM xaxis WHERE x<1.2),\n", " yaxis(y) AS (VALUES(-1.0) UNION ALL SELECT y+0.1 FROM yaxis WHERE y<1.0),\n", " m(iter, cx, cy, x, y) AS (\n", " SELECT 0, x, y, 0.0, 0.0 FROM xaxis, yaxis\n", @@ -759,17 +758,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.