Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 15ed002aed12556a To 7fe11274fc05a377
2024-05-23
| ||
23:34 | Increase the version number to 3.46.1 check-in: 170e3a91d5 user: drh tags: branch-3.46 | |
23:30 | Fix the window-function group_concat() so that it returns an empty string if it has one or more empty string inputs. check-in: 7fe11274fc user: drh tags: branch-3.46 | |
23:26 | Fix the window-function group_concat() so that it returns an empty string if it has one or more empty string inputs. This fixes a bug introduced by [c6da39115d3e2b0f] on 2019-03-26 (version 3.28.0) and reported by [forum:/forumpost/bf8f43aa522c2299|forum post bf8f43aa522c2299]. check-in: cec6bb3fc9 user: drh tags: trunk | |
13:25 | Version 3.46.0 check-in: 96c92aba00 user: drh tags: trunk, release, major-release, version-3.46.0 | |
2023-12-28
| ||
21:02 | Enable SQLITE_DIRECT_OVERFLOW_READ unless it is specifically disabled using the -DSQLITE_DIRECT_OVERFLOW_READ=0 compile-time option. check-in: 630604a4e6 user: drh tags: trunk | |
20:54 | Update fts5origintext4.test to work with SQLITE_DIRECT_OVERFLOW_READ. check-in: 15ed002aed user: dan tags: trunk | |
19:18 | Performance improvement by unwinding a loop in jsonAppendString(). check-in: 190ab3c084 user: drh tags: trunk | |
Changes to Makefile.in.
︙ | ︙ | |||
414 415 416 417 418 419 420 421 422 423 424 425 426 427 | $(TOP)/src/test_wsd.c \ $(TOP)/ext/fts3/fts3_term.c \ $(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/rbu/test_rbu.c # Statically linked extensions # TESTSRC += \ $(TOP)/ext/expert/sqlite3expert.c \ $(TOP)/ext/expert/test_expert.c \ | > > | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | $(TOP)/src/test_wsd.c \ $(TOP)/ext/fts3/fts3_term.c \ $(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 += \ $(TOP)/ext/expert/sqlite3expert.c \ $(TOP)/ext/expert/test_expert.c \ |
︙ | ︙ | |||
811 812 813 814 815 816 817 | rm tsrc/sqlite.h.in tsrc/parse.y $(TCLSH_CMD) $(TOP)/tool/vdbe-compress.tcl $(OPTS) <tsrc/vdbe.c >vdbe.new 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 | | | | 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 | rm tsrc/sqlite.h.in tsrc/parse.y $(TCLSH_CMD) $(TOP)/tool/vdbe-compress.tcl $(OPTS) <tsrc/vdbe.c >vdbe.new 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) 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 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) sqlite3ext.h: .target_source cp tsrc/sqlite3ext.h . tclsqlite3.c: sqlite3.c echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c cat sqlite3.c >>tclsqlite3.c |
︙ | ︙ | |||
1148 1149 1150 1151 1152 1153 1154 | cat $(TOP)/VERSION | $(TCLSH_CMD) $(TOP)/tool/replace.tcl exact . , >>$@ echo '#endif' >>sqlite3rc.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash$(BEXE) >keywordhash.h | | | | | | > > | > | | | | | | | | | | | | < < | | | | | | | > | | 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 | cat $(TOP)/VERSION | $(TCLSH_CMD) $(TOP)/tool/replace.tcl exact . , >>$@ echo '#endif' >>sqlite3rc.h 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 $(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c # Rules to build the extension objects. # |
︙ | ︙ | |||
1304 1305 1306 1307 1308 1309 1310 | TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION)) testfixture$(TEXE): has_tclsh85 $(TESTFIXTURE_SRC) $(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \ -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS) | | | | 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 | TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION)) 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) testprogs: $(TESTPROGS) 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 alltest: $(TESTPROGS) ./testfixture$(TEXE) $(TOP)/test/all.test $(TESTOPTS) |
︙ | ︙ |
Changes to Makefile.msc.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # <<mark>> # Set this non-0 to create and use the SQLite amalgamation file. # !IFNDEF USE_AMALGAMATION USE_AMALGAMATION = 1 !ENDIF # <</mark>> # Set this non-0 to enable full warnings (-W4, etc) when compiling. # !IFNDEF USE_FULLWARN USE_FULLWARN = 1 !ENDIF | > > > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # <<mark>> # Set this non-0 to create and use the SQLite amalgamation file. # !IFNDEF USE_AMALGAMATION USE_AMALGAMATION = 1 !ENDIF # <</mark>> # 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 |
︙ | ︙ | |||
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 | $(TOP)\ext\misc\spellfix.c \ $(TOP)\ext\misc\totype.c \ $(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\recover\dbdata.c # If use of zlib is enabled, add the "zipfile.c" source file. # !IF $(USE_ZLIB)!=0 TESTEXT = $(TESTEXT) $(TOP)\ext\misc\zipfile.c !ENDIF | > > | 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 | $(TOP)\ext\misc\spellfix.c \ $(TOP)\ext\misc\totype.c \ $(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 TESTEXT = $(TESTEXT) $(TOP)\ext\misc\zipfile.c !ENDIF |
︙ | ︙ | |||
1824 1825 1826 1827 1828 1829 1830 | # <</block2>> $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H) $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) shell.c $(SHELL_CORE_SRC) \ /link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) # <<mark>> | | | | 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 | # <</block2>> $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H) $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) shell.c $(SHELL_CORE_SRC) \ /link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) # <<mark>> sqldiff.exe: $(TOP)\tool\sqldiff.c $(TOP)\ext\consio\console_io.h $(TOP)\ext\consio\console_io.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS) $(LTLINK) $(NO_WARN) -I$(TOP)\ext\consio $(TOP)\tool\sqldiff.c $(TOP)\ext\consio\console_io.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS) dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSCRUB_STANDALONE=1 $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) |
︙ | ︙ | |||
1909 1910 1911 1912 1913 1914 1915 | copy /B tsrc\fts5.h +,, del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL $(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 | | | 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 | copy /B tsrc\fts5.h +,, del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL $(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) sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl $(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl # <</mark>> # Rule to build the amalgamation # |
︙ | ︙ | |||
2259 2260 2261 2262 2263 2264 2265 | mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c $(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) \ $(TOP)\tool\mkkeywordhash.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS) keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe .\mkkeywordhash.exe > keywordhash.h | | | | | | > > > > | | | | | | | | | | | | | | | | | | | > | | | | 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 | mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c $(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) \ $(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 # 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 !ENDIF shell.c: $(SHELL_DEP) $(TOP)\tool\mkshellc.tcl $(TCLSH_CMD) $(TOP)\tool\mkshellc.tcl > shell.c zlib: pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) && popd # Rules to build the extension objects. # |
︙ | ︙ | |||
2478 2479 2480 2481 2482 2483 2484 | extensiontest: testfixture.exe testloadext.dll @set PATH=$(LIBTCLPATH);$(PATH) .\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 | | | | 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 | extensiontest: testfixture.exe testloadext.dll @set PATH=$(LIBTCLPATH);$(PATH) .\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 testprogs: $(TESTPROGS) srcck1.exe fuzzcheck.exe sessionfuzz.exe fulltest: alltest fuzztest alltest: $(TESTPROGS) @set PATH=$(LIBTCLPATH);$(PATH) .\testfixture.exe $(TOP)\test\all.test $(TESTOPTS) |
︙ | ︙ | |||
2535 2536 2537 2538 2539 2540 2541 | devtest: testfixture.exe fuzztest testrunner mdevtest: $(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest # Testing for a release # | | | | 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 | devtest: testfixture.exe fuzztest testrunner mdevtest: $(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest # Testing for a release # releasetest: testfixture.exe testfixture.exe $(TOP)\test\testrunner.tcl release smoketest: $(TESTPROGS) @set PATH=$(LIBTCLPATH);$(PATH) .\testfixture.exe $(TOP)\test\main.test $(TESTOPTS) shelltest: $(TESTPROGS) .\testfixture.exe $(TOP)\test\permutations.test shell sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\consio\console_io.h $(TOP)\ext\consio\console_io.c $(SQLITE_TCL_DEP) $(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in > $@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS) $(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \ /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS) sqltclsh.c: sqlite3.c $(TOP)\src\tclsqlite.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in |
︙ | ︙ |
Changes to VERSION.
|
| | | 1 | 3.46.0 |
Added art/icon-243x273.gif.
cannot compute difference between binary files
Added art/icon-80x90.gif.
cannot compute difference between binary files
Changes to autoconf/Makefile.msc.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ############################################################################### # The toplevel directory of the source tree. This is the directory # that contains this "Makefile.msc". # TOP = . # Set this non-0 to enable full warnings (-W4, etc) when compiling. # !IFNDEF USE_FULLWARN USE_FULLWARN = 1 !ENDIF | > > > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ############################################################################### # The toplevel directory of the source tree. This is the directory # 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 |
︙ | ︙ |
Changes to autoconf/tea/configure.ac.
︙ | ︙ | |||
15 16 17 18 19 20 21 | # This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION # set as provided. These will also be added as -D defs in your Makefile # so you can encode the package version directly into the source files. # This will also define a special symbol for Windows (BUILD_<PACKAGE_NAME> # so that we create the export library with the dll. #----------------------------------------------------------------------- | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION # set as provided. These will also be added as -D defs in your Makefile # so you can encode the package version directly into the source files. # This will also define a special symbol for Windows (BUILD_<PACKAGE_NAME> # so that we create the export library with the dll. #----------------------------------------------------------------------- AC_INIT([sqlite],[3.46.0]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. # This will define a ${TEA_PLATFORM} variable == "unix" or "windows" # as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE. #-------------------------------------------------------------------- |
︙ | ︙ |
Changes to configure.
1 2 | #! /bin/sh # Guess values for system-dependent variables and create Makefiles. | | | 1 2 3 4 5 6 7 8 9 10 | #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for sqlite 3.46.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. |
︙ | ︙ | |||
722 723 724 725 726 727 728 | subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' | | | | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' PACKAGE_VERSION='3.46.0' PACKAGE_STRING='sqlite 3.46.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include <stdio.h> #ifdef HAVE_SYS_TYPES_H |
︙ | ︙ | |||
1468 1469 1470 1471 1472 1473 1474 | # # Report the --help message. # 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 | | | 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 | # # Report the --help message. # 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. 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. Defaults for the options are specified in brackets. |
︙ | ︙ | |||
1533 1534 1535 1536 1537 1538 1539 | --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in | | | 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 | --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of sqlite 3.46.0:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] |
︙ | ︙ | |||
1664 1665 1666 1667 1668 1669 1670 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF | | | 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF sqlite configure 3.46.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit |
︙ | ︙ | |||
2083 2084 2085 2086 2087 2088 2089 | eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # 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. | | | 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 | eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # 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 generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { |
︙ | ︙ | |||
12477 12478 12479 12480 12481 12482 12483 | test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 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=" | | | 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 12491 | test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 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 generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ |
︙ | ︙ | |||
12543 12544 12545 12546 12547 12548 12549 | Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ | | | 12543 12544 12545 12546 12547 12548 12549 12550 12551 12552 12553 12554 12555 12556 12557 | Report bugs to the package provider." _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 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 gives unlimited permission to copy, distribute and modify it." |
︙ | ︙ |
Changes to doc/lemon.html.
︙ | ︙ | |||
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 | <li><tt><a href='#default_destructor'>%default_destructor</a></tt> <li><tt><a href='#default_type'>%default_type</a></tt> <li><tt><a href='#destructor'>%destructor</a></tt> <li><tt><a href='#pifdef'>%else</a></tt> <li><tt><a href='#pifdef'>%endif</a></tt> <li><tt><a href='#extraarg'>%extra_argument</a></tt> <li><tt><a href='#pfallback'>%fallback</a></tt> <li><tt><a href='#pifdef'>%if</a></tt> <li><tt><a href='#pifdef'>%ifdef</a></tt> <li><tt><a href='#pifdef'>%ifndef</a></tt> <li><tt><a href='#pinclude'>%include</a></tt> <li><tt><a href='#pleft'>%left</a></tt> <li><tt><a href='#pname'>%name</a></tt> <li><tt><a href='#pnonassoc'>%nonassoc</a></tt> <li><tt><a href='#parse_accept'>%parse_accept</a></tt> <li><tt><a href='#parse_failure'>%parse_failure</a></tt> <li><tt><a href='#pright'>%right</a></tt> <li><tt><a href='#stack_overflow'>%stack_overflow</a></tt> <li><tt><a href='#stack_size'>%stack_size</a></tt> <li><tt><a href='#start_symbol'>%start_symbol</a></tt> <li><tt><a href='#syntax_error'>%syntax_error</a></tt> <li><tt><a href='#token'>%token</a></tt> <li><tt><a href='#token_class'>%token_class</a></tt> <li><tt><a href='#token_destructor'>%token_destructor</a></tt> | > > | 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 | <li><tt><a href='#default_destructor'>%default_destructor</a></tt> <li><tt><a href='#default_type'>%default_type</a></tt> <li><tt><a href='#destructor'>%destructor</a></tt> <li><tt><a href='#pifdef'>%else</a></tt> <li><tt><a href='#pifdef'>%endif</a></tt> <li><tt><a href='#extraarg'>%extra_argument</a></tt> <li><tt><a href='#pfallback'>%fallback</a></tt> <li><tt><a href='#reallc'>%free</a></tt> <li><tt><a href='#pifdef'>%if</a></tt> <li><tt><a href='#pifdef'>%ifdef</a></tt> <li><tt><a href='#pifdef'>%ifndef</a></tt> <li><tt><a href='#pinclude'>%include</a></tt> <li><tt><a href='#pleft'>%left</a></tt> <li><tt><a href='#pname'>%name</a></tt> <li><tt><a href='#pnonassoc'>%nonassoc</a></tt> <li><tt><a href='#parse_accept'>%parse_accept</a></tt> <li><tt><a href='#parse_failure'>%parse_failure</a></tt> <li><tt><a href='#pright'>%right</a></tt> <li><tt><a href='#reallc'>%realloc</a></tt> <li><tt><a href='#stack_overflow'>%stack_overflow</a></tt> <li><tt><a href='#stack_size'>%stack_size</a></tt> <li><tt><a href='#start_symbol'>%start_symbol</a></tt> <li><tt><a href='#syntax_error'>%syntax_error</a></tt> <li><tt><a href='#token'>%token</a></tt> <li><tt><a href='#token_class'>%token_class</a></tt> <li><tt><a href='#token_destructor'>%token_destructor</a></tt> |
︙ | ︙ | |||
1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 | period. This directive specifies that the identified token should match any input token.</p> <p>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.</p> <a id='errors'></a> <h2>5.0 Error Processing</h2> <p>After extensive experimentation over several years, it has been discovered that the error recovery strategy used by yacc is about as good as it gets. And so that is what Lemon uses.</p> | > > > > > > > > > > > > > > > | 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 | period. This directive specifies that the identified token should match any input token.</p> <p>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.</p> <a id='reallc'></a> <h4>4.4.26 The <tt>%realloc</tt> and <tt>%free</tt> directives</h4> <p>The <tt>%realloc</tt> and <tt>%free</tt> 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. <p>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 <tt>%stack_size</tt> or the -DYYSTACKDEPTH compile-time flag. <a id='errors'></a> <h2>5.0 Error Processing</h2> <p>After extensive experimentation over several years, it has been discovered that the error recovery strategy used by yacc is about as good as it gets. And so that is what Lemon uses.</p> |
︙ | ︙ | |||
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 | <p>If the parser pops its stack until the stack is empty, and it still is unable to shift the error symbol, then the <tt><a href='#parse_failure'>%parse_failure</a></tt> 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.</p> <a id='history'></a> <h2>6.0 History of Lemon</h2> <p>Lemon was originally written by Richard Hipp sometime in the late 1980s on a Sun4 Workstation using K&R C. There was a companion LL(1) parser generator program named "Lime". | > | 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 | <p>If the parser pops its stack until the stack is empty, and it still is unable to shift the error symbol, then the <tt><a href='#parse_failure'>%parse_failure</a></tt> 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.</p> <a id='history'></a> <h2>6.0 History of Lemon</h2> <p>Lemon was originally written by Richard Hipp sometime in the late 1980s on a Sun4 Workstation using K&R C. There was a companion LL(1) parser generator program named "Lime". |
︙ | ︙ |
Changes to doc/testrunner.md.
︙ | ︙ | |||
13 14 15 16 17 18 19 | <li> 3. <a href=#source_code_tests>Source Tests</a> <ul type=none> <li> 3.1. <a href=#commands_to_run_tests>Commands to Run SQLite Tests</a> <li> 3.2. <a href=#zipvfs_tests>Running ZipVFS Tests</a> <li> 3.3. <a href=#source_code_test_failures>Investigating Source Code Test Failures</a> </ul> <li> 4. <a href=#testrunner_options>Extra testrunner.tcl Options</a> | < | | > > > | > | > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <li> 3. <a href=#source_code_tests>Source Tests</a> <ul type=none> <li> 3.1. <a href=#commands_to_run_tests>Commands to Run SQLite Tests</a> <li> 3.2. <a href=#zipvfs_tests>Running ZipVFS Tests</a> <li> 3.3. <a href=#source_code_test_failures>Investigating Source Code Test Failures</a> </ul> <li> 4. <a href=#testrunner_options>Extra testrunner.tcl Options</a> <li> 5. <a href=#cpu_cores>Controlling CPU Core Utilization</a> </ul> <a name=overview></a> # 1. Overview 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` 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.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: ``` SELECT * FROM script WHERE state='failed' |
︙ | ︙ | |||
56 57 58 59 60 61 62 | ``` watch ./testfixture $(TESTDIR)/testrunner.tcl status ``` in another terminal is a good way to keep an eye on a long running test. | | | | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | ``` 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 to run tests (see "Binary Tests" below). Sometimes it builds testfixture and other binaries in specific configurations to test (see "Source Tests"). <a name=binary_tests></a> # 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 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 these tests is therefore: 1. Build the "testfixture" (or "testfixture.exe" for windows) binary using whatever method seems convenient. 2. Test the binary built in step 1 by running testrunner.tcl with it, perhaps with various options. |
︙ | ︙ | |||
189 190 191 192 193 194 195 | TODO: ./configure + Makefile.msc build systems. <a name=commands_to_run_tests></a> ## 3.1. Commands to Run SQLite Tests The **mdevtest** command is equivalent to running the veryquick tests and | | | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | TODO: ./configure + Makefile.msc build systems. <a name=commands_to_run_tests></a> ## 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 with debugging enabled and one without: ``` tclsh $TESTDIR/testrunner.tcl mdevtest ``` In other words, it is equivalent to running: |
︙ | ︙ | |||
279 280 281 282 283 284 285 | tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh # 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 | | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh # 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, or else to build a testfixture (or testfixture.exe) binary with which to run a Tcl test script, as <a href=#binary_test_failures>described above</a>. <a name=testrunner_options></a> # 4. Extra testrunner.tcl Options The testrunner.tcl script options in this section may be used with both source |
︙ | ︙ | |||
305 306 307 308 309 310 311 312 313 314 315 316 317 318 | or running any tests. Instead, it just writes the shell commands that it would normally execute into the testrunner.log file. Example: ``` # Log the shell commmands that make up the mdevtest test. tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest" ``` <a name=cpu_cores></a> # 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. | > > > > > > > > > > | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | or running any tests. Instead, it just writes the shell commands that it would normally execute into the testrunner.log file. Example: ``` # 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 ``` <a name=cpu_cores></a> # 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. |
︙ | ︙ | |||
335 336 337 338 339 340 341 | The number of jobs may also be changed while an instance of testrunner.tcl is running by exucuting the following command from the directory containing the testrunner.log and testrunner.db files: ``` $ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS ``` | < < < | 350 351 352 353 354 355 356 | The number of jobs may also be changed while an instance of testrunner.tcl is running by exucuting the following command from the directory containing the testrunner.log and testrunner.db files: ``` $ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS ``` |
Changes to ext/consio/console_io.c.
︙ | ︙ | |||
20 21 22 23 24 25 26 | #ifndef SHELL_NO_SYSINC # include <stdarg.h> # include <string.h> # include <stdlib.h> # include <limits.h> # include <assert.h> | | > > | > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #ifndef SHELL_NO_SYSINC # include <stdarg.h> # include <string.h> # include <stdlib.h> # include <limits.h> # include <assert.h> # 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 <io.h> # include <fcntl.h> # undef WIN32_LEAN_AND_MEAN |
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 | return rv; # else ppst->pf = pf; ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); return ppst->reachesConsole; # 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) # define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ | > > > > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | return rv; # else ppst->pf = pf; 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) # define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ |
︙ | ︙ | |||
670 671 672 673 674 675 676 677 678 | # endif return fgets(cBuf, ncMax, pfIn); # if CIO_WIN_WC_XLATE } # endif } #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ #undef SHELL_INVALID_FILE_PTR | > > > > | 679 680 681 682 683 684 685 686 687 688 689 690 691 | # endif return fgets(cBuf, ncMax, pfIn); # if CIO_WIN_WC_XLATE } # endif } #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ #if defined(_MSC_VER) # pragma warning(default : 4204) #endif #undef SHELL_INVALID_FILE_PTR |
Changes to ext/consio/console_io.h.
︙ | ︙ | |||
24 25 26 27 28 29 30 | ** This code may change in tandem with other project code as needed. ** ** When this .h file and its companion .c are directly incorporated into ** a source conglomeration (such as shell.c), the preprocessor symbol ** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O ** translation for Windows is effected for the build. */ | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ** This code may change in tandem with other project code as needed. ** ** When this .h file and its companion .c are directly incorporated into ** a source conglomeration (such as shell.c), the preprocessor symbol ** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O ** translation for Windows is effected for the build. */ #define HAVE_CONSOLE_IO_H 1 #ifndef SQLITE_INTERNAL_LINKAGE # define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */ # include <stdio.h> #else # define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ #endif |
︙ | ︙ |
Changes to ext/expert/expert1.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2009 Nov 11 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the CLI shell tool. Specifically, # the ".recommend" command. # # # Test plan: | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2009 Nov 11 # # 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. # #*********************************************************************** # TESTRUNNER: shell # # The focus of this file is testing the CLI shell tool. Specifically, # the ".recommend" command. # # # Test plan: |
︙ | ︙ |
Changes to ext/expert/sqlite3expert.c.
︙ | ︙ | |||
1944 1945 1946 1947 1948 1949 1950 | #endif /* 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_%%'" | | | 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 | #endif /* 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" ); 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); } idxFinalize(&rc, pSql); } |
︙ | ︙ |
Changes to ext/fts3/fts3.c.
︙ | ︙ | |||
4002 4003 4004 4005 4006 4007 4008 | return 0; } /* ** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual ** table. */ | | < | | < < | < < < | < < < | < < < | > > > > > | | | 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 | return 0; } /* ** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual ** table. */ static int fts3IntegrityMethod( sqlite3_vtab *pVtab, /* The virtual table to be checked */ const char *zSchema, /* Name of schema in which pVtab lives */ 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 bOk = 0; UNUSED_PARAMETER(isQuick); rc = sqlite3Fts3IntegrityCheck(p, &bOk); assert( rc!=SQLITE_CORRUPT_VTAB ); if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ *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 ){ *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; } static const sqlite3_module fts3Module = { /* iVersion */ 4, /* xCreate */ fts3CreateMethod, |
︙ | ︙ | |||
4064 4065 4066 4067 4068 4069 4070 | /* xRollback */ fts3RollbackMethod, /* xFindFunction */ fts3FindFunctionMethod, /* xRename */ fts3RenameMethod, /* xSavepoint */ fts3SavepointMethod, /* xRelease */ fts3ReleaseMethod, /* xRollbackTo */ fts3RollbackToMethod, /* xShadowName */ fts3ShadowName, | | | 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 | /* xRollback */ fts3RollbackMethod, /* xFindFunction */ fts3FindFunctionMethod, /* xRename */ fts3RenameMethod, /* xSavepoint */ fts3SavepointMethod, /* xRelease */ fts3ReleaseMethod, /* xRollbackTo */ fts3RollbackToMethod, /* xShadowName */ fts3ShadowName, /* xIntegrity */ fts3IntegrityMethod, }; /* ** This function is registered as the module destructor (called when an ** FTS3 enabled database connection is closed). It frees the memory ** allocated for the tokenizer hash table. */ |
︙ | ︙ |
Changes to ext/fts3/fts3Int.h.
︙ | ︙ | |||
648 649 650 651 652 653 654 655 656 657 | #ifndef SQLITE_DISABLE_FTS3_UNICODE int sqlite3FtsUnicodeFold(int, int); int sqlite3FtsUnicodeIsalnum(int); int sqlite3FtsUnicodeIsdiacritic(int); #endif int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ | > > | 648 649 650 651 652 653 654 655 656 657 658 659 | #ifndef SQLITE_DISABLE_FTS3_UNICODE int sqlite3FtsUnicodeFold(int, int); int sqlite3FtsUnicodeIsalnum(int); int sqlite3FtsUnicodeIsdiacritic(int); #endif int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk); #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ |
Changes to ext/fts3/fts3_snippet.c.
︙ | ︙ | |||
442 443 444 445 446 447 448 | if( (mCover|mCovered)&mPhrase ){ iScore++; }else{ iScore += 1000; } mCover |= mPhrase; | | | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | if( (mCover|mCovered)&mPhrase ){ iScore++; }else{ iScore += 1000; } mCover |= mPhrase; for(j=0; j<pPhrase->nToken && j<pIter->nSnippet; j++){ mHighlight |= (mPos>>j); } if( 0==(*pCsr & 0x0FE) ) break; fts3GetDeltaPosition(&pCsr, &iCsr); } } |
︙ | ︙ |
Changes to ext/fts3/fts3_write.c.
︙ | ︙ | |||
5290 5291 5292 5293 5294 5295 5296 | ** content table. If no error occurs and the contents do match, set *pbOk ** to true and return SQLITE_OK. Or if the contents do not match, set *pbOk ** to false before returning. ** ** If an error occurs (e.g. an OOM or IO error), return an SQLite error ** code. The final value of *pbOk is undefined in this case. */ | | | 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 | ** content table. If no error occurs and the contents do match, set *pbOk ** to true and return SQLITE_OK. Or if the contents do not match, set *pbOk ** to false before returning. ** ** If an error occurs (e.g. an OOM or IO error), return an SQLite error ** code. The final value of *pbOk is undefined in this case. */ int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){ int rc = SQLITE_OK; /* Return code */ u64 cksum1 = 0; /* Checksum based on FTS index contents */ u64 cksum2 = 0; /* Checksum based on %_content contents */ sqlite3_stmt *pAllLangid = 0; /* Statement to return all language-ids */ /* This block calculates the checksum according to the FTS index. */ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); |
︙ | ︙ | |||
5368 5369 5370 5371 5372 5373 5374 | } } } sqlite3_finalize(pStmt); } | > > | > > > | 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 | } } } sqlite3_finalize(pStmt); } if( rc==SQLITE_CORRUPT_VTAB ){ rc = SQLITE_OK; *pbOk = 0; }else{ *pbOk = (rc==SQLITE_OK && cksum1==cksum2); } return rc; } /* ** Run the integrity-check. If no error occurs and the current contents of ** the FTS index are correct, return SQLITE_OK. Or, if the contents of the ** FTS index are incorrect, return SQLITE_CORRUPT_VTAB. |
︙ | ︙ | |||
5408 5409 5410 5411 5412 5413 5414 | ** passed. */ static int fts3DoIntegrityCheck( Fts3Table *p /* FTS3 table handle */ ){ int rc; int bOk = 0; | | | 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 | ** passed. */ static int fts3DoIntegrityCheck( Fts3Table *p /* FTS3 table handle */ ){ int rc; int bOk = 0; rc = sqlite3Fts3IntegrityCheck(p, &bOk); if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB; return rc; } /* ** Handle a 'special' INSERT of the form: ** |
︙ | ︙ |
Changes to ext/fts5/fts5.h.
︙ | ︙ | |||
51 52 53 54 55 56 57 | const unsigned char *b; }; /* ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): | | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | const unsigned char *b; }; /* ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): ** Return a copy of the pUserData pointer passed to the xCreateFunction() ** API when the extension function was registered. ** ** 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 ** the total number of tokens in column iCol, considering all rows in ** the FTS5 table. |
︙ | ︙ |
Changes to ext/fts5/fts5_index.c.
︙ | ︙ | |||
6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 | ** argument bFrom is false, then the iterator is advanced to the next ** entry. Or, if bFrom is true, it is advanced to the first entry with ** a rowid of iFrom or greater. */ static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ int ii; Fts5TokenDataIter *pT = pIter->pTokenDataIter; for(ii=0; ii<pT->nIter; ii++){ Fts5Iter *p = pT->apIter[ii]; if( p->base.bEof==0 && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom)) ){ | > | | | > | > | 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 | ** argument bFrom is false, then the iterator is advanced to the next ** entry. Or, if bFrom is true, it is advanced to the first entry with ** 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; ii<pT->nIter; ii++){ Fts5Iter *p = pT->apIter[ii]; if( p->base.bEof==0 && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom)) ){ fts5MultiIterNext(pIndex, p, bFrom, iFrom); while( bFrom && p->base.bEof==0 && p->base.iRowid<iFrom && pIndex->rc==SQLITE_OK ){ fts5MultiIterNext(pIndex, p, 0, 0); } } } if( pIndex->rc==SQLITE_OK ){ fts5IterSetOutputsTokendata(pIter); } } /* ** If the segment-iterator passed as the first argument is at EOF, then ** set pIter->term to a copy of buffer pTerm. */ static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ |
︙ | ︙ |
Changes to ext/fts5/fts5_main.c.
︙ | ︙ | |||
1582 1583 1584 1585 1586 1587 1588 | #ifdef SQLITE_DEBUG }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){ pConfig->bPrefixIndex = sqlite3_value_int(pVal); #endif }else if( 0==sqlite3_stricmp("flush", zCmd) ){ rc = sqlite3Fts5FlushToDisk(&pTab->p); }else{ | > > | > | 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 | #ifdef SQLITE_DEBUG }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){ pConfig->bPrefixIndex = sqlite3_value_int(pVal); #endif }else if( 0==sqlite3_stricmp("flush", zCmd) ){ rc = sqlite3Fts5FlushToDisk(&pTab->p); }else{ rc = sqlite3Fts5FlushToDisk(&pTab->p); if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); } if( rc==SQLITE_OK ){ rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError); } if( rc==SQLITE_OK ){ if( bError ){ rc = SQLITE_ERROR; }else{ |
︙ | ︙ | |||
2727 2728 2729 2730 2731 2732 2733 | ** Discard the contents of the pending terms table. */ static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; int rc = SQLITE_OK; fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); fts5TripCursors(pTab); | < > | 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 | ** Discard the contents of the pending terms table. */ static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; int rc = SQLITE_OK; fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); fts5TripCursors(pTab); if( (iSavepoint+1)<=pTab->iSavepoint ){ pTab->p.pConfig->pgsz = 0; rc = sqlite3Fts5StorageRollback(pTab->pStorage); } return rc; } /* ** Register a new auxiliary function with global context pGlobal. |
︙ | ︙ | |||
2964 2965 2966 2967 2968 2969 2970 | sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */ const char *zSchema, /* Name of schema in which this table lives */ const char *zTabname, /* Name of the table itself */ int isQuick, /* True if this is a quick-check */ char **pzErr /* Write error message here */ ){ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | < < < > | < < < < < > | > | | | 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 | sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */ const char *zSchema, /* Name of schema in which this table lives */ const char *zTabname, /* Name of the table itself */ int isQuick, /* True if this is a quick-check */ char **pzErr /* Write error message here */ ){ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; int rc; assert( pzErr!=0 && *pzErr==0 ); 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; } static int fts5Init(sqlite3 *db){ static const sqlite3_module fts5Mod = { /* iVersion */ 4, /* xCreate */ fts5CreateMethod, /* xConnect */ fts5ConnectMethod, |
︙ | ︙ |
Changes to ext/fts5/fts5_tcl.c.
︙ | ︙ | |||
1165 1166 1167 1168 1169 1170 1171 | }; /* ** Delete the OriginTextCtx object indicated by the only argument. */ static void f5tOrigintextTokenizerDelete(void *pCtx){ OriginTextCtx *p = (OriginTextCtx*)pCtx; | | | 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 | }; /* ** Delete the OriginTextCtx object indicated by the only argument. */ static void f5tOrigintextTokenizerDelete(void *pCtx){ OriginTextCtx *p = (OriginTextCtx*)pCtx; ckfree((char*)p); } static int f5tOrigintextCreate( void *pCtx, const char **azArg, int nArg, Fts5Tokenizer **ppOut |
︙ | ︙ |
Changes to ext/fts5/test/fts5corrupt5.test.
︙ | ︙ | |||
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 | | 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version. | end crash-d57c01958e48ab.db }]} {} do_catchsql_test 8.1 { SELECT rowid FROM t1('a* NOT ý') ; } {0 {1 2 3 4 5 6 7}} sqlite3_fts5_may_be_corrupt 0 finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 | | 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version. | end crash-d57c01958e48ab.db }]} {} do_catchsql_test 8.1 { SELECT rowid FROM t1('a* NOT ý') ; } {0 {1 2 3 4 5 6 7}} #------------------------------------------------------------------------- reset_db do_test 9.0 { sqlite3 db {} db deserialize [decode_hexdb { .open --hexdb | size 32768 pagesize 4096 filename crash-c76a16c24c8ba6.db | page 1 offset 0 | 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. | 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 08 .....@ ........ | 32: 00 00 00 02 00 00 00 01 00 00 00 09 00 00 00 04 ................ | 96: 00 00 00 00 0d 0f c7 00 07 0d 92 00 0f 8d 0f 36 ...............6 | 112: 0e cb 0e 6b 0e 0e 0d b6 0d 92 0d 92 00 00 00 00 ...k............ | 3472: 00 00 22 08 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet | 3488: 32 74 32 08 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE | 3504: 20 74 32 28 78 29 56 07 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta | 3520: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c | 3536: 6f 6e 66 69 67 07 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB | 3552: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k | 3568: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v) | 3584: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 06 WITHOUT ROWID[. | 3600: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d | 3616: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize | 3632: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't | 3648: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN | 3664: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE | 3680: 59 2c 20 73 7a 20 42 4c 4f 42 29 5e 05 07 17 21 Y, sz BLOB)^...! | 3696: 21 01 81 07 74 61 62 6c 65 74 31 5f 63 6f 6e 74 !...tablet1_cont | 3712: 65 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 05 43 52 entt1_content.CR | 3728: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 EATE TABLE 't1_c | 3744: 6f 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 ontent'(id INTEG | 3760: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY, | 3776: 63 30 2c 20 63 31 2c 20 63 32 29 69 04 07 17 19 c0, c1, c2)i.... | 3792: 19 01 81 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 ...-tablet1_idxt | 3808: 31 5f 69 64 78 04 43 52 45 41 54 45 20 54 41 42 1_idx.CREATE TAB | 3824: 4c 45 20 27 74 31 5f 69 64 78 27 28 73 65 67 69 LE 't1_idx'(segi | 3840: 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 d, term, pgno, P | 3856: 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 RIMARY KEY(segid | 3872: 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 , term)) WITHOUT | 3888: 20 52 4f 57 49 44 55 03 07 17 1b 1b 01 81 01 74 ROWIDU........t | 3904: 61 62 6c 65 74 31 5f 64 61 74 61 74 31 5f 64 61 ablet1_datat1_da | 3920: 74 61 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 ta.CREATE TABLE | 3936: 27 74 31 5f 64 61 74 61 27 28 69 64 20 49 4e 54 't1_data'(id INT | 3952: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY | 3968: 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 38 02 06 , block BLOB)8.. | 3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52 ...._tablet1t1CR | 4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 EATE VIRTUAL TAB | 4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 LE t1 USING fts5 | 4032: 28 61 2c 62 2c 63 29 00 00 00 00 00 00 00 00 00 (a,b,c)......... | page 3 offset 8192 | 0: 0d 00 00 00 03 0c 94 00 0f e6 0f ef 0c 94 00 00 ................ | 3216: 00 00 00 00 86 4a 84 80 80 80 80 01 04 00 8d 18 .....J.......... | 3232: 00 00 03 2b 02 30 30 01 02 06 01 02 06 01 02 06 ...+.00......... | 3248: 1f 02 03 01 02 03 01 02 03 01 08 32 30 31 36 30 ...........20160 | 3264: 36 30 39 01 02 07 01 02 07 01 02 07 01 01 34 01 609...........4. | 3280: 02 05 01 02 05 01 02 05 01 01 35 01 02 04 01 02 ..........5..... | 3296: 04 01 02 04 02 07 30 30 30 30 30 30 30 1c 02 04 ......0000000... | 3312: 01 02 04 01 02 04 01 06 62 69 6e 61 72 79 03 06 ........binary.. | 3328: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................ | 3344: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................ | 3360: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................ | 3376: 03 06 01 02 02 03 06 01 02 02 01 08 63 6f 6d 70 ............comp | 3392: 69 6c 65 72 01 02 02 01 02 02 01 02 02 01 06 64 iler...........d | 3408: 62 73 74 61 74 07 02 03 01 02 03 01 02 03 02 04 bstat........... | 3424: 65 62 75 67 04 02 02 01 02 02 01 02 02 01 06 65 ebug...........e | 3440: 6e 61 62 6c 65 07 02 02 01 02 02 01 02 02 01 02 nable........... | 3456: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................ | 3472: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................ | 3488: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................ | 3504: 02 01 02 02 02 08 78 74 65 6e 73 69 6f 6e 1f 02 ......xtension.. | 3520: 04 01 02 04 01 02 04 01 04 66 74 73 34 0a 02 03 .........fts4... | 3536: 01 02 03 01 02 03 04 01 35 0d 02 03 01 02 03 01 ........5....... | 3552: 02 03 01 03 67 63 63 01 02 03 01 02 03 01 02 03 ....gcc......... | 3568: 02 06 65 6f 70 6f 6c 79 10 02 03 01 02 03 01 02 ..eopoly........ | 3584: 03 01 05 6a 73 6f 6e 31 13 02 03 01 02 03 01 02 ...json1........ | 3600: 03 01 04 6c 6f 61 64 1f 02 03 01 02 03 01 02 03 ...load......... | 3616: 01 03 6d 61 78 1c 02 02 01 02 02 01 02 02 02 05 ..max........... | 3632: 65 6d 6f 72 79 1c 02 03 01 02 03 01 02 03 04 04 emory........... | 3648: 73 79 73 35 16 02 03 01 02 03 01 02 03 01 06 6e sys5...........n | 3664: 6f 63 61 73 65 02 06 01 02 02 03 06 01 02 02 03 ocase........... | 3680: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................ | 3696: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................ | 3712: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................ | 3728: 02 01 04 6f 6d 69 74 1f 02 02 01 02 02 01 02 02 ...omit......... | 3744: 01 05 72 74 72 65 65 19 02 03 01 02 03 01 02 03 ..rtree......... | 3760: 04 02 69 6d 01 06 01 02 02 03 06 01 02 02 03 06 ..im............ | 3776: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................ | 3792: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................ | 3808: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................ | 3824: 01 0a 74 68 72 65 61 64 73 61 66 65 03 57 34 56 ..threadsafe.W4V | 3840: 94 64 91 46 85 84 04 76 74 61 62 07 02 04 01 02 .d.F...vtab..... | 3856: 04 01 02 04 01 01 78 01 06 01 01 02 01 06 01 01 ......x......... | 3872: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 10 02 ................ | 3888: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................ | 3904: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................ | 3920: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................ | 3936: 01 02 01 06 01 01 10 01 06 01 01 02 01 06 01 01 ................ | 3952: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................ | 3968: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................ | 3984: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................ | 4000: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................ | 4016: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................ | 4032: 02 01 06 01 01 02 01 06 01 01 02 04 15 13 0c 0c ................ | 4048: 12 44 13 11 0f 47 13 0f 0c 0e 11 10 0f 0e 10 0f .D...G.......... | 4064: 44 0f 10 40 15 0f 07 01 03 00 14 24 5a 24 24 0f D..@.......$Z$$. | 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............ | page 4 offset 12288 | 0: 0a 00 00 00 01 0f fa 00 00 00 00 00 00 00 00 00 ................ | 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................ | page 5 offset 16384 | 0: 0d 00 00 00 24 0c 0a 00 0f d8 0f af 0f 86 0f 74 ....$..........t | 16: 0f 61 0f 4e 0f 2f 0f 0f 0e ef 0e d7 0e be 0e a5 .a.N./.......... | 32: 0e 8d 0e 74 0e 5b 0e 40 0e 24 0e 08 0d ef 0d d5 ...t.[.@.$...... | 48: 0d bb 0d a0 0d 84 0d 68 0d 4f 0d 35 0d 1b 0c fb .......h.O.5.... | 64: 0c da 0c b9 0c 99 0c 78 0c 57 0c 3e 0c 24 0c 0a .......x.W.>.$.. | 3072: 00 00 00 00 00 00 00 00 00 00 18 24 05 00 25 0f ...........$..%. | 3088: 19 54 48 52 45 41 44 53 41 46 45 3d 30 58 42 49 .THREADSAFE=0XBI | 3104: 4e 41 52 59 18 23 05 00 25 0f 19 54 48 52 45 41 NARY.#..%..THREA | 3120: 44 53 41 46 45 3d 30 58 4e 4f 43 41 53 45 17 22 DSAFE=0XNOCASE.. | 3136: 05 00 25 0f 17 54 48 52 45 41 44 53 31 46 45 3d ..%..THREADS1FE= | 3152: 30 58 52 64 52 49 4d 1f 21 05 00 33 0f 19 4f 4d 0XRdRIM.!..3..OM | 3168: 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 49 4f IT LOAD EXTENSIO | 3184: 4e 58 42 49 4e 41 52 59 1f 20 05 00 33 0f 19 4f NXBINARY. ..3..O | 3200: 4d 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 49 MIT LOAD EXTENSI | 3216: 4f 4e 58 4e 4f 43 41 53 45 1e 1f 05 00 33 0f 17 ONXNOCASE....3.. | 3232: 4f 4d 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 OMIT LOAD EXTENS | 3248: 49 4f 4e 58 52 54 52 49 4d 1f 1e 05 00 33 0f 19 IONXRTRIM....3.. | 3264: 4d 41 58 20 4d 45 4d 4f 52 59 3d 35 30 30 30 30 MAX MEMORY=50000 | 3280: 30 30 30 58 42 49 4e 41 52 59 1f 1d 05 00 33 0f 000XBINARY....3. | 3296: 19 4d 41 58 20 4d 45 4d 4f 52 59 3d 35 30 30 30 .MAX MEMORY=5000 | 3312: 30 30 30 30 58 4e 4f 43 41 53 45 1e 1c 05 00 33 0000XNOCASE....3 | 3328: 0f 17 4d 41 58 20 4d 45 4d 4f 52 59 3d 35 30 30 ..MAX MEMORY=500 | 3344: 30 30 30 30 30 58 52 54 52 49 4d 18 1b 05 00 25 00000XRTRIM....% | 3360: 0f 19 45 4e 41 42 4c 45 20 52 54 52 45 45 58 42 ..ENABLE RTREEXB | 3376: 49 4e 41 52 59 18 1a 05 00 25 0f 19 45 4e 41 42 INARY....%..ENAB | 3392: 4c 45 20 52 54 52 45 45 58 4e 4f 43 41 53 45 17 LE RTREEXNOCASE. | 3408: 19 05 00 25 0f 17 45 4e 41 42 4c 45 20 52 54 52 ...%..ENABLE RTR | 3424: 45 45 58 52 54 52 49 4d 1a 18 05 00 29 0f 19 45 EEXRTRIM....)..E | 3440: 4e 41 42 4b 45 20 4d 45 4d 53 59 53 35 58 42 49 NABKE MEMSYS5XBI | 3456: 4e 41 52 59 1a 17 05 00 29 0f 19 45 4e 41 42 4c NARY....)..ENABL | 3472: 42 60 2d 45 4d 53 59 53 35 58 4e 4f 43 41 53 45 B`-EMSYS5XNOCASE | 3488: 19 16 05 00 29 0f 17 45 4e 41 42 4c 45 20 4d 45 ....)..ENABLE ME | 3504: 4d 53 59 53 35 58 52 54 52 49 4d 18 15 05 00 25 MSYS5XRTRIM....% | 3520: 0f 19 45 4e 41 42 4c 45 20 4a 53 4f 4e 31 58 42 ..ENABLE JSON1XB | 3536: 49 4e 41 52 59 18 14 05 00 25 0f 19 45 4e 41 42 INARY....%..ENAB | 3552: 4c 45 20 4a 53 4f 4e 31 58 4e 4f 43 41 53 45 17 LE JSON1XNOCASE. | 3568: 13 05 00 25 0f 17 45 4e 41 42 4c 45 20 4a 53 4f ...%..ENABLE JSO | 3584: 4e 31 58 52 54 52 49 4d 1a 12 05 00 29 0f 19 45 N1XRTRIM....)..E | 3600: 4e 41 42 4c 45 20 47 45 4f 50 4f 4c 59 58 42 49 NABLE GEOPOLYXBI | 3616: 4e 41 52 59 1a 11 05 00 39 0f 19 45 4e 41 42 4c NARY....9..ENABL | 3632: 45 20 47 45 4f 50 4f 4c 59 58 4e 4f 43 41 53 45 E GEOPOLYXNOCASE | 3648: 19 10 05 00 29 0f 17 45 4e 41 42 4c 45 20 47 45 ....)..ENABLE GE | 3664: 4f 50 4f 4c 59 58 52 54 52 49 4d 17 0f 05 00 23 OPOLYXRTRIM....# | 3680: 0f 19 45 4e 41 42 4c 45 20 46 54 53 35 58 42 49 ..ENABLE FTS5XBI | 3696: 4e 41 52 59 17 0e 05 00 23 0f 19 45 4e 41 42 4c NARY....#..ENABL | 3712: 45 20 46 54 53 35 58 4e 4f 43 41 53 45 16 0d 05 E FTS5XNOCASE... | 3728: 00 23 0f 17 45 4e 41 42 4c 45 20 46 54 53 35 58 .#..ENABLE FTS5X | 3744: 52 54 52 49 4d 17 0c 05 00 23 0f 19 45 4e 41 42 RTRIM....#..ENAB | 3760: 4c 45 20 46 54 53 34 58 42 49 4e 41 52 59 17 0b LE FTS4XBINARY.. | 3776: 05 00 23 0f 19 45 4e 41 42 4c 45 20 46 54 53 34 ..#..ENABLE FTS4 | 3792: 58 4e 4f 43 41 53 45 16 0a 05 00 23 0f 17 45 4e XNOCASE....#..EN | 3808: 41 42 4c 45 20 46 54 53 34 58 52 54 52 49 4d 1e ABLE FTS4XRTRIM. | 3824: 09 05 00 31 0f 19 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS | 3840: 54 41 54 20 56 54 41 42 58 42 49 4e 41 52 59 1e TAT VTABXBINARY. | 3856: 08 05 00 31 0f 19 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS | 3872: 54 41 54 20 56 54 24 15 48 4e 4f 43 41 53 45 1d TAT VT$.HNOCASE. | 3888: 07 05 00 31 0f 17 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS | 3904: 54 41 54 20 56 54 41 42 58 52 54 52 49 4d 11 06 TAT VTABXRTRIM.. | 3920: 05 00 17 0f 19 44 45 42 55 47 58 42 49 4e 41 52 .....DEBUGXBINAR | 3936: 59 11 05 05 00 17 0f 19 44 45 42 55 47 58 4e 4f Y.......DEBUGXNO | 3952: 43 41 53 45 10 04 05 00 17 0f 17 44 45 42 55 47 CASE.......DEBUG | 3968: 58 52 54 52 49 4d 27 03 05 00 43 0f 19 43 4f 4d XRTRIM'...C..COM | 3984: 50 49 4c 45 52 3d 67 63 63 2d 35 2e 34 2e 30 20 PILER=gcc-5.4.0 | 4000: 32 30 31 36 30 36 30 39 58 42 49 4e 41 52 59 27 20160609XBINARY' | 4016: 02 05 00 43 0f 19 43 4f 4d 50 49 4c 45 52 3c 67 ...C..COMPILER<g | 4032: 63 63 2d 35 2e 34 2e 30 20 32 30 31 36 30 36 30 cc-5.4.0 2016060 | 4048: 39 58 4e 4f 43 41 53 45 26 01 05 00 43 0f 17 43 9XNOCASE&...C..C | 4064: 4f 4d 50 49 4c 45 52 3d 67 63 63 2d 35 2e 34 2e OMPILER=gcc-5.4. | 4080: 30 20 32 30 31 36 30 36 30 39 58 52 54 52 49 4d 0 20160609XRTRIM | page 6 offset 20480 | 0: 0d 00 00 00 24 0e e0 00 0f f8 0f f0 0f e8 0f e0 ....$........... | 16: 0f d8 0f d0 0f c8 0f c0 0f b8 0f b0 0f a8 0f a0 ................ | 32: 1f 98 0f 90 0f 88 0f 80 0f 78 0f 70 0f 68 0f 60 .........x.p.h.` | 48: 0f 58 0f 50 0f 48 0f 40 0f 38 0f 30 0f 28 0f 20 .X.P.H.@.8.0.(. | 64: 0f 18 0f 10 0f 08 0f 00 0e f8 0e f0 0e e8 0e e0 ................ | 3808: 06 24 03 00 12 02 01 01 06 23 03 00 12 02 01 01 .$.......#...... | 3824: 06 22 03 00 12 02 01 01 06 21 03 00 12 03 01 01 .........!...... | 3840: 06 20 03 00 12 03 01 01 06 1f 03 00 12 03 01 01 . .............. | 3856: 06 1e 03 00 12 03 01 01 06 1d 03 00 12 03 01 01 ................ | 3872: 06 1c 03 00 12 03 01 01 06 1b 03 00 12 02 01 01 ................ | 3888: 06 1a 03 00 12 02 01 01 06 19 03 00 12 02 01 01 ................ | 3904: 06 18 03 00 12 02 01 01 06 17 03 00 12 02 01 01 ................ | 3920: 06 15 f3 00 12 02 01 01 06 15 03 00 12 02 01 01 ................ | 3936: 06 14 03 00 12 02 01 01 06 13 03 00 12 02 01 01 ................ | 3952: 06 12 03 00 12 02 01 01 06 11 03 00 12 02 01 01 ................ | 3968: 06 10 03 00 12 02 01 01 06 0f 03 00 12 02 01 01 ................ | 3984: 06 0e 03 00 12 02 01 01 06 0d 03 00 12 02 01 01 ................ | 4000: 06 0c 03 00 12 02 01 01 06 0b 03 00 12 02 01 01 ................ | 4016: 06 0a 03 00 12 02 01 01 06 09 03 00 12 03 01 01 ................ | 4032: 06 08 03 00 12 03 01 01 06 07 03 00 12 03 01 01 ................ | 4048: 06 06 03 00 12 01 01 01 06 05 03 00 12 01 01 01 ................ | 4064: 06 04 03 00 12 01 01 01 06 03 03 00 12 06 01 01 ................ | 4080: 06 02 03 00 12 06 01 01 06 01 03 00 12 06 01 01 ................ | page 7 offset 24576 | 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................ | 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version. | page 8 offset 28672 | 0: 0d 00 00 00 03 0f d6 00 0f f4 0f e9 0f d6 00 00 ................ | 4048: 00 00 00 00 00 00 11 03 02 2b 69 6e 74 65 67 72 .........+integr | 4064: 69 74 79 2d 63 68 65 63 6b 09 02 02 1b 72 65 62 ity-check....reb | 4080: 75 69 6c 64 0a 01 02 1d 6f 70 74 69 00 00 00 00 uild....opti.... | end crash-c76a16c24c8ba6.db }]} {} #.testctrl prng_seed 1 db #.testctrl internal_functions #.testctrl json_selfcheck on # do_execsql_test 9.1 { UPDATE t1 SET b=quote(zeroblob(current_date)) WHERE t1 MATCH 't*'; SAVEPOINT a; UPDATE t1 SET b=quote(zeroblob(current_date)) WHERE t1 MATCH 't*'; INSERT INTO t1(t1,rank) VALUES('secure-delete',1); } do_catchsql_test 9.2 { DELETE FROM t1; } {1 {database disk image is malformed}} sqlite3_fts5_may_be_corrupt 0 finish_test |
Changes to ext/fts5/test/fts5fault8.test.
︙ | ︙ | |||
53 54 55 56 57 58 59 | } -test { faultsim_test_result {0 {1 3}} {1 SQLITE_NOMEM} } } } ;# foreach_detail_mode... | < | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | } -test { 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); INSERT INTO x2 VALUES('a b c d'); INSERT INTO x2 VALUES('e f g h'); INSERT INTO x2 VALUES('i j k l'); |
︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 84 | do_faultsim_test 4 -faults oom-* -prep { faultsim_restore_and_reopen } -body { execsql { INSERT INTO x2(x2) VALUES('optimize') } } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} } finish_test | > > > > > > > > > > > > > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | do_faultsim_test 4 -faults oom-* -prep { faultsim_restore_and_reopen } -body { 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 |
Changes to ext/fts5/test/fts5faultH.test.
︙ | ︙ | |||
123 124 125 126 127 128 129 | INSERT INTO t1(rowid, x) VALUES(32, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(33, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(34, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(35, 'aaa bbb BBB'); COMMIT; } | | > > > > > > > > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | INSERT INTO t1(rowid, x) VALUES(32, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(33, 'bbb Bbb BBB'); 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 { } -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 |
Changes to ext/fts5/test/fts5integrity.test.
︙ | ︙ | |||
350 351 352 353 354 355 356 357 358 | do_execsql_test 11.3 { PRAGMA integrity_check(t2); } {ok} do_execsql_test 11.4 { DROP TABLE t1; PRAGMA integrity_check(t2); } {ok} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | do_execsql_test 11.3 { PRAGMA integrity_check(t2); } {ok} do_execsql_test 11.4 { DROP TABLE t1; PRAGMA integrity_check(t2); } {ok} #------------------------------------------------------------------- reset_db do_execsql_test 12.1 { CREATE VIRTUAL TABLE x1 USING fts5(a, b); INSERT INTO x1 VALUES('one', 'two'); INSERT INTO x1 VALUES('three', 'four'); INSERT INTO x1 VALUES('five', 'six'); } do_execsql_test 12.2 { PRAGMA integrity_check } {ok} db close sqlite3 db test.db -readonly 1 explain_i { PRAGMA integrity_check } do_execsql_test 12.3 { PRAGMA integrity_check } {ok} finish_test |
Changes to ext/fts5/test/fts5misc.test.
︙ | ︙ | |||
510 511 512 513 514 515 516 517 518 519 | do_execsql_test 18.1 { DROP TABLE t2; } do_execsql_test 18.2 { COMMIT; } finish_test | > > > > > > > > > > > > > > > > > > > > | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 | do_execsql_test 18.1 { DROP TABLE t2; } do_execsql_test 18.2 { COMMIT; } #------------------------------------------------------------------------- reset_db do_execsql_test 19.0 { CREATE VIRTUAL TABLE t1 USING fts5(text); CREATE TABLE t2(text); BEGIN; INSERT INTO t1 VALUES('one'); INSERT INTO t1 VALUES('two'); INSERT INTO t1 VALUES('three'); INSERT INTO t1 VALUES('one'); INSERT INTO t1 VALUES('two'); INSERT INTO t1 VALUES('three'); SAVEPOINT one; INSERT INTO t2 VALUES('one'); INSERT INTO t2 VALUES('two'); INSERT INTO t2 VALUES('three'); ROLLBACK TO one; COMMIT; } finish_test |
Changes to ext/icu/icu.c.
︙ | ︙ | |||
467 468 469 470 471 472 473 | sqlite3 *db = (sqlite3 *)sqlite3_user_data(p); UErrorCode status = U_ZERO_ERROR; 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() */ | | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 | sqlite3 *db = (sqlite3 *)sqlite3_user_data(p); UErrorCode status = U_ZERO_ERROR; 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); (void)nArg; /* Unused parameter */ zLocale = (const char *)sqlite3_value_text(apArg[0]); zName = (const char *)sqlite3_value_text(apArg[1]); if( !zLocale || !zName ){ return; } pUCollator = ucol_open(zLocale, &status); 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]); i++){ if( sqlite3_stricmp(zOption,aStrength[i].zName)==0 ){ ucol_setStrength(pUCollator, aStrength[i].val); break; } } if( 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<sizeof(aStrength)/sizeof(aStrength[0]); i++){ sqlite3_str_appendf(pStr, " %s", aStrength[i].zName); } sqlite3_result_error(p, sqlite3_str_value(pStr), -1); sqlite3_free(sqlite3_str_finish(pStr)); return; } } rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator, icuCollationColl, icuCollationDel ); if( rc!=SQLITE_OK ){ ucol_close(pUCollator); sqlite3_result_error(p, "Error registering collation function", -1); } |
︙ | ︙ | |||
505 506 507 508 509 510 511 512 513 514 515 516 517 518 | const char *zName; /* Function name */ unsigned char nArg; /* Number of arguments */ unsigned int enc; /* Optimal text encoding */ unsigned char iContext; /* sqlite3_user_data() context */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); } scalars[] = { {"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation}, #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) {"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc}, {"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, {"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, {"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16}, {"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16}, {"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, | > | 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | const char *zName; /* Function name */ unsigned char nArg; /* Number of arguments */ unsigned int enc; /* Optimal text encoding */ unsigned char iContext; /* sqlite3_user_data() context */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); } scalars[] = { {"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation}, {"icu_load_collation",3,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation}, #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) {"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc}, {"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, {"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, {"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16}, {"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16}, {"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, |
︙ | ︙ |
Added ext/intck/intck1.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | # 2008 Feb 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the incremental integrity check # (intck) extension. # source [file join [file dirname [info script]] intck_common.tcl] set testprefix intck1 return_if_no_intck foreach {tn sql} { 1 "CREATE TABLE t1(a PRIMARY KEY, b)" 2 "CREATE TABLE t2(a PRIMARY KEY, b) WITHOUT ROWID " 3 "CREATE TABLE t3(a PRIMARY KEY, b) WITHOUT rowID;" 4 "CREATE TABLE t4(a PRIMARY KEY, ROWID)" 5 {CREATE TABLE t5(a PRIMARY KEY, ROWID) WITHOUT ROWID } } { do_test 1.1.$tn { db eval $sql set {} {} } {} } set space " \n\v\t\r\f" do_execsql_test 1.2 { SELECT name, (rtrim(sql, $space) LIKE '%rowid') FROM sqlite_schema WHERE type='table' ORDER BY 1 } { t1 0 t2 1 t3 1 t4 0 t5 1 } do_execsql_test 1.3 { CREATE TABLE x1(a COLLATE nocase, b INTEGER, c BLOB); INSERT INTO x1 VALUES('lEtTeRs', 1234, 1234); } do_execsql_test 1.3.1 { WITH wrapper(c1, c2, c3) AS ( SELECT a, b, c FROM x1 ) SELECT * FROM wrapper WHERE c1='letters'; } {lEtTeRs 1234 1234} do_execsql_test 1.3.2 { WITH wrapper(c1, c2, c3) AS ( SELECT a, b, c FROM x1 ) SELECT * FROM wrapper WHERE c2='1234'; } {lEtTeRs 1234 1234} do_execsql_test 1.3.2 { WITH wrapper(c1, c2, c3) AS ( SELECT a, b, c FROM x1 ) SELECT * FROM wrapper WHERE c3='1234'; } {} do_execsql_test 1.4 { CREATE TABLE z1(a, b); CREATE INDEX z1ab ON z1(a+b COLLATE nocase); } do_execsql_test 1.4.1 { SELECT * FROM z1 INDEXED BY z1ab } do_catchsql_test 1.5.1 { CREATE INDEX z1b ON z1(b ASC NULLS LAST); } {1 {unsupported use of NULLS LAST}} do_catchsql_test 1.5.2 { CREATE INDEX z1b ON z1(b DESC NULLS LAST); } {1 {unsupported use of NULLS LAST}} do_catchsql_test 1.5.3 { CREATE INDEX z1b ON z1(b ASC NULLS FIRST); } {1 {unsupported use of NULLS FIRST}} do_catchsql_test 1.5.4 { CREATE INDEX z1b ON z1(b DESC NULLS FIRST); } {1 {unsupported use of NULLS FIRST}} reset_db do_execsql_test 1.6.1 { CREATE TABLE t1(i INTEGER PRIMARY KEY, b, c); CREATE INDEX i1 ON t1(b); ANALYZE; INSERT INTO sqlite_stat1 VALUES('t1', 'i1', '10000 10000'); ANALYZE sqlite_schema; } {} do_eqp_test 1.6.2 { SELECT 1 FROM t1 INDEXED BY i1 WHERE (b, i) IS (?, ?); } {SEARCH} #------------------------------------------------------------------------- reset_db do_test 2.0 { set ic [sqlite3_intck db main] $ic close } {} do_execsql_test 2.1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); CREATE INDEX i1 ON t1(a COLLATE nocase); CREATE INDEX i2 ON t1(b, a); CREATE INDEX i3 ON t1(b + a COLLATE nocase) WHERE a!=1; } do_intck_test 2.2 { } # Delete a row from each of the i1 and i2 indexes using the imposter # table interface. # do_test 2.3 { db eval {SELECT name, rootpage FROM sqlite_schema} { set R($name) $rootpage } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(i1) db eval { CREATE TABLE imp1(a PRIMARY KEY, rowid) WITHOUT ROWID; } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(i2) db eval { CREATE TABLE imp2(b, a, rowid, PRIMARY KEY(b, a)) WITHOUT ROWID; } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 db eval { DELETE FROM imp1 WHERE rowid=1; DELETE FROM imp2 WHERE rowid=2; } db close sqlite3 db test.db } {} do_intck_test 2.4 { {entry (1,1) missing from index i1} {entry (4,3,2) missing from index i2} } #------------------------------------------------------------------------- reset_db do_execsql_test 3.0 { CREATE TABLE x1(a, b, c, PRIMARY KEY(c, b)) WITHOUT ROWID; CREATE INDEX x1a ON x1(a COLLATE nocase); INSERT INTO x1 VALUES(1, 2, 'three'); INSERT INTO x1 VALUES(4, 5, 'six'); INSERT INTO x1 VALUES(7, 8, 'nine'); } do_intck_test 3.1 { } do_test 3.2 { db eval {SELECT name, rootpage FROM sqlite_schema} { set R($name) $rootpage } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(x1a) db eval { CREATE TABLE imp1(c, b, a, PRIMARY KEY(c, b)) WITHOUT ROWID } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 db eval { DELETE FROM imp1 WHERE a=5; } execsql_pp { } db close sqlite3 db test.db } {} do_intck_test 3.3 { {entry (4,'six',5) missing from index x1a} } #------------------------------------------------------------------------- reset_db do_execsql_test 4.0 { CREATE TABLE www(x, y, z); CREATE INDEX w1 ON www( (x+1), z ); INSERT INTO www VALUES(1, 1, 1), (2, 2, 2); } do_intck_test 4.1 { } #------------------------------------------------------------------------- reset_db do_execsql_test 5.0 { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a COLLATE NOCASE); INSERT INTO t1 VALUES(1, 1); INSERT INTO t1 VALUES(2, 2); } do_test 5.1 { set ic [sqlite3_intck db nosuchdb] $ic step } {SQLITE_ERROR} do_test 5.2 { $ic close set ic [sqlite3_intck db {}] while {[$ic step]=="SQLITE_OK"} {} set res [$ic error] $ic close set res } {SQLITE_OK {}} do_test 5.3 { test_do_intck db "main" } {} do_test 5.4 { set ret {} set ic [sqlite3_intck db main] db eval [$ic test_sql t1] { if {$error_message!=""} { lappend ret $error_message } } $ic close set ret } {} do_test 5.5 { set ret {} set ic [sqlite3_intck db main] db eval [$ic test_sql {}] { if {$error_message!=""} { lappend ret $error_message } } $ic close set ret } {} db cache flush do_test 5.6 { set ret {} set ic [sqlite3_intck db main] $ic step db eval [$ic test_sql {}] { if {$error_message!=""} { lappend ret $error_message } } $ic close set ret } {} #------------------------------------------------------------------------- reset_db do_execsql_test 6.0 { CREATE TABLE t1(x, y, PRIMARY KEY(x)) WITHOUT ROWID; CREATE INDEX i1 ON t1(y, x); INSERT INTO t1 VALUES(X'0000', X'1111'); } do_intck_test 6.1 {} do_execsql_test 6.2.1 { PRAGMA writable_schema = 1; UPDATE sqlite_schema SET sql = 'CREATE INDEX i1' WHERE name='i1'; } {} do_intck_test 6.2.2 {} do_execsql_test 6.3.1 { UPDATE sqlite_schema SET sql = 'CREATE INDEX i1(y' WHERE name='i1'; } {} do_intck_test 6.3.2 {} do_execsql_test 6.4.1 { UPDATE sqlite_schema SET sql = 'CREATE INDEX i1(y) hello world' WHERE name='i1'; } {} do_intck_test 6.4.2 {} do_execsql_test 6.5.1 { UPDATE sqlite_schema SET sql = 'CREATE INDEX i1(y, x) WHERE 1 ' WHERE name='i1'; } {} do_intck_test 6.5.2 {} do_execsql_test 6.6.1 { UPDATE sqlite_schema SET sql = 'CREATE INDEX i1( , ) WHERE 1 ' WHERE name='i1'; } {} do_test 6.7.2 { set ic [sqlite3_intck db main] $ic step } {SQLITE_ERROR} do_test 6.5.3 { $ic error } {SQLITE_ERROR {near "AS": syntax error}} $ic close do_execsql_test 6.6.1 { UPDATE sqlite_schema SET sql = 'CREATE INDEX i1([y' WHERE name='i1'; } {} do_intck_test 6.6.2 {} #------------------------------------------------------------------------- reset_db do_execsql_test 7.0 { CREATE TABLE x1("1", "22", "3333", four); CREATE INDEX i1 ON x1( "1" , "22", NULL); INSERT INTO x1 VALUES(1, 22, 3333, NULL); INSERT INTO x1 VALUES(1, 22, 3333, NULL); } do_execsql_test 7.1 " CREATE INDEX i2 ON x1( \"1\"\r\n\t ) " do_execsql_test 7.2 { CREATE INDEX i3 ON x1( "22" || 'abc''def' || `1` ) } do_execsql_test 7.3 { CREATE INDEX i4 ON x1( [22] + [1] ) } do_execsql_test 7.4 { CREATE INDEX i5 ON x1( four||'hello' ) } do_intck_test 7.5 {} finish_test |
Added ext/intck/intck2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | # 2024 Feb 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the incremental integrity check # (intck) extension. # source [file join [file dirname [info script]] intck_common.tcl] set testprefix intck2 return_if_no_intck do_execsql_test 1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); INSERT INTO t1 VALUES(3, 'three'); CREATE INDEX i1 ON t1(b); } proc imposter_edit {obj create sql} { sqlite3 xdb test.db set pgno [xdb one {SELECT rootpage FROM sqlite_schema WHERE name=$obj}] sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER xdb main 1 $pgno xdb eval $create sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER xdb main 0 0 xdb eval $sql xdb close } imposter_edit i1 { CREATE TABLE imp(b, a, PRIMARY KEY(b)) WITHOUT ROWID; } { DELETE FROM imp WHERE b='two'; INSERT INTO imp(b, a) VALUES('four', 4); } do_intck_test 1.1 { {surplus entry ('four',4) in index i1} {entry ('two',2) missing from index i1} } #------------------------------------------------------------------------- reset_db do_execsql_test 2.0 { CREATE TABLE x1(a, b, "c d"); CREATE INDEX x1a ON x1(a COLLATE nocase DESC , b ASC); CREATE INDEX x1b ON x1( a || b || ' "''" ' COLLATE binary ASC ); CREATE INDEX x1c ON x1( format('%s', a)ASC, format('%d', "c d" ) ); INSERT INTO x1 VALUES('one', 2, 3); INSERT INTO x1 VALUES('One', 4, 5); INSERT INTO x1 VALUES('ONE', 6, 7); INSERT INTO x1 VALUES(NULL, NULL, NULL); } do_intck_test 2.1 {} imposter_edit x1 { CREATE TABLE imp(a, b, c); } { DELETE FROM imp WHERE c=7; } do_intck_test 2.2 { {surplus entry ('ONE',6,3) in index x1a} {surplus entry ('ONE6 "''" ',3) in index x1b} {surplus entry ('ONE','7',3) in index x1c} } #------------------------------------------------------------------------- reset_db do_execsql_test 3.0 { CREATE TABLE x1(a, b, c); CREATE INDEX x1all ON x1(a DESC, b ASC, c DESC); INSERT INTO x1 VALUES(2, 1, 2); INSERT INTO x1 VALUES(2, 1, 1); INSERT INTO x1 VALUES(2, 2, 2); INSERT INTO x1 VALUES(2, 2, 1); INSERT INTO x1 VALUES(1, 1, 2); INSERT INTO x1 VALUES(1, 1, 1); INSERT INTO x1 VALUES(1, 2, 2); INSERT INTO x1 VALUES(1, 2, 1); } do_intck_test 3.1 { } imposter_edit x1 { CREATE TABLE imp(a, b, c); } { DELETE FROM imp WHERE 1; } db close sqlite3 db test.db do_intck_test 3.2 { {surplus entry (2,1,2,1) in index x1all} {surplus entry (2,1,1,2) in index x1all} {surplus entry (2,2,2,3) in index x1all} {surplus entry (2,2,1,4) in index x1all} {surplus entry (1,1,2,5) in index x1all} {surplus entry (1,1,1,6) in index x1all} {surplus entry (1,2,2,7) in index x1all} {surplus entry (1,2,1,8) in index x1all} } do_execsql_test 3.3 { DELETE FROM x1; INSERT INTO x1 VALUES(NULL, NULL, NULL); INSERT INTO x1 VALUES(NULL, NULL, NULL); INSERT INTO x1 VALUES(NULL, NULL, NULL); INSERT INTO x1 VALUES(NULL, NULL, NULL); } do_intck_test 3.4 { } imposter_edit x1 { CREATE TABLE imp(a, b, c); } { DELETE FROM imp WHERE 1; INSERT INTO imp(rowid) VALUES(-123); INSERT INTO imp(rowid) VALUES(456); } db close sqlite3 db test.db do_intck_test 3.5 { {entry (NULL,NULL,NULL,-123) missing from index x1all} {entry (NULL,NULL,NULL,456) missing from index x1all} {surplus entry (NULL,NULL,NULL,1) in index x1all} {surplus entry (NULL,NULL,NULL,2) in index x1all} {surplus entry (NULL,NULL,NULL,3) in index x1all} {surplus entry (NULL,NULL,NULL,4) in index x1all} } reset_db do_execsql_test 3.6 { CREATE TABLE w1(a PRIMARY KEY, b, c); INSERT INTO w1 VALUES(1.0, NULL, NULL); INSERT INTO w1 VALUES(33.0, NULL, NULL); INSERT INTO w1 VALUES(100.0, NULL, NULL); CREATE INDEX w1bc ON w1(b, c); } do_intck_test 3.7 { } imposter_edit w1 { CREATE TABLE imp(a, b, c); } { DELETE FROM imp WHERE a=33; INSERT INTO imp(a) VALUES(1234.5); INSERT INTO imp(a) VALUES(-1234.5); } do_intck_test 3.8 { {surplus entry (33.0,2) in index sqlite_autoindex_w1_1} {entry (1234.5,4) missing from index sqlite_autoindex_w1_1} {entry (NULL,NULL,4) missing from index w1bc} {entry (-1234.5,5) missing from index sqlite_autoindex_w1_1} {entry (NULL,NULL,5) missing from index w1bc} {surplus entry (NULL,NULL,2) in index w1bc} } finish_test |
Added ext/intck/intck_common.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | # 2024 Feb 18 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl ifcapable !vtab||!pragma { proc return_if_no_intck {} { finish_test return -code return } return } else { proc return_if_no_intck {} {} } proc do_intck {db {bSuspend 0}} { set ic [sqlite3_intck $db main] set ret [list] while {"SQLITE_OK"==[$ic step]} { set msg [$ic message] if {$msg!=""} { lappend ret $msg } if {$bSuspend} { $ic unlock #puts "SQL: [$ic test_sql {}]" #execsql_pp "EXPLAIN query plan [$ic test_sql {}]" #explain_i [$ic test_sql {}] } } set err [$ic error] if {[lindex $err 0]!="SQLITE_OK"} { error $err } $ic close return $ret } proc intck_sql {db tbl} { set ic [sqlite3_intck $db main] set sql [$ic test_sql $tbl] $ic close return $sql } proc do_intck_test {tn expect} { uplevel [list do_test $tn.a [list do_intck db] [list {*}$expect]] uplevel [list do_test $tn.b [list do_intck db 1] [list {*}$expect]] } |
Added ext/intck/intckbusy.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # 2024 February 24 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # source [file join [file dirname [info script]] intck_common.tcl] set testprefix intckbusy return_if_no_intck do_execsql_test 1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(2, 'two', 'three'); INSERT INTO t1 VALUES(3, NULL, NULL); CREATE INDEX i1 ON t1(b, c); } sqlite3 db2 test.db do_execsql_test -db db2 1.1 { BEGIN EXCLUSIVE; INSERT INTO t1 VALUES(4, 5, 6); } do_test 1.2 { set ic [sqlite3_intck db main] $ic step } {SQLITE_BUSY} do_test 1.3 { $ic unlock } {SQLITE_BUSY} do_test 1.4 { $ic error } {SQLITE_BUSY {database is locked}} do_test 1.4 { $ic close } {} finish_test |
Added ext/intck/intckcorrupt.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | # 2024 Feb 21 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the intck extensions response # to corruption at the b-tree level. # source [file join [file dirname [info script]] intck_common.tcl] set testprefix intckcorrupt return_if_no_intck #------------------------------------------------------------------------- reset_db do_test 1.0 { sqlite3 db {} db deserialize [decode_hexdb { | size 356352 pagesize 4096 filename crash-acaae0347204ae.db | page 1 offset 0 | 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. | 16: 10 00 01 01 00 40 20 20 00 00 00 00 d0 00 00 00 .....@ ........ | 32: 40 00 ea 00 00 00 00 00 00 40 00 00 00 40 00 00 @........@...@.. | 96: 00 00 00 00 0d 00 00 00 04 0e 9c 00 0f ad 0f 4f ...............O | 112: 0e fc 0e 9c 00 00 00 00 00 00 00 00 00 00 00 00 ................ | 3728: 00 00 00 00 00 00 00 00 00 00 00 00 5e 04 07 17 ............^... | 3744: 1f 1f 01 81 0b 74 61 62 6c 65 74 31 5f 70 61 72 .....tablet1_par | 3760: 65 6e 74 74 31 5f 70 61 72 65 6e 74 04 43 52 45 entt1_parent.CRE | 3776: 41 54 45 20 54 41 42 4c 45 20 22 74 31 5f 70 61 ATE TABLE .t1_pa | 3792: 72 65 6e 74 22 28 6e 6f 64 65 6e 6f 20 49 4e 54 rent.(nodeno INT | 3808: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY | 3824: 2c 70 61 72 65 6e 74 6e 6f 64 65 29 51 03 06 17 ,parentnode)Q... | 3840: 1b 1b 01 7b 74 61 62 6c 65 74 31 5f 6e 6f 64 65 ....tablet1_node | 3856: 74 31 5f 6e 6f 64 65 03 43 52 45 41 54 45 20 54 t1_node.CREATE T | 3872: 41 42 4c 45 20 22 74 31 5f 6e 6f 64 65 22 28 6e ABLE .t1_node.(n | 3888: 6f 64 65 6e 6f 20 49 4e 54 45 47 45 52 20 50 52 odeno INTEGER PR | 3904: 49 4d 41 52 59 20 4b 45 59 2c 64 61 74 61 29 5c IMARY KEY,data). | 3920: 02 07 17 1d 1d 01 81 0b 74 61 62 6c 65 74 31 5f ........tablet1_ | 3936: 72 6f 77 69 64 74 31 5f 72 6f 77 69 64 02 43 52 rowidt1_rowid.CR | 3952: 45 41 54 45 20 54 41 42 4c 45 20 22 74 31 5f 72 EATE TABLE .t1_r | 3968: 6f 77 69 64 22 28 72 6f 77 69 64 20 49 4e 54 45 owid.(rowid INTE | 3984: 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c GER PRIMARY KEY, | 4000: 6e 6f 64 65 6e 6f 2c 61 30 2c 61 31 29 51 01 07 nodeno,a0,a1)Q.. | 4016: 17 11 11 08 81 0f 74 61 62 6c 65 74 31 74 31 43 ......tablet1t1C | 4032: 52 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 REATE VIRTUAL TA | 4048: 42 4c 45 20 74 31 20 55 53 49 4e 47 20 72 74 72 BLE t1 USING rtr | 4064: 65 65 28 69 64 2c 78 30 20 50 52 49 4d 41 52 59 ee(id,x0 PRIMARY | 4080: 20 4b 45 59 2c 70 61 72 65 6e 74 6e 6f 64 65 29 KEY,parentnode) | page 2 offset 4096 | 0: 51 03 06 17 1b 1b 01 7b 74 61 62 6c 65 74 31 5f Q.......tablet1_ | 16: 6e 6f 64 65 74 31 5f 6e 6f 64 65 03 43 52 45 41 nodet1_node.CREA | 32: 54 45 20 54 41 42 4c 45 20 22 74 31 5f 6e 6f 64 TE TABLE .t1_nod | 48: 65 22 28 6e 6f 64 65 6e 6f 20 49 4e 54 45 47 45 e.(nodeno INTEGE | 64: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 64 61 R PRIMARY KEY,da | 80: 74 61 29 5c 02 07 17 1d 1d 01 81 0b 74 61 62 6c ta).........tabl | 96: 65 74 31 5f 72 6f 77 69 64 74 31 5f 72 6f 77 69 et1_rowidt1_rowi | 112: 64 02 43 52 45 41 54 45 20 54 41 42 4c 45 00 00 d.CREATE TABLE.. | 128: 01 0a 02 00 00 00 01 0e 0d 00 00 00 00 24 0e 0d .............$.. | 144: 0c 1a 06 85 50 46 60 27 70 08 00 00 00 00 00 00 ....PF`'p....... | 3824: 00 00 00 00 00 00 00 0d 0e 05 00 09 1d 00 74 6f ..............to | 3840: 79 20 68 61 6c 66 10 0d 05 00 09 23 00 62 6f 74 y half.....#.bot | 3856: 74 6f 6d 20 68 61 6c 66 0f 0c 05 00 09 21 00 72 tom half.....!.r | 3872: 69 67 68 74 20 68 61 6c 66 0e 0b 05 00 09 1f 00 ight half....... | 3888: 6c 65 66 74 20 43 15 f6 e6 f6 46 50 34 35 24 54 left C....FP45$T | 3904: 15 44 52 05 44 14 24 c4 52 02 27 43 15 f6 e6 f6 .DR.D.$.R.'C.... | 3920: 46 52 22 8e 6f 64 65 6e 6f 20 49 4e 54 45 47 45 FR..odeno INTEGE | 3936: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 64 61 R PRIMARY KEY,da | 3952: 74 61 29 5c 02 07 17 1d 1d 01 81 0b 74 61 62 6c ta).........tabl | 3968: 65 74 31 5f 72 6f 74 74 6f 6d 20 65 64 67 65 0f et1_rottom edge. | 3984: 07 05 00 09 21 00 72 69 67 68 74 20 65 64 67 65 ....!.right edge | 4000: 0e 06 05 00 09 1f 00 6c 65 66 74 20 65 64 67 65 .......left edge | 4016: 0b 05 05 00 09 19 00 63 65 6e 74 65 72 17 04 05 .......center... | 4032: 00 09 31 00 75 70 70 65 72 2d 72 69 67 68 74 20 ..1.upper-right | 4048: 63 6f 72 6e 65 72 17 03 05 00 09 31 00 6c 6f 77 corner.....1.low | 4064: 65 72 2d 72 69 67 68 74 20 63 6f 72 6e 65 72 16 er-right corner. | 4080: 02 05 00 09 2f 00 75 70 70 65 72 2d 6c 65 66 74 ..../.upper-left | page 3 offset 8192 | 0: 20 63 6f 72 6e 65 72 16 01 05 00 09 2f 01 8c 6f corner...../..o | 16: 77 65 72 2d 6c 53 51 4c 69 74 65 20 66 6f 72 6d wer-lSQLite form | 32: 61 74 20 33 00 10 00 01 01 00 40 20 20 00 00 00 at 3......@ ... | 48: 00 00 00 00 2f 00 00 0d eb 13 00 00 00 03 00 00 ..../........... | 64: 00 04 00 00 00 00 00 00 00 06 00 00 00 01 00 00 ................ | 80: 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................ | page 6 offset 20480 | 128: 00 00 00 00 00 00 00 00 97 3d 04 ae 7c 01 00 00 .........=..|... | 624: 00 00 00 00 00 00 21 97 3d 04 ae 7c 01 00 00 00 ......!.=..|.... | 1120: 00 00 00 00 00 20 97 3d 04 ae 7c 01 00 00 00 00 ..... .=..|..... | 1616: 00 00 00 00 1f 97 3d 04 ae 7c 01 00 00 00 00 00 ......=..|...... | 2112: 00 00 00 1e 97 3d 04 ae 7c 01 00 00 00 00 00 00 .....=..|....... | 2608: 00 00 1d 97 d3 d0 4a e7 c0 00 00 00 00 00 00 00 ......J......... | 3088: 00 00 00 00 00 00 00 00 00 00 00 00 01 f3 00 00 ................ | 3600: 23 97 3d 04 ae 7c 01 00 00 00 00 00 00 00 00 00 #.=..|.......... | 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26 ...............& | page 8 offset 28672 | 0: 0d 00 00 00 01 04 30 00 04 30 00 00 00 00 00 00 ......0..0...... | 1072: 97 4d 1e 14 00 ae 7c 00 00 00 00 00 00 00 00 00 .M....|......... | 1088: 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 ................ | 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................ | page 10 offset 36864 | 0: 0d 00 00 00 01 04 30 00 04 30 00 00 00 00 00 00 ......0..0...... | 1072: 9a ee c1 80 fd 78 1f ce 1b ae eb b4 00 00 00 00 .....x.......... | 1088: 13 20 ff 20 00 70 00 00 00 60 50 00 00 00 11 e0 . . .p...`P..... | 1104: 00 00 00 70 00 00 00 60 50 05 35 14 c6 97 46 52 ...p...`P.5...FR | 1120: 06 66 f7 26 d6 17 42 03 30 01 00 00 10 10 04 02 .f.&..B.0....... | 1136: 02 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 .........@...... | 1152: 00 00 00 00 00 40 00 00 00 40 00 00 00 00 00 00 .....@...@...... | 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 ................ | page 12 offset 45056 | 0: 0d 00 00 00 01 04 30 00 04 30 e1 b4 30 97 4d 46 ......0..0..0.MF | 16: 14 00 ae 7c 00 00 00 00 00 00 00 03 00 00 43 00 ...|..........C. | page 47 offset 188416 | 2512: 00 00 00 00 00 00 00 00 be 00 00 00 00 00 00 00 ................ | page 87 offset 352256 | 2512: 00 00 00 00 00 00 00 00 aa 00 00 00 00 00 00 00 ................ | end crash-acaae0347204ae.db }]} {} do_intck_test 1.1 { {corruption found while reading database schema} } #------------------------------------------------------------------------- reset_db do_test 2.0 { sqlite3 db {} db deserialize [decode_hexdb { | size 28672 pagesize 4096 filename crash-3afa1ca9e9c1bd.db | page 1 offset 0 | 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. | 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 07 .....@ ........ | 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................ | 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................ | 96: 00 00 00 00 0d 00 00 00 06 0e 88 00 0f b8 0f 6d ...............m | 112: 0f 3a 0f 0b 0e d5 0e 88 01 00 00 00 00 00 00 00 .:.............. | 3712: 00 00 00 00 00 00 00 00 4b 06 06 17 25 25 01 5b ........K...%%.[ | 3728: 74 61 62 6c 65 73 71 6c 69 74 65 5f 73 74 61 74 tablesqlite_stat | 3744: 31 73 71 6c 69 74 65 5f 73 74 61 74 31 07 43 52 1sqlite_stat1.CR | 3760: 45 41 54 45 20 54 41 42 4c 45 20 73 71 6c 69 74 EATE TABLE sqlit | 3776: 65 5f 73 74 61 74 31 28 74 62 6c 2c 69 64 78 2c e_stat1(tbl,idx, | 3792: 73 74 61 74 29 34 05 06 17 13 11 01 53 69 6e 64 stat)4......Sind | 3808: 65 78 63 31 63 63 31 06 43 52 45 41 54 45 20 55 exc1cc1.CREATE U | 3824: 4e 49 51 55 45 20 49 4e 44 45 58 20 63 31 63 20 NIQUE INDEX c1c | 3840: 4f 4e 20 63 31 28 63 2c 20 62 29 2d 04 06 17 13 ON c1(c, b)-.... | 3856: 11 01 45 69 6e 64 65 78 63 31 64 63 31 05 43 52 ..Eindexc1dc1.CR | 3872: 45 41 54 45 20 49 4e 44 45 58 20 63 31 64 20 4f EATE INDEX c1d O | 3888: 4e 20 63 31 28 64 2c 20 62 29 31 03 06 17 13 11 N c1(d, b)1..... | 3904: 01 4d 69 6e 64 65 78 62 31 63 62 31 05 43 52 45 .Mindexb1cb1.CRE | 3920: 41 54 45 20 55 4e 49 51 55 45 20 49 4e 44 45 58 ATE UNIQUE INDEX | 3936: 20 62 31 63 20 4f 4e 20 62 31 28 63 29 49 02 06 b1c ON b1(c)I.. | 3952: 17 11 11 0f 7f 74 61 62 6c 65 63 31 63 31 03 43 .....tablec1c1.C | 3968: 52 45 41 54 45 20 54 41 42 4c 45 20 63 31 28 61 REATE TABLE c1(a | 3984: 20 49 4e 54 20 50 52 49 4d 41 52 59 20 4b 45 59 INT PRIMARY KEY | 4000: 2c 20 62 2c 20 63 2c 20 64 29 20 57 49 54 48 4f , b, c, d) WITHO | 4016: 55 54 20 52 4f 57 49 44 46 01 06 17 11 11 01 79 UT ROWIDF......y | 4032: 74 61 62 6c 65 62 31 62 31 02 43 52 45 41 54 45 tableb1b1.CREATE | 4048: 20 54 41 42 4c 45 20 62 31 28 61 20 49 4e 54 20 TABLE b1(a INT | 4064: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 2c 20 PRIMARY KEY, b, | 4080: 63 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 c) WITHOUT ROWID | page 2 offset 4096 | 0: 0a 00 00 00 07 0f ca 00 0f fa 0f f2 0f ea 0f e2 ................ | 16: 0f da 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................ | 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 06 ................ | 4048: 67 07 07 04 01 0f 01 06 66 06 07 04 01 0f 01 05 g.......f....... | 4064: 65 05 07 04 01 0f 01 04 64 04 07 04 01 0f 01 03 e.......d....... | 4080: 63 03 07 04 01 0f 01 02 62 0f 05 04 09 0f 09 61 c.......b......a | page 3 offset 8192 | 0: 0a 00 00 00 07 0f bd 00 0f f9 0f ef 0f e5 0f db ................ | 16: 0f d1 0f c7 0f bd 00 00 00 00 01 00 00 00 00 00 ................ | 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 09 05 01 ................ | 4032: 0f 01 01 07 61 07 07 09 05 01 0f 01 01 06 61 06 ....a.........a. | 4048: 06 09 05 01 0f 01 01 05 61 05 05 09 05 01 0f 01 ........a....... | 4064: 01 04 61 04 04 09 05 01 0f 01 01 03 61 03 03 09 ..a.........a... | 4080: 05 01 0f 01 01 02 61 0f 02 06 05 09 0f 09 09 61 ......a........a | page 4 offset 12288 | 0: 0a 00 00 00 07 0f d8 00 0f fc 0f f0 0f ea 0f e4 ................ | 16: 0f de 0f d8 0f f6 00 00 00 00 00 00 00 00 00 00 ................ | 4048: 00 00 00 00 00 00 00 00 05 03 01 01 07 07 05 03 ................ | 4064: 01 01 06 06 05 03 01 01 05 05 05 03 01 01 04 04 ................ | 4080: 05 03 01 01 03 03 05 03 01 01 0f 02 03 03 09 09 ................ | page 5 offset 16384 | 0: 0a 00 00 00 07 0f ca 00 0f fa 0f f2 0f ea 0f 00 ................ | 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 07 ................ | 4048: 61 07 07 04 01 0f 01 06 61 06 07 04 01 0f 01 05 a.......a....... | 4064: 61 05 07 04 01 1f 01 04 61 04 07 04 01 0f 01 03 a.......a....... | 4080: 61 03 07 04 01 0f 01 02 61 02 05 04 09 0f 09 61 a.......a......a | page 6 offset 20480 | 0: 0a 00 00 00 07 0f ca 00 0f fa 0f ea 0f e2 00 00 ................ | 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 07 ................ | 4048: 61 07 07 04 01 0f 01 06 61 06 07 04 01 0f 01 05 a.......a....... | 4064: 61 05 07 04 01 0f 01 04 61 04 07 04 01 0f 01 03 a.......a....... | 4080: 61 03 07 04 01 0f 01 0f 61 02 05 04 09 0f 09 61 a.......a......a | page 7 offset 24576 | 0: 0d 00 00 00 05 0f 1c 00 0f f0 0f e0 0f d3 0f c5 ................ | 16: 0f b8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ | 4016: 00 00 00 00 00 00 00 00 0b 05 04 11 11 13 62 31 ..............b1 | 4032: 62 31 37 20 31 0c 04 04 11 13 13 62 31 62 31 63 b17 1......b1b1c | 4048: 37 20 31 0b 03 04 11 11 13 63 31 63 31 37 20 31 7 1......c1c17 1 | 4064: 0e 02 04 11 13 07 63 31 63 31 64 37 20 31 20 31 ......c1c1d7 1 1 | 4080: 0e 01 04 11 13 17 63 31 63 31 63 37 20 31 00 00 ......c1c1c7 1.. | end crash-3afa1ca9e9c1bd.db }]} {} do_intck_test 2.1 { {corruption found while reading database schema} } #------------------------------------------------------------------------- reset_db do_execsql_test 3.0 { PRAGMA page_size = 1024; CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a); INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 3); } do_test 3.1 { set pgno [db one {SELECT rootpage FROM sqlite_schema WHERE name='t1'}] db close hexio_write test.db [expr ($pgno-1)*1024] 0000 } {2} sqlite3 db test.db do_intck_test 3.2 { {corruption found while scanning database object i1} {corruption found while scanning database object t1} } finish_test |
Added ext/intck/intckfault.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | # 2024 February 24 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # source [file join [file dirname [info script]] intck_common.tcl] set testprefix intckfault return_if_no_intck do_execsql_test 1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(2, 'two', 'three'); INSERT INTO t1 VALUES(3, NULL, NULL); CREATE INDEX i1 ON t1(b, c); } do_faultsim_test 1 -faults oom-t* -prep { } -body { set ::ic [sqlite3_intck db main] set nStep 0 while {"SQLITE_OK"==[$::ic step]} { incr nStep if {$nStep==3} { $::ic unlock } } set res [$::ic error] $::ic close set res } -test { catch { $::ic close } faultsim_test_result {0 {SQLITE_OK {}}} {0 {SQLITE_NOMEM {}}} {0 {SQLITE_NOMEM {out of memory}}} } finish_test |
Added ext/intck/sqlite3intck.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 | /* ** 2024-02-08 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* */ #include "sqlite3intck.h" #include <string.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> /* ** 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; ii<p->nKeyVal; 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; jj<ii-1; jj++){ const char *zAlias = (const char*)sqlite3_column_name(p->pCheck,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; ii<p->nKeyVal; 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(<sql>, <icol>); */ 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 <index>", and then, if the index ** is a partial index " WHERE <condition>". */ " || 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; } |
Added ext/intck/sqlite3intck.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | /* ** 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 */ |
Added ext/intck/test_intck.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | /* ** 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 <string.h> #include <assert.h> /* 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; } |
Changes to ext/jni/src/org/sqlite/jni/capi/CApi.java.
︙ | ︙ | |||
252 253 254 255 256 257 258 | } /** Convenience overload which is a simple proxy for sqlite3_bind_nio_buffer(). */ @Experimental | | | | 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | } /** Convenience overload which is a simple proxy for sqlite3_bind_nio_buffer(). */ @Experimental /*public*/ static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data, int begin, int n ){ return sqlite3_bind_nio_buffer(stmt, ndx, data, begin, n); } /** Convenience overload which is equivalant to passing its arguments to sqlite3_bind_nio_buffer() with the values 0 and -1 for the final two arguments. */ @Experimental /*public*/ static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data ){ return sqlite3_bind_nio_buffer(stmt, ndx, data, 0, -1); } private static native int sqlite3_bind_double( @NotNull long ptrToStmt, int ndx, double v |
︙ | ︙ | |||
342 343 344 345 346 347 348 | instead of ByteBuffer, but it can only operate on "direct" buffers and the only such class offered by Java is (apparently) ByteBuffer. @see https://docs.oracle.com/javase/8/docs/api/java/nio/Buffer.html */ @Experimental | | | | 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | instead of ByteBuffer, but it can only operate on "direct" buffers and the only such class offered by Java is (apparently) ByteBuffer. @see https://docs.oracle.com/javase/8/docs/api/java/nio/Buffer.html */ @Experimental /*public*/ static native int sqlite3_bind_nio_buffer( @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data, int beginPos, int howMany ); /** Convenience overload which binds the given buffer's entire contents, up to its limit() (as opposed to its capacity()). */ @Experimental /*public*/ static int sqlite3_bind_nio_buffer( @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data ){ return sqlite3_bind_nio_buffer(stmt, ndx, data, 0, -1); } /** Binds the given object at the given index. If o is null then this behaves like |
︙ | ︙ | |||
596 597 598 599 600 601 602 | Returns SQLITE_MISUSE if src is null, tgt is null, or sqlite3_jni_supports_nio() returns false. Returns SQLITE_ERROR if howMany or either offset are negative. If argument validation succeeds, it returns the result of the underlying call to sqlite3_blob_read() (0 on success). */ @Experimental | | | 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 | Returns SQLITE_MISUSE if src is null, tgt is null, or sqlite3_jni_supports_nio() returns false. Returns SQLITE_ERROR if howMany or either offset are negative. If argument validation succeeds, it returns the result of the underlying call to sqlite3_blob_read() (0 on success). */ @Experimental /*public*/ static int sqlite3_blob_read_nio_buffer( @NotNull sqlite3_blob src, int srcOffset, @NotNull java.nio.ByteBuffer tgt, int tgtOffset, int howMany ){ return (JNI_SUPPORTS_NIO && src!=null && tgt!=null) ? sqlite3_blob_read_nio_buffer( src.getNativePointer(), srcOffset, tgt, tgtOffset, howMany ) |
︙ | ︙ | |||
620 621 622 623 624 625 626 | Returns null if sqlite3_jni_supports_nio(), any arguments are invalid, if the number of bytes to read is 0 or is larger than the src blob, or the underlying call to sqlite3_blob_read() fails for any reason. */ @Experimental | | | | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | Returns null if sqlite3_jni_supports_nio(), any arguments are invalid, if the number of bytes to read is 0 or is larger than the src blob, or the underlying call to sqlite3_blob_read() fails for any reason. */ @Experimental /*public*/ static java.nio.ByteBuffer sqlite3_blob_read_nio_buffer( @NotNull sqlite3_blob src, int srcOffset, int howMany ){ if( !JNI_SUPPORTS_NIO || src==null ) return null; else if( srcOffset<0 ) return null; final int nB = sqlite3_blob_bytes(src); if( srcOffset>=nB ) return null; else if( howMany<0 ) howMany = nB - srcOffset; if( srcOffset + howMany > nB ) return null; final java.nio.ByteBuffer tgt = java.nio.ByteBuffer.allocateDirect(howMany); final int rc = sqlite3_blob_read_nio_buffer( src.getNativePointer(), srcOffset, tgt, 0, howMany ); return 0==rc ? tgt : null; } /** Overload alias for sqlite3_blob_read_nio_buffer(). */ @Experimental /*public*/ static int sqlite3_blob_read( @NotNull sqlite3_blob src, int srcOffset, @NotNull java.nio.ByteBuffer tgt, int tgtOffset, int howMany ){ return sqlite3_blob_read_nio_buffer( src, srcOffset, tgt, tgtOffset, howMany ); |
︙ | ︙ | |||
664 665 666 667 668 669 670 | error, tgt.limit() is not modified. Returns 0 on success. Returns SQLITE_MISUSE is either argument is null or sqlite3_jni_supports_nio() returns false. Else it returns the result of the underlying call to sqlite3_blob_read(). */ @Experimental | | | 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 | error, tgt.limit() is not modified. Returns 0 on success. Returns SQLITE_MISUSE is either argument is null or sqlite3_jni_supports_nio() returns false. Else it returns the result of the underlying call to sqlite3_blob_read(). */ @Experimental /*public*/ static int sqlite3_blob_read( @NotNull sqlite3_blob src, @NotNull java.nio.ByteBuffer tgt ){ if(!JNI_SUPPORTS_NIO || src==null || tgt==null) return SQLITE_MISUSE; final int nSrc = sqlite3_blob_bytes(src); final int nTgt = tgt.limit(); final int nRead = nTgt<nSrc ? nTgt : nSrc; |
︙ | ︙ | |||
723 724 725 726 727 728 729 | Returns SQLITE_MISUSE if src is null or sqlite3_jni_supports_nio() returns false. Returns SQLITE_ERROR if either offset is negative. If argument validation succeeds, it returns the result of the underlying call to sqlite3_blob_read(). */ @Experimental | | | 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | Returns SQLITE_MISUSE if src is null or sqlite3_jni_supports_nio() returns false. Returns SQLITE_ERROR if either offset is negative. If argument validation succeeds, it returns the result of the underlying call to sqlite3_blob_read(). */ @Experimental /*public*/ static int sqlite3_blob_write_nio_buffer( @NotNull sqlite3_blob tgt, int tgtOffset, @NotNull java.nio.ByteBuffer src, int srcOffset, int howMany ){ return sqlite3_blob_write_nio_buffer( tgt.getNativePointer(), tgtOffset, src, srcOffset, howMany ); |
︙ | ︙ | |||
752 753 754 755 756 757 758 | } /** Convenience overload which writes all of src to the given offset of b. */ @Experimental | | | | 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | } /** Convenience overload which writes all of src to the given offset of b. */ @Experimental /*public*/ static int sqlite3_blob_write( @NotNull sqlite3_blob tgt, int tgtOffset, @NotNull java.nio.ByteBuffer src ){ return sqlite3_blob_write_nio_buffer( tgt.getNativePointer(), tgtOffset, src, 0, -1 ); } /** Convenience overload which writes all of src to offset 0 of tgt. */ @Experimental /*public*/ static int sqlite3_blob_write( @NotNull sqlite3_blob tgt, @NotNull java.nio.ByteBuffer src ){ return sqlite3_blob_write_nio_buffer( tgt.getNativePointer(), 0, src, 0, -1 ); } |
︙ | ︙ | |||
922 923 924 925 926 927 928 | /** A variant of sqlite3_column_blob() which returns the blob as a ByteBuffer object. Returns null if its argument is null, if sqlite3_jni_supports_nio() is false, or if sqlite3_column_blob() would return null for the same inputs. */ @Experimental | | | 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 | /** A variant of sqlite3_column_blob() which returns the blob as a ByteBuffer object. Returns null if its argument is null, if sqlite3_jni_supports_nio() is false, or if sqlite3_column_blob() would return null for the same inputs. */ @Experimental /*public*/ static native java.nio.ByteBuffer sqlite3_column_nio_buffer( @NotNull sqlite3_stmt stmt, int ndx ); private static native String sqlite3_column_origin_name(@NotNull long ptrToStmt, int ndx); /** Only available if built with SQLITE_ENABLE_COLUMN_METADATA. |
︙ | ︙ | |||
1844 1845 1846 1847 1848 1849 1850 | sqlite3_result_null(): the blob is null, the resulting slice of the blob is empty. If the resulting slice of the buffer exceeds SQLITE_LIMIT_LENGTH then this function behaves like sqlite3_result_error_toobig(). */ @Experimental | | | | 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 | sqlite3_result_null(): the blob is null, the resulting slice of the blob is empty. If the resulting slice of the buffer exceeds SQLITE_LIMIT_LENGTH then this function behaves like sqlite3_result_error_toobig(). */ @Experimental /*public*/ static native void sqlite3_result_nio_buffer( @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob, int begin, int n ); /** Convenience overload which uses the whole input object as the result blob content. */ @Experimental /*public*/ static void sqlite3_result_nio_buffer( @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob ){ sqlite3_result_nio_buffer(cx, blob, 0, -1); } public static native void sqlite3_result_null( @NotNull sqlite3_context cx |
︙ | ︙ | |||
1959 1960 1961 1962 1963 1964 1965 | } /** Convenience overload which behaves like sqlite3_result_nio_buffer(). */ @Experimental | | | | 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 | } /** Convenience overload which behaves like sqlite3_result_nio_buffer(). */ @Experimental /*public*/ static void sqlite3_result_blob( @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob, int begin, int n ){ sqlite3_result_nio_buffer(cx, blob, begin, n); } /** Convenience overload which behaves like the two-argument overload of sqlite3_result_nio_buffer(). */ @Experimental /*public*/ static void sqlite3_result_blob( @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob ){ sqlite3_result_nio_buffer(cx, blob); } /** Binds the given text using C's sqlite3_result_blob64() unless: |
︙ | ︙ | |||
2383 2384 2385 2386 2387 2388 2389 | /** A variant of sqlite3_column_blob() which returns the blob as a ByteBuffer object. Returns null if its argument is null, if sqlite3_jni_supports_nio() is false, or if sqlite3_value_blob() would return null for the same input. */ @Experimental | | | 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 | /** A variant of sqlite3_column_blob() which returns the blob as a ByteBuffer object. Returns null if its argument is null, if sqlite3_jni_supports_nio() is false, or if sqlite3_value_blob() would return null for the same input. */ @Experimental /*public*/ static native java.nio.ByteBuffer sqlite3_value_nio_buffer( @NotNull sqlite3_value v ); private static native int sqlite3_value_nochange(@NotNull long ptrToValue); public static int sqlite3_value_nochange(@NotNull sqlite3_value v){ return sqlite3_value_nochange(v.getNativePointer()); |
︙ | ︙ |
Changes to ext/jni/src/org/sqlite/jni/fts5/TesterFts5.java.
︙ | ︙ | |||
621 622 623 624 625 626 627 | "SELECT fts5_columntext(ft, 0) FROM ft('x') ORDER BY rowid", "[x, x, x y z, x y z, x y z, x]" ); do_execsql_test(db, "SELECT fts5_columntext(ft, 1) FROM ft('x') ORDER BY rowid", "[x, x, x y z, x z, x y z, x]" ); | > > > > | | | | > > > > > > | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 | "SELECT fts5_columntext(ft, 0) FROM ft('x') ORDER BY rowid", "[x, x, x y z, x y z, x y z, x]" ); do_execsql_test(db, "SELECT fts5_columntext(ft, 1) FROM ft('x') ORDER BY rowid", "[x, x, x y z, x z, x y z, x]" ); boolean threw = false; try{ /* columntext() used to return NULLs when given an out-of bounds column but now results in a range error. */ do_execsql_test(db, "SELECT fts5_columntext(ft, 2) FROM ft('x') ORDER BY rowid", "[null, null, null, null, null, null]" ); }catch(Exception e){ threw = true; affirm( e.getMessage().matches(".*column index out of range") ); } affirm( threw ); threw = false; /* Test fts5_columntotalsize() */ do_execsql_test(db, "SELECT fts5_columntotalsize(ft, 0) FROM ft('x') ORDER BY rowid", "[12, 12, 12, 12, 12, 12]" ); do_execsql_test(db, |
︙ | ︙ |
Changes to ext/misc/cksumvfs.c.
︙ | ︙ | |||
442 443 444 445 446 447 448 | } /* Verify the checksum if ** (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 */ | | | | | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | } /* Verify the checksum if ** (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) */ ){ u8 cksum[8]; cksmCompute((u8*)zBuf, iAmt-8, cksum); if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){ sqlite3_log(SQLITE_IOERR_DATA, "checksum fault offset %lld of \"%s\"", iOfst, p->zFName); |
︙ | ︙ |
Changes to ext/misc/fileio.c.
︙ | ︙ | |||
368 369 370 371 372 373 374 | mode_t mode, /* MODE parameter passed to writefile() */ sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */ ){ if( zFile==0 ) return 1; #if !defined(_WIN32) && !defined(WIN32) if( S_ISLNK(mode) ){ const char *zTo = (const char*)sqlite3_value_text(pData); | > > | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | mode_t mode, /* MODE parameter passed to writefile() */ sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */ ){ 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; }else #endif { if( S_ISDIR(mode) ){ if( mkdir(zFile, mode) ){ /* The mkdir() call to create the directory failed. This might not ** be an error though - if there is already a directory at the same |
︙ | ︙ | |||
454 455 456 457 458 459 460 | times[0].tv_nsec = times[1].tv_nsec = 0; times[0].tv_sec = time(0); times[1].tv_sec = mtime; if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){ return 1; } #else | | > > > > > | | | | | | > | 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | times[0].tv_nsec = times[1].tv_nsec = 0; times[0].tv_sec = time(0); 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; } } #endif } return 0; } |
︙ | ︙ |
Changes to ext/misc/noop.c.
︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 | sqlite3_context *context, int argc, sqlite3_value **argv ){ assert( argc==1 ); sqlite3_result_value(context, argv[0]); } #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_noop_init( sqlite3 *db, char **pzErrMsg, | > > > > > > > > > > > > > > > > > > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | sqlite3_context *context, int argc, sqlite3_value **argv ){ 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, char **pzErrMsg, |
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 | rc = sqlite3_create_function(db, "noop_do", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_DIRECTONLY, 0, noopfunc, 0, 0); if( rc ) return rc; rc = sqlite3_create_function(db, "noop_nd", 1, SQLITE_UTF8, 0, noopfunc, 0, 0); return rc; } | > > > > | 78 79 80 81 82 83 84 85 86 87 88 89 90 | rc = sqlite3_create_function(db, "noop_do", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_DIRECTONLY, 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; } |
Changes to ext/misc/series.c.
︙ | ︙ | |||
99 100 101 102 103 104 105 | #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** 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. */ | | > | | > | > > | | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** 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 ){ /* Get ix into signed i64 range. */ ix -= mxI64; /* 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; } /* 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; smBase += ix2*smStep; ix -= ix2; |
︙ | ︙ | |||
368 369 370 371 372 373 374 | ** once prior to any call to seriesColumn() or seriesRowid() or ** seriesEof(). ** ** 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: ** | | | | < < | | > > | | | | > > > > > > > > > > > > > > > > > > | | | | | > > > > > | | > > > > > > > > > > > > > > > > > > | > > | > > > > > | | > | | | > > > | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 | ** once prior to any call to seriesColumn() or seriesRowid() or ** seriesEof(). ** ** 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 ** ** 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. */ static int seriesFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStrUnused, int argc, sqlite3_value **argv ){ series_cursor *pCur = (series_cursor *)pVtabCursor; int i = 0; (void)idxStrUnused; if( idxNum & 0x01 ){ pCur->ss.iBase = sqlite3_value_int64(argv[i++]); }else{ pCur->ss.iBase = 0; } if( idxNum & 0x02 ){ pCur->ss.iTerm = sqlite3_value_int64(argv[i++]); }else{ pCur->ss.iTerm = 0xffffffff; } if( idxNum & 0x04 ){ 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; } }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( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm; } } } for(i=0; i<argc; i++){ if( sqlite3_value_type(argv[i])==SQLITE_NULL ){ /* If any of the constraints have a NULL value, then return no rows. ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */ pCur->ss.iBase = 1; pCur->ss.iTerm = 0; pCur->ss.iStep = 1; break; } } if( idxNum & 0x08 ){ pCur->ss.isReversing = pCur->ss.iStep > 0; }else{ pCur->ss.isReversing = pCur->ss.iStep < 0; } setupSequence( &pCur->ss ); return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the generate_series virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. ** ** 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 */ 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 */ 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; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; 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->iColumn<SERIES_COLUMN_START ) continue; iCol = pConstraint->iColumn - 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( pConstraint->usable==0 ){ unusableMask |= iMask; continue; }else if( 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++){ if( (j = aIdx[i])>=0 ){ pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY || i>=3; } } /* 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 ** to obtain the legacy behavior */ #ifndef ZERO_ARGUMENT_GENERATE_SERIES if( !bStartSeen ){ sqlite3_free(pVTab->zErrMsg); pVTab->zErrMsg = sqlite3_mprintf( "first argument to \"generate_series()\" missing or unusable"); return SQLITE_ERROR; } #endif if( (unusableMask & ~idxNum)!=0 ){ /* 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 ){ /* 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; }else{ idxNum |= 0x10; } 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; } pIdxInfo->idxNum = idxNum; |
︙ | ︙ |
Changes to ext/misc/sqlar.c.
︙ | ︙ | |||
77 78 79 80 81 82 83 | */ static void sqlarUncompressFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ uLong nData; | | > | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | */ static void sqlarUncompressFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ uLong nData; sqlite3_int64 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) ){ sqlite3_result_error(context, "error in uncompress()", -1); }else{ sqlite3_result_blob(context, pOut, szf, SQLITE_TRANSIENT); } sqlite3_free(pOut); } } #ifdef _WIN32 __declspec(dllexport) |
︙ | ︙ |
Changes to ext/misc/totype.c.
︙ | ︙ | |||
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | /* store the result */ *pResult = result; /* return true if number and no extra non-whitespace chracters after */ return z>=zEnd && nDigits>0 && eValid && nonNum==0; } /* ** tointeger(X): If X is any value (integer, double, blob, or string) that ** can be losslessly converted into an integer, then make the conversion and ** return the result. Otherwise, return NULL. */ static void tointegerFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ assert( argc==1 ); (void)argc; switch( sqlite3_value_type(argv[0]) ){ case SQLITE_FLOAT: { double rVal = sqlite3_value_double(argv[0]); | > > > > > > > > > > > > > > | | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | /* store the result */ *pResult = result; /* return true if number and no extra non-whitespace chracters after */ return z>=zEnd && nDigits>0 && eValid && nonNum==0; } /* ** Convert a floating point value to an integer. Or, if this cannot be ** done in a way that avoids 'outside the range of representable values' ** warnings from UBSAN, return 0. ** ** This function is a modified copy of internal SQLite function ** sqlite3RealToI64(). */ static sqlite3_int64 totypeDoubleToInt(double r){ if( r<-9223372036854774784.0 ) return 0; if( r>+9223372036854774784.0 ) return 0; return (sqlite3_int64)r; } /* ** tointeger(X): If X is any value (integer, double, blob, or string) that ** can be losslessly converted into an integer, then make the conversion and ** return the result. Otherwise, return NULL. */ static void tointegerFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ assert( argc==1 ); (void)argc; switch( sqlite3_value_type(argv[0]) ){ case SQLITE_FLOAT: { double rVal = sqlite3_value_double(argv[0]); sqlite3_int64 iVal = totypeDoubleToInt(rVal); if( rVal==(double)iVal ){ sqlite3_result_int64(context, iVal); } break; } case SQLITE_INTEGER: { sqlite3_result_int64(context, sqlite3_value_int64(argv[0])); |
︙ | ︙ | |||
436 437 438 439 440 441 442 | case SQLITE_FLOAT: { sqlite3_result_double(context, sqlite3_value_double(argv[0])); break; } case SQLITE_INTEGER: { sqlite3_int64 iVal = sqlite3_value_int64(argv[0]); double rVal = (double)iVal; | | | 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | case SQLITE_FLOAT: { sqlite3_result_double(context, sqlite3_value_double(argv[0])); break; } case SQLITE_INTEGER: { sqlite3_int64 iVal = sqlite3_value_int64(argv[0]); double rVal = (double)iVal; if( iVal==totypeDoubleToInt(rVal) ){ sqlite3_result_double(context, rVal); } break; } case SQLITE_BLOB: { const unsigned char *zBlob = sqlite3_value_blob(argv[0]); if( zBlob ){ |
︙ | ︙ |
Changes to ext/misc/vtablog.c.
︙ | ︙ | |||
34 35 36 37 38 39 40 41 | /* vtablog_vtab is a subclass of sqlite3_vtab which will ** 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 */ int nRow; /* Number of rows in the table */ | > > < | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | /* vtablog_vtab is a subclass of sqlite3_vtab which will ** 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 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 ** over rows of the result */ |
︙ | ︙ | |||
163 164 165 166 167 168 169 | sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr, int isCreate ){ | < < > | | > | > < | < > | > > > > > > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr, int isCreate ){ vtablog_vtab *pNew; int i; int rc; char *zSchema = 0; char *zNRow = 0; printf("%s.%s.%s():\n", argv[1], argv[2], isCreate ? "xCreate" : "xConnect"); printf(" argc=%d\n", argc); for(i=0; i<argc; i++){ printf(" argv[%d] = ", i); if( argv[i] ){ printf("[%s]\n", argv[i]); }else{ printf("NULL\n"); } } for(i=3; i<argc; i++){ const char *z = argv[i]; if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){ rc = SQLITE_ERROR; goto vtablog_end_connect; } if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){ rc = SQLITE_ERROR; goto vtablog_end_connect; } } if( zSchema==0 ){ zSchema = sqlite3_mprintf("%s","CREATE TABLE x(a,b);"); } printf(" schema = '%s'\n", zSchema); rc = sqlite3_declare_vtab(db, zSchema); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); pNew->nRow = 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); return rc; } static int vtablogCreate( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, |
︙ | ︙ | |||
233 234 235 236 237 238 239 | /* ** This method is the destructor for vtablog_cursor objects. */ static int vtablogDisconnect(sqlite3_vtab *pVtab){ vtablog_vtab *pTab = (vtablog_vtab*)pVtab; | | > > | > > > | | | > | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | /* ** 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); 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); sqlite3_free(pVtab); return SQLITE_OK; } /* ** 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); pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->iCursor = pTab->nCursor; *ppCursor = &pCur->base; return SQLITE_OK; } /* ** 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); sqlite3_free(cur); return SQLITE_OK; } /* ** 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); pCur->iRowid++; return SQLITE_OK; } /* ** Return values of columns for the row at which the vtablog_cursor ** is currently pointing. |
︙ | ︙ | |||
306 307 308 309 310 311 312 | if( i<26 ){ sqlite3_snprintf(sizeof(zVal),zVal,"%c%d", "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid); }else{ sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid); } | | | | | | | | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | if( i<26 ){ 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); sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT); return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** 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); *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ 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); return rc; } /* ** Output an sqlite3_value object's value as an SQL literal. */ static void vtablogQuote(sqlite3_value *p){ |
︙ | ︙ | |||
413 414 415 416 417 418 419 | static int vtablogFilter( sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ vtablog_cursor *pCur = (vtablog_cursor *)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; | | | > > > > > | > > > > > > > > > > > > > > > | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | static int vtablogFilter( sqlite3_vtab_cursor *cur, 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); pCur->iRowid = 0; return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the vtablog virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. */ static int vtablogBestIndex( sqlite3_vtab *tab, sqlite3_index_info *p ){ 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; i<p->nConstraint; 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; i<p->nOrderBy; 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); return SQLITE_OK; } /* ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from ** the table. ** ** This implementation does not actually make any changes to the table ** content. It merely logs the fact that the method was invoked */ static int vtablogUpdate( sqlite3_vtab *tab, int argc, sqlite3_value **argv, sqlite_int64 *pRowid ){ vtablog_vtab *pTab = (vtablog_vtab*)tab; int i; printf("%s.%s.xUpdate():\n", pTab->zDb, pTab->zName); printf(" argc=%d\n", argc); for(i=0; i<argc; i++){ printf(" argv[%d]=", i); vtablogQuote(argv[i]); printf("\n"); } return SQLITE_OK; } static int vtablogBegin(sqlite3_vtab *tab){ vtablog_vtab *pTab = (vtablog_vtab*)tab; printf("%s.%s.xBegin()\n", pTab->zDb, 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 */ vtablogCreate, /* xCreate */ vtablogConnect, /* xConnect */ vtablogBestIndex, /* xBestIndex */ vtablogDisconnect, /* xDisconnect */ vtablogDestroy, /* xDestroy */ vtablogOpen, /* xOpen - open a cursor */ vtablogClose, /* xClose - close a cursor */ vtablogFilter, /* xFilter - configure scan constraints */ 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 */ }; #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_vtablog_init( sqlite3 *db, |
︙ | ︙ |
Changes to ext/rbu/sqlite3rbu.c.
︙ | ︙ | |||
195 196 197 198 199 200 201 202 203 204 205 206 207 208 | typedef struct RbuUpdateStmt RbuUpdateStmt; #if !defined(SQLITE_AMALGAMATION) typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; typedef sqlite3_int64 i64; #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 ** format. */ | > | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | typedef struct RbuUpdateStmt RbuUpdateStmt; #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 ** format. */ |
︙ | ︙ | |||
881 882 883 884 885 886 887 888 889 890 891 892 893 894 | if( pIter->bCleanup ){ rbuObjIterFreeCols(pIter); pIter->bCleanup = 0; rc = sqlite3_step(pIter->pTblIter); if( rc!=SQLITE_ROW ){ rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg); pIter->zTbl = 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; } }else{ if( pIter->zIdx==0 ){ | > | 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 | if( pIter->bCleanup ){ rbuObjIterFreeCols(pIter); 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; } }else{ if( pIter->zIdx==0 ){ |
︙ | ︙ | |||
2975 2976 2977 2978 2979 2980 2981 | static i64 rbuShmChecksum(sqlite3rbu *p){ i64 iRet = 0; 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 ){ | | | 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 | static i64 rbuShmChecksum(sqlite3rbu *p){ i64 iRet = 0; 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]); } } return iRet; } /* ** This function is called as part of initializing or reinitializing an |
︙ | ︙ |
Changes to ext/recover/dbdata.c.
︙ | ︙ | |||
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | #ifndef SQLITE_OMIT_VIRTUALTABLE #define DBDATA_PADDING_BYTES 100 typedef struct DbdataTable DbdataTable; typedef struct DbdataCursor DbdataCursor; /* Cursor object */ struct DbdataCursor { sqlite3_vtab_cursor base; /* Base class. Must be first */ sqlite3_stmt *pStmt; /* For fetching database pages */ int iPgno; /* Current page number */ u8 *aPage; /* Buffer containing page */ int nPage; /* Size of aPage[] in bytes */ int nCell; /* Number of cells on aPage[] */ int iCell; /* Current cell number */ int bOnePage; /* True to stop after one page */ int szDb; sqlite3_int64 iRowid; /* Only for the sqlite_dbdata table */ | > > > > > > > > > | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | #ifndef SQLITE_OMIT_VIRTUALTABLE #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 */ int iPgno; /* Current page number */ u8 *aPage; /* Buffer containing page */ int nPage; /* Size of aPage[] in bytes */ int nCell; /* Number of cells on aPage[] */ int iCell; /* Current cell number */ int bOnePage; /* True to stop after one page */ int szDb; sqlite3_int64 iRowid; /* Only for the sqlite_dbdata table */ DbdataBuffer rec; 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; u32 enc; /* Text encoding */ |
︙ | ︙ | |||
144 145 146 147 148 149 150 151 152 153 154 155 156 157 | #define DBPTR_COLUMN_SCHEMA 2 #define DBPTR_SCHEMA \ "CREATE TABLE x(" \ " pgno INTEGER," \ " child INTEGER," \ " schema TEXT HIDDEN" \ ")" /* ** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual ** table. */ static int dbdataConnect( sqlite3 *db, | > > > > > > > > > > > > > > > > > > > > > > > > > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | #define DBPTR_COLUMN_SCHEMA 2 #define DBPTR_SCHEMA \ "CREATE TABLE x(" \ " 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( sqlite3 *db, |
︙ | ︙ | |||
285 286 287 288 289 290 291 | } pCsr->pStmt = 0; pCsr->iPgno = 1; pCsr->iCell = 0; pCsr->iField = 0; pCsr->bOnePage = 0; sqlite3_free(pCsr->aPage); | < | > | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | } pCsr->pStmt = 0; pCsr->iPgno = 1; pCsr->iCell = 0; pCsr->iField = 0; pCsr->bOnePage = 0; sqlite3_free(pCsr->aPage); dbdataBufferFree(&pCsr->rec); pCsr->aPage = 0; pCsr->nRec = 0; } /* ** Close an sqlite_dbdata or sqlite_dbptr cursor. */ static int dbdataClose(sqlite3_vtab_cursor *pCursor){ DbdataCursor *pCsr = (DbdataCursor*)pCursor; |
︙ | ︙ | |||
429 430 431 432 433 434 435 | static void dbdataValue( sqlite3_context *pCtx, u32 enc, int eType, u8 *pData, sqlite3_int64 nData ){ | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > | | > > > > > > > > > > > | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 | static void dbdataValue( sqlite3_context *pCtx, 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 /* ** Move an sqlite_dbdata or sqlite_dbptr cursor to the next entry. */ static int dbdataNext(sqlite3_vtab_cursor *pCursor){ DbdataCursor *pCsr = (DbdataCursor*)pCursor; DbdataTable *pTab = (DbdataTable*)pCursor->pVtab; |
︙ | ︙ | |||
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 | if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; } assert( iOff+3+2<=pCsr->nPage ); pCsr->iCell = pTab->bPtr ? -2 : 0; pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]); } if( pTab->bPtr ){ if( pCsr->aPage[iOff]!=0x02 && pCsr->aPage[iOff]!=0x05 ){ pCsr->iCell = pCsr->nCell; } pCsr->iCell++; if( pCsr->iCell>=pCsr->nCell ){ sqlite3_free(pCsr->aPage); pCsr->aPage = 0; if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; }else{ return SQLITE_OK; } }else{ /* If there is no record loaded, load it now. */ | > > > > | | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 | if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; } 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; } pCsr->iCell++; if( pCsr->iCell>=pCsr->nCell ){ sqlite3_free(pCsr->aPage); pCsr->aPage = 0; if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; }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 ){ int bHasRowid = 0; int nPointer = 0; sqlite3_int64 nPayload = 0; sqlite3_int64 nHdr = 0; int iHdr; int U, X; int nLocal; |
︙ | ︙ | |||
562 563 564 565 566 567 568 569 | pCsr->iCell = pCsr->nCell; break; } if( pCsr->iCell>=pCsr->nCell ){ bNextPage = 1; }else{ | > < | | | > | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 | pCsr->iCell = pCsr->nCell; break; } if( pCsr->iCell>=pCsr->nCell ){ bNextPage = 1; }else{ int iCellPtr = iOff + 8 + nPointer + pCsr->iCell*2; if( iCellPtr>pCsr->nPage ){ bNextPage = 1; }else{ iOff = get_uint16(&pCsr->aPage[iCellPtr]); } /* 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 ){ 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 && iOff<pCsr->nPage ){ iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey); } |
︙ | ︙ | |||
613 614 615 616 617 618 619 | if( bNextPage || nLocal+iOff>pCsr->nPage ){ bNextPage = 1; }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. */ | | | < | | | > > > | | | | > > | | < | | < | | 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | if( bNextPage || nLocal+iOff>pCsr->nPage ){ bNextPage = 1; }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 ); /* Load the nLocal bytes of payload */ memcpy(pCsr->rec.aBuf, &pCsr->aPage[iOff], nLocal); iOff += nLocal; /* Load content from overflow pages */ if( nPayload>nLocal ){ sqlite3_int64 nRem = nPayload - nLocal; u32 pgnoOvfl = get_uint32(&pCsr->aPage[iOff]); while( nRem>0 ){ u8 *aOvfl = 0; int nOvfl = 0; int nCopy; rc = dbdataLoadPage(pCsr, pgnoOvfl, &aOvfl, &nOvfl); assert( rc!=SQLITE_OK || aOvfl==0 || nOvfl==pCsr->nPage ); 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); 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); if( nHdr>nPayload ) nHdr = 0; pCsr->nHdr = nHdr; pCsr->pHdrPtr = &pCsr->rec.aBuf[iHdr]; pCsr->pPtr = &pCsr->rec.aBuf[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 ){ bNextPage = 1; }else{ int szField = 0; pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType); szField = dbdataValueBytes(iType); if( (pCsr->nRec - (pCsr->pPtr - pCsr->rec.aBuf))<szField ){ pCsr->pPtr = &pCsr->rec.aBuf[pCsr->nRec]; }else{ pCsr->pPtr += szField; } } } } if( bNextPage ){ sqlite3_free(pCsr->aPage); pCsr->aPage = 0; pCsr->nRec = 0; if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; }else{ if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->rec.aBuf[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; pCsr->iCell++; } } } assert( !"can't get here" ); return SQLITE_OK; |
︙ | ︙ | |||
877 878 879 880 881 882 883 | break; case DBDATA_COLUMN_FIELD: sqlite3_result_int(ctx, pCsr->iField); break; case DBDATA_COLUMN_VALUE: { if( pCsr->iField<0 ){ sqlite3_result_int64(ctx, pCsr->iIntkey); | | | | 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 | break; case DBDATA_COLUMN_FIELD: 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 ){ sqlite3_int64 iType; dbdataGetVarintU32(pCsr->pHdrPtr, &iType); dbdataValue( ctx, pCsr->enc, iType, pCsr->pPtr, &pCsr->rec.aBuf[pCsr->nRec] - pCsr->pPtr ); } break; } } } return SQLITE_OK; |
︙ | ︙ |
Changes to ext/recover/recover1.test.
︙ | ︙ | |||
338 339 340 341 342 343 344 | CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); INSERT INTO t1 VALUES('abc', 'def'); PRAGMA writable_schema = 1; DELETE FROM sqlite_schema WHERE name='t1'; } | | | > > > > > > > > > > > | 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); INSERT INTO t1 VALUES('abc', 'def'); PRAGMA writable_schema = 1; DELETE FROM sqlite_schema WHERE name='t1'; } proc my_sql_hook2 {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] $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 |
Changes to ext/recover/recovercorrupt2.test.
︙ | ︙ | |||
519 520 521 522 523 524 525 526 527 528 | | end x1.db }]} {} do_test 7.1 { set R [sqlite3_recover_init db main test.db2] catch { $R run } list [catch { $R finish } msg] $msg } {1 {file is not a database}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 | | end x1.db }]} {} do_test 7.1 { 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 |
Added ext/recover/recovercorrupt3.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | # 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 |
Added ext/recover/recovercorrupt4.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | # 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 |
Changes to ext/recover/sqlite3recover.c.
︙ | ︙ | |||
1185 1186 1187 1188 1189 1190 1191 | } rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); 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); | | | 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 | } rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); 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); } recoverReset(p, pTblname); } }else if( rc!=SQLITE_ERROR ){ recoverDbError(p, p->dbOut); } sqlite3_free(zFree); |
︙ | ︙ |
Changes to ext/recover/test_recover.c.
︙ | ︙ | |||
232 233 234 235 236 237 238 | Tcl_WrongNumArgs(interp, 1, objv, zErr); return TCL_ERROR; } if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR; zDb = Tcl_GetString(objv[2]); if( zDb[0]=='\0' ) zDb = 0; | | | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | Tcl_WrongNumArgs(interp, 1, objv, zErr); return TCL_ERROR; } 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)); if( bSql==0 ){ zUri = Tcl_GetString(objv[3]); pNew->p = sqlite3_recover_init(db, zDb, zUri); }else{ pNew->interp = interp; pNew->pScript = objv[3]; Tcl_IncrRefCount(pNew->pScript); |
︙ | ︙ |
Changes to ext/rtree/rtree.c.
︙ | ︙ | |||
690 691 692 693 694 695 696 | return pNode; } /* ** Clear the Rtree.pNodeBlob object */ static void nodeBlobReset(Rtree *pRtree){ | < | | | < | | 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 | return pNode; } /* ** Clear the Rtree.pNodeBlob object */ static void nodeBlobReset(Rtree *pRtree){ sqlite3_blob *pBlob = pRtree->pNodeBlob; pRtree->pNodeBlob = 0; sqlite3_blob_close(pBlob); } /* ** Obtain a reference to an r-tree node. */ static int nodeAcquire( Rtree *pRtree, /* R-tree structure */ i64 iNode, /* Node number to load */ RtreeNode *pParent, /* Either the parent node or NULL */ RtreeNode **ppNode /* OUT: Acquired node */ ){ int rc = SQLITE_OK; RtreeNode *pNode = 0; /* Check if the requested node is already in the hash table. If so, ** increase its reference count and return it. */ if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ if( pParent && ALWAYS(pParent!=pNode->pParent) ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } pNode->nRef++; *ppNode = pNode; return SQLITE_OK; } |
︙ | ︙ | |||
738 739 740 741 742 743 744 | } if( pRtree->pNodeBlob==0 ){ rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName, "data", iNode, 0, &pRtree->pNodeBlob); } if( rc ){ | < | 736 737 738 739 740 741 742 743 744 745 746 747 748 749 | } if( pRtree->pNodeBlob==0 ){ rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName, "data", iNode, 0, &pRtree->pNodeBlob); } if( rc ){ *ppNode = 0; /* If unable to open an sqlite3_blob on the desired row, that can only ** be because the shadow tables hold erroneous data. */ if( rc==SQLITE_ERROR ){ rc = SQLITE_CORRUPT_VTAB; RTREE_IS_CORRUPT(pRtree); } |
︙ | ︙ | |||
798 799 800 801 802 803 804 805 806 807 808 809 810 811 | nodeHashInsert(pRtree, pNode); }else{ rc = SQLITE_CORRUPT_VTAB; RTREE_IS_CORRUPT(pRtree); } *ppNode = pNode; }else{ if( pNode ){ pRtree->nNodeRef--; sqlite3_free(pNode); } *ppNode = 0; } | > | 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 | nodeHashInsert(pRtree, pNode); }else{ rc = SQLITE_CORRUPT_VTAB; RTREE_IS_CORRUPT(pRtree); } *ppNode = pNode; }else{ nodeBlobReset(pRtree); if( pNode ){ pRtree->nNodeRef--; sqlite3_free(pNode); } *ppNode = 0; } |
︙ | ︙ | |||
942 943 944 945 946 947 948 949 950 951 952 953 954 955 | static void nodeGetCoord( Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* The node from which to extract a coordinate */ int iCell, /* The index of the cell within the node */ int iCoord, /* Which coordinate to extract */ RtreeCoord *pCoord /* OUT: Space to write result to */ ){ readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); } /* ** Deserialize cell iCell of node pNode. Populate the structure pointed ** to by pCell with the results. */ | > | 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 | static void nodeGetCoord( Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* The node from which to extract a coordinate */ int iCell, /* The index of the cell within the node */ int iCoord, /* Which coordinate to extract */ RtreeCoord *pCoord /* OUT: Space to write result to */ ){ assert( iCell<NCELL(pNode) ); readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); } /* ** Deserialize cell iCell of node pNode. Populate the structure pointed ** to by pCell with the results. */ |
︙ | ︙ | |||
1131 1132 1133 1134 1135 1136 1137 | Rtree *pRtree = (Rtree *)(cur->pVtab); RtreeCursor *pCsr = (RtreeCursor *)cur; assert( pRtree->nCursor>0 ); resetCursor(pCsr); sqlite3_finalize(pCsr->pReadAux); sqlite3_free(pCsr); pRtree->nCursor--; | > | > | 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | Rtree *pRtree = (Rtree *)(cur->pVtab); RtreeCursor *pCsr = (RtreeCursor *)cur; assert( pRtree->nCursor>0 ); resetCursor(pCsr); sqlite3_finalize(pCsr->pReadAux); sqlite3_free(pCsr); pRtree->nCursor--; if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){ nodeBlobReset(pRtree); } return SQLITE_OK; } /* ** Rtree virtual table module xEof method. ** ** Return non-zero if the cursor does not currently point to a valid |
︙ | ︙ | |||
1716 1717 1718 1719 1720 1721 1722 | */ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc==SQLITE_OK && ALWAYS(p) ){ | > > > | > > | 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 | */ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc==SQLITE_OK && ALWAYS(p) ){ if( p->iCell>=NCELL(pNode) ){ rc = SQLITE_ABORT; }else{ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); } } return rc; } /* ** Rtree virtual table module xColumn method. */ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ Rtree *pRtree = (Rtree *)cur->pVtab; RtreeCursor *pCsr = (RtreeCursor *)cur; RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); RtreeCoord c; int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; if( NEVER(p==0) ) return SQLITE_OK; if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c); #ifndef SQLITE_RTREE_INT_ONLY if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ sqlite3_result_double(ctx, c.f); |
︙ | ︙ | |||
1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 | pCons->op = RTREE_QUERY; pCons->u.xQueryFunc = pBlob->cb.xQueryFunc; } pCons->pInfo = pInfo; return SQLITE_OK; } /* ** Rtree virtual table module xFilter method. */ static int rtreeFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv | > > | 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 | pCons->op = RTREE_QUERY; pCons->u.xQueryFunc = pBlob->cb.xQueryFunc; } pCons->pInfo = pInfo; return SQLITE_OK; } int sqlite3IntFloatCompare(i64,double); /* ** Rtree virtual table module xFilter method. */ static int rtreeFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv |
︙ | ︙ | |||
1860 1861 1862 1863 1864 1865 1866 | /* Special case - lookup by rowid. */ RtreeNode *pLeaf; /* Leaf on which the required cell resides */ 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 | | > | 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 | /* Special case - lookup by rowid. */ RtreeNode *pLeaf; /* Leaf on which the required cell resides */ 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]))) ){ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode); }else{ rc = SQLITE_OK; pLeaf = 0; } if( rc==SQLITE_OK && pLeaf!=0 ){ |
︙ | ︙ | |||
3216 3217 3218 3219 3220 3221 3222 | /* ** Called when a transaction starts. */ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; assert( pRtree->inWrTrans==0 ); | | > > > | 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 | /* ** 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; } /* ** Called when a transaction completes (either by COMMIT or ROLLBACK). ** The sqlite3_blob object should be released at this point. */ static int rtreeEndTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; pRtree->inWrTrans = 0; nodeBlobReset(pRtree); return SQLITE_OK; } static int rtreeRollback(sqlite3_vtab *pVtab){ return rtreeEndTransaction(pVtab); } /* ** The xRename method for rtree module virtual tables. */ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){ Rtree *pRtree = (Rtree *)pVtab; |
︙ | ︙ | |||
3348 3349 3350 3351 3352 3353 3354 | rtreeEof, /* xEof */ rtreeColumn, /* xColumn - read data */ rtreeRowid, /* xRowid - read data */ rtreeUpdate, /* xUpdate - write data */ rtreeBeginTransaction, /* xBegin - begin transaction */ rtreeEndTransaction, /* xSync - sync transaction */ rtreeEndTransaction, /* xCommit - commit transaction */ | | | 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 | rtreeEof, /* xEof */ rtreeColumn, /* xColumn - read data */ rtreeRowid, /* xRowid - read data */ rtreeUpdate, /* xUpdate - write data */ rtreeBeginTransaction, /* xBegin - begin transaction */ rtreeEndTransaction, /* xSync - sync transaction */ rtreeEndTransaction, /* xCommit - commit transaction */ rtreeRollback, /* xRollback - rollback transaction */ 0, /* xFindFunction - function overloading */ rtreeRename, /* xRename - rename the table */ rtreeSavepoint, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ rtreeShadowName, /* xShadowName */ rtreeIntegrity /* xIntegrity */ |
︙ | ︙ | |||
3448 3449 3450 3451 3452 3453 3454 | if( zSql ){ rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0); }else{ rc = SQLITE_NOMEM; } sqlite3_free(zSql); } | | | 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 | if( zSql ){ rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0); }else{ rc = SQLITE_NOMEM; } sqlite3_free(zSql); } if( pRtree->nAux && rc!=SQLITE_NOMEM ){ pRtree->zReadAuxSql = sqlite3_mprintf( "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1", zDb, zPrefix); if( pRtree->zReadAuxSql==0 ){ rc = SQLITE_NOMEM; }else{ sqlite3_str *p = sqlite3_str_new(db); |
︙ | ︙ | |||
4137 4138 4139 4140 4141 4142 4143 | /* Initialize the context object */ memset(&check, 0, sizeof(check)); check.db = db; check.zDb = zDb; check.zTab = zTab; /* Find the number of auxiliary columns */ | < | | | | | | | < | 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 | /* Initialize the context object */ memset(&check, 0, sizeof(check)); check.db = db; check.zDb = zDb; check.zTab = zTab; /* Find the number of auxiliary columns */ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab); if( pStmt ){ nAux = sqlite3_column_count(pStmt) - 2; sqlite3_finalize(pStmt); }else if( check.rc!=SQLITE_NOMEM ){ check.rc = SQLITE_OK; } /* Find number of dimensions in the rtree table. */ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab); if( pStmt ){ int rc; check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2; |
︙ | ︙ | |||
4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 | UNUSED_PARAMETER(zSchema); UNUSED_PARAMETER(zName); UNUSED_PARAMETER(isQuick); rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr); if( rc==SQLITE_OK && *pzErr ){ *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z", pRtree->zDb, pRtree->zName, *pzErr); } return rc; } /* ** Usage: ** | > | 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 | UNUSED_PARAMETER(zSchema); UNUSED_PARAMETER(zName); UNUSED_PARAMETER(isQuick); rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr); if( rc==SQLITE_OK && *pzErr ){ *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z", pRtree->zDb, pRtree->zName, *pzErr); if( (*pzErr)==0 ) rc = SQLITE_NOMEM; } return rc; } /* ** Usage: ** |
︙ | ︙ |
Changes to ext/rtree/rtree1.test.
︙ | ︙ | |||
792 793 794 795 796 797 798 799 800 | # reset_db do_test 23.0 { 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} finish_test | > > > > > > > > > > > > > > > > > > | 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 | # reset_db do_test 23.0 { 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 |
Added ext/rtree/rtreeJ.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | # 2024-02-03 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # ROLLBACK in the middle of an RTREE query # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl set testprefix rtreeJ ifcapable !rtree { finish_test ; return } do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2); INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2); } {} do_execsql_test 1.1 { SELECT * FROM t1 } {1 1.0 1.0 2 2.0 2.0} # If a ROLLBACK occurs that backs out changes to the RTREE, then # all pending queries to the RTREE are aborted. # do_test 1.2 { db eval { BEGIN; INSERT INTO t1 VALUES(3, 3, 3); INSERT INTO t1 VALUES(4, 4, 4); } set rc [catch { db eval { SELECT * FROM t1 } { if {$id==1} { db eval { ROLLBACK } } lappend res $id $x1 $x2 } } msg] list $rc $msg } {1 {query aborted}} do_execsql_test 1.3 { SELECT * FROM t1; } {1 1.0 1.0 2 2.0 2.0} # A COMMIT of changes to the RTREE does not affect pending queries # do_test 1.4 { set res {} db eval { BEGIN; INSERT INTO t1 VALUES(5, 5, 5); INSERT INTO t1 VALUES(6, 6, 6); } db eval { SELECT * FROM t1 } { if {$id==1} { db eval { COMMIT } } lappend res $id $x1 $x2 } set res } {1 1.0 1.0 2 2.0 2.0 5 5.0 5.0 6 6.0 6.0} do_execsql_test 1.5 { SELECT * FROM t1; } {1 1.0 1.0 2 2.0 2.0 5 5.0 5.0 6 6.0 6.0} do_execsql_test 1.6 { DELETE FROM t1; INSERT INTO t1 VALUES(1,1,1),(2,2,2),(3,3,3),(4,4,4); CREATE TABLE t2(x); SELECT * FROM t1; } {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0} # A rollback that does not affect the rtree table because # the rtree table has not been written to does not cause # a query abort. # do_test 1.7 { set res {} db eval { BEGIN; INSERT INTO t2(x) VALUES(12345); } db eval { SELECT * FROM t1 } { if {$id==1} { db eval { ROLLBACK } } lappend res $id $x1 $x2 } set res } {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0} # ROLLBACK TO that affects the RTREE does cause a query abort. # do_test 1.8 { db eval { DELETE FROM t1 WHERE rowid>1; BEGIN; DELETE FROM t2; INSERT INTO t2(x) VALUES(23456); SAVEPOINT 'one'; INSERT INTO t1 VALUES(2,2,2),(3,3,3); } set rc [catch { db eval { SELECT * FROM t1 } { if {$id==1} { db eval { ROLLBACK TO 'one'; } } lappend res $id $x1 $x2 } } msg] list $rc $msg } {1 {query aborted}} do_execsql_test 1.9 { COMMIT; SELECT * FROM t1; } {1 1.0 1.0} # ROLLBACK TO that does not affect the RTREE does not cause a query abort. # do_execsql_test 1.10 { DELETE FROM t1; INSERT INTO t1 VALUES(1,1,1),(2,2,2),(3,3,3); BEGIN; DELETE FROM t2; INSERT INTO t2(x) VALUES(34567); SAVEPOINT 'one'; INSERT INTO t2(x) VALUES('a string'); SELECT * FROM t1; } {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0} do_test 1.11 { set rc [catch { set res {} db eval { SELECT * FROM t1 } { if {$id==2} { # db eval { ROLLBACK TO 'one'; } } lappend res $id $x1 $x2 } set res } msg] list $rc $msg } {0 {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0}} do_execsql_test 1.12 { COMMIT; SELECT * FROM t1; } {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0} #---------------------------------------------------------------------- reset_db do_execsql_test 2.0 { CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2); INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2); CREATE TABLE t2(x); } {} do_test 2.1 { db eval { BEGIN; INSERT INTO t1 VALUES(3, 3, 3); PRAGMA writable_schema = RESET; } set rc [catch { db eval { SELECT x1, x2 FROM t1 } { if {$x1==1} { db eval { ROLLBACK } } lappend res $x1 $x2 } } msg] list $rc $msg } {1 {query aborted}} do_execsql_test 2.1 { CREATE TABLE bak_node(nodeno, data); CREATE TABLE bak_parent(nodeno, parentnode); CREATE TABLE bak_rowid(rowid, nodeno); } proc save_t1 {} { db eval { DELETE FROM bak_node; DELETE FROM bak_parent; DELETE FROM bak_rowid; INSERT INTO bak_node SELECT * FROM t1_node; INSERT INTO bak_parent SELECT * FROM t1_parent; INSERT INTO bak_rowid SELECT * FROM t1_rowid; } } proc restore_t1 {} { db eval { DELETE FROM t1_node; DELETE FROM t1_parent; DELETE FROM t1_rowid; INSERT INTO t1_node SELECT * FROM bak_node; INSERT INTO t1_parent SELECT * FROM bak_parent; INSERT INTO t1_rowid SELECT * FROM bak_rowid; } } do_test 2.3 { save_t1 db eval { INSERT INTO t1 VALUES(3, 3, 3); } set rc [catch { db eval { SELECT rowid, x1, x2 FROM t1 } { if {$x1==1} { restore_t1 } lappend res $x1 $x2 } } msg] list $rc $msg } {1 {query aborted}} do_execsql_test 2.4 { SELECT * FROM t1 } {1 1.0 1.0 2 2.0 2.0} do_test 2.5 { save_t1 db eval { INSERT INTO t1 VALUES(3, 3, 3); } set rc [catch { db eval { SELECT x1 FROM t1 } { if {$x1==1} { restore_t1 } lappend res $x1 $x2 } } msg] list $rc $msg } {1 {query aborted}} do_execsql_test 2.6 { SELECT * FROM t1 } {1 1.0 1.0 2 2.0 2.0} do_test 2.7 { save_t1 db eval { INSERT INTO t1 VALUES(3, 3, 3); } set ::res [list] set rc [catch { db eval { SELECT 'abc' FROM t1 } { if {$::res==[list]} { restore_t1 set ::bDone 1 } lappend res abc } } msg] set res } {abc abc abc} do_execsql_test 2.6 { SELECT * FROM t1 } {1 1.0 1.0 2 2.0 2.0} finish_test |
Added ext/session/sessionchange.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | # 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 |
Added ext/session/sessionconflict.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | # 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 |
Changes to ext/session/sessionstat1.test.
︙ | ︙ | |||
88 89 90 91 92 93 94 | ) INSERT INTO t1 SELECT i, i%8, i%2 FROM s; ANALYZE; } } {} do_execsql_test -db db2 2.2 { | | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | ) INSERT INTO t1 SELECT i, i%8, i%2 FROM s; ANALYZE; } } {} do_execsql_test -db db2 2.2 { SELECT * FROM sqlite_stat1 ORDER BY tbl, idx } { t1 sqlite_autoindex_t1_1 {32 1} t1 t1b {32 4} t1 t1c {32 16} } 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; } { t1 sqlite_autoindex_t1_1 {32 1} t1 t1b {32 4} } do_test 2.3 { do_then_apply_sql -ignorenoop { DROP TABLE t1 } |
︙ | ︙ |
Changes to ext/session/sqlite3session.c.
︙ | ︙ | |||
2344 2345 2346 2347 2348 2349 2350 | sqlite3_mutex_leave(sqlite3_db_mutex(db)); sqlite3ValueFree(pSession->pZeroBlob); /* Delete all attached table objects. And the contents of their ** associated hash-tables. */ sessionDeleteTable(pSession, pSession->pTable); | < | < | 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 | sqlite3_mutex_leave(sqlite3_db_mutex(db)); sqlite3ValueFree(pSession->pZeroBlob); /* Delete all attached table objects. And the contents of their ** associated hash-tables. */ sessionDeleteTable(pSession, pSession->pTable); /* Free the session object. */ sqlite3_free(pSession); } /* ** Set a table filter on a Session Object. */ void sqlite3session_table_filter( |
︙ | ︙ | |||
3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 | /* Make sure the buffer contains at least 10 bytes of input data, or all ** remaining data if there are less than 10 bytes available. This is ** 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; /* If the iterator is already at the end of the changeset, return DONE. */ if( p->in.iNext>=p->in.nData ){ return SQLITE_DONE; } | > > > < < < | 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 | /* Make sure the buffer contains at least 10 bytes of input data, or all ** remaining data if there are less than 10 bytes available. This is ** 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; } 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; if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; p->in.iCurrent = p->in.iNext; |
︙ | ︙ | |||
5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 | /* ** sqlite3_changegroup handle. */ struct sqlite3_changegroup { int rc; /* Error code */ int bPatch; /* True to accumulate patchsets */ SessionTable *pList; /* List of tables in current patch */ sqlite3 *db; /* Configured by changegroup_schema() */ char *zDb; /* Configured by changegroup_schema() */ }; /* ** This function is called to merge two changes to the same row together as | > | 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 | /* ** sqlite3_changegroup handle. */ 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() */ }; /* ** This function is called to merge two changes to the same row together as |
︙ | ︙ | |||
5723 5724 5725 5726 5727 5728 5729 | sessionAppendBlob(pOut, aRec, nRec, &rc); } return rc; } /* | > | | > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | < < > > > > > > > < | < < | < < < < < < | | | | | | | < | < | < < < | | < < < < | | < < < < | < | < < < < | < < | | > | < < | | | < < < < < < < < < | < | | < < < < | < < < | < < < < > < < < < | > > < > | | | | | | > > | | > > > > > > > > > > > > > > > > > > | 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 | sessionAppendBlob(pOut, aRec, nRec, &rc); } 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 && nCol<pTab->nCol ){ 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. */ iHash = sessionChangeHash( pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange ); for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ int bPkOnly1 = 0; int bPkOnly2 = 0; if( pIter->bPatchset ){ bPkOnly1 = (*pp)->op==SQLITE_DELETE; bPkOnly2 = op==SQLITE_DELETE; } if( sessionChangeEqual(pTab, bPkOnly1, (*pp)->aRecord, bPkOnly2, aRec) ){ pExist = *pp; *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==SQLITE_OK ) rc = pIter->rc; return rc; } /* ** Serialize a changeset (or patchset) based on all changesets (or patchsets) ** added to the changegroup object passed as the first argument. |
︙ | ︙ | |||
5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 | rc = sqlite3changeset_start(&pIter, nData, pData); if( rc==SQLITE_OK ){ rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; } /* ** Obtain a buffer containing a changeset representing the concatenation ** of all changesets added to the group so far. */ int sqlite3changegroup_output( sqlite3_changegroup *pGrp, | > > > > > > > > > > > > > > > > > | 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 | rc = sqlite3changeset_start(&pIter, nData, pData); if( rc==SQLITE_OK ){ rc = sessionChangesetToHash(pIter, pGrp, 0); } 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( sqlite3_changegroup *pGrp, |
︙ | ︙ | |||
6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 | /* ** Delete a changegroup object. */ void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); sqlite3_free(pGrp); } } /* ** Combine two changesets together. */ | > | 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 | /* ** Delete a changegroup object. */ void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); sqlite3_free(pGrp->rec.aBuf); sqlite3_free(pGrp); } } /* ** Combine two changesets together. */ |
︙ | ︙ | |||
6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 | /* ** Destroy a rebaser object */ void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ sessionDeleteTable(0, p->grp.pList); sqlite3_free(p); } } /* ** Global configuration */ | > | 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 | /* ** 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); } } /* ** Global configuration */ |
︙ | ︙ |
Changes to ext/session/sqlite3session.h.
︙ | ︙ | |||
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 | ** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ** occurs during processing, this function returns SQLITE_NOMEM. ** ** 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: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup | > > > > > > > > > > > > > > > > > > > > > > > > | 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 | ** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ** occurs during processing, this function returns SQLITE_NOMEM. ** ** 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 ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup |
︙ | ︙ |
Changes to ext/session/test_session.c.
︙ | ︙ | |||
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 | }else{ assert_changeset_is_ok(sOut.n, sOut.p); Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n)); } sqlite3_free(sOut.p); return rc; } /* ** sqlite3session_foreach VARNAME CHANGESET SCRIPT */ static int SQLITE_TCLAPI test_sqlite3session_foreach( void * clientData, Tcl_Interp *interp, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 | }else{ assert_changeset_is_ok(sOut.n, sOut.p); Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n)); } 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; i<nCol; i++){ zPK[i] = (abPK[i] ? 'X' : '.'); } Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1)); ckfree(zPK); pOld = Tcl_NewObj(); if( op!=SQLITE_INSERT ){ for(i=0; i<nCol; i++){ sqlite3_value *pVal; sqlite3changeset_old(pIter, i, &pVal); test_append_value(pOld, pVal); } } pNew = Tcl_NewObj(); if( op!=SQLITE_DELETE ){ for(i=0; i<nCol; i++){ sqlite3_value *pVal; sqlite3changeset_new(pIter, i, &pVal); test_append_value(pNew, pVal); } } Tcl_ListObjAppendElement(0, pVar, pOld); Tcl_ListObjAppendElement(0, pVar, pNew); return pVar; } /* ** sqlite3session_foreach VARNAME CHANGESET SCRIPT */ static int SQLITE_TCLAPI test_sqlite3session_foreach( void * clientData, Tcl_Interp *interp, |
︙ | ︙ | |||
1107 1108 1109 1110 1111 1112 1113 | } } if( rc!=SQLITE_OK ){ return test_session_error(interp, rc, 0); } while( SQLITE_ROW==sqlite3changeset_next(pIter) ){ | < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 | } } if( rc!=SQLITE_OK ){ return test_session_error(interp, rc, 0); } while( SQLITE_ROW==sqlite3changeset_next(pIter) ){ Tcl_Obj *pVar = 0; /* Tcl value to set $VARNAME to */ pVar = testIterData(pIter); Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0); rc = Tcl_EvalObjEx(interp, pScript, 0); if( rc!=TCL_OK && rc!=TCL_CONTINUE ){ sqlite3changeset_finalize(pIter); return rc==TCL_BREAK ? TCL_OK : rc; } } |
︙ | ︙ | |||
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 | return TCL_OK; } typedef struct TestChangegroup TestChangegroup; struct TestChangegroup { sqlite3_changegroup *pGrp; }; /* ** Destructor for Tcl changegroup command object. */ static void test_changegroup_del(void *clientData){ TestChangegroup *pGrp = (TestChangegroup*)clientData; sqlite3changegroup_delete(pGrp->pGrp); | > > > > > > | 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 | return TCL_OK; } typedef struct TestChangegroup TestChangegroup; struct TestChangegroup { sqlite3_changegroup *pGrp; }; typedef struct TestChangeIter TestChangeIter; struct TestChangeIter { sqlite3_changeset_iter *pIter; }; /* ** Destructor for Tcl changegroup command object. */ static void test_changegroup_del(void *clientData){ TestChangegroup *pGrp = (TestChangegroup*)clientData; sqlite3changegroup_delete(pGrp->pGrp); |
︙ | ︙ | |||
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 | const char *zMsg; int iSub; } aSub[] = { { "schema", 2, "DB DBNAME", }, /* 0 */ { "add", 1, "CHANGESET", }, /* 1 */ { "output", 0, "", }, /* 2 */ { "delete", 0, "", }, /* 3 */ { 0 } }; int rc = TCL_OK; int iSub = 0; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); | > | 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 | const char *zMsg; int iSub; } aSub[] = { { "schema", 2, "DB DBNAME", }, /* 0 */ { "add", 1, "CHANGESET", }, /* 1 */ { "output", 0, "", }, /* 2 */ { "delete", 0, "", }, /* 3 */ { "add_change", 1, "ITERATOR", }, /* 4 */ { 0 } }; int rc = TCL_OK; int iSub = 0; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); |
︙ | ︙ | |||
1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 | rc = test_session_error(interp, rc, 0); }else{ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(aByte, nByte)); } sqlite3_free(aByte); break; }; default: { /* delete */ assert( iSub==3 ); Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); break; } } | > > > > > > > > > > > > > > > > > > | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 | rc = test_session_error(interp, rc, 0); }else{ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(aByte, nByte)); } sqlite3_free(aByte); break; }; case 4: { /* add_change */ Tcl_CmdInfo cmdInfo; /* Database Tcl command (objv[2]) info */ TestChangeIter *pIter = 0; const char *zIter = Tcl_GetString(objv[2]); if( 0==Tcl_GetCommandInfo(interp, zIter, &cmdInfo) ){ Tcl_AppendResult(interp, "no such iter: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } pIter = (struct TestChangeIter*)cmdInfo.objClientData; rc = sqlite3changegroup_add_change(p->pGrp, 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; } } |
︙ | ︙ | |||
1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 | Tcl_CreateObjCommand( interp, Tcl_GetString(objv[1]), test_changegroup_cmd, (ClientData)p, test_changegroup_del ); Tcl_SetObjResult(interp, objv[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 }, { "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 }, { "sqlite3changeset_apply_replace_all", test_sqlite3changeset_apply_replace_all }, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 | Tcl_CreateObjCommand( interp, Tcl_GetString(objv[1]), test_changegroup_cmd, (ClientData)p, test_changegroup_del ); 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 }, { "sqlite3changeset_apply_replace_all", test_sqlite3changeset_apply_replace_all }, |
︙ | ︙ |
Changes to ext/userauth/user-auth.txt.
1 2 3 4 5 6 7 | 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. When using the SQLite amalgamation, it is sufficient to append | > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | *********************************** 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. When using the SQLite amalgamation, it is sufficient to append |
︙ | ︙ |
Changes to ext/wasm/GNUmakefile.
︙ | ︙ | |||
38 39 40 41 42 43 44 | # # Significant TODOs for this build include, but are not necessarily # limited to: # # 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 | | > > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | # # Significant TODOs for this build include, but are not necessarily # limited to: # # 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.) # default: all #default: quick SHELL := $(shell which bash 2>/dev/null) MAKEFILE := $(lastword $(MAKEFILE_LIST)) CLEAN_FILES := DISTCLEAN_FILES := ./--dummy-- release: oz ######################################################################## # JS_BUILD_NAMES exists for documentation purposes only. It enumerates # the core build styles: # # - sqlite3 = canonical library build # # - sqlite3-wasmfs = WASMFS-capable library build # JS_BUILD_NAMES := sqlite3 sqlite3-wasmfs ######################################################################## # JS_BUILD_MODES exists for documentation purposes only. It enumerates # the various "flavors" of build, each of which requires slight # customization of the output: # # - vanilla = plain-vanilla JS for use in browsers. This is the # canonical build mode. # # - esm = ES6 module, a.k.a. ESM, for use in browsers. # # - bundler-friendly = esm slightly tweaked for "bundler" # tools. Bundlers are invariably based on node.js, so these builds # are intended to be read at build-time by node.js but with a final # target of browsers. # # - node = for use by node.js for node.js, as opposed to by node.js on # behalf o browser-side code (use bundler-friendly for that). Note # that persistent storage (OPFS) is not available in these builds. # JS_BUILD_MODES := vanilla esm bunder-friendly node ######################################################################## # Emscripten SDK home dir and related binaries... EMSDK_HOME ?= $(word 1,$(wildcard $(HOME)/emsdk $(HOME)/src/emsdk)) emcc.bin ?= $(word 1,$(wildcard $(EMSDK_HOME)/upstream/emscripten/emcc) $(shell which emcc)) ifeq (,$(emcc.bin)) $(error Cannot find emcc.) endif emcc.version := $(shell "$(emcc.bin)" --version | sed -n 1p \ |
︙ | ︙ | |||
89 90 91 92 93 94 95 96 | ifeq (,$(wasm-strip)) maybe-wasm-strip = echo "not wasm-stripping" else maybe-wasm-strip = $(wasm-strip) endif dir.top := ../.. | > > > | | | | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | ifeq (,$(wasm-strip)) maybe-wasm-strip = echo "not wasm-stripping" else maybe-wasm-strip = $(wasm-strip) endif ######################################################################## # dir.top = the top dir of the canonical build tree, where # sqlite3.[ch] live. dir.top := ../.. # Maintenance reminder: some Emscripten flags require absolute paths # but we want relative paths for most stuff simply to reduce # noise. The $(abspath...) GNU make function can transform relative # paths to absolute. dir.wasm := $(patsubst %/,%,$(dir $(MAKEFILE))) dir.api := api dir.jacc := jaccwabyt dir.common := common dir.fiddle := fiddle dir.tool := $(dir.top)/tool CLEAN_FILES += *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ $(dir.fiddle)/*~ |
︙ | ︙ | |||
139 140 141 142 143 144 145 | dir._tmp := $(shell mkdir -p $(dir.tmp)) endif ######################################################################## # Set up sqlite3.c and sqlite3.h... # # To build with SEE (https://sqlite.org/see), either put sqlite3-see.c | < | | | | | > > > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | dir._tmp := $(shell mkdir -p $(dir.tmp)) endif ######################################################################## # Set up sqlite3.c and sqlite3.h... # # To build with SEE (https://sqlite.org/see), either put sqlite3-see.c # in $(dir.top) or pass sqlite3.c=PATH_TO_sqlite3-see.c to the $(MAKE) # invocation. Note that only encryption modules with no 3rd-party # dependencies will currently work here: AES256-OFB, AES128-OFB, and # AES128-CCM. Not coincidentally, those 3 modules are included in the # sqlite3-see.c bundle. Note, however, that distributing an SEE build # of the WASM on a public site is in violation of the SEE license # because it effectively provides a usable copy of the SEE build to # all visitors. # # A custom sqlite3.c must not have any spaces in its name. # $(sqlite3.canonical.c) must point to the sqlite3.c in # the sqlite3 canonical source tree, as that source file # is required for certain utility and test code. sqlite3.canonical.c := $(dir.top)/sqlite3.c sqlite3.c ?= $(firstword $(wildcard $(dir.top)/sqlite3-see.c) $(sqlite3.canonical.c)) |
︙ | ︙ | |||
189 190 191 192 193 194 195 196 197 198 199 200 201 202 | #SQLITE_OPT += -DSQLITE_DEBUG # Enabling SQLITE_DEBUG will break sqlite3_wasm_vfs_create_file() # (and thus sqlite3_js_vfs_create_file()). Those functions are # deprecated and alternatives are in place, but this crash behavior # can be used to find errant uses of sqlite3_js_vfs_create_file() # in client code. .NOTPARALLEL: $(sqlite3.h) $(sqlite3.h): $(MAKE) -C $(dir.top) sqlite3.c $(sqlite3.c): $(sqlite3.h) .PHONY: clean distclean clean: | > > > > | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | #SQLITE_OPT += -DSQLITE_DEBUG # Enabling SQLITE_DEBUG will break sqlite3_wasm_vfs_create_file() # (and thus sqlite3_js_vfs_create_file()). Those functions are # deprecated and alternatives are in place, but this crash behavior # can be used to find errant uses of sqlite3_js_vfs_create_file() # in client code. ########################################################################@ # It's important that sqlite3.h be built to completion before any # other parts of the build run, thus we use .NOTPARALLEL to disable # parallel build of that file and its dependants. .NOTPARALLEL: $(sqlite3.h) $(sqlite3.h): $(MAKE) -C $(dir.top) sqlite3.c $(sqlite3.c): $(sqlite3.h) .PHONY: clean distclean clean: |
︙ | ︙ | |||
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | sqlite3_wasm_extra_init.c ?= $(wildcard sqlite3_wasm_extra_init.c) cflags.wasm_extra_init := ifneq (,$(sqlite3_wasm_extra_init.c)) $(info Enabling SQLITE_EXTRA_INIT via $(sqlite3_wasm_extra_init.c).) cflags.wasm_extra_init := -DSQLITE_WASM_EXTRA_INIT endif # 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. 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 # bin.stripcomments is used for stripping C/C++-style comments from JS # files. The JS files contain large chunks of documentation which we # don't need for all builds. That app's -k flag is of particular # importance here, as it allows us to retain the opening comment | > > > > > > > > | | > | | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | sqlite3_wasm_extra_init.c ?= $(wildcard sqlite3_wasm_extra_init.c) cflags.wasm_extra_init := ifneq (,$(sqlite3_wasm_extra_init.c)) $(info Enabling SQLITE_EXTRA_INIT via $(sqlite3_wasm_extra_init.c).) cflags.wasm_extra_init := -DSQLITE_WASM_EXTRA_INIT endif ######################################################################### # 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 ######################################################################### # bin.stripcomments is used for stripping C/C++-style comments from JS # files. The JS files contain large chunks of documentation which we # don't need for all builds. That app's -k flag is of particular # importance here, as it allows us to retain the opening comment # block(s), which contain the license header and version info. bin.stripccomments := $(dir.tool)/stripccomments $(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE) $(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) # # 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 # of each makes that untennable. # |
︙ | ︙ | |||
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | # preprocessing, would be to have separate source files for ES6 # builds, which would have a higher maintenance burden than c-pp.c # seems likely to. # # c-pp.c was written specifically for the sqlite project's JavaScript # builds but is maintained as a standalone project: # https://fossil.wanderinghorse.net/r/c-pp 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 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) | > > > > > > > > > > | | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | # preprocessing, would be to have separate source files for ES6 # builds, which would have a higher maintenance burden than c-pp.c # seems likely to. # # c-pp.c was written specifically for the sqlite project's JavaScript # 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) CLEAN_FILES += $(2) endef # /end C-PP.FILTER ######################################################################## # cflags.common = C compiler flags for all builds cflags.common := -I. -I$(dir $(sqlite3.c)) |
︙ | ︙ | |||
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | # difficult to say whether -Os gives any speed benefit over -Oz. # # Much practice has demonstrated that -O2 consistently gives the best # runtime speeds, but not by a large enough factor to rule out use of # -Oz when small deliverable size is a priority. ######################################################################## # EXPORTED_FUNCTIONS.* = files for use with Emscripten's # -sEXPORTED_FUNCTION flag. EXPORTED_FUNCTIONS.api.main := $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api) EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.main) ifeq (1,$(SQLITE_C_IS_SEE)) EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-see) endif EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api $(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE) cat $(EXPORTED_FUNCTIONS.api.in) > $@ # sqlite3-license-version.js = generated JS file with the license # header and version info. sqlite3-license-version.js := $(dir.tmp)/sqlite3-license-version.js # sqlite3-license-version-header.js = JS file containing only the # license header. sqlite3-license-version-header.js := $(dir.api)/sqlite3-license-version-header.js # sqlite3-api-build-version.js = generated JS file which populates the # sqlite3.version object using $(bin.version-info). sqlite3-api-build-version.js := $(dir.tmp)/sqlite3-api-build-version.js # sqlite3-api.jses = the list of JS files which make up # $(sqlite3-api.js.in), in the order they need to be assembled. sqlite3-api.jses := $(sqlite3-license-version.js) sqlite3-api.jses += $(dir.api)/sqlite3-api-prologue.js sqlite3-api.jses += $(dir.common)/whwasmutil.js sqlite3-api.jses += $(dir.jacc)/jaccwabyt.js sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.js sqlite3-api.jses += $(sqlite3-api-build-version.js) sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js | > > > > > > > > > > | > > > > > > > > | > > > > > | < < < | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | # difficult to say whether -Os gives any speed benefit over -Oz. # # Much practice has demonstrated that -O2 consistently gives the best # runtime speeds, but not by a large enough factor to rule out use of # -Oz when small deliverable size is a priority. ######################################################################## ######################################################################## # EXPORTED_FUNCTIONS.* = files for use with Emscripten's # -sEXPORTED_FUNCTION flag. EXPORTED_FUNCTIONS.api.main := $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api) EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.main) ifeq (1,$(SQLITE_C_IS_SEE)) EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-see) endif EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api $(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE) cat $(EXPORTED_FUNCTIONS.api.in) > $@ ######################################################################## # sqlite3-license-version.js = generated JS file with the license # header and version info. sqlite3-license-version.js := $(dir.tmp)/sqlite3-license-version.js # sqlite3-license-version-header.js = JS file containing only the # license header. sqlite3-license-version-header.js := $(dir.api)/sqlite3-license-version-header.js # sqlite3-api-build-version.js = generated JS file which populates the # sqlite3.version object using $(bin.version-info). sqlite3-api-build-version.js := $(dir.tmp)/sqlite3-api-build-version.js # sqlite3-api.jses = the list of JS files which make up # $(sqlite3-api.js.in), in the order they need to be assembled. sqlite3-api.jses := $(sqlite3-license-version.js) # sqlite3-api-prologue.js: initial boostrapping bits: sqlite3-api.jses += $(dir.api)/sqlite3-api-prologue.js # whwhasm.js and jaccwabyt.js: Low-level utils, mostly replacing # Emscripten glue: sqlite3-api.jses += $(dir.common)/whwasmutil.js sqlite3-api.jses += $(dir.jacc)/jaccwabyt.js # sqlite3-api-glue.js Glues the previous part together: sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.js # $(sqlite3-api-build-version.js) = library version info 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-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 # any extraneous global symbols which are needed temporarily # by the previous files. sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js ######################################################################## # 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) $(SOAP.js.bld): $(SOAP.js) cp $< $@ ######################################################################## # $(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 # loaders, but in practice that breaks down because the WASM loader # has to be able to provide all of the necessary "imports" to # sqlite3.wasm, and that list of imports is unknown until sqlite3.wasm |
︙ | ︙ | |||
434 435 436 437 438 439 440 | # -v is _very_ loud but also informative about what it's doing endif ######################################################################## # emcc flags for .c/.o. emcc.cflags := emcc.cflags += -std=c99 -fPIC | | > > | > > > > > > | > > > > > | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 | # -v is _very_ loud but also informative about what it's doing endif ######################################################################## # emcc flags for .c/.o. emcc.cflags := emcc.cflags += -std=c99 -fPIC # -------------^^^^^^^^ we need c99 for $(sqlite3-wasm.c), primarily # for variadic macros and snprintf() to implement # sqlite3_wasm_enum_json(). emcc.cflags += -I. -I$(dir.top) ######################################################################## # emcc flags specific to building .js/.wasm files... emcc.jsflags := -fPIC emcc.jsflags += --minify 0 emcc.jsflags += --no-entry emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT) emcc.jsflags += -sMODULARIZE emcc.jsflags += -sDYNAMIC_EXECUTION=0 emcc.jsflags += -sNO_POLYFILL emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.api) emcc.exportedRuntimeMethods := \ -sEXPORTED_RUNTIME_METHODS=wasmMemory # wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY emcc.jsflags += $(emcc.exportedRuntimeMethods) emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY emcc.jsflags += -sSTRICT_JS=0 # STRICT_JS disabled due to: # 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 # Note that adding ",node" to the list for the other builds causes # Emscripten to generate code which confuses node: it cannot reliably # determine whether the build is for a browser or for node. ######################################################################## # -sINITIAL_MEMORY: How much memory we need to start with is governed # at least in part by whether -sALLOW_MEMORY_GROWTH is enabled. If so, # we can start with less. If not, we need as much as we'll ever # possibly use (which, of course, we can't know for sure). Note, # however, that speedtest1 shows that performance for even moderate # 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 emcc.INITIAL_MEMORY.16 := 16777216 emcc.INITIAL_MEMORY.8 := 8388608 |
︙ | ︙ | |||
514 515 516 517 518 519 520 | # # That said... we can change $(sqlite3.js.init-func) as long as the # name "sqlite3InitModule" is the one which gets exposed via the # resulting JS files. That can be accomplished via # extern-post-js.js. However... using a temporary symbol name here # and then adding sqlite3InitModule() ourselves results in 2 global # symbols: we cannot "delete" the Emscripten-defined | > | | | 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 | # # That said... we can change $(sqlite3.js.init-func) as long as the # name "sqlite3InitModule" is the one which gets exposed via the # resulting JS files. That can be accomplished via # extern-post-js.js. However... using a temporary symbol name here # and then adding sqlite3InitModule() ourselves results in 2 global # symbols: we cannot "delete" the Emscripten-defined # $(sqlite3.js.init-func) from vanilla builds (as opposed to ESM # builds) because it's declared with "var". sqlite3.js.init-func := sqlite3InitModule emcc.jsflags += -sEXPORT_NAME=$(sqlite3.js.init-func) emcc.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. #emcc.jsflags += -sSTRICT # fails due to missing __syscall_...() #emcc.jsflags += -sALLOW_UNIMPLEMENTED_SYSCALLS #emcc.jsflags += -sFILESYSTEM=0 # only for experimentation. fiddle needs the FS API #emcc.jsflags += -sABORTING_MALLOC # only for experimentation emcc.jsflags += -sALLOW_TABLE_GROWTH # ^^^^ -sALLOW_TABLE_GROWTH is required for installing new SQL UDFs emcc.jsflags += -Wno-limited-postlink-optimizations # ^^^^ emcc likes to warn when we have "limited optimizations" via the # -g3 flag. # emcc.jsflags += -sSTANDALONE_WASM # causes OOM errors, not sure why. |
︙ | ︙ | |||
564 565 566 567 568 569 570 571 572 573 | # -sSINGLE_FILE=1 would be _really_ nice but we have to build with -g3 # for -O2 and higher to work (else minification breaks the code) and # cannot wasm-strip the binary before it gets encoded into the JS # file. The result is that the generated JS file is, because of the # -g3 debugging info, _huge_. ######################################################################## $(sqlite3-api-build-version.js): $(bin.version-info) $(MAKEFILE) @echo "Making $@..." @{ \ | > > > | | | | | > > > > > > > | | | > > > > | | > > > > > > > | > | | | | | | 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 | # -sSINGLE_FILE=1 would be _really_ nice but we have to build with -g3 # for -O2 and higher to work (else minification breaks the code) and # cannot wasm-strip the binary before it gets encoded into the JS # file. The result is that the generated JS file is, because of the # -g3 debugging info, _huge_. ######################################################################## ######################################################################## # $(sqlite3-api-build-version.js) injects the build version info into # the bundle in JSON form. $(sqlite3-api-build-version.js): $(bin.version-info) $(MAKEFILE) @echo "Making $@..." @{ \ echo 'globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){'; \ echo -n ' sqlite3.version = '; \ $(bin.version-info) --json; \ echo ';'; \ echo '});'; \ } > $@ ######################################################################## # $(sqlite3-license-version.js) contains the license header and # in-comment build version info. # # Maintenance reminder: there are awk binaries out there which do not # support -e SCRIPT. $(sqlite3-license-version.js): $(sqlite3.h) $(sqlite3-license-version-header.js) \ $(MAKEFILE) @echo "Making $@..."; { \ cat $(sqlite3-license-version-header.js); \ echo '/*'; \ echo '** This code was built from sqlite3 version...'; \ echo "**"; \ awk '/define SQLITE_VERSION/{$$1=""; print "**" $$0}' $(sqlite3.h); \ awk '/define SQLITE_SOURCE_ID/{$$1=""; print "**" $$0}' $(sqlite3.h); \ echo "**"; \ echo "** Using the Emscripten SDK version $(emcc.version)."; \ echo '*/'; \ } > $@ ######################################################################## # --post-js and --pre-js are emcc flags we use to append/prepend JS to # the generated emscripten module file. These rules set up the core # pre/post files for use by the various builds. --pre-js is used to # inject code which needs to run as part of the pre-WASM-load phase. # --post-js injects code which runs after the WASM module is loaded # and includes the entirety of the library plus some # Emscripten-specific post-bootstrapping code. pre-js.js.in := $(dir.api)/pre-js.c-pp.js post-js.js.in := $(dir.tmp)/post-js.c-pp.js post-jses.js := \ $(dir.api)/post-js-header.js \ $(sqlite3-api.js.in) \ $(dir.api)/post-js-footer.js $(post-js.js.in): $(post-jses.js) $(MAKEFILE) @echo "Making $@..." @for i in $(post-jses.js); do \ echo "/* BEGIN FILE: $$i */"; \ cat $$i; \ echo "/* END FILE: $$i */"; \ done > $@ ######################################################################## # call-make-pre-post is a $(call)able which creates rules for # pre-js.$(1)-$(2).js. $1 = the base name of the JS file on whose # behalf this pre-js is for (one of: $(JS_BUILD_NAMES)). $2 is # the build mode: one of $(JS_BUILD_MODES). This sets up # --[extern-][pre/post]-js flags in $(pre-post-$(1)-$(2).flags) and # dependencies in $(pre-post-$(1)-$(2).deps). The resulting files get # filtered using $(C-PP.FILTER). Any flags necessary for such # filtering need to be set in $(c-pp.D.$(1)-$(2)) before $(call)ing # this. # # Maintenance note: a shell script was written to generate these rules # with the hope that it would make them more legible and maintainable, # but embedding makefile code in another language makes it even less # legible than having the level of $(eval) indirection which we have # here. define call-make-pre-post pre-post-$(1)-$(2).flags ?= pre-js.js.$(1)-$(2).intermediary := $$(dir.tmp)/pre-js.$(1)-$(2).intermediary.js pre-js.js.$(1)-$(2) := $$(dir.tmp)/pre-js.$(1)-$(2).js #$$(error $$(pre-js.js.$(1)-$(2).intermediary) $$(pre-js.js.$(1)-$(2))) $$(eval $$(call C-PP.FILTER,$$(pre-js.js.in),$$(pre-js.js.$(1)-$(2).intermediary),$$(c-pp.D.$(1)-$(2)))) post-js.js.$(1)-$(2) := $$(dir.tmp)/post-js.$(1)-$(2).js $$(eval $$(call C-PP.FILTER,$$(post-js.js.in),$$(post-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2)))) extern-post-js.js.$(1)-$(2) := $$(dir.tmp)/extern-post-js.$(1)-$(2).js $$(eval $$(call C-PP.FILTER,$$(extern-post-js.js.in),$$(extern-post-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2)))) pre-post-common.flags.$(1)-$(2) := \ $$(pre-post-common.flags) \ --post-js=$$(post-js.js.$(1)-$(2)) \ --extern-post-js=$$(extern-post-js.js.$(1)-$(2)) pre-post-jses.$(1)-$(2).deps := $$(pre-post-jses.deps.common) \ $$(post-js.js.$(1)-$(2)) $$(extern-post-js.js.$(1)-$(2)) $$(pre-js.js.$(1)-$(2)): $$(pre-js.js.$(1)-$(2).intermediary) $$(MAKEFILE) cp $$(pre-js.js.$(1)-$(2).intermediary) $$@ @if [ sqlite3-wasmfs = $(1) ]; then \ echo "delete Module[xNameOfInstantiateWasm] /*for WASMFS build*/;"; \ elif [ sqlite3 != $(1) ]; then \ echo "Module[xNameOfInstantiateWasm].uri = '$(1).wasm';"; \ fi >> $$@ pre-post-$(1)-$(2).deps := \ $$(pre-post-jses.$(1)-$(2).deps) \ $$(dir.tmp)/pre-js.$(1)-$(2).js pre-post-$(1)-$(2).flags += \ $$(pre-post-common.flags.$(1)-$(2)) \ --pre-js=$$(dir.tmp)/pre-js.$(1)-$(2).js endef # /post-js and pre-js ######################################################################## # Undocumented Emscripten feature: if the target file extension is # "mjs", it defaults to ES6 module builds: # https://github.com/emscripten-core/emscripten/issues/14383 |
︙ | ︙ | |||
679 680 681 682 683 684 685 | # overwritten version of that function and cannot "export default" # twice. Because of this, we have to sed *.mjs to remove the _first_ # instance (only) of /^export default/. # # Upstream RFE: # https://github.com/emscripten-core/emscripten/issues/18237 # | | | > > | | | > | 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 | # overwritten version of that function and cannot "export default" # twice. Because of this, we have to sed *.mjs to remove the _first_ # instance (only) of /^export default/. # # Upstream RFE: # https://github.com/emscripten-core/emscripten/issues/18237 # # Maintenance reminder: Mac sed works differently than GNU sed, so we # use awk instead of sed for this. define SQLITE3.xJS.ESM-EXPORT-DEFAULT if [ x1 = x$(1) ]; then \ echo "Fragile workaround for emscripten/issues/18237. See SQLITE3.xJS.RECIPE."; \ {\ awk '/^export default/ && !f{f=1; next} 1' $@ > $@.tmp && mv $@.tmp $@; \ } || exit $$?; \ if [ x != x$(2) ]; then \ if ! grep -q '^export default' $@; then \ echo "Cannot find export default." 1>&2; \ exit 1; \ fi; \ fi; \ fi endef ######################################################################## # extern-post-js* and extern-pre-js* are files for use with # Emscripten's --extern-pre-js and --extern-post-js flags. extern-pre-js.js := $(dir.api)/extern-pre-js.js extern-post-js.js.in := $(dir.api)/extern-post-js.c-pp.js # Emscripten flags for --[extern-][pre|post]-js=... for the # various builds. pre-post-common.flags := \ --extern-pre-js=$(sqlite3-license-version.js) # pre-post-jses.deps.* = a list of dependencies for the # --[extern-][pre/post]-js files. pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js) ######################################################################## # SETUP_LIB_BUILD_MODE is a $(call)'able which sets up numerous pieces # for one of the build modes. # # $1 = one of: $(JS_BUILD_NAMES) # $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 # # 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. # # $(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 # append emcc-specific flags to a given build mode. define SETUP_LIB_BUILD_MODE |
︙ | ︙ | |||
777 778 779 780 781 782 783 | sqlite3-bundler-friendly.mjs := $(dir.dout)/sqlite3-bundler-friendly.mjs sqlite3-api-node.mjs := $(dir.dout)/sqlite3-api-node.mjs sqlite3-node.mjs := $(dir.dout)/sqlite3-node.mjs #$(info $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0, $(sqlite3-api.js), $(sqlite3.js))) $(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0,\ $(sqlite3-api.js), $(sqlite3.js))) $(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,esm,1,\ | | < | | 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 | sqlite3-bundler-friendly.mjs := $(dir.dout)/sqlite3-bundler-friendly.mjs sqlite3-api-node.mjs := $(dir.dout)/sqlite3-api-node.mjs sqlite3-node.mjs := $(dir.dout)/sqlite3-node.mjs #$(info $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0, $(sqlite3-api.js), $(sqlite3.js))) $(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0,\ $(sqlite3-api.js), $(sqlite3.js))) $(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,esm,1,\ $(sqlite3-api.mjs), $(sqlite3.mjs), -Dtarget=es6-module)) $(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,bundler-friendly,1,\ $(sqlite3-api-bundler-friendly.mjs),$(sqlite3-bundler-friendly.mjs),\ $(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly)) $(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,node,1,\ $(sqlite3-api-node.mjs),$(sqlite3-node.mjs),\ $(c-pp.D.sqlite3-bundler-friendly) -Dtarget=node)) # The various -D... values used by *.c-pp.js include: # # -Dtarget=es6-module: for all ESM module builds # # -Dtarget=node: for node.js builds # # -Dtarget=es6-module -Dtarget=es6-bundler-friendly: intended for # "bundler-friendly" ESM module build. These have some restrictions # on how URL() objects are constructed in some contexts: URLs which # refer to files which are part of this project must be referenced # as string literals so that bundlers' static-analysis tools can # find those files and include them in their bundles. # # -Dtarget=es6-module -Dtarget=es6-bundler-friendly -Dtarget=node: is # intended for use by node.js for node.js, as opposed to by # node.js on behalf of a browser. Mixing -sENVIRONMENT=web and # -sENVIRONMENT=node leads to ambiguity and confusion on node's |
︙ | ︙ | |||
830 831 832 833 834 835 836 | # themselves use importScripts() or Workers or URL() constructors # which refer to other in-tree (m)JS files quire a bundler-friendly # 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 | > | | > > | > > > > > > > > > > > > > > | > > > > | 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 | # themselves use importScripts() or Workers or URL() constructors # which refer to other in-tree (m)JS files quire a bundler-friendly # 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-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),\ $(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-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 ######################################################################## # 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 speedtest1 := ../../speedtest1 speedtest1.c := ../../test/speedtest1.c |
︙ | ︙ | |||
882 883 884 885 886 887 888 889 890 891 892 893 894 895 | emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY)) emcc.speedtest1.common += -sINVOKE_RUN=0 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 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 emcc.speedtest1.common += -sDYNAMIC_EXECUTION=0 emcc.speedtest1.common += --minify 0 | > > > | 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 | emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY)) emcc.speedtest1.common += -sINVOKE_RUN=0 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 emcc.speedtest1.common += -sDYNAMIC_EXECUTION=0 emcc.speedtest1.common += --minify 0 |
︙ | ︙ |
Changes to ext/wasm/SQLTester/SQLTester.mjs.
︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 | scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/, mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/, command: /^--(([a-z-]+)( .*)?)$/, //! "Special" characters - we have to escape output if it contains any. special: /[\x00-\x20\x22\x5c\x7b\x7d]/, squiggly: /[{}]/ }); const Util = newObj({ toss, | > > | > > > > | | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/, mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/, command: /^--(([a-z-]+)( .*)?)$/, //! "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); }, argvToString: (list)=>{ const m = [...list]; m.shift() /* strip command name */; return m.join(" ") }, utf8Decode: function(arrayBuffer, begin, end){ return __utf8Decoder.decode( (arrayBuffer.buffer instanceof __SAB) ? arrayBuffer.slice(begin, end) : arrayBuffer.subarray(begin, end) ); }, utf8Encode: (str)=>__utf8Encoder.encode(str), strglob: sqlite3.wasm.xWrap('sqlite3__wasm_SQLTester_strglob','int', ['string','string']) })/*Util*/; class Outer { #lnBuf = []; #verbosity = 0; #logger = console.log.bind(console); |
︙ | ︙ |
Changes to ext/wasm/api/README.md.
︙ | ︙ | |||
74 75 76 77 78 79 80 | and runs the Worker #1 API which is implemented in `sqlite3-api-worker1.js`. - **`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. | | | | > > | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | and runs the Worker #1 API which is implemented in `sqlite3-api-worker1.js`. - **`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-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`**\ is the asynchronous backend part of the OPFS proxy. It speaks directly to the (async) OPFS API and channels those results back |
︙ | ︙ |
Changes to ext/wasm/api/post-js-header.js.
︙ | ︙ | |||
15 16 17 18 19 20 21 | - post-js-header.js (this file) - sqlite3-api-prologue.js => Bootstrapping bits to attach the rest to - 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 | | > | > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | - post-js-header.js (this file) - sqlite3-api-prologue.js => Bootstrapping bits to attach the rest to - 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-api-cleanup.js => final API cleanup - post-js-footer.js => closes this postRun() function */ |
Changes to ext/wasm/api/sqlite3-api-glue.js.
︙ | ︙ | |||
10 11 12 13 14 15 16 | *********************************************************************** 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 | | > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | *********************************************************************** 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. */ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'use strict'; const toss = (...args)=>{throw new Error(args.join(' '))}; const toss3 = sqlite3.SQLite3Error.toss; const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; globalThis.WhWasmUtilInstaller(wasm); |
︙ | ︙ | |||
324 325 326 327 328 329 330 | if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ /* ^^^ "the problem" is that this is an option feature and the build-time function-export list does not currently take optional features into account. */ wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); } | > | > > > > > > > > > > > > | > > > | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ /* ^^^ "the problem" is that this is an option feature and the 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. */ 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. */ wasm.bindingSignatures.int64 = [ ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]], ["sqlite3_changes64","i64", ["sqlite3*"]], ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]], ["sqlite3_create_module", "int", ["sqlite3*","string","sqlite3_module*","*"]], ["sqlite3_create_module_v2", "int", ["sqlite3*","string","sqlite3_module*","*","*"]], ["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_drop_modules", "int", ["sqlite3*", "**"]], ["sqlite3_last_insert_rowid", "i64", ["sqlite3*"]], ["sqlite3_malloc64", "*","i64"], ["sqlite3_msize", "i64", "*"], ["sqlite3_overload_function", "int", ["sqlite3*","string","int"]], ["sqlite3_preupdate_blobwrite", "int", "sqlite3*"], ["sqlite3_preupdate_count", "int", "sqlite3*"], |
︙ | ︙ | |||
418 419 420 421 422 423 424 | ["sqlite3_vtab_nochange","int", "sqlite3_context*"], ["sqlite3_vtab_on_conflict","int", "sqlite3*"], ["sqlite3_vtab_rhs_value","int", "sqlite3_index_info*", "int", "**"] ]; // Add session/changeset APIs... if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){ | < < | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | ["sqlite3_vtab_nochange","int", "sqlite3_context*"], ["sqlite3_vtab_on_conflict","int", "sqlite3*"], ["sqlite3_vtab_rhs_value","int", "sqlite3_index_info*", "int", "**"] ]; // Add session/changeset APIs... if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){ /** 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. */ const __ipsProxy = { |
︙ | ︙ | |||
597 598 599 600 601 602 603 | ]] ]); }/*session/changeset APIs*/ /** Functions which are intended solely for API-internal use by the WASM components, not client code. These get installed into | | | > > > > > | | | > > > | | | | > | 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 | ]] ]); }/*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. */ 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"] ]; /** Install JS<->C struct bindings for the non-opaque struct types we need... */ sqlite3.StructBinder = globalThis.Jaccwabyt({ heap: 0 ? wasm.memory : wasm.heap8u, |
︙ | ︙ | |||
648 649 650 651 652 653 654 | string, map its pointer to (''+v) for the remainder of the application's life, and returns that pointer value for this 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 | | | 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 | string, map its pointer to (''+v) for the remainder of the application's life, and returns that pointer value for this 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 value seen by those functions is long-lived and behaves as they need it to. */ wasm.xWrap.argAdapter( 'string:static', function(v){ if(wasm.isPtr(v)) return v; |
︙ | ︙ | |||
670 671 672 673 674 675 676 | Add some descriptive xWrap() aliases for '*' intended to (A) initially improve readability/correctness of 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('*'); | | > > < | 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 | Add some descriptive xWrap() aliases for '*' intended to (A) initially improve readability/correctness of 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*/ }; wasm.xWrap.argAdapter('sqlite3_filename', __xArgPtr) ('sqlite3_context*', __xArgPtr) ('sqlite3_value*', __xArgPtr) ('void*', __xArgPtr) ('sqlite3_changegroup*', __xArgPtr) ('sqlite3_changeset_iter*', __xArgPtr) ('sqlite3_session*', __xArgPtr) ('sqlite3_stmt*', (v)=> __xArgPtr((v instanceof (sqlite3?.oo1?.Stmt || nilType)) ? v.pointer : v)) ('sqlite3*', (v)=> __xArgPtr((v instanceof (sqlite3?.oo1?.DB || nilType)) ? v.pointer : v)) |
︙ | ︙ | |||
738 739 740 741 742 743 744 | sqlite3.config.warn( "Disabling sqlite3.wasm.xWrap.doArgcCheck due to environmental quirks." ); } for(const e of wasm.bindingSignatures){ capi[e[0]] = wasm.xWrap.apply(null, e); } | | | | 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | sqlite3.config.warn( "Disabling sqlite3.wasm.xWrap.doArgcCheck due to environmental quirks." ); } 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 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 to elide these functions altogether, which seems likely to cause more confusion. */ |
︙ | ︙ | |||
761 762 763 764 765 766 767 | : fI64Disabled(e[0]); } /* There's no need to expose bindingSignatures to clients, implicitly making it part of the public interface. */ delete wasm.bindingSignatures; | | | | | | | | | 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 | : fI64Disabled(e[0]); } /* 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){ const __db_err = wasm.xWrap( 'sqlite3__wasm_db_error', 'int', 'sqlite3*', 'int', 'string' ); /** Sets the given db's error state. Accepts: - (sqlite3*, int code, string msg) - (sqlite3*, Error e [,string msg = ''+e]) If passed a WasmAllocError, the message is ignored and the result code is SQLITE_NOMEM. If passed any other Error type, the result code defaults to SQLITE_ERROR unless the Error object has a resultCode property, in which case that is used (e.g. SQLite3Error has that). If passed a non-WasmAllocError 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){ if(resultCode instanceof sqlite3.WasmAllocError){ resultCode = capi.SQLITE_NOMEM; message = 0 /*avoid allocating message string*/; }else if(resultCode instanceof Error){ message = message || ''+resultCode; 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); return errCode; }; } }/*xWrap() bindings*/ {/* Import C-level constants and structs... */ const cJson = wasm.xCall('sqlite3__wasm_enum_json'); if(!cJson){ 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... const defineGroups = ['access', 'authorizer', 'blobFinalizers', 'changeset', |
︙ | ︙ | |||
873 874 875 876 877 878 879 | for(const k of ['sqlite3_index_constraint', 'sqlite3_index_orderby', 'sqlite3_index_constraint_usage']){ capi.sqlite3_index_info[k] = capi[k]; delete capi[k]; } capi.sqlite3_vtab_config = wasm.xWrap( | | | | | 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 | for(const k of ['sqlite3_index_constraint', 'sqlite3_index_orderby', '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*', 'int', 'int'] ); }/* end vtab-related setup */ }/*end C constant and struct imports*/ /** 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, 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( pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding." ); }; /** __dbCleanupMap is infrastructure for recording registration of UDFs and collations so that sqlite3_close_v2() can clean up any |
︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 | try{ const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); if(0===rc && xCompare instanceof Function){ __dbCleanupMap.addCollation(pDb, zName); } return rc; }catch(e){ | | | 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 | try{ const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); if(0===rc && xCompare instanceof Function){ __dbCleanupMap.addCollation(pDb, zName); } return rc; }catch(e){ return util.sqlite3__wasm_db_error(pDb, e); } }; capi.sqlite3_create_collation = (pDb,zName,eTextRep,pArg,xCompare)=>{ return (5===arguments.length) ? capi.sqlite3_create_collation_v2(pDb,zName,eTextRep,pArg,xCompare,0) : __dbArgcMismatch(pDb, 'sqlite3_create_collation', 5); |
︙ | ︙ | |||
1250 1251 1252 1253 1254 1255 1256 | || xFinal instanceof Function || xDestroy instanceof Function)){ __dbCleanupMap.addFunction(pDb, funcName, nArg); } return rc; }catch(e){ console.error("sqlite3_create_function_v2() setup threw:",e); | | | 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 | || xFinal instanceof Function || xDestroy instanceof Function)){ __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); } }; /* Documented in the api object's initializer. */ capi.sqlite3_create_function = function f( pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal |
︙ | ︙ | |||
1295 1296 1297 1298 1299 1300 1301 | || xInverse instanceof Function || xDestroy instanceof Function)){ __dbCleanupMap.addWindowFunc(pDb, funcName, nArg); } return rc; }catch(e){ console.error("sqlite3_create_window_function() setup threw:",e); | | | 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 | || xInverse instanceof Function || xDestroy instanceof Function)){ __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); } }; /** A _deprecated_ alias for capi.sqlite3_result_js() which predates the addition of that function in the public API. */ capi.sqlite3_create_function_v2.udfSetResult = |
︙ | ︙ | |||
1390 1391 1392 1393 1394 1395 1396 | return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length); } 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: | | | 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 | return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length); } 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( pDb, capi.SQLITE_MISUSE, "Invalid SQL argument type for sqlite3_prepare_v2/v3()." ); } }; /* Documented in the capi object's initializer. */ |
︙ | ︙ | |||
1434 1435 1436 1437 1438 1439 1440 | try{ if(util.isSQLableTypedArray(text)){ p = wasm.allocFromTypedArray(text); n = text.byteLength; }else if('string'===typeof text){ [p, n] = wasm.allocCString(text); }else{ | | | | 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 | try{ if(util.isSQLableTypedArray(text)){ p = wasm.allocFromTypedArray(text); n = text.byteLength; }else if('string'===typeof text){ [p, n] = wasm.allocCString(text); }else{ 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( capi.sqlite3_db_handle(pStmt), e ); } }/*sqlite3_bind_text()*/; /** Documented in the capi object's initializer. */ capi.sqlite3_bind_blob = function f(pStmt, iCol, pMem, nMem, xDestroy){ |
︙ | ︙ | |||
1468 1469 1470 1471 1472 1473 1474 | try{ if(util.isBindableTypedArray(pMem)){ p = wasm.allocFromTypedArray(pMem); n = nMem>=0 ? nMem : pMem.byteLength; }else if('string'===typeof pMem){ [p, n] = wasm.allocCString(pMem); }else{ | | | | 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 | try{ if(util.isBindableTypedArray(pMem)){ 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( 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( capi.sqlite3_db_handle(pStmt), e ); } }/*sqlite3_bind_blob()*/; }/*sqlite3_bind_text/blob()*/ |
︙ | ︙ | |||
1500 1501 1502 1503 1504 1505 1506 | switch(op){ case capi.SQLITE_CONFIG_COVERING_INDEX_SCAN: // 20 /* int */ 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 */ | | | | > > > > | 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 | switch(op){ case capi.SQLITE_CONFIG_COVERING_INDEX_SCAN: // 20 /* int */ 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]); case capi.SQLITE_CONFIG_LOOKASIDE: // 13 /* int int */ 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]); 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 */ case capi.SQLITE_CONFIG_LOG: // 16 /* xFunc, void* */ case capi.SQLITE_CONFIG_MALLOC:// 4 /* sqlite3_mem_methods* */ case capi.SQLITE_CONFIG_MMAP_SIZE: // 22 /* sqlite3_int64, sqlite3_int64 */ case capi.SQLITE_CONFIG_MULTITHREAD: // 2 /* nil */ case capi.SQLITE_CONFIG_MUTEX: // 10 /* sqlite3_mutex_methods* */ case capi.SQLITE_CONFIG_PAGECACHE: // 7 /* void*, int sz, int N */ case capi.SQLITE_CONFIG_PCACHE2: // 18 /* sqlite3_pcache_methods2* */ case capi.SQLITE_CONFIG_PCACHE: // 14 /* no-op */ case capi.SQLITE_CONFIG_PCACHE_HDRSZ: // 24 /* int *psz */ case capi.SQLITE_CONFIG_PMASZ: // 25 /* unsigned int szPma */ 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() */ {/*auto-extension bindings.*/ const __autoExtFptr = new Set; |
︙ | ︙ | |||
1570 1571 1572 1573 1574 1575 1576 | }; }/* auto-extension */ const pKvvfs = capi.sqlite3_vfs_find("kvvfs"); if( pKvvfs ){/* kvvfs-specific glue */ if(util.isUIThread()){ const kvvfsMethods = new capi.sqlite3_kvvfs_methods( | | | | | 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 | }; }/* auto-extension */ 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() ); delete capi.sqlite3_kvvfs_methods; 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 implementations with these, which use localStorage or sessionStorage for their backing store. */ const kvvfsImpls = { xRead: (zClass, zKey, zBuf, nBuf)=>{ const stack = pstack.pointer, astack = wasm.scopedAllocPush(); |
︙ | ︙ | |||
1663 1664 1665 1666 1667 1668 1669 1670 1671 | /* Worker thread: unregister kvvfs to avoid it being used for anything other than local/sessionStorage. It "can" be used that way but it's not really intended to be. */ capi.sqlite3_vfs_unregister(pKvvfs); } }/*pKvvfs*/ wasm.xWrap.FuncPtrAdapter.warnOnUse = true; }); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 | /* Worker thread: unregister kvvfs to avoid it being used for anything other than local/sessionStorage. It "can" 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 <Function, memberName> */; 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); }; }); |
Changes to ext/wasm/api/sqlite3-api-oo1.js.
1 2 3 4 5 6 7 | /* 2022-07-22 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. | > | 1 2 3 4 5 6 7 8 | //#ifnot omit-oo1 /* 2022-07-22 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. |
︙ | ︙ | |||
82 83 84 85 86 87 88 89 90 91 92 93 94 95 | A map of sqlite3_vfs pointers to SQL code or a callback function to run when the DB constructor opens a database with the given 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); /** 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, e.g. JsStorageDb and OpfsDb. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | A map of sqlite3_vfs pointers to SQL code or a callback function to run when the DB constructor opens a database with the given 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, e.g. JsStorageDb and OpfsDb. |
︙ | ︙ | |||
170 171 172 173 174 175 176 177 | }finally{ wasm.pstack.restore(stack); } this.filename = fnJs; __ptrMap.set(this, pDb); __stmtMap.set(this, Object.create(null)); try{ // Check for per-VFS post-open SQL/callback... | > > > | | > > > > > > > > | | | | | | > | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | }finally{ wasm.pstack.restore(stack); } 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 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) ); } } }catch(e){ this.close(); throw e; } }; |
︙ | ︙ | |||
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | in the form of a single configuration object with the following properties: - `filename`: database file name - `flags`: open-mode flags - `vfs`: the VFS fname 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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 | in the form of a single configuration object with the following properties: - `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. 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 any vfs name set in the arguments. */ |
︙ | ︙ | |||
428 429 430 431 432 433 434 | toss3("Invalid returnValue value:",opt.returnValue); } if(!opt.callback && !opt.returnValue && undefined!==opt.rowMode){ if(!opt.resultRows) opt.resultRows = []; out.returnVal = ()=>opt.resultRows; } if(opt.callback || opt.resultRows){ | | > > > > > > > > > > > > | | > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | toss3("Invalid returnValue value:",opt.returnValue); } 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); } } return out; }; /** Internal impl of the DB.selectValue(), selectArray(), and |
︙ | ︙ | |||
879 880 881 882 883 884 885 886 | we need to delay fetching of the column names until after the first step() (if we step() at all) because 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){ for(; stmt.step(); stmt._lockedByExec = false){ | > > > | > > | | 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 | we need to delay fetching of the column names until after the first step() (if we step() at all) because 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 || [])); } stmt._lockedByExec = true; const row = arg.cbArg(stmt,cbArgCache); if(resultRows) resultRows.push(row); if(callback && false === callback.call(opt, row, stmt)){ break; } } stmt._lockedByExec = false; } |
︙ | ︙ | |||
1517 1518 1519 1520 1521 1522 1523 | property when binding an array/object (see below) is treated the same as null. - 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 | | | 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 | property when binding an array/object (see below) is treated the same as null. - 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 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 of precision. If the BigInt is _too BigInt_ then it will throw. |
︙ | ︙ | |||
1701 1702 1703 1704 1705 1706 1707 | Fetches the value from the given 0-based column index of the current data row, throwing if index is out of range. 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 | | | 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 | Fetches the value from the given 0-based column index of the current data row, throwing if index is out of range. 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 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 to, e.g., fetch an integer value as a string, in which case sqlite3 will convert the value to a string. |
︙ | ︙ | |||
1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 | }/*oo1 object*/; 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'. */ sqlite3.oo1.JsStorageDb = function(storageName='session'){ if('session'!==storageName && 'local'!==storageName){ toss3("JsStorageDb db name must be one of 'session' or 'local'."); } | > > > > > > > > > > > > > > | < < < < | 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 | }/*oo1 object*/; 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); }; const jdb = sqlite3.oo1.JsStorageDb; jdb.prototype = Object.create(DB.prototype); /** Equivalent to sqlite3_js_kvvfs_clear(). */ jdb.clearStorage = capi.sqlite3_js_kvvfs_clear; /** Clears this database instance's storage or throws if this |
︙ | ︙ | |||
1936 1937 1938 1939 1940 1941 1942 | */ jdb.prototype.storageSize = function(){ return jdb.storageSize(affirmDbOpen(this).filename); }; }/*main-window-only bits*/ }); | | > > | 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 | */ jdb.prototype.storageSize = function(){ return jdb.storageSize(affirmDbOpen(this).filename); }; }/*main-window-only bits*/ }); //#else /* Built with the omit-oo1 flag. */ //#endif ifnot omit-oo1 |
Changes to ext/wasm/api/sqlite3-api-prologue.js.
︙ | ︙ | |||
33 34 35 36 37 38 39 | This function is not intended for client-level use. It is intended 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 | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | This function is not intended for client-level use. It is intended 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 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 missing. |
︙ | ︙ | |||
94 95 96 97 98 99 100 101 102 103 104 105 106 | [^1] = This property may optionally be a function, in which case this function calls that function to fetch the value, enabling delayed evaluation. The returned object is the top-level sqlite3 namespace object. */ 'use strict'; globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( apiConfig = (globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) ){ if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */ | > > > > > > > > > > > > > > > > > > > > > > | | > | > > > > > > | > > | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | [^1] = This property may optionally be a function, in which case 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." ); 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? */ } return !!globalThis.BigInt64Array; })(), debug: console.debug.bind(console), warn: console.warn.bind(console), error: console.error.bind(console), log: console.log.bind(console), |
︙ | ︙ | |||
145 146 147 148 149 150 151 152 153 154 155 156 157 158 | // the result of calling that function... 'exports', 'memory', 'wasmfsOpfsDir' ].forEach((k)=>{ if('function' === typeof config[k]){ config[k] = config[k](); } }); /** 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: https://www.sqlite.org/c3ref/intro.html | > > > > > > > > > | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | // the result of calling that function... 'exports', 'memory', 'wasmfsOpfsDir' ].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: https://www.sqlite.org/c3ref/intro.html |
︙ | ︙ | |||
201 202 203 204 205 206 207 | If called with exactly 2 arguments and the 2nd is an object, 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 | | | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | If called with exactly 2 arguments and the 2nd is an object, 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 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, else it will be set to capi.SQLITE_ERROR. */ |
︙ | ︙ | |||
1057 1058 1059 1060 1061 1062 1063 | */ wasm.pstack = Object.assign(Object.create(null),{ /** Sets the current pstack position to the given pointer. Results are undefined if the passed-in value did not come from this.pointer. */ | | | 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 | */ wasm.pstack = Object.assign(Object.create(null),{ /** 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, /** 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 memory must eventually be released using restore(). |
︙ | ︙ | |||
1079 1080 1081 1082 1083 1084 1085 | results when reading and writing 64-bit values from/to the WASM heap. Similarly, the returned address is always 8-byte aligned. */ alloc: function(n){ if('string'===typeof n && !(n = wasm.sizeofIR(n))){ WasmAllocError.toss("Invalid value for pstack.alloc(",arguments[0],")"); } | | | 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 | results when reading and writing 64-bit values from/to the WASM heap. Similarly, the returned address is always 8-byte aligned. */ 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) || WasmAllocError.toss("Could not allocate",n, "bytes from the pstack."); }, /** alloc()'s n chunks, each sz bytes, as a single memory block and returns the addresses as an array of n element, each holding the address of one chunk. |
︙ | ︙ | |||
1159 1160 1161 1162 1163 1164 1165 | position pointer. This value is intended _only_ to be saved for passing to restore(). Writing to this memory, without first reserving it via wasm.pstack.alloc() and friends, leads to undefined results. */ pointer: { configurable: false, iterable: true, writeable: false, | | | | | | 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 | position pointer. This value is intended _only_ to be saved for passing to restore(). Writing to this memory, without 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 //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 }, /** 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 }, /** 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 } })/*wasm.pstack properties*/; capi.sqlite3_randomness = (...args)=>{ if(1===args.length && util.isTypedArray(args[0]) && 1===args[0].BYTES_PER_ELEMENT){ const ta = args[0]; |
︙ | ︙ | |||
1252 1253 1254 1255 1256 1257 1258 | || !globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle){ return __wasmfsOpfsDir = ""; } try{ if(pdir && 0===wasm.xCallWrapped( | | | | 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 | || !globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle){ return __wasmfsOpfsDir = ""; } try{ if(pdir && 0===wasm.xCallWrapped( 'sqlite3__wasm_init_wasmfs', 'i32', ['string'], pdir )){ return __wasmfsOpfsDir = pdir; }else{ return __wasmfsOpfsDir = ""; } }catch(e){ // sqlite3__wasm_init_wasmfs() is not available return __wasmfsOpfsDir = ""; } }; /** Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a non-empty string and the given name starts with (that string + |
︙ | ︙ | |||
1361 1362 1363 1364 1365 1366 1367 | that memory block, fetching the value of pSize after the export reads a garbage size because it's not on an 8-byte memory boundary! */ const zSchema = schema ? (wasm.isPtr(schema) ? schema : wasm.scopedAllocCString(''+schema)) : 0; | | | 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 | that memory block, fetching the value of pSize after the export reads a garbage size because it's not on an 8-byte memory boundary! */ const zSchema = schema ? (wasm.isPtr(schema) ? schema : wasm.scopedAllocCString(''+schema)) : 0; 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)); } pOut = wasm.peekPtr(ppOut); |
︙ | ︙ | |||
1387 1388 1389 1390 1391 1392 1393 | /** Given a `sqlite3*` and a database name (JS string or WASM 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 = | | | 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 | /** Given a `sqlite3*` and a database name (JS string or WASM 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); /** 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 _not_ throw if that function returns 0. That special case is intended for use with xFinal() implementations. |
︙ | ︙ | |||
1445 1446 1447 1448 1449 1450 1451 | }else{ SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file()."); } try{ if(!util.isInt32(dataLen) || dataLen<0){ SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file()."); } | | | 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 | }else{ SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file()."); } 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); if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); }finally{ wasm.dealloc(pData); } }; |
︙ | ︙ | |||
1547 1548 1549 1550 1551 1552 1553 | pData = 0; } if(!util.isInt32(dataLen) || dataLen<0){ wasm.dealloc(pData); SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file()."); } try{ | | | 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 | pData = 0; } 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); if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); }finally{ wasm.dealloc(pData); } }; |
︙ | ︙ | |||
1668 1669 1670 1671 1672 1673 1674 | Returns capi.SQLITE_MISUSE if op is not a valid operation ID. 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){ | | | | | 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 | Returns capi.SQLITE_MISUSE if op is not a valid operation ID. 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', ['sqlite3*', 'int', 'string:static'] /* MAINDBNAME requires a static string */); this.pii = wasm.xWrap('sqlite3__wasm_db_config_pii', 'int', ['sqlite3*', 'int', '*','int', '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: case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: |
︙ | ︙ | |||
1794 1795 1796 1797 1798 1799 1800 | } return tgt; }; /** Calls either sqlite3_result_error_nomem(), if e is-a WasmAllocError, or sqlite3_result_error(). In the latter case, | | | 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 | } return tgt; }; /** 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 message. The first argument is a (sqlite3_context*). Returns void. Does not throw. */ capi.sqlite3_result_error_js = function(pCtx,e){ if(e instanceof WasmAllocError){ |
︙ | ︙ |
Changes to ext/wasm/api/sqlite3-api-worker1.js.
1 2 3 4 5 6 7 | /** 2022-07-22 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. | > | 1 2 3 4 5 6 7 8 | //#ifnot omit-oo1 /** 2022-07-22 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. |
︙ | ︙ | |||
354 355 356 357 358 359 360 361 362 363 364 365 366 367 | mimetype: "application/x-sqlite3" } } ``` */ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 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."); } const sqlite3 = this.sqlite3 || toss("Missing this.sqlite3 object."); | > | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | mimetype: "application/x-sqlite3" } } ``` */ 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."); } const sqlite3 = this.sqlite3 || toss("Missing this.sqlite3 object."); |
︙ | ︙ | |||
404 405 406 407 408 409 410 | if(this.dbList.indexOf(db)<0) this.dbList.push(db); return db; }, close: function(db,alsoUnlink){ if(db){ delete this.dbs[getDbId(db)]; const filename = db.filename; | | | | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | if(this.dbList.indexOf(db)<0) this.dbList.push(db); return db; }, 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); 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); } } }, /** Posts the given worker message value. If xferList is provided, it must be an array, in which case a copy of it passed as postMessage()'s second argument and xferList.length is set to |
︙ | ︙ | |||
453 454 455 456 457 458 459 | return affirmExists ? affirmDbOpen(db) : db; }; const getDefaultDbId = function(){ return wState.dbList[0] && getDbId(wState.dbList[0]); }; | < < < < < | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | return affirmExists ? affirmDbOpen(db) : db; }; const getDefaultDbId = function(){ return wState.dbList[0] && getDbId(wState.dbList[0]); }; const isSpecialDbFilename = (n)=>{ return ""===n || ':'===n[0]; }; /** A level of "organizational abstraction" for the Worker1 API. Each method in this object must map directly to a Worker1 |
︙ | ︙ | |||
479 480 481 482 483 484 485 | const wMsgHandler = { open: function(ev){ 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); | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 | const wMsgHandler = { open: function(ev){ 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); oargs.vfs = args.vfs; oargs.filename = args.filename || ""; 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(); return rc; }, |
︙ | ︙ | |||
685 686 687 688 689 690 691 | //}, result: result }, wState.xfer); }; globalThis.postMessage({type:'sqlite3-api',result:'worker1-ready'}); }.bind({sqlite3}); }); | > > > | 654 655 656 657 658 659 660 661 662 663 | //}, result: result }, wState.xfer); }; globalThis.postMessage({type:'sqlite3-api',result:'worker1-ready'}); }.bind({sqlite3}); }); //#else /* Built with the omit-oo1 flag. */ //#endif ifnot omit-oo1 |
Changes to ext/wasm/api/sqlite3-opfs-async-proxy.js.
︙ | ︙ | |||
47 48 49 50 51 52 53 | synchronous, but we do do not use those APIs that way. i.e. we don't _need_ to change anything for this, but at some point (after Chrome 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}); | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | synchronous, but we do do not use those APIs that way. i.e. we don't _need_ to change anything for this, but at some point (after Chrome 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 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){ toss("This API requires navigator.storage.getDirectory."); } |
︙ | ︙ | |||
557 558 559 560 561 562 563 564 565 566 567 568 569 570 | [hDir, filenamePart] = await getDirForFilename(filename, !!create); }catch(e){ state.s11n.storeException(1,e); storeAndNotify(opName, state.sq3Codes.SQLITE_NOTFOUND); mTimeEnd(); wTimeEnd(); return; } const hFile = await hDir.getFileHandle(filenamePart, {create}); wTimeEnd(); const fh = Object.assign(Object.create(null),{ fid: fid, filenameAbs: filename, filenamePart: filenamePart, | > > > > > > > > | 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 | [hDir, filenamePart] = await getDirForFilename(filename, !!create); }catch(e){ state.s11n.storeException(1,e); 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, filenamePart: filenamePart, |
︙ | ︙ | |||
907 908 909 910 911 912 913 | }else if(!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory){ wPost('opfs-unavailable',"Missing required OPFS APIs."); }else{ | | | 915 916 917 918 919 920 921 922 923 | }else if(!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory){ wPost('opfs-unavailable',"Missing required OPFS APIs."); }else{ installAsyncProxy(); } |
Deleted ext/wasm/api/sqlite3-v-helper.js.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added ext/wasm/api/sqlite3-vfs-helper.c-pp.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /* ** 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()*/); |
Changes to ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js.
︙ | ︙ | |||
1133 1134 1135 1136 1137 1138 1139 | - void importDb(name, bytes) 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. | > | | | 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 | - void importDb(name, bytes) 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 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 it by the given callback function. It calls the callback (which may be async) repeatedly, expecting either a Uint8Array or |
︙ | ︙ | |||
1267 1268 1269 1270 1271 1272 1273 | } ); }/*extend sqlite3.oo1*/ thePool.log("VFS initialized."); return poolUtil; }).catch(async (e)=>{ await thePool.removeVfs().catch(()=>{}); | | | 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 | } ); }/*extend sqlite3.oo1*/ thePool.log("VFS initialized."); return poolUtil; }).catch(async (e)=>{ await thePool.removeVfs().catch(()=>{}); throw e; }); }).catch((err)=>{ //error("rejecting promise:",err); return initPromises[vfsName] = Promise.reject(err); }); }/*installOpfsSAHPoolVfs()*/; }/*sqlite3ApiBootstrap.initializers*/); //#else /* The OPFS SAH Pool VFS parts are elided from builds targeting node.js. */ //#endif target=node |
Changes to ext/wasm/api/sqlite3-vfs-opfs.c-pp.js.
︙ | ︙ | |||
241 242 243 244 245 246 247 | const dVfs = pDVfs ? new sqlite3_vfs(pDVfs) : 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; | | > | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | const dVfs = pDVfs ? new sqlite3_vfs(pDVfs) : 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.$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, 'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null) ); |
︙ | ︙ | |||
418 419 420 421 422 423 424 | ].forEach((k)=>{ if(undefined === (state.sq3Codes[k] = capi[k])){ toss("Maintenance required: not found:",k); } }); state.opfsFlags = Object.assign(Object.create(null),{ /** | | | > > > > > > > > > > > > > > > | 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | ].forEach((k)=>{ if(undefined === (state.sq3Codes[k] = capi[k])){ 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. */ 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. |
︙ | ︙ | |||
870 871 872 873 874 875 876 | }, //xSleep is optionally defined below xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){ mTimeStart('xOpen'); let opfsFlags = 0; if(0===zName){ zName = randomFilename(); | | > > > > | 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 | }, //xSleep is optionally defined below xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){ mTimeStart('xOpen'); let opfsFlags = 0; if(0===zName){ zName = randomFilename(); }else if(wasm.isPtr(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); fh.flags = flags; const rc = opRun('xOpen', pFile, zName, flags, opfsFlags); |
︙ | ︙ | |||
989 990 991 992 993 994 995 | /** 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; | < < < < < < < < < < < < < < < < < < < < < | 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 | /** 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; /** 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 (described below) and `files` is a list of file names for all files in that directory. |
︙ | ︙ | |||
1208 1209 1210 1211 1212 1213 1214 1215 1216 | if( sah ) await sah.close(); } }; /** Asynchronously imports the given bytes (a byte array or ArrayBuffer) into the given database file. If passed a function for its second argument, its behaviour | > > > | | | | | | | | < | 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 | if( sah ) await sah.close(); } }; /** 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. 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 become a convenience for importing arbitrary files into OPFS. |
︙ | ︙ |
Added ext/wasm/api/sqlite3-vtab-helper.c-pp.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | /* ** 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<T> 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()*/); |
Changes to ext/wasm/api/sqlite3-wasm.c.
︙ | ︙ | |||
234 235 236 237 238 239 240 | ** Emscripten defines its own native one and we'd stomp on each ** other's memory. Other than that complication, basic tests show it ** to work just fine. ** ** Another option is to malloc() a chunk of our own and call that our ** "stack". */ | | | | | | | | | | | 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | ** Emscripten defines its own native one and we'd stomp on each ** other's memory. Other than that complication, basic tests show it ** 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){ extern void __heap_base /* see https://stackoverflow.com/questions/10038964 */; return &__heap_base; } 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(); return pWasmStackPtr; } SQLITE_WASM_EXPORT void sqlite3__wasm_stack_restore(void * p){ pWasmStackPtr = p; } 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(); 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 ** 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. ** ** This particular allocator is intended for small allocations such as ** storage for output pointers. We cannot reasonably size it large |
︙ | ︙ | |||
287 288 289 290 291 292 293 | &PStack_mem[0], &PStack_mem[0] + sizeof(PStack_mem), &PStack_mem[0] + sizeof(PStack_mem) }; /* ** Returns the current pstack position. */ | | | | | | | | | | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | &PStack_mem[0], &PStack_mem[0] + sizeof(PStack_mem), &PStack_mem[0] + sizeof(PStack_mem) }; /* ** Returns the current pstack position. */ 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(). */ 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; } } /* ** Allocate and zero out n bytes from the pstack. Returns a pointer to ** the memory on success, 0 on error (including a negative n value). n ** 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){ 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; 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. */ 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); } /* ** 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){ return (int)(PStack.pEnd - PStack.pBegin); } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** For purposes of certain hand-crafted C/Wasm function bindings, we ** need a way of reporting errors which is consistent with the rest of ** the C API, as opposed to throwing JS exceptions. To that end, this ** internal-use-only function is a thin proxy around ** sqlite3ErrorWithMessage(). The intent is that it only be used from ** Wasm bindings such as sqlite3_prepare_v2/v3(), and definitely not ** from client code. ** ** Returns err_code. */ SQLITE_WASM_EXPORT 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); sqlite3_mutex_leave(sqlite3_db_mutex(db)); }else{ |
︙ | ︙ | |||
376 377 378 379 380 381 382 | void * ppV; const char * cstr; int64_t v8; void (*xFunc)(void*); }; typedef struct WasmTestStruct WasmTestStruct; SQLITE_WASM_EXPORT | | | 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | void * ppV; const char * cstr; int64_t v8; void (*xFunc)(void*); }; typedef struct WasmTestStruct WasmTestStruct; SQLITE_WASM_EXPORT void sqlite3__wasm_test_struct(WasmTestStruct * s){ if(s){ s->v4 *= 2; s->v8 = s->v4 * 2; s->ppV = s; s->cstr = __FILE__; if(s->xFunc) s->xFunc(s); } |
︙ | ︙ | |||
404 405 406 407 408 409 410 | ** function is called and that result is reused for all future calls. ** ** 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 | | | | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | ** function is called and that result is reused for all future calls. ** ** 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){ 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 */; char const * const zEnd = &aBuffer[0] + sizeof(aBuffer) /* one-past-the-end */; if(aBuffer[0]) return aBuffer; /* Leave aBuffer[0] at 0 until the end to help guard against a tiny ** race condition. If this is called twice concurrently, they might ** end up both writing to aBuffer, but they'll both write the same ** thing, so that's okay. If we set byte 0 up front then the 2nd ** 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."); \ 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) #define CloseBrace(LEVEL) \ assert(LEVEL<5); memset(zPos, '}', LEVEL); zPos+=LEVEL; lenCheck |
︙ | ︙ | |||
541 542 543 544 545 546 547 548 549 550 551 552 553 554 | DefInt(SQLITE_CONFIG_WIN32_HEAPSIZE); DefInt(SQLITE_CONFIG_PCACHE_HDRSZ); DefInt(SQLITE_CONFIG_PMASZ); DefInt(SQLITE_CONFIG_STMTJRNL_SPILL); DefInt(SQLITE_CONFIG_SMALL_MALLOC); DefInt(SQLITE_CONFIG_SORTERREF_SIZE); DefInt(SQLITE_CONFIG_MEMDB_MAXSIZE); } _DefGroup; DefGroup(dataTypes) { DefInt(SQLITE_INTEGER); DefInt(SQLITE_FLOAT); DefInt(SQLITE_TEXT); DefInt(SQLITE_BLOB); | > > > > | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | DefInt(SQLITE_CONFIG_WIN32_HEAPSIZE); DefInt(SQLITE_CONFIG_PCACHE_HDRSZ); 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); DefInt(SQLITE_TEXT); DefInt(SQLITE_BLOB); |
︙ | ︙ | |||
1099 1100 1101 1102 1103 1104 1105 | M(xSavepoint, "i(pi)"); M(xRelease, "i(pi)"); M(xRollbackTo, "i(pi)"); // ^^^ v2. v3+ follows... M(xShadowName, "i(s)"); } _StructBinder; #undef CurrentStruct | | | 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 | M(xSavepoint, "i(pi)"); M(xRelease, "i(pi)"); M(xRollbackTo, "i(pi)"); // ^^^ v2. v3+ follows... M(xShadowName, "i(s)"); } _StructBinder; #undef CurrentStruct /** ** Workaround: in order to map the various inner structs from ** sqlite3_index_info, we have to uplift those into constructs we ** can access by type name. These structs _must_ match their ** in-sqlite3_index_info counterparts byte for byte. */ typedef struct { |
︙ | ︙ | |||
1216 1217 1218 1219 1220 1221 1222 | ** This function invokes the xDelete method of the given VFS (or the ** default VFS if pVfs is NULL), passing on the given filename. If ** 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 | | | | 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 | ** This function invokes the xDelete method of the given VFS (or the ** default VFS if pVfs is NULL), passing on the given filename. If ** 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 rc = SQLITE_MISUSE /* ??? */; if( 0==pVfs && 0!=zName ) pVfs = sqlite3_vfs_find(0); if( zName && pVfs && pVfs->xDelete ){ rc = pVfs->xDelete(pVfs, zName, 1); } return rc; } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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 * pVfs = 0; sqlite3_file_control(pDb, zDbName ? zDbName : "main", SQLITE_FCNTL_VFS_POINTER, &pVfs); return pVfs; } /* |
︙ | ︙ | |||
1257 1258 1259 1260 1261 1262 1263 | ** xDestroy() called, so will leak if they require that function for ** proper cleanup. ** ** Returns 0 on success, an SQLITE_xxx code on error. Returns ** SQLITE_MISUSE if pDb is NULL. */ SQLITE_WASM_EXPORT | | | 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 | ** xDestroy() called, so will leak if they require that function for ** proper cleanup. ** ** 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 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 ){ rc = sqlite3_exec(pDb, "VACUUM", 0, 0, 0); sqlite3_db_config(pDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); |
︙ | ︙ | |||
1284 1285 1286 1287 1288 1289 1290 | ** on success, non-0 on error. This function returns 0 on success, ** SQLITE_NOTFOUND if no db is open, or propagates any other non-0 ** 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 | | | | 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 | ** on success, non-0 on error. This function returns 0 on success, ** SQLITE_NOTFOUND if no db is open, or propagates any other non-0 ** 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 ** this. */ SQLITE_WASM_EXPORT 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]; int nBuf = (int)sizeof(buf); int rc = pDb |
︙ | ︙ | |||
1339 1340 1341 1342 1343 1344 1345 | ** contiguous memory representation, in which case `*pOut` will be ** NULL but 0 will be returned. ** ** If `*pOut` is not NULL, the caller is responsible for passing it to ** sqlite3_free() to free it. */ SQLITE_WASM_EXPORT | | | 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 | ** contiguous memory representation, in which case `*pOut` will be ** NULL but 0 will be returned. ** ** 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, unsigned char **pOut, sqlite3_int64 *nOut, unsigned int mFlags ){ unsigned char * z; if( !pDb || !pOut ) return SQLITE_MISUSE; if( nOut ) *nOut = 0; z = sqlite3_serialize(pDb, zSchema ? zSchema : "main", nOut, mFlags); if( z || (SQLITE_SERIALIZE_NOCOPY & mFlags) ){ |
︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 | ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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 | | | 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 | ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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 ** 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 ** data. ** |
︙ | ︙ | |||
1403 1404 1405 1406 1407 1408 1409 | ** or propagates a code from one of the I/O methods. ** ** 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 | | | 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 | ** or propagates a code from one of the I/O methods. ** ** 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, const char *zFilename, const unsigned char * pData, int nData ){ int rc; sqlite3_file *pFile = 0; sqlite3_io_methods const *pIo; const int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
︙ | ︙ | |||
1493 1494 1495 1496 1497 1498 1499 | ** ** Creates or overwrites a file using the POSIX file API, ** 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 | | | 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 | ** ** Creates or overwrites a file using the POSIX file API, ** 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, const unsigned char * pData, int nData ){ int rc; FILE * pFile = 0; int fileExisted = 0; size_t nWrote = 1; |
︙ | ︙ | |||
1516 1517 1518 1519 1520 1521 1522 | } /* ** 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 | | | | | | | | | 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 | } /* ** 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, ** 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. */ SQLITE_WASM_EXPORT char * sqlite3__wasm_kvvfsMakeKeyOnPstack(const char *zClass, const char *zKeyIn){ assert(sqlite3KvvfsMethods.nKeySize>24); char *zKeyOut = (char *)sqlite3__wasm_pstack_alloc(sqlite3KvvfsMethods.nKeySize); if(zKeyOut){ kvstorageMakeKey(zClass, zKeyIn, zKeyOut); } return zKeyOut; } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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){ return &sqlite3KvvfsMethods; } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** This is a proxy for the variadic sqlite3_vtab_config() which passes ** its argument on, or not, to sqlite3_vtab_config(), depending on the ** 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){ switch(op){ case SQLITE_VTAB_DIRECTONLY: case SQLITE_VTAB_INNOCUOUS: return sqlite3_vtab_config(pDb, op); case SQLITE_VTAB_CONSTRAINT_SUPPORT: return sqlite3_vtab_config(pDb, op, arg); default: return SQLITE_MISUSE; } } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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){ switch(op){ case SQLITE_DBCONFIG_ENABLE_FKEY: case SQLITE_DBCONFIG_ENABLE_TRIGGER: case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: case SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: case SQLITE_DBCONFIG_ENABLE_QPSG: |
︙ | ︙ | |||
1609 1610 1611 1612 1613 1614 1615 | ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** Wrapper for the variants of sqlite3_db_config() which take ** (void*,int,int) variadic args. */ SQLITE_WASM_EXPORT | | | | | | < < < | < < < < < < | < < | | < < < < < > > > > | > > | < | 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 | ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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){ switch(op){ case SQLITE_DBCONFIG_LOOKASIDE: return sqlite3_db_config(pDb, op, pArg1, arg2, arg3); default: return SQLITE_MISUSE; } } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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){ switch(op){ case SQLITE_DBCONFIG_MAINDBNAME: return sqlite3_db_config(pDb, op, zArg); default: return SQLITE_MISUSE; } } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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){ return sqlite3_config(op, arg); } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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){ return sqlite3_config(op, arg1, arg2); } /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** 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){ return sqlite3_config(op, arg); } /* ** 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; } #if defined(__EMSCRIPTEN__) && defined(SQLITE_ENABLE_WASMFS) #include <emscripten/wasmfs.h> /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings, specifically |
︙ | ︙ | |||
1725 1726 1727 1728 1729 1730 1731 | ** ** Returns 0 on success, SQLITE_NOMEM if instantiation of the backend ** 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 | | | | | | | | | | | | | | | | | | 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 | ** ** Returns 0 on success, SQLITE_NOMEM if instantiation of the backend ** 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){ static backend_t pOpfs = 0; if( !zMountPoint || !*zMountPoint ) zMountPoint = "/opfs"; if( !pOpfs ){ pOpfs = wasmfs_create_opfs_backend(); } /** It's not enough to instantiate the backend. We have to create a mountpoint in the VFS and attach the backend to it. */ if( pOpfs && 0!=access(zMountPoint, F_OK) ){ /* Note that this check and is not robust but it will hypothetically suffice for the transient wasm-based virtual filesystem we're currently running in. */ const int rc = wasmfs_create_directory(zMountPoint, 0777, pOpfs); /*emscripten_console_logf("OPFS mkdir(%s) rc=%d", zMountPoint, rc);*/ if(rc) return SQLITE_IOERR; } return pOpfs ? 0 : SQLITE_NOMEM; } #else SQLITE_WASM_EXPORT 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){ return *p = *p * 2; } SQLITE_WASM_EXPORT void * sqlite3__wasm_test_voidptr(void * p){ return p; } SQLITE_WASM_EXPORT 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(); } SQLITE_WASM_EXPORT 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(); /*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);*/ return *p = *p * 2; } SQLITE_WASM_EXPORT 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 * s = fail ? 0 : (char *)sqlite3_malloc(6); if(s){ memcpy(s, "hello", 5); s[5] = 0; } return s; } |
︙ | ︙ | |||
1834 1835 1836 1837 1838 1839 1840 | ** ** [^...] Matches one character not in the enclosed list. ** ** '#' Matches any sequence of one or more digits with an ** optional + or - sign in front, or a hexadecimal ** literal of the form 0x... */ | | | | 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 | ** ** [^...] Matches one character not in the enclosed list. ** ** '#' 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){ int c, c2; int invert; int seen; typedef int (*recurse_f)(const char *,const char *); 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; } if( c==0 ){ |
︙ | ︙ | |||
1914 1915 1916 1917 1918 1919 1920 | if( c!=(*(z++)) ) return 0; } } return *z==0; } SQLITE_WASM_EXPORT | | | < | 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 | if( c!=(*(z++)) ) return 0; } } return *z==0; } SQLITE_WASM_EXPORT 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 |
Changes to ext/wasm/api/sqlite3-worker1-promiser.c-pp.js.
1 2 3 4 5 6 7 | /* 2022-08-24 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: * May you do good and not evil. | > | 1 2 3 4 5 6 7 8 | //#ifnot omit-oo1 /* 2022-08-24 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: * May you do good and not evil. |
︙ | ︙ | |||
37 38 39 40 41 42 43 | config option may alternately be a function, in which case this function re-assigns this property with the result of calling that 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 | | | | > > > > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | config option may alternately be a function, in which case this function re-assigns this property with the result of calling that 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. - `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. - `generateMessageId` (optional): a function which, when passed an |
︙ | ︙ | |||
151 152 153 154 155 156 157 158 159 160 161 162 163 164 | const genMsgId = config.generateMessageId || function(msg){ return msg.type+'#'+(idTypeMap[msg.type] = (idTypeMap[msg.type]||0) + 1); }; 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; 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*/ | > | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | const genMsgId = config.generateMessageId || function(msg){ return msg.type+'#'+(idTypeMap[msg.type] = (idTypeMap[msg.type]||0) + 1); }; 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); return; } msgHandler = handlerMap[ev.type] /* check for exec per-row callback */; if(msgHandler && msgHandler.onrow){ msgHandler.onrow(ev); return; } |
︙ | ︙ | |||
187 188 189 190 191 192 193 | break; default: break; } try {msgHandler.resolve(ev)} catch(e){msgHandler.reject(e)} }/*worker.onmessage()*/; | | | | 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | break; default: break; } try {msgHandler.resolve(ev)} catch(e){msgHandler.reject(e)} }/*worker.onmessage()*/; return promiserFunc = 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."); } if(!msg.dbId && msg.type!=='open') msg.dbId = dbId; msg.messageId = genMsgId(msg); msg.departureTime = performance.now(); const proxy = Object.create(null); proxy.message = msg; let rowCallbackId /* message handler ID for exec on-row callback proxy */; |
︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 | debug("Posting",msg.type,"message to Worker dbId="+(dbId||'default')+':',msg); config.worker.postMessage(msg); }); if(rowCallbackId) p = p.finally(()=>delete handlerMap[rowCallbackId]); return p; }; }/*sqlite3Worker1Promiser()*/; globalThis.sqlite3Worker1Promiser.defaultConfig = { worker: function(){ | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | debug("Posting",msg.type,"message to Worker dbId="+(dbId||'default')+':',msg); config.worker.postMessage(msg); }); if(rowCallbackId) p = p.finally(()=>delete handlerMap[rowCallbackId]); return p; }; }/*sqlite3Worker1Promiser()*/; globalThis.sqlite3Worker1Promiser.defaultConfig = { worker: function(){ //#if target=es6-module return new Worker(new URL("sqlite3-worker1-bundler-friendly.mjs", import.meta.url),{ type: 'module' }); //#else let theJs = "sqlite3-worker1.js"; if(this.currentScript){ const src = this.currentScript.src.split('/'); src.pop(); theJs = src.join('/')+'/' + theJs; //sqlite3.config.warn("promiser currentScript, theJs =",this.currentScript,theJs); }else if(globalThis.location){ //sqlite3.config.warn("promiser globalThis.location =",globalThis.location); const urlParams = new URL(globalThis.location.href).searchParams; if(urlParams.has('sqlite3.dir')){ theJs = urlParams.get('sqlite3.dir') + '/' + theJs; } } return new Worker(theJs + globalThis.location.search); //#endif } //#ifnot target=es6-module .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 |
Changes to ext/wasm/api/sqlite3-worker1.c-pp.js.
1 2 3 4 5 6 7 | /* 2022-05-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. | > | 1 2 3 4 5 6 7 8 | //#ifnot omit-oo1 /* 2022-05-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. |
︙ | ︙ | |||
44 45 46 47 48 49 50 | theJs = urlParams.get('sqlite3.dir') + '/' + theJs; } //console.warn("worker1 theJs =",theJs); importScripts(theJs); } //#endif sqlite3InitModule().then(sqlite3 => sqlite3.initWorker1API()); | > > > | 45 46 47 48 49 50 51 52 53 54 | theJs = urlParams.get('sqlite3.dir') + '/' + theJs; } //console.warn("worker1 theJs =",theJs); importScripts(theJs); } //#endif sqlite3InitModule().then(sqlite3 => sqlite3.initWorker1API()); //#else /* Built with the omit-oo1 flag. */ //#endif ifnot omit-oo1 |
Changes to ext/wasm/common/whwasmutil.js.
︙ | ︙ | |||
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 | /** Looks up a WASM-exported function named fname from target.exports. If found, it is called, passed all remaining 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. 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){ | > > > > | | | 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 | /** Looks up a WASM-exported function named fname from target.exports. If found, it is called, passed all remaining 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); 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) /* 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); }; |
︙ | ︙ | |||
1533 1534 1535 1536 1537 1538 1539 | is solely for debugging and error-reporting purposes. If not provided, an empty string is assumed. - signature: a function signature string compatible with jsFuncToWasm(). - bindScope (string): one of ('transient', 'context', | | | 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 | is solely for debugging and error-reporting purposes. If not provided, an empty string is assumed. - signature: a function signature string compatible with jsFuncToWasm(). - bindScope (string): one of ('transient', 'context', 'singleton', 'permanent'). 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. - 'singleton': holds one function-pointer binding for this |
︙ | ︙ | |||
1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 | const __xArgAdapterCheck = (t)=>xArg.get(t) || toss("Argument adapter not found:",t); const __xResultAdapterCheck = (t)=>xResult.get(t) || toss("Result adapter not found:",t); cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args); cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args); cache.xWrap.convertResult = (t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined)); cache.xWrap.convertResultNoCheck = (t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined)); /** Creates a wrapper for another function which converts the arguments of the wrapper to argument types accepted by the wrapped function, then converts the wrapped function's result to another form | > > > > > > > > > > > > > > > > > > | 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 | const __xArgAdapterCheck = (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 of the wrapper to argument types accepted by the wrapped function, then converts the wrapped function's result to another form |
︙ | ︙ | |||
1899 1900 1901 1902 1903 1904 1905 | client has already allocated and it's passed on as a WASM pointer. - `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. | | | | | | | | | | | 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 | client has already allocated and it's passed on as a WASM pointer. - `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: ```js target.xWrap.resultAdapter('string:my_free',(i)=>{ try { return i ? target.cstrToJs(i) : null } finally{ target.exports.my_free(i) } }; ``` |
︙ | ︙ | |||
2008 2009 2010 2011 2012 2013 2014 2015 | AbstractArgAdapter.convertArgs() gets its 2nd+ arguments, and how FuncPtrAdapter.contextKey() gets its args, is also 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. */ | > > > > | | 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 | AbstractArgAdapter.convertArgs() gets its 2nd+ arguments, and how FuncPtrAdapter.contextKey() gets its args, is also 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( argTypes[i], args[i], args, i ); return cxw.convertResultNoCheck(resultType, xf.apply(null,args)); }finally{ target.scopedAllocPop(scope); } }; |
︙ | ︙ |
Added ext/wasm/demo-worker1-promiser.c-pp.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <!doctype html> <html lang="en-us"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon"> <link rel="stylesheet" href="common/emscripten.css"/> <link rel="stylesheet" href="common/testing.css"/> //#if target=es6-module <title>worker-promise (via ESM) tests</title> //#else <title>worker-promise tests</title> //#endif </head> <body> <header id='titlebar'><span>worker-promise tests</span></header> <!-- emscripten bits --> <figure id="module-spinner"> <div class="spinner"></div> <div class='center'><strong>Initializing app...</strong></div> <div class='center'> 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. </div> </figure> <div class="emscripten" id="module-status">Downloading...</div> <div class="emscripten"> <progress value="0" max="100" id="module-progress" hidden='1'></progress> </div><!-- /emscripten bits --> <div>Most stuff on this page happens in the dev console.</div> <hr> <div id='test-output'></div> <script src="common/SqliteTestUtil.js"></script> //#if target=es6-module <script src="demo-worker1-promiser.mjs" type="module"></script> //#else <script src="jswasm/sqlite3-worker1-promiser.js"></script> <script src="demo-worker1-promiser.js"></script> //#endif </body> </html> |
Added ext/wasm/demo-worker1-promiser.c-pp.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | /* 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(); })(); |
Deleted ext/wasm/demo-worker1-promiser.html.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted ext/wasm/demo-worker1-promiser.js.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to ext/wasm/dist.make.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 | ######################################################################## # 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 (,$(filter snapshot,$(MAKECMDGOALS))) | > > > > > | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ######################################################################## # 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) endif dist-name := $(dist-name-prefix)-TEMP ######################################################################## # dist.build must be the name of a target which triggers the build of # the files to be packed into the dist archive. The intention is that # it be one of (o0, o1, o2, o3, os, oz), each of which uses like-named |
︙ | ︙ | |||
45 46 47 48 49 50 51 | dist-dir.common := $(dist-dir.top)/common dist.top.extras := \ 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 \ | | > | > > > > > | > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | dist-dir.common := $(dist-dir.top)/common dist.top.extras := \ 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) 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 # $2 = flags for $(bin.stripcomments) define DIST_STRIP_COMMENTS $(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) # 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) ######################################################################## # dist: create the end-user deliverable archive. # # Maintenance reminder: because dist depends on $(dist.build), and # $(dist.build) will depend on clean, having any deps on # $(dist-archive) which themselves may be cleaned up by the clean # target will lead to grief in parallel builds (-j #). Thus # dist's deps must be trimmed to non-generated files or # files which are _not_ cleaned up by the clean target. # # Note that we require $(bin.version-info) in order to figure out the # 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) @cp -p README-dist.txt $(dist-dir.top)/README.txt @cp -p index-dist.html $(dist-dir.top)/index.html |
︙ | ︙ |
Changes to ext/wasm/fiddle.make.
1 2 3 4 5 6 7 8 9 10 11 12 | #!/do/not/make #^^^ help emacs select edit mode # # Intended to include'd by ./GNUmakefile. ####################################################################### MAKEFILE.fiddle := $(lastword $(MAKEFILE_LIST)) ######################################################################## # 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' $(eval $(shell $(make-np-0) | grep -e '^SHELL_OPT ' | $(make-np-1))) | > > | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/do/not/make #^^^ help emacs select edit mode # # Intended to include'd by ./GNUmakefile. ####################################################################### MAKEFILE.fiddle := $(lastword $(MAKEFILE_LIST)) ######################################################################## # 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))) # ^^^ 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.) endif $(dir.top)/shell.c: $(SHELL_DEP) $(dir.top)/tool/mkshellc.tcl $(sqlite3.c) $(MAKE) -C $(dir.top) shell.c # /shell.c ######################################################################## EXPORTED_FUNCTIONS.fiddle := $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle fiddle.emcc-flags = \ $(emcc.cflags) $(emcc_opt_full) \ |
︙ | ︙ |
Changes to ext/wasm/fiddle/fiddle-worker.js.
︙ | ︙ | |||
162 163 164 165 166 167 168 | stderr("Fatal error initializing sqlite3 shell."); fiddleModule.isDead = true; return false; } stdout("SQLite version", capi.sqlite3_libversion(), capi.sqlite3_sourceid().substr(0,19)); stdout('Welcome to the "fiddle" shell.'); | | < | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | stderr("Fatal error initializing sqlite3 shell."); fiddleModule.isDead = true; 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")){ 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."); } stdout('\nEnter ".help" for usage hints.'); this.exec([ // initialization commands... '.nullvalue NULL', '.headers on' ].join('\n')); return true; |
︙ | ︙ | |||
313 314 315 316 317 318 319 | stderr("Error installing db",fn+":",e.message); } return; } }; console.warn("Unknown fiddle-worker message type:",ev); }; | | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | stderr("Error installing db",fn+":",e.message); } return; } }; console.warn("Unknown fiddle-worker message type:",ev); }; /** emscripten module for use with build mode -sMODULARIZE. */ const fiddleModule = { print: stdout, printErr: stderr, /** |
︙ | ︙ | |||
370 371 372 373 374 375 376 | */ sqlite3InitModule(fiddleModule).then((_sqlite3)=>{ 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']); | | < < | 369 370 371 372 373 374 375 376 377 378 379 380 381 | */ sqlite3InitModule(fiddleModule).then((_sqlite3)=>{ 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); wMsg('fiddle-ready'); }).catch(e=>{ console.error("Fiddle worker init failed:",e); }); })(); |
Changes to ext/wasm/fiddle/fiddle.js.
︙ | ︙ | |||
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | self.onSFLoaded = function(){ delete this.onSFLoaded; // 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'); btnClearIn.addEventListener('click',function(){ taInput.value = ''; },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(); ev.stopPropagation(); btnShellExec.click(); | > > | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | self.onSFLoaded = function(){ delete this.onSFLoaded; // 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(); ev.stopPropagation(); btnShellExec.click(); |
︙ | ︙ | |||
729 730 731 732 733 734 735 736 737 738 739 740 741 | "-- If a subset of the text is currently selected,\n", "-- only that part is executed.\n", "-- ================================================\n", ".help\n" ]}, //{name: "Timer on", sql: ".timer on"}, // ^^^ re-enable if emscripten re-enables getrusage() {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" ]}, | > | < < | | > > > > > > | 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 | "-- If a subset of the text is currently selected,\n", "-- only that part is executed.\n", "-- ================================================\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:[ "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", " UNION ALL\n", " SELECT iter+1, cx, cy, x*x-y*y + cx, 2.0*x*y + cy FROM m \n", " WHERE (x*x + y*y) < 4.0 AND iter<28\n", " ),\n", " m2(iter, cx, cy) AS (\n", " SELECT max(iter), cx, cy FROM m GROUP BY cx, cy\n", " ),\n", " 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; if(!val) o.setAttribute('disabled',true); o.appendChild(document.createTextNode(lbl)); |
︙ | ︙ |
Changes to ext/wasm/index-dist.html.
︙ | ︙ | |||
93 94 95 96 97 98 99 100 101 102 103 104 105 106 | demo of using the key-value VFS for storing a persistent db in JS <code>localStorage</code> or <code>sessionStorage</code>.</li> <li><a href='demo-worker1.html'>demo-worker1</a>: Worker-based wrapper of the OO API #1. Its Promise-based wrapper is significantly easier to use, however.</li> <li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>: a demo of the Promise-based wrapper of the Worker1 API.</li> </ul> </li> </ul> </div> <style> #test-list { font-size: 120%; } </style> | > > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | demo of using the key-value VFS for storing a persistent db in JS <code>localStorage</code> or <code>sessionStorage</code>.</li> <li><a href='demo-worker1.html'>demo-worker1</a>: Worker-based wrapper of the OO API #1. Its Promise-based wrapper is significantly easier to use, however.</li> <li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>: a demo of the Promise-based wrapper of the Worker1 API.</li> <li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>: same as the previous demo except loads the promiser from an ESM module.</li> </ul> </li> </ul> </div> <style> #test-list { font-size: 120%; } </style> |
︙ | ︙ |
Changes to ext/wasm/index.html.
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | demo of using the key-value VFS for storing a persistent db in JS <code>localStorage</code> or <code>sessionStorage</code>.</li> <li><a href='demo-worker1.html'>demo-worker1</a>: Worker-based wrapper of the OO API #1. Its Promise-based wrapper is significantly easier to use, however.</li> <li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>: a demo of the Promise-based wrapper of the Worker1 API.</li> </ul> </li> <li>speedtest1 ports (sqlite3's primary benchmarking tool)... <ul> <li><a href='speedtest1.html'>speedtest1</a>: a main-thread WASM build of speedtest1.</li> <li><a href='speedtest1.html?vfs=kvvfs'>speedtest1?vfs=kvvfs</a>: speedtest1 with the kvvfs.</li> <li><a href='speedtest1-worker.html?size=15'>speedtest1-worker</a>: an interactive Worker-thread variant of speedtest1.</li> | > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | demo of using the key-value VFS for storing a persistent db in JS <code>localStorage</code> or <code>sessionStorage</code>.</li> <li><a href='demo-worker1.html'>demo-worker1</a>: Worker-based wrapper of the OO API #1. Its Promise-based wrapper is significantly easier to use, however.</li> <li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>: a demo of the Promise-based wrapper of the Worker1 API.</li> <li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>: same as the previous demo except loads the promiser from an ESM module.</li> </ul> </li> <li>speedtest1 ports (sqlite3's primary benchmarking tool)... <ul> <li><a href='speedtest1.html'>speedtest1</a>: a main-thread WASM build of speedtest1.</li> <li><a href='speedtest1.html?vfs=kvvfs'>speedtest1?vfs=kvvfs</a>: speedtest1 with the kvvfs.</li> <li><a href='speedtest1-worker.html?size=15'>speedtest1-worker</a>: an interactive Worker-thread variant of speedtest1.</li> |
︙ | ︙ |
Changes to ext/wasm/jaccwabyt/jaccwabyt.md.
︙ | ︙ | |||
202 203 204 205 206 207 208 | dealloc: function(pointerToFree){...} }); ``` It also offers a number of other settings, but all are optional except for the ones shown above. Those three config options abstract away details which are specific to a given WASM environment. They provide | | | < | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | dealloc: function(pointerToFree){...} }); ``` It also offers a number of other settings, but all are optional except for the ones shown above. Those three config options abstract away details which are specific to a given WASM environment. They provide the WASM "heap" memory, the memory allocator, and the deallocator. In a conventional Emscripten setup, that config might simply look like: > ```javascript { heap: Module['asm']['memory'], //Or: // heap: ()=>Module['HEAP8'], |
︙ | ︙ |
Changes to ext/wasm/speedtest1-worker.js.
︙ | ︙ | |||
107 108 109 110 111 112 113 | printErr: logErr, setStatus: (text)=>mPost('load-status',text) }; log("Initializing speedtest1 module..."); self.sqlite3InitModule(EmscriptenModule).then(async (sqlite3)=>{ const S = globalThis.S = App.sqlite3 = sqlite3; log("Loaded speedtest1 module. Setting up..."); | < < < < | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | printErr: logErr, setStatus: (text)=>mPost('load-status',text) }; log("Initializing speedtest1 module..."); self.sqlite3InitModule(EmscriptenModule).then(async (sqlite3)=>{ const S = globalThis.S = App.sqlite3 = sqlite3; log("Loaded speedtest1 module. Setting up..."); App.pDir = wasmfsDir(S.wasm); App.wasm = S.wasm; //if(App.pDir) log("Persistent storage:",pDir); //else log("Using transient storage."); mPost('ready',true); log("Registered VFSes:", ...S.capi.sqlite3_js_vfs_list()); }).catch(e=>{ |
︙ | ︙ |
Changes to ext/wasm/tester1.c-pp.js.
︙ | ︙ | |||
59 60 61 62 63 64 65 | /* Predicate for tests/groups. */ const isUIThread = ()=>(globalThis.window===self && globalThis.document); /* Predicate for tests/groups. */ const isWorker = ()=>!isUIThread(); /* Predicate for tests/groups. */ const testIsTodo = ()=>false; const haveWasmCTests = ()=>{ | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | /* Predicate for tests/groups. */ const isUIThread = ()=>(globalThis.window===self && globalThis.document); /* Predicate for tests/groups. */ const isWorker = ()=>!isUIThread(); /* Predicate for tests/groups. */ const testIsTodo = ()=>false; const haveWasmCTests = ()=>{ return !!wasm.exports.sqlite3__wasm_test_intptr; }; const hasOpfs = ()=>{ return globalThis.FileSystemHandle && globalThis.FileSystemDirectoryHandle && globalThis.FileSystemFileHandle && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && navigator?.storage?.getDirectory; |
︙ | ︙ | |||
718 719 720 721 722 723 724 | .assert(0===w.peek8(cstr+n)) .assert(chr('d')===w.peek8(cstr+n-1)); }); }/*scopedAlloc()*/ //log("xCall()..."); { | | | | | 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 | .assert(0===w.peek8(cstr+n)) .assert(chr('d')===w.peek8(cstr+n-1)); }); }/*scopedAlloc()*/ //log("xCall()..."); { const pJson = w.xCall('sqlite3__wasm_enum_json'); T.assert(Number.isFinite(pJson)).assert(w.cstrlen(pJson)>300); } //log("xWrap()..."); { T.mustThrowMatching(()=>w.xWrap('sqlite3_libversion',null,'i32'), /requires 0 arg/). assert(w.xWrap.resultAdapter('i32') instanceof Function). assert(w.xWrap.argAdapter('i32') instanceof Function); let fw = w.xWrap('sqlite3_libversion','utf8'); T.mustThrowMatching(()=>fw(1), /requires 0 arg/); let rc = fw(); T.assert('string'===typeof rc).assert(rc.length>5); rc = w.xCallWrapped('sqlite3__wasm_enum_json','*'); T.assert(rc>0 && Number.isFinite(rc)); rc = w.xCallWrapped('sqlite3__wasm_enum_json','utf8'); T.assert('string'===typeof rc).assert(rc.length>300); { // 'string:static' argAdapter() sanity checks... let argAd = w.xWrap.argAdapter('string:static'); let p0 = argAd('foo'), p1 = argAd('bar'); T.assert(w.isPtr(p0) && w.isPtr(p1)) |
︙ | ︙ | |||
817 818 819 820 821 822 823 | }finally{ wasm.uninstallFunction(fp); } } if(haveWasmCTests()){ if(!sqlite3.config.useStdAlloc){ | | | | | 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 | }finally{ wasm.uninstallFunction(fp); } } if(haveWasmCTests()){ if(!sqlite3.config.useStdAlloc){ fw = w.xWrap('sqlite3__wasm_test_str_hello', 'utf8:dealloc',['i32']); rc = fw(0); T.assert('hello'===rc); rc = fw(1); T.assert(null===rc); } if(w.bigIntEnabled){ w.xWrap.resultAdapter('thrice', (v)=>3n*BigInt(v)); w.xWrap.argAdapter('twice', (v)=>2n*BigInt(v)); fw = w.xWrap('sqlite3__wasm_test_int64_times2','thrice','twice'); rc = fw(1); T.assert(12n===rc); w.scopedAllocCall(function(){ const pI1 = w.scopedAlloc(8), pI2 = pI1+4; w.pokePtr([pI1, pI2], 0); const f = w.xWrap('sqlite3__wasm_test_int64_minmax',undefined,['i64*','i64*']); const [r1, r2] = w.peek64([pI1, pI2]); T.assert(!Number.isSafeInteger(r1)).assert(!Number.isSafeInteger(r2)); }); } } }/*xWrap()*/ }/*WhWasmUtil*/) |
︙ | ︙ | |||
938 939 940 941 942 943 944 | assert(WTStruct.isA(wts)). assert(wts instanceof WTStruct). assert(wts instanceof StructType). assert(StructType.isA(wts)). assert(wts.pointer>0).assert(0===wts.$v4).assert(0n===wts.$v8). assert(0===wts.$ppV).assert(0===wts.$xFunc); const testFunc = | | | 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | assert(WTStruct.isA(wts)). assert(wts instanceof WTStruct). assert(wts instanceof StructType). assert(StructType.isA(wts)). assert(wts.pointer>0).assert(0===wts.$v4).assert(0n===wts.$v8). assert(0===wts.$ppV).assert(0===wts.$xFunc); const testFunc = W.xGet('sqlite3__wasm_test_struct'/*name gets mangled in -O3 builds!*/); let counter = 0; //log("wts.pointer =",wts.pointer); const wtsFunc = function(arg){ /*log("This from a JS function called from C, "+ "which itself was called from JS. arg =",arg);*/ ++counter; if(3===counter){ |
︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 | }) ;/*end sqlite3_randomness() checks*/ //////////////////////////////////////////////////////////////////////// T.g('sqlite3.oo1') .t('Create db', function(sqlite3){ const dbFile = '/tester1.db'; | | | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 | }) ;/*end sqlite3_randomness() checks*/ //////////////////////////////////////////////////////////////////////// T.g('sqlite3.oo1') .t('Create db', function(sqlite3){ const dbFile = '/tester1.db'; sqlite3.util.sqlite3__wasm_vfs_unlink(0, dbFile); const db = this.db = new sqlite3.oo1.DB(dbFile, 0 ? 'ct' : 'c'); db.onclose = { disposeAfter: [], disposeBefore: [ (db)=>{ //console.debug("db.onclose.before dropping modules"); //sqlite3.capi.sqlite3_drop_modules(db.pointer, 0); |
︙ | ︙ | |||
1455 1456 1457 1458 1459 1460 1461 | rowMode: 0 }); T.assert(Array.isArray(rv)).assert(3===rv.length) .assert(-1===rv[0]).assert(-3===rv[2]); rv = db.exec("SELECT 1 WHERE 0",{rowMode: 0}); T.assert(Array.isArray(rv)).assert(0===rv.length); if(wasm.bigIntEnabled && haveWasmCTests()){ | | | 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 | rowMode: 0 }); T.assert(Array.isArray(rv)).assert(3===rv.length) .assert(-1===rv[0]).assert(-3===rv[2]); rv = db.exec("SELECT 1 WHERE 0",{rowMode: 0}); T.assert(Array.isArray(rv)).assert(0===rv.length); if(wasm.bigIntEnabled && haveWasmCTests()){ const mI = wasm.xCall('sqlite3__wasm_test_int64_max'); const b = BigInt(Number.MAX_SAFE_INTEGER * 2); T.assert(b === db.selectValue("SELECT "+b)). assert(b === db.selectValue("SELECT ?", b)). assert(mI == db.selectValue("SELECT $x", {$x:mI})); }else{ /* Curiously, the JS spec seems to be off by one with the definitions of MIN/MAX_SAFE_INTEGER: |
︙ | ︙ | |||
1478 1479 1480 1481 1482 1483 1484 | T.assert(0===st.columnCount); const ndx = st.getParamIndex(':b'); T.assert(1===ndx); st.bindAsBlob(ndx, "ima blob") /*step() skipped intentionally*/.reset(true); } finally { T.assert(0===st.finalize()) | | | 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 | T.assert(0===st.columnCount); const ndx = st.getParamIndex(':b'); T.assert(1===ndx); st.bindAsBlob(ndx, "ima blob") /*step() skipped intentionally*/.reset(true); } finally { T.assert(0===st.finalize()) .assert(undefined===st.finalize()); } try { db.prepare("/*empty SQL*/"); toss("Must not be reached."); }catch(e){ T.assert(e instanceof sqlite3.SQLite3Error) |
︙ | ︙ | |||
1681 1682 1683 1684 1685 1686 1687 | const db2 = new sqlite3.oo1.DB(filename,'r'); try { const sql = "select count(*) from t"; const n = db.selectValue(sql); T.assert(n>0 && db2.selectValue(sql) === n); }finally{ db2.close(); | | | 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 | const db2 = new sqlite3.oo1.DB(filename,'r'); try { const sql = "select count(*) from t"; const n = db.selectValue(sql); T.assert(n>0 && db2.selectValue(sql) === n); }finally{ db2.close(); sqlite3.util.sqlite3__wasm_vfs_unlink(0, filename); } } }/*sqlite3_js_posix_create_file()*/) //////////////////////////////////////////////////////////////////// .t({ name:'Scalar UDFs', |
︙ | ︙ | |||
2071 2072 2073 2074 2075 2076 2077 | const w = wasm, db = this.db; const stack = w.scopedAllocPush(); let ptrInt; const origValue = 512; try{ ptrInt = w.scopedAlloc(4); w.poke32(ptrInt,origValue); | | | | | | | | 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 | const w = wasm, db = this.db; const stack = w.scopedAllocPush(); let ptrInt; const origValue = 512; try{ ptrInt = w.scopedAlloc(4); w.poke32(ptrInt,origValue); const cf = w.xGet('sqlite3__wasm_test_intptr'); const oldPtrInt = ptrInt; T.assert(origValue === w.peek32(ptrInt)); const rc = cf(ptrInt); T.assert(2*origValue === rc). assert(rc === w.peek32(ptrInt)). assert(oldPtrInt === ptrInt); const pi64 = w.scopedAlloc(8)/*ptr to 64-bit integer*/; const o64 = 0x010203040506/*>32-bit integer*/; if(w.bigIntEnabled){ w.poke64(pi64, o64); //log("pi64 =",pi64, "o64 = 0x",o64.toString(16), o64); const v64 = ()=>w.peek64(pi64) T.assert(v64() == o64); //T.assert(o64 === w.peek64(pi64)); const cf64w = w.xGet('sqlite3__wasm_test_int64ptr'); cf64w(pi64); T.assert(v64() == BigInt(2 * o64)); cf64w(pi64); T.assert(v64() == BigInt(4 * o64)); const biTimes2 = w.xGet('sqlite3__wasm_test_int64_times2'); T.assert(BigInt(2 * o64) === biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError in the call :/ */)); const pMin = w.scopedAlloc(16); const pMax = pMin + 8; const g64 = (p)=>w.peek64(p); w.poke64([pMin, pMax], 0); const minMaxI64 = [ w.xCall('sqlite3__wasm_test_int64_min'), w.xCall('sqlite3__wasm_test_int64_max') ]; T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)). assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER)); //log("int64_min/max() =",minMaxI64, typeof minMaxI64[0]); w.xCall('sqlite3__wasm_test_int64_minmax', pMin, pMax); T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch"). assert(g64(pMax) === minMaxI64[1], "int64 mismatch"); //log("pMin",g64(pMin), "pMax",g64(pMax)); w.poke64(pMin, minMaxI64[0]); T.assert(g64(pMin) === minMaxI64[0]). assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))). assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax))); |
︙ | ︙ | |||
2556 2557 2558 2559 2560 2561 2562 | } T.assert(capi.SQLITE_ERROR_MISSING_COLLSEQ === rc); })/*custom collation*/ //////////////////////////////////////////////////////////////////////// .t('Close db', function(){ T.assert(this.db).assert(wasm.isPtr(this.db.pointer)); | | | 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 | } T.assert(capi.SQLITE_ERROR_MISSING_COLLSEQ === rc); })/*custom collation*/ //////////////////////////////////////////////////////////////////////// .t('Close db', function(){ T.assert(this.db).assert(wasm.isPtr(this.db.pointer)); //wasm.sqlite3__wasm_db_reset(this.db); // will leak virtual tables! this.db.close(); T.assert(!this.db.pointer); }) ;/* end of oo1 checks */ //////////////////////////////////////////////////////////////////////// T.g('kvvfs') |
︙ | ︙ | |||
2583 2584 2585 2586 2587 2588 2589 | predicate: ()=>(isUIThread() || "local/sessionStorage are unavailable in a Worker"), test: function(sqlite3){ const filename = this.kvvfsDbFile = 'session'; const pVfs = capi.sqlite3_vfs_find('kvvfs'); T.assert(pVfs); const JDb = this.JDb = sqlite3.oo1.JsStorageDb; | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 | predicate: ()=>(isUIThread() || "local/sessionStorage are unavailable in a Worker"), test: function(sqlite3){ const filename = this.kvvfsDbFile = 'session'; const pVfs = capi.sqlite3_vfs_find('kvvfs'); T.assert(pVfs); const JDb = this.JDb = sqlite3.oo1.JsStorageDb; const unlink = this.kvvfsUnlink = ()=>JDb.clearStorage(this.kvvfsDbFile); unlink(); let db = new JDb(filename); try { db.exec([ 'create table kvvfs(a);', 'insert into kvvfs(a) values(1),(2),(3)' ]); T.assert(3 === db.selectValue('select count(*) from kvvfs')); db.close(); db = new JDb(filename); db.exec('insert into kvvfs(a) values(4),(5),(6)'); T.assert(6 === db.selectValue('select count(*) from kvvfs')); }finally{ db.close(); } } }/*kvvfs sanity checks*/) //#if enable-see .t({ name: 'kvvfs with SEE encryption', predicate: ()=>(isUIThread() || "Only available in main thread."), test: function(sqlite3){ this.kvvfsUnlink(); let db; const encOpt1 = 1 ? {textkey: 'foo'} : {key: 'foo'}; const encOpt2 = encOpt1.textkey ? encOpt1 : {hexkey: new Uint8Array([0x66,0x6f,0x6f]/*==>"foo"*/)} try{ db = new this.JDb({ filename: this.kvvfsDbFile, ...encOpt1 }); db.exec([ "create table t(a,b);", "insert into t(a,b) values(1,2),(3,4)" ]); db.close(); let err; try{ db = new this.JDb({ filename: this.kvvfsDbFile, flags: 'ct' }); T.assert(db) /* opening is fine, but... */; db.exec("select 1 from sqlite_schema"); console.warn("sessionStorage =",sessionStorage); }catch(e){ err = e; }finally{ db.close(); } T.assert(err,"Expecting an exception") .assert(sqlite3.capi.SQLITE_NOTADB==err.resultCode, "Expecting NOTADB"); db = new sqlite3.oo1.DB({ filename: this.kvvfsDbFile, vfs: 'kvvfs', ...encOpt2 }); T.assert( 4===db.selectValue('select sum(a) from t') ); }finally{ if( db ) db.close(); this.kvvfsUnlink(); } } })/*kvvfs with SEE*/ //#endif enable-see ;/* end kvvfs tests */ //////////////////////////////////////////////////////////////////////// T.g('Hook APIs') .t({ name: "sqlite3_commit/rollback/update_hook()", predicate: ()=>wasm.bigIntEnabled || "Update hook requires int64", |
︙ | ︙ | |||
2884 2885 2886 2887 2888 2889 2890 2891 | //////////////////////////////////////////////////////////////////////// T.g('OPFS: Origin-Private File System', (sqlite3)=>(sqlite3.capi.sqlite3_vfs_find("opfs") || 'requires "opfs" VFS')) .t({ name: 'OPFS db sanity checks', test: async function(sqlite3){ const filename = this.opfsDbFile = '/dir/sqlite3-tester1.db'; | > > | < | < | > > | | < < < > > > > > > | > > < > > | 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 | //////////////////////////////////////////////////////////////////////// T.g('OPFS: Origin-Private File System', (sqlite3)=>(sqlite3.capi.sqlite3_vfs_find("opfs") || 'requires "opfs" VFS')) .t({ name: 'OPFS db sanity checks', test: async function(sqlite3){ T.assert(capi.sqlite3_vfs_find('opfs')); const opfs = sqlite3.opfs; const filename = this.opfsDbFile = '/dir/sqlite3-tester1.db'; const fileUri = 'file://'+filename+'?delete-before-open=1'; const initSql = [ 'create table p(a);', 'insert into p(a) values(1),(2),(3)' ]; let db = new sqlite3.oo1.OpfsDb(fileUri); try { db.exec(initSql); T.assert(3 === db.selectValue('select count(*) from p')); db.close(); db = new sqlite3.oo1.OpfsDb(filename); db.exec('insert into p(a) values(4),(5),(6)'); T.assert(6 === db.selectValue('select count(*) from p')); this.opfsDbExport = capi.sqlite3_js_db_export(db); T.assert(this.opfsDbExport instanceof Uint8Array) .assert(this.opfsDbExport.byteLength>0 && 0===this.opfsDbExport.byteLength % 512); }finally{ db.close(); } T.assert(await opfs.entryExists(filename)); try { db = new sqlite3.oo1.OpfsDb(fileUri); db.exec(initSql) /* will throw if delete-before-open did not work */; T.assert(3 === db.selectValue('select count(*) from p')); }finally{ if(db) db.close(); } } }/*OPFS db sanity checks*/) .t({ name: 'OPFS import', test: async function(sqlite3){ let db; const filename = this.opfsDbFile; try { const exp = this.opfsDbExport; delete this.opfsDbExport; this.opfsImportSize = await sqlite3.oo1.OpfsDb.importDb(filename, exp); db = new sqlite3.oo1.OpfsDb(this.opfsDbFile); T.assert(6 === db.selectValue('select count(*) from p')). assert( this.opfsImportSize == exp.byteLength ); db.close(); const unlink = this.opfsUnlink = (fn=filename)=>sqlite3.util.sqlite3__wasm_vfs_unlink("opfs",fn); this.opfsUnlink(filename); T.assert(!(await sqlite3.opfs.entryExists(filename))); // Try again with a function as an input source: let cursor = 0; const blockSize = 512, end = exp.byteLength; const reader = async function(){ if(cursor >= exp.byteLength){ |
︙ | ︙ | |||
2950 2951 2952 2953 2954 2955 2956 | } } }/*OPFS export/import*/) .t({ name: '(Internal-use) OPFS utility APIs', test: async function(sqlite3){ const filename = this.opfsDbFile; | < | < | 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 | } } }/*OPFS export/import*/) .t({ name: '(Internal-use) OPFS utility APIs', test: async function(sqlite3){ const filename = this.opfsDbFile; const unlink = this.opfsUnlink; T.assert(filename && !!unlink); delete this.opfsDbFile; delete this.opfsUnlink; /************************************************************** ATTENTION CLIENT-SIDE USERS: sqlite3.opfs is NOT intended for client-side use. It is only for this project's own internal use. Its APIs are subject to change or removal at any time. ***************************************************************/ |
︙ | ︙ | |||
3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 | globalThis.sqlite3InitModule.__isUnderTest = true /* disables certain API-internal cleanup so that we can test internal APIs from here */; globalThis.sqlite3InitModule({ print: log, printErr: error }).then(async function(sqlite3){ log("Done initializing WASM/JS bits. Running tests..."); sqlite3.config.warn("Installing sqlite3 bits as global S for local dev/test purposes."); globalThis.S = sqlite3; /*await sqlite3.installOpfsSAHPoolVfs(sahPoolConfig) .then((u)=>log("Loaded",u.vfsName,"VFS")) .catch(e=>{ log("Cannot install OpfsSAHPool.",e); });*/ capi = sqlite3.capi; wasm = sqlite3.wasm; log("sqlite3 version:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); if(wasm.bigIntEnabled){ log("BigInt/int64 support is enabled."); }else{ logClass('warning',"BigInt/int64 support is disabled."); } if(haveWasmCTests()){ | > | | | 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 | globalThis.sqlite3InitModule.__isUnderTest = true /* disables certain API-internal cleanup so that we can test internal APIs from here */; globalThis.sqlite3InitModule({ print: log, printErr: error }).then(async function(sqlite3){ TestUtil.assert(!!sqlite3.util); log("Done initializing WASM/JS bits. Running tests..."); sqlite3.config.warn("Installing sqlite3 bits as global S for local dev/test purposes."); globalThis.S = sqlite3; /*await sqlite3.installOpfsSAHPoolVfs(sahPoolConfig) .then((u)=>log("Loaded",u.vfsName,"VFS")) .catch(e=>{ log("Cannot install OpfsSAHPool.",e); });*/ capi = sqlite3.capi; wasm = sqlite3.wasm; log("sqlite3 version:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); if(wasm.bigIntEnabled){ log("BigInt/int64 support is enabled."); }else{ logClass('warning',"BigInt/int64 support is disabled."); } if(haveWasmCTests()){ log("sqlite3__wasm_test_...() APIs are available."); }else{ logClass('warning',"sqlite3__wasm_test_...() APIs unavailable."); } log("registered vfs list =",capi.sqlite3_js_vfs_list().join(', ')); TestUtil.runTests(sqlite3); }); })(self); |
Changes to main.mk.
︙ | ︙ | |||
226 227 228 229 230 231 232 | $(TOP)/ext/userauth/sqlite3userauth.h SRC += \ $(TOP)/ext/rbu/sqlite3rbu.c \ $(TOP)/ext/rbu/sqlite3rbu.h SRC += \ $(TOP)/ext/misc/stmt.c | < | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | $(TOP)/ext/userauth/sqlite3userauth.h SRC += \ $(TOP)/ext/rbu/sqlite3rbu.c \ $(TOP)/ext/rbu/sqlite3rbu.h SRC += \ $(TOP)/ext/misc/stmt.c # FTS5 things # FTS5_HDR = \ $(TOP)/ext/fts5/fts5.h \ $(TOP)/ext/fts5/fts5Int.h \ fts5parse.h |
︙ | ︙ | |||
371 372 373 374 375 376 377 | $(TOP)/ext/misc/zipfile.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_tok.c \ $(TOP)/ext/rtree/test_rtreedoc.c \ $(TOP)/ext/recover/sqlite3recover.c \ $(TOP)/ext/recover/dbdata.c \ | | > > | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | $(TOP)/ext/misc/zipfile.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_tok.c \ $(TOP)/ext/rtree/test_rtreedoc.c \ $(TOP)/ext/recover/sqlite3recover.c \ $(TOP)/ext/recover/dbdata.c \ $(TOP)/ext/recover/test_recover.c \ $(TOP)/ext/intck/test_intck.c \ $(TOP)/ext/intck/sqlite3intck.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ $(TOP)/src/attach.c \ $(TOP)/src/backup.c \ |
︙ | ︙ | |||
655 656 657 658 659 660 661 | rm tsrc/sqlite.h.in tsrc/parse.y tclsh $(TOP)/tool/vdbe-compress.tcl $(OPTS) <tsrc/vdbe.c >vdbe.new 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 | | | | 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 | rm tsrc/sqlite.h.in tsrc/parse.y tclsh $(TOP)/tool/vdbe-compress.tcl $(OPTS) <tsrc/vdbe.c >vdbe.new 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 tclsh $(TOP)/tool/mksqlite3c.tcl $(EXTRA_SRC) cp tsrc/sqlite3ext.h . cp $(TOP)/ext/session/sqlite3session.h . echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c cat sqlite3.c >>tclsqlite3.c echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c sqlite3ext.h: target_source cp tsrc/sqlite3ext.h . sqlite3.c-debug: target_source $(TOP)/tool/mksqlite3c.tcl src-verify tclsh $(TOP)/tool/mksqlite3c.tcl --linemacros=1 $(EXTRA_SRC) echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c cat sqlite3.c >>tclsqlite3.c echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c echo '#line 1 "tclsqlite.c"' >>tclsqlite3.c cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl |
︙ | ︙ | |||
713 714 715 716 717 718 719 | # Rules to build opcodes.c and opcodes.h # opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl tclsh $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl | | < | 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 | # Rules to build opcodes.c and opcodes.h # opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl tclsh $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl cat parse.h $(TOP)/src/vdbe.c | tclsh $(TOP)/tool/mkopcodeh.tcl >opcodes.h # Rules to build parse.c and parse.h - the outputs of lemon. # parse.h: parse.c parse.c: $(TOP)/src/parse.y lemon cp $(TOP)/src/parse.y . |
︙ | ︙ | |||
737 738 739 740 741 742 743 | cat $(TOP)/VERSION | tclsh $(TOP)/tool/replace.tcl exact . , >>$@ echo '#endif' >>sqlite3rc.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash >keywordhash.h | | | | > > > > > > | | | | | | | | | | | | < < | | | | | | | > | | 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 | cat $(TOP)/VERSION | tclsh $(TOP)/tool/replace.tcl exact . , >>$@ echo '#endif' >>sqlite3rc.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash >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 tclsh $(TOP)/tool/mkshellc.tcl >shell.c # Rules to build the extension objects. # icu.o: $(TOP)/ext/icu/icu.c $(HDR) $(EXTHDR) |
︙ | ︙ |
Changes to src/alter.c.
︙ | ︙ | |||
2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 | regOut = reg+1+iPos-(iPos>iColPos); }else{ regOut = reg+1+nField; } if( i==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); }else{ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); } nField++; } } if( nField==0 ){ /* dbsqlfuzz 5f09e7bcc78b4954d06bf9f2400d7715f48d1fef */ pParse->nMem++; | > > > > > | 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 | regOut = reg+1+iPos-(iPos>iColPos); }else{ regOut = reg+1+nField; } if( i==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); }else{ char aff = pTab->aCol[i].affinity; if( aff==SQLITE_AFF_REAL ){ pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC; } sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); pTab->aCol[i].affinity = aff; } nField++; } } if( nField==0 ){ /* dbsqlfuzz 5f09e7bcc78b4954d06bf9f2400d7715f48d1fef */ pParse->nMem++; |
︙ | ︙ |
Changes to src/analyze.c.
︙ | ︙ | |||
260 261 262 263 264 265 266 | ** Three SQL functions - stat_init(), stat_push(), and stat_get() - ** share an instance of the following structure to hold their state ** information. */ typedef struct StatAccum StatAccum; typedef struct StatSample StatSample; struct StatSample { | < > | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | ** Three SQL functions - stat_init(), stat_push(), and stat_get() - ** share an instance of the following structure to hold their state ** information. */ typedef struct StatAccum StatAccum; typedef struct StatSample StatSample; struct StatSample { tRowcnt *anDLt; /* sqlite_stat4.nDLt */ #ifdef SQLITE_ENABLE_STAT4 tRowcnt *anEq; /* sqlite_stat4.nEq */ tRowcnt *anLt; /* sqlite_stat4.nLt */ union { i64 iRowid; /* Rowid in main table of the key */ u8 *aRowid; /* Key for WITHOUT ROWID tables */ } u; u32 nRowid; /* Sizeof aRowid[] */ u8 isPSample; /* True if a periodic sample */ |
︙ | ︙ | |||
420 421 422 423 424 425 426 | nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol; nKeyCol = sqlite3_value_int(argv[1]); assert( nKeyCol<=nCol ); assert( nKeyCol>0 ); /* Allocate the space required for the StatAccum object */ n = sizeof(*p) | < | > < > | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol; nKeyCol = sqlite3_value_int(argv[1]); assert( nKeyCol<=nCol ); assert( nKeyCol>0 ); /* Allocate the space required for the StatAccum object */ n = sizeof(*p) + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ #ifdef SQLITE_ENABLE_STAT4 n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */ if( mxSample ){ n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); } #endif p = sqlite3DbMallocZero(db, n); if( p==0 ){ sqlite3_result_error_nomem(context); return; } p->db = db; p->nEst = sqlite3_value_int64(argv[2]); p->nRow = 0; p->nLimit = sqlite3_value_int64(argv[3]); p->nCol = nCol; p->nKeyCol = nKeyCol; p->nSkipAhead = 0; p->current.anDLt = (tRowcnt*)&p[1]; #ifdef SQLITE_ENABLE_STAT4 p->current.anEq = &p->current.anDLt[nColUp]; p->mxSample = p->nLimit==0 ? mxSample : 0; if( mxSample ){ u8 *pSpace; /* Allocated space not yet assigned */ int i; /* Used to iterate through p->aSample[] */ p->iGet = -1; p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1); |
︙ | ︙ | |||
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 | UNUSED_PARAMETER( argc ); UNUSED_PARAMETER( context ); assert( p->nCol>0 ); assert( iChng<p->nCol ); if( p->nRow==0 ){ /* This is the first call to this function. Do initialization. */ for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1; }else{ /* Second and subsequent calls get processed here */ #ifdef SQLITE_ENABLE_STAT4 if( p->mxSample ) samplePushPrevious(p, iChng); #endif /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply ** to the current row of the index. */ for(i=0; i<iChng; i++){ p->current.anEq[i]++; } for(i=iChng; i<p->nCol; i++){ p->current.anDLt[i]++; #ifdef SQLITE_ENABLE_STAT4 if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; | > > > > < > | 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 | UNUSED_PARAMETER( argc ); UNUSED_PARAMETER( context ); assert( p->nCol>0 ); assert( iChng<p->nCol ); if( p->nRow==0 ){ /* This is the first call to this function. Do initialization. */ #ifdef SQLITE_ENABLE_STAT4 for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1; #endif }else{ /* Second and subsequent calls get processed here */ #ifdef SQLITE_ENABLE_STAT4 if( p->mxSample ) samplePushPrevious(p, iChng); #endif /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply ** to the current row of the index. */ #ifdef SQLITE_ENABLE_STAT4 for(i=0; i<iChng; i++){ p->current.anEq[i]++; } #endif for(i=iChng; i<p->nCol; i++){ p->current.anDLt[i]++; #ifdef SQLITE_ENABLE_STAT4 if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; p->current.anEq[i] = 1; #endif } } p->nRow++; #ifdef SQLITE_ENABLE_STAT4 if( p->mxSample ){ tRowcnt nLt; |
︙ | ︙ | |||
863 864 865 866 867 868 869 | sqlite3_str_appendf(&sStat, "%llu", p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); for(i=0; i<p->nKeyCol; i++){ u64 nDistinct = p->current.anDLt[i] + 1; u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; sqlite3_str_appendf(&sStat, " %llu", iVal); | > | > | 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 | sqlite3_str_appendf(&sStat, "%llu", p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); for(i=0; i<p->nKeyCol; i++){ u64 nDistinct = p->current.anDLt[i] + 1; u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; sqlite3_str_appendf(&sStat, " %llu", iVal); #ifdef SQLITE_ENABLE_STAT4 assert( p->current.anEq[i] || p->nRow==0 ); #endif } sqlite3ResultStrAccum(context, &sStat); } #ifdef SQLITE_ENABLE_STAT4 else if( eCall==STAT_GET_ROWID ){ if( p->iGet<0 ){ samplePushPrevious(p, 0); |
︙ | ︙ | |||
1047 1048 1049 1050 1051 1052 1053 | iIdxCur = iTab++; pParse->nTab = MAX(pParse->nTab, iTab); sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); sqlite3VdbeLoadString(v, regTabname, pTab->zName); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; /* Number of columns in pIdx. "N" */ | | | 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 | iIdxCur = iTab++; pParse->nTab = MAX(pParse->nTab, iTab); sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); sqlite3VdbeLoadString(v, regTabname, pTab->zName); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; /* Number of columns in pIdx. "N" */ int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */ int addrNextRow; /* Address of "next_row:" */ const char *zIdxName; /* Name of the index */ int nColTest; /* Number of columns to test for changes */ if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0; if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIdx) ){ |
︙ | ︙ | |||
1071 1072 1073 1074 1075 1076 1077 1078 | /* Populate the register containing the index name. */ sqlite3VdbeLoadString(v, regIdxname, zIdxName); VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName)); /* ** Pseudo-code for loop that calls stat_push(): ** ** Rewind csr | > | > > | > > | 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 | /* Populate the register containing the index name. */ sqlite3VdbeLoadString(v, regIdxname, zIdxName); VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName)); /* ** Pseudo-code for loop that calls stat_push(): ** ** regChng = 0 ** Rewind csr ** if eof(csr){ ** stat_init() with count = 0; ** goto end_of_scan; ** } ** count() ** stat_init() ** goto chng_addr_0; ** ** next_row: ** regChng = 0 ** if( idx(0) != regPrev(0) ) goto chng_addr_0 ** regChng = 1 ** if( idx(1) != regPrev(1) ) goto chng_addr_1 |
︙ | ︙ | |||
1112 1113 1114 1115 1116 1117 1118 | /* Open a read-only cursor on the index being analyzed. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); | | | > > > > > > > > > > > > > > | < < < < < < < < < < < | < | < > > < < < < < < < < | 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 | /* Open a read-only cursor on the index being analyzed. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); /* Implementation of the following: ** ** regChng = 0 ** Rewind csr ** if eof(csr){ ** stat_init() with count = 0; ** goto end_of_scan; ** } ** count() ** stat_init() ** goto chng_addr_0; */ assert( regTemp2==regStat+4 ); sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); /* Arguments to stat_init(): ** (1) the number of columns in the index including the rowid ** (or for a WITHOUT ROWID table, the number of PK columns), ** (2) the number of columns in the key without the rowid/pk ** (3) estimated number of rows in the index. */ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); assert( regRowid==regStat+2 ); sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, OptimizationDisabled(db, SQLITE_Stat4)); sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, &statInitFuncdef, 0); addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); addrNextRow = sqlite3VdbeCurrentAddr(v); if( nColTest>0 ){ int endDistinctTest = sqlite3VdbeMakeLabel(pParse); int *aGotoChng; /* Array of jump instruction addresses */ aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest); |
︙ | ︙ | |||
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 | sqlite3VdbeJumpHere(v, j3); }else{ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); } } /* Add the entry to the stat1 table. */ callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); assert( "BBB"[0]==SQLITE_AFF_TEXT ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE); | > > > > > > | 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 | sqlite3VdbeJumpHere(v, j3); }else{ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); } } /* Add the entry to the stat1 table. */ if( pIdx->pPartIdxWhere ){ /* Partial indexes might get a zero-entry in sqlite_stat1. But ** an empty table is omitted from sqlite_stat1. */ sqlite3VdbeJumpHere(v, addrGotoEnd); addrGotoEnd = 0; } callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); assert( "BBB"[0]==SQLITE_AFF_TEXT ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE); |
︙ | ︙ | |||
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 | int regDLt = regStat1+2; int regSample = regStat1+3; int regCol = regStat1+4; int regSampleRowid = regCol + nCol; int addrNext; int addrIsNull; u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; if( doOnce ){ int mxCol = nCol; Index *pX; /* Compute the maximum number of columns in any index */ for(pX=pTab->pIndex; pX; pX=pX->pNext){ | > > > > > > > | 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 | int regDLt = regStat1+2; int regSample = regStat1+3; int regCol = regStat1+4; int regSampleRowid = regCol + nCol; int addrNext; int addrIsNull; u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; /* No STAT4 data is generated if the number of rows is zero */ if( addrGotoEnd==0 ){ sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER); addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); VdbeCoverage(v); } if( doOnce ){ int mxCol = nCol; Index *pX; /* Compute the maximum number of columns in any index */ for(pX=pTab->pIndex; pX; pX=pX->pNext){ |
︙ | ︙ | |||
1328 1329 1330 1331 1332 1333 1334 | sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid); sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */ sqlite3VdbeJumpHere(v, addrIsNull); } #endif /* SQLITE_ENABLE_STAT4 */ /* End of analysis */ | | | 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 | sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid); sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */ sqlite3VdbeJumpHere(v, addrIsNull); } #endif /* SQLITE_ENABLE_STAT4 */ /* End of analysis */ if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd); } /* Create a single sqlite_stat1 entry containing NULL as the index ** name and the row count as the content. */ if( pOnlyIdx==0 && needTableCnt ){ |
︙ | ︙ | |||
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 | else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){ pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9)); } #endif while( z[0]!=0 && z[0]!=' ' ) z++; while( z[0]==' ' ) z++; } } } /* ** This callback is invoked once for each index when reading the ** sqlite_stat1 table. ** | > > > > > > > > > > | 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 | else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){ pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9)); } #endif while( z[0]!=0 && z[0]!=' ' ) z++; while( z[0]==' ' ) z++; } /* Set the bLowQual flag if the peak number of rows obtained ** from a full equality match is so large that a full table scan ** seems likely to be faster than using the index. */ if( aLog[0] > 66 /* Index has more than 100 rows */ && aLog[0] <= aLog[nOut-1] /* And only a single value seen */ ){ pIndex->bLowQual = 1; } } } /* ** This callback is invoked once for each index when reading the ** sqlite_stat1 table. ** |
︙ | ︙ |
Changes to src/btree.c.
︙ | ︙ | |||
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | return SQLITE_CORRUPT_BKPT; } # define SQLITE_CORRUPT_PAGE(pMemPage) corruptPageError(__LINE__, pMemPage) #else # define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) #endif #ifndef SQLITE_OMIT_SHARED_CACHE #ifdef SQLITE_DEBUG /* **** This function is only used as part of an assert() statement. *** ** ** Check to see if pBtree holds the required locks to read or write to the ** table with root page iRoot. Return 1 if it does and 0 if not. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | return SQLITE_CORRUPT_BKPT; } # define SQLITE_CORRUPT_PAGE(pMemPage) corruptPageError(__LINE__, pMemPage) #else # define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) #endif /* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled ** or if the lock tracking is disabled. This is always the value for ** release builds. */ #define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/ #ifndef SQLITE_OMIT_SHARED_CACHE #if 0 /* ^---- Change to 1 and recompile to enable shared-lock tracing ** for debugging purposes. ** ** Print all shared-cache locks on a BtShared. Debugging use only. */ static void sharedLockTrace( BtShared *pBt, const char *zMsg, int iRoot, int eLockType ){ BtLock *pLock; if( iRoot>0 ){ printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W"); }else{ printf("%s-%p:", zMsg, pBt); } for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ printf(" %p/%u%s", pLock->pBtree, pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W"); while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){ pLock = pLock->pNext; printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W"); } } printf("\n"); fflush(stdout); } #undef SHARED_LOCK_TRACE #define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE) #endif /* Shared-lock tracing */ #ifdef SQLITE_DEBUG /* **** This function is only used as part of an assert() statement. *** ** ** Check to see if pBtree holds the required locks to read or write to the ** table with root page iRoot. Return 1 if it does and 0 if not. ** |
︙ | ︙ | |||
224 225 226 227 228 229 230 231 232 233 234 235 236 237 | iTab = pIdx->pTable->tnum; bSeen = 1; } } }else{ iTab = iRoot; } /* Search for the required lock. Either a write-lock on root-page iTab, a ** write-lock on the schema table, or (if the client is reading) a ** read-lock on iTab will suffice. Return 1 if any of these are found. */ for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ if( pLock->pBtree==pBtree && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) | > > | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | iTab = pIdx->pTable->tnum; bSeen = 1; } } }else{ iTab = iRoot; } SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType); /* Search for the required lock. Either a write-lock on root-page iTab, a ** write-lock on the schema table, or (if the client is reading) a ** read-lock on iTab will suffice. Return 1 if any of these are found. */ for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ if( pLock->pBtree==pBtree && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) |
︙ | ︙ | |||
358 359 360 361 362 363 364 365 366 367 368 369 370 371 | ** is returned if a malloc attempt fails. */ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pLock = 0; BtLock *pIter; assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); /* A connection with the read-uncommitted flag set will never try to ** obtain a read-lock using this function. The only read-lock obtained ** by a connection in read-uncommitted mode is on the sqlite_schema | > > | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | ** is returned if a malloc attempt fails. */ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pLock = 0; BtLock *pIter; SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock); assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); /* A connection with the read-uncommitted flag set will never try to ** obtain a read-lock using this function. The only read-lock obtained ** by a connection in read-uncommitted mode is on the sqlite_schema |
︙ | ︙ | |||
425 426 427 428 429 430 431 432 433 434 435 436 437 438 | BtShared *pBt = p->pBt; BtLock **ppIter = &pBt->pLock; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->sharable || 0==*ppIter ); assert( p->inTrans>0 ); while( *ppIter ){ BtLock *pLock = *ppIter; assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; assert( pLock->iTable!=1 || pLock==&p->lock ); | > > | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | BtShared *pBt = p->pBt; BtLock **ppIter = &pBt->pLock; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->sharable || 0==*ppIter ); assert( p->inTrans>0 ); SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0); while( *ppIter ){ BtLock *pLock = *ppIter; assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; assert( pLock->iTable!=1 || pLock==&p->lock ); |
︙ | ︙ | |||
463 464 465 466 467 468 469 470 471 472 473 474 475 476 | } /* ** This function changes all write-locks held by Btree p into read-locks. */ static void downgradeAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; if( pBt->pWriter==p ){ BtLock *pLock; pBt->pWriter = 0; pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); pLock->eLock = READ_LOCK; | > > > | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 | } /* ** This function changes all write-locks held by Btree p into read-locks. */ static void downgradeAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0); if( pBt->pWriter==p ){ BtLock *pLock; pBt->pWriter = 0; pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); pLock->eLock = READ_LOCK; |
︙ | ︙ | |||
5076 5077 5078 5079 5080 5081 5082 | ** means "not yet known" (the cache is lazily populated). */ if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; if( pCur->aOverflow==0 || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) ){ | | > > > | < > > > > > > > | 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 | ** means "not yet known" (the cache is lazily populated). */ if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; if( pCur->aOverflow==0 || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) ){ Pgno *aNew; if( sqlite3FaultSim(413) ){ aNew = 0; }else{ aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno)); } if( aNew==0 ){ return SQLITE_NOMEM_BKPT; }else{ pCur->aOverflow = aNew; } } memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); pCur->curFlags |= BTCF_ValidOvfl; }else{ /* Sanity check the validity of the overflow page cache */ assert( pCur->aOverflow[0]==nextPage || pCur->aOverflow[0]==0 || CORRUPT_DB ); assert( pCur->aOverflow[0]!=0 || pCur->aOverflow[offset/ovflSize]==0 ); /* If the overflow page-list cache has been allocated and the ** entry for the first required overflow page is valid, skip ** directly to it. */ if( pCur->aOverflow[offset/ovflSize] ){ iIdx = (offset/ovflSize); nextPage = pCur->aOverflow[iIdx]; |
︙ | ︙ | |||
5157 5158 5159 5160 5161 5162 5163 | ){ sqlite3_file *fd = sqlite3PagerFile(pBt->pPager); u8 aSave[4]; u8 *aWrite = &pBuf[-4]; assert( aWrite>=pBufStart ); /* due to (6) */ memcpy(aSave, aWrite, 4); rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); | < | 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 | ){ sqlite3_file *fd = sqlite3PagerFile(pBt->pPager); u8 aSave[4]; u8 *aWrite = &pBuf[-4]; assert( aWrite>=pBufStart ); /* due to (6) */ memcpy(aSave, aWrite, 4); rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); nextPage = get4byte(aWrite); memcpy(aWrite, aSave, 4); }else #endif { DbPage *pDbPage; |
︙ | ︙ | |||
5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 | }else if( rc==SQLITE_EMPTY ){ assert( pCur->pgnoRoot==0 || (pCur->pPage!=0 && pCur->pPage->nCell==0) ); *pRes = 1; rc = SQLITE_OK; } return rc; } /* Move the cursor to the last entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. */ static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){ int rc = moveToRoot(pCur); | > > > > > > > > > > > > > > > > > | 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 | }else if( rc==SQLITE_EMPTY ){ assert( pCur->pgnoRoot==0 || (pCur->pPage!=0 && pCur->pPage->nCell==0) ); *pRes = 1; rc = SQLITE_OK; } return rc; } #ifdef SQLITE_DEBUG /* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that ** this flags are true for a consistent database. ** ** This routine is is called from within assert() statements only. ** It is an internal verification routine and does not appear in production ** builds. */ static int cursorIsAtLastEntry(BtCursor *pCur){ int ii; for(ii=0; ii<pCur->iPage; ii++){ if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0; } return pCur->ix==pCur->pPage->nCell-1 && pCur->pPage->leaf!=0; } #endif /* Move the cursor to the last entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. */ static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){ int rc = moveToRoot(pCur); |
︙ | ︙ | |||
5598 5599 5600 5601 5602 5603 5604 | } int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); /* If the cursor already points to the last entry, this is a no-op. */ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ | < < < < < < < | < < < < | 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 | } int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); /* If the cursor already points to the last entry, this is a no-op. */ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); *pRes = 0; return SQLITE_OK; } return btreeLast(pCur, pRes); } /* Move the cursor so that it points to an entry in a table (a.k.a INTKEY) |
︙ | ︙ | |||
5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 | if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ if( pCur->info.nKey==intKey ){ *pRes = 0; return SQLITE_OK; } if( pCur->info.nKey<intKey ){ if( (pCur->curFlags & BTCF_AtLast)!=0 ){ *pRes = -1; return SQLITE_OK; } /* If the requested key is one more than the previous key, then ** try to get there using sqlite3BtreeNext() rather than a full ** binary search. This is an optimization only. The correct answer ** is still obtained without this case, only a little more slowly. */ | > | 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 | if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ if( pCur->info.nKey==intKey ){ *pRes = 0; return SQLITE_OK; } if( pCur->info.nKey<intKey ){ if( (pCur->curFlags & BTCF_AtLast)!=0 ){ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); *pRes = -1; return SQLITE_OK; } /* If the requested key is one more than the previous key, then ** try to get there using sqlite3BtreeNext() rather than a full ** binary search. This is an optimization only. The correct answer ** is still obtained without this case, only a little more slowly. */ |
︙ | ︙ | |||
6128 6129 6130 6131 6132 6133 6134 | i64 sqlite3BtreeRowCountEst(BtCursor *pCur){ i64 n; u8 i; assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); | | > | < | | 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 | i64 sqlite3BtreeRowCountEst(BtCursor *pCur){ i64 n; u8 i; assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); /* Currently this interface is only called by the OP_IfSizeBetween ** opcode and the OP_Count opcode with P3=1. In either case, ** the cursor will always be valid unless the btree is empty. */ if( pCur->eState!=CURSOR_VALID ) return 0; if( NEVER(pCur->pPage->leaf==0) ) return -1; n = pCur->pPage->nCell; for(i=0; i<pCur->iPage; i++){ n *= pCur->apPage[i]->nCell; } return n; |
︙ | ︙ | |||
6277 6278 6279 6280 6281 6282 6283 | if( CURSOR_SKIPNEXT==pCur->eState ){ pCur->eState = CURSOR_VALID; if( pCur->skipNext<0 ) return SQLITE_OK; } } pPage = pCur->pPage; | > | > > | 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 | if( CURSOR_SKIPNEXT==pCur->eState ){ pCur->eState = CURSOR_VALID; if( pCur->skipNext<0 ) return SQLITE_OK; } } pPage = pCur->pPage; if( sqlite3FaultSim(412) ) pPage->isInit = 0; if( !pPage->isInit ){ return SQLITE_CORRUPT_BKPT; } if( !pPage->leaf ){ int idx = pCur->ix; rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); if( rc ) return rc; rc = moveToRightmost(pCur); }else{ while( pCur->ix==0 ){ |
︙ | ︙ | |||
6950 6951 6952 6953 6954 6955 6956 | pPayload = &pCell[nHeader]; if( nPayload<=pPage->maxLocal ){ /* This is the common case where everything fits on the btree page ** and no overflow pages are required. */ n = nHeader + nPayload; testcase( n==3 ); testcase( n==4 ); | | > > > | 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 | pPayload = &pCell[nHeader]; if( nPayload<=pPage->maxLocal ){ /* This is the common case where everything fits on the btree page ** and no overflow pages are required. */ n = nHeader + nPayload; testcase( n==3 ); testcase( n==4 ); if( n<4 ){ n = 4; pPayload[nPayload] = 0; } *pnSize = n; assert( nSrc<=nPayload ); testcase( nSrc<nPayload ); memcpy(pPayload, pSrc, nSrc); memset(pPayload+nSrc, 0, nPayload-nSrc); return SQLITE_OK; } |
︙ | ︙ | |||
8256 8257 8258 8259 8260 8261 8262 | u8 *piEnd; VVA_ONLY( int nCellAtStart = b.nCell; ) /* Verify that all sibling pages are of the same "type" (table-leaf, ** table-interior, index-leaf, or index-interior). */ if( pOld->aData[0]!=apOld[0]->aData[0] ){ | | | 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 | u8 *piEnd; VVA_ONLY( int nCellAtStart = b.nCell; ) /* Verify that all sibling pages are of the same "type" (table-leaf, ** table-interior, index-leaf, or index-interior). */ if( pOld->aData[0]!=apOld[0]->aData[0] ){ rc = SQLITE_CORRUPT_PAGE(pOld); goto balance_cleanup; } /* Load b.apCell[] with pointers to all cells in pOld. If pOld ** contains overflow cells, include them in the b.apCell[] array ** in the correct spot. ** |
︙ | ︙ | |||
8280 8281 8282 8283 8284 8285 8286 | ** offset section of the btree page will be overwritten and we will no ** long be able to find the cells if a pointer to each cell is not saved ** first. */ memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); if( pOld->nOverflow>0 ){ if( NEVER(limit<pOld->aiOvfl[0]) ){ | | | 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 | ** offset section of the btree page will be overwritten and we will no ** long be able to find the cells if a pointer to each cell is not saved ** first. */ memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); if( pOld->nOverflow>0 ){ if( NEVER(limit<pOld->aiOvfl[0]) ){ rc = SQLITE_CORRUPT_PAGE(pOld); goto balance_cleanup; } limit = pOld->aiOvfl[0]; for(j=0; j<limit; j++){ b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell)); piCell += 2; b.nCell++; |
︙ | ︙ | |||
8923 8924 8925 8926 8927 8928 8929 | static int anotherValidCursor(BtCursor *pCur){ BtCursor *pOther; for(pOther=pCur->pBt->pCursor; pOther; pOther=pOther->pNext){ if( pOther!=pCur && pOther->eState==CURSOR_VALID && pOther->pPage==pCur->pPage ){ | | | 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 | static int anotherValidCursor(BtCursor *pCur){ BtCursor *pOther; for(pOther=pCur->pBt->pCursor; pOther; pOther=pOther->pNext){ if( pOther!=pCur && pOther->eState==CURSOR_VALID && pOther->pPage==pCur->pPage ){ return SQLITE_CORRUPT_PAGE(pCur->pPage); } } return SQLITE_OK; } /* ** The page that pCur currently points to has just been modified in |
︙ | ︙ | |||
8983 8984 8985 8986 8987 8988 8989 | }else{ break; } }else if( sqlite3PagerPageRefcount(pPage->pDbPage)>1 ){ /* The page being written is not a root page, and there is currently ** more than one reference to it. This only happens if the page is one ** of its own ancestor pages. Corruption. */ | | | 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 | }else{ break; } }else if( sqlite3PagerPageRefcount(pPage->pDbPage)>1 ){ /* The page being written is not a root page, and there is currently ** more than one reference to it. This only happens if the page is one ** of its own ancestor pages. Corruption. */ rc = SQLITE_CORRUPT_PAGE(pPage); }else{ MemPage * const pParent = pCur->apPage[iPage-1]; int const iIdx = pCur->aiIdx[iPage-1]; rc = sqlite3PagerWrite(pParent->pDbPage); if( rc==SQLITE_OK && pParent->nFree<0 ){ rc = btreeComputeFreeSpace(pParent); |
︙ | ︙ | |||
9147 9148 9149 9150 9151 9152 9153 | ovflPgno = get4byte(pCur->info.pPayload + iOffset); pBt = pPage->pBt; ovflPageSize = pBt->usableSize - 4; do{ rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); if( rc ) return rc; if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ | | | 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 | ovflPgno = get4byte(pCur->info.pPayload + iOffset); pBt = pPage->pBt; ovflPageSize = pBt->usableSize - 4; do{ rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); if( rc ) return rc; if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ rc = SQLITE_CORRUPT_PAGE(pPage); }else{ if( iOffset+ovflPageSize<(u32)nTotal ){ ovflPgno = get4byte(pPage->aData); }else{ ovflPageSize = nTotal - iOffset; } rc = btreeOverwriteContent(pPage, pPage->aData+4, pX, |
︙ | ︙ | |||
9175 9176 9177 9178 9179 9180 9181 | static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ MemPage *pPage = pCur->pPage; /* Page being written */ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd || pCur->info.pPayload < pPage->aData + pPage->cellOffset ){ | | | 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 | static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ MemPage *pPage = pCur->pPage; /* Page being written */ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd || pCur->info.pPayload < pPage->aData + pPage->cellOffset ){ return SQLITE_CORRUPT_PAGE(pPage); } if( pCur->info.nLocal==nTotal ){ /* The entire cell is local */ return btreeOverwriteContent(pPage, pCur->info.pPayload, pX, 0, pCur->info.nLocal); }else{ /* The cell contains overflow content */ |
︙ | ︙ | |||
9256 9257 9258 9259 9260 9261 9262 | if( rc ) return rc; if( loc && pCur->iPage<0 ){ /* This can only happen if the schema is corrupt such that there is more ** than one table or index with the same root page as used by the cursor. ** Which can only happen if the SQLITE_NoSchemaError flag was set when ** the schema was loaded. This cannot be asserted though, as a user might ** set the flag, load the schema, and then unset the flag. */ | | | 9325 9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 | if( rc ) return rc; if( loc && pCur->iPage<0 ){ /* This can only happen if the schema is corrupt such that there is more ** than one table or index with the same root page as used by the cursor. ** Which can only happen if the SQLITE_NoSchemaError flag was set when ** the schema was loaded. This cannot be asserted though, as a user might ** set the flag, load the schema, and then unset the flag. */ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); } } /* Ensure that the cursor is not in the CURSOR_FAULT state and that it ** points to a valid cell. */ if( pCur->eState>=CURSOR_REQUIRESEEK ){ |
︙ | ︙ | |||
9379 9380 9381 9382 9383 9384 9385 | pPage = pCur->pPage; assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); assert( pPage->leaf || !pPage->intKey ); if( pPage->nFree<0 ){ if( NEVER(pCur->eState>CURSOR_INVALID) ){ /* ^^^^^--- due to the moveToRoot() call above */ | | | > > > | 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469 9470 9471 9472 9473 9474 9475 9476 9477 9478 9479 9480 9481 9482 | pPage = pCur->pPage; assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); assert( pPage->leaf || !pPage->intKey ); if( pPage->nFree<0 ){ if( NEVER(pCur->eState>CURSOR_INVALID) ){ /* ^^^^^--- due to the moveToRoot() call above */ rc = SQLITE_CORRUPT_PAGE(pPage); }else{ rc = btreeComputeFreeSpace(pPage); } if( rc ) return rc; } TRACE(("INSERT: table=%u nkey=%lld ndata=%u page=%u %s\n", pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, loc==0 ? "overwrite" : "new entry")); assert( pPage->isInit || CORRUPT_DB ); newCell = p->pBt->pTmpSpace; assert( newCell!=0 ); assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); if( flags & BTREE_PREFORMAT ){ rc = SQLITE_OK; szNew = p->pBt->nPreformatSize; if( szNew<4 ){ szNew = 4; newCell[3] = 0; } if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){ CellInfo info; pPage->xParseCell(pPage, newCell, &info); if( info.nPayload!=info.nLocal ){ Pgno ovfl = get4byte(&newCell[szNew-4]); ptrmapPut(p->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc); if( NEVER(rc) ) goto end_insert; |
︙ | ︙ | |||
9418 9419 9420 9421 9422 9423 9424 | assert( szNew <= MX_CELL_SIZE(p->pBt) ); idx = pCur->ix; pCur->info.nSize = 0; if( loc==0 ){ CellInfo info; assert( idx>=0 ); if( idx>=pPage->nCell ){ | | | 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 | assert( szNew <= MX_CELL_SIZE(p->pBt) ); idx = pCur->ix; pCur->info.nSize = 0; if( loc==0 ){ CellInfo info; assert( idx>=0 ); if( idx>=pPage->nCell ){ return SQLITE_CORRUPT_PAGE(pPage); } rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ){ goto end_insert; } oldCell = findCell(pPage, idx); if( !pPage->leaf ){ |
︙ | ︙ | |||
9445 9446 9447 9448 9449 9450 9451 | ** calling dropCell() and insertCell(). ** ** This optimization cannot be used on an autovacuum database if the ** new entry uses overflow pages, as the insertCell() call below is ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ | | | | | 9517 9518 9519 9520 9521 9522 9523 9524 9525 9526 9527 9528 9529 9530 9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541 9542 9543 9544 | ** calling dropCell() and insertCell(). ** ** This optimization cannot be used on an autovacuum database if the ** new entry uses overflow pages, as the insertCell() call below is ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ return SQLITE_CORRUPT_PAGE(pPage); } if( oldCell+szNew > pPage->aDataEnd ){ return SQLITE_CORRUPT_PAGE(pPage); } memcpy(oldCell, newCell, szNew); return SQLITE_OK; } dropCell(pPage, idx, info.nSize, &rc); if( rc ) goto end_insert; }else if( loc<0 && pPage->nCell>0 ){ assert( pPage->leaf ); idx = ++pCur->ix; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); }else{ assert( pPage->leaf ); } rc = insertCellFast(pPage, idx, newCell, szNew); assert( pPage->nOverflow==0 || rc==SQLITE_OK ); assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); |
︙ | ︙ | |||
9488 9489 9490 9491 9492 9493 9494 | ** the b-tree if possible. If the cursor is left pointing to the last ** entry in the table, and the next row inserted has an integer key ** larger than the largest existing key, it is possible to insert the ** row without seeking the cursor. This can be a big performance boost. */ if( pPage->nOverflow ){ assert( rc==SQLITE_OK ); | | | 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 9570 9571 9572 9573 9574 | ** the b-tree if possible. If the cursor is left pointing to the last ** entry in the table, and the next row inserted has an integer key ** larger than the largest existing key, it is possible to insert the ** row without seeking the cursor. This can be a big performance boost. */ if( pPage->nOverflow ){ assert( rc==SQLITE_OK ); pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); rc = balance(pCur); /* Must make sure nOverflow is reset to zero even if the balance() ** fails. Internal data structure corruption will result otherwise. ** Also, set the cursor state to invalid. This stops saveCursorPosition() ** from trying to save the current position of the cursor. */ pCur->pPage->nOverflow = 0; |
︙ | ︙ | |||
9550 9551 9552 9553 9554 9555 9556 | }else{ aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload); } if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); nIn = pSrc->info.nLocal; aIn = pSrc->info.pPayload; if( aIn+nIn>pSrc->pPage->aDataEnd ){ | | | 9622 9623 9624 9625 9626 9627 9628 9629 9630 9631 9632 9633 9634 9635 9636 | }else{ aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload); } if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); nIn = pSrc->info.nLocal; aIn = pSrc->info.pPayload; if( aIn+nIn>pSrc->pPage->aDataEnd ){ return SQLITE_CORRUPT_PAGE(pSrc->pPage); } nRem = pSrc->info.nPayload; if( nIn==nRem && nIn<pDest->pPage->maxLocal ){ memcpy(aOut, aIn, nIn); pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace); return SQLITE_OK; }else{ |
︙ | ︙ | |||
9575 9576 9577 9578 9579 9580 9581 | if( nOut<pSrc->info.nPayload ){ pPgnoOut = &aOut[nOut]; pBt->nPreformatSize += 4; } if( nRem>nIn ){ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){ | | | 9647 9648 9649 9650 9651 9652 9653 9654 9655 9656 9657 9658 9659 9660 9661 | if( nOut<pSrc->info.nPayload ){ pPgnoOut = &aOut[nOut]; pBt->nPreformatSize += 4; } if( nRem>nIn ){ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){ return SQLITE_CORRUPT_PAGE(pSrc->pPage); } ovflIn = get4byte(&pSrc->info.pPayload[nIn]); } do { nRem -= nOut; do{ |
︙ | ︙ | |||
9671 9672 9673 9674 9675 9676 9677 | assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); if( pCur->eState!=CURSOR_VALID ){ if( pCur->eState>=CURSOR_REQUIRESEEK ){ rc = btreeRestoreCursorPosition(pCur); assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); if( rc || pCur->eState!=CURSOR_VALID ) return rc; }else{ | | | | | | 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 9769 9770 9771 9772 9773 | assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); if( pCur->eState!=CURSOR_VALID ){ if( pCur->eState>=CURSOR_REQUIRESEEK ){ rc = btreeRestoreCursorPosition(pCur); assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); if( rc || pCur->eState!=CURSOR_VALID ) return rc; }else{ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); } } assert( pCur->eState==CURSOR_VALID ); iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; if( pPage->nCell<=iCellIdx ){ return SQLITE_CORRUPT_PAGE(pPage); } pCell = findCell(pPage, iCellIdx); if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ return SQLITE_CORRUPT_PAGE(pPage); } if( pCell<&pPage->aCellIdx[pPage->nCell] ){ return SQLITE_CORRUPT_PAGE(pPage); } /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must ** be preserved following this delete operation. If the current delete ** will cause a b-tree rebalance, then this is done by saving the cursor ** key and leaving the cursor in CURSOR_REQUIRESEEK state before ** returning. |
︙ | ︙ | |||
9778 9779 9780 9781 9782 9783 9784 | } if( iCellDepth<pCur->iPage-1 ){ n = pCur->apPage[iCellDepth+1]->pgno; }else{ n = pCur->pPage->pgno; } pCell = findCell(pLeaf, pLeaf->nCell-1); | | | 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 | } if( iCellDepth<pCur->iPage-1 ){ n = pCur->apPage[iCellDepth+1]->pgno; }else{ n = pCur->pPage->pgno; } pCell = findCell(pLeaf, pLeaf->nCell-1); if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf); nCell = pLeaf->xCellSize(pLeaf, pCell); assert( MX_CELL_SIZE(pBt) >= nCell ); pTmp = pBt->pTmpSpace; assert( pTmp!=0 ); rc = sqlite3PagerWrite(pLeaf->pDbPage); if( rc==SQLITE_OK ){ rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n); |
︙ | ︙ | |||
9894 9895 9896 9897 9898 9899 9900 | /* Read the value of meta[3] from the database to determine where the ** root page of the new table should go. meta[3] is the largest root-page ** created so far, so the new root-page is (meta[3]+1). */ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); if( pgnoRoot>btreePagecount(pBt) ){ | | | 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 | /* Read the value of meta[3] from the database to determine where the ** root page of the new table should go. meta[3] is the largest root-page ** created so far, so the new root-page is (meta[3]+1). */ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); if( pgnoRoot>btreePagecount(pBt) ){ return SQLITE_CORRUPT_PGNO(pgnoRoot); } pgnoRoot++; /* The new root-page may not be allocated on a pointer-map page, or the ** PENDING_BYTE page. */ while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || |
︙ | ︙ | |||
9942 9943 9944 9945 9946 9947 9948 | /* Move the page currently at pgnoRoot to pgnoMove. */ rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); if( rc!=SQLITE_OK ){ return rc; } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ | | | 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 | /* Move the page currently at pgnoRoot to pgnoMove. */ rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); if( rc!=SQLITE_OK ){ return rc; } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ rc = SQLITE_CORRUPT_PGNO(pgnoRoot); } if( rc!=SQLITE_OK ){ releasePage(pRoot); return rc; } assert( eType!=PTRMAP_ROOTPAGE ); assert( eType!=PTRMAP_FREEPAGE ); |
︙ | ︙ | |||
10032 10033 10034 10035 10036 10037 10038 | unsigned char *pCell; int i; int hdr; CellInfo info; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ | | | | 10104 10105 10106 10107 10108 10109 10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122 10123 10124 10125 | unsigned char *pCell; int i; int hdr; CellInfo info; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ return SQLITE_CORRUPT_PGNO(pgno); } rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; if( (pBt->openFlags & BTREE_SINGLE)==0 && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) ){ rc = SQLITE_CORRUPT_PAGE(pPage); goto cleardatabasepage_out; } hdr = pPage->hdrOffset; for(i=0; i<pPage->nCell; i++){ pCell = findCell(pPage, i); if( !pPage->leaf ){ rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); |
︙ | ︙ | |||
10143 10144 10145 10146 10147 10148 10149 | MemPage *pPage = 0; BtShared *pBt = p->pBt; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->inTrans==TRANS_WRITE ); assert( iTable>=2 ); if( iTable>btreePagecount(pBt) ){ | | | 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 | MemPage *pPage = 0; BtShared *pBt = p->pBt; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->inTrans==TRANS_WRITE ); assert( iTable>=2 ); if( iTable>btreePagecount(pBt) ){ return SQLITE_CORRUPT_PGNO(iTable); } rc = sqlite3BtreeClearTable(p, iTable, 0); if( rc ) return rc; rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); if( NEVER(rc) ){ releasePage(pPage); |
︙ | ︙ | |||
10737 10738 10739 10740 10741 10742 10743 10744 10745 10746 10747 10748 10749 10750 | contentOffset = get2byteNotZero(&data[hdr+5]); assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the ** number of cells on the page. */ nCell = get2byte(&data[hdr+3]); assert( pPage->nCell==nCell ); /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page ** immediately follows the b-tree page header. */ cellStart = hdr + 12 - 4*pPage->leaf; assert( pPage->aCellIdx==&data[cellStart] ); pCellIdx = &data[cellStart + 2*(nCell-1)]; | > > > | 10809 10810 10811 10812 10813 10814 10815 10816 10817 10818 10819 10820 10821 10822 10823 10824 10825 | contentOffset = get2byteNotZero(&data[hdr+5]); assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the ** number of cells on the page. */ nCell = get2byte(&data[hdr+3]); assert( pPage->nCell==nCell ); if( pPage->leaf || pPage->intKey==0 ){ pCheck->nRow += nCell; } /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page ** immediately follows the b-tree page header. */ cellStart = hdr + 12 - 4*pPage->leaf; assert( pPage->aCellIdx==&data[cellStart] ); pCellIdx = &data[cellStart + 2*(nCell-1)]; |
︙ | ︙ | |||
10848 10849 10850 10851 10852 10853 10854 10855 10856 10857 10858 10859 10860 10861 | for(i=nCell-1; i>=0; i--){ u32 size; pc = get2byteAligned(&data[cellStart+i*2]); size = pPage->xCellSize(pPage, &data[pc]); btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); } } /* Add the freeblocks to the min-heap ** ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header ** is the offset of the first freeblock, or zero if there are no ** freeblocks on the page. */ i = get2byte(&data[hdr+1]); | > | 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 | for(i=nCell-1; i>=0; i--){ u32 size; pc = get2byteAligned(&data[cellStart+i*2]); size = pPage->xCellSize(pPage, &data[pc]); btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); } } assert( heap!=0 ); /* Add the freeblocks to the min-heap ** ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header ** is the offset of the first freeblock, or zero if there are no ** freeblocks on the page. */ i = get2byte(&data[hdr+1]); |
︙ | ︙ | |||
10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 | ** the unverified btrees. Except, if aRoot[1] is 1, then the freelist ** checks are still performed. */ int sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr, /* OUT: Write number of errors seen to this variable */ char **pzOut /* OUT: Write the error message string here */ ){ Pgno i; IntegrityCk sCheck; BtShared *pBt = p->pBt; u64 savedDbFlags = pBt->db->flags; char zErr[100]; int bPartial = 0; /* True if not checking all btrees */ int bCkFreelist = 1; /* True to scan the freelist */ VVA_ONLY( int nRef ); assert( nRoot>0 ); /* aRoot[0]==0 means this is a partial check */ if( aRoot[0]==0 ){ assert( nRoot>1 ); bPartial = 1; if( aRoot[1]!=1 ) bCkFreelist = 0; } | > > > | 11023 11024 11025 11026 11027 11028 11029 11030 11031 11032 11033 11034 11035 11036 11037 11038 11039 11040 11041 11042 11043 11044 11045 11046 11047 11048 11049 11050 11051 11052 11053 | ** the unverified btrees. Except, if aRoot[1] is 1, then the freelist ** checks are still performed. */ int sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ Mem *aCnt, /* Memory cells to write counts for each tree to */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr, /* OUT: Write number of errors seen to this variable */ char **pzOut /* OUT: Write the error message string here */ ){ Pgno i; IntegrityCk sCheck; BtShared *pBt = p->pBt; u64 savedDbFlags = pBt->db->flags; char zErr[100]; int bPartial = 0; /* True if not checking all btrees */ int bCkFreelist = 1; /* True to scan the freelist */ VVA_ONLY( int nRef ); assert( nRoot>0 ); assert( aCnt!=0 ); /* aRoot[0]==0 means this is a partial check */ if( aRoot[0]==0 ){ assert( nRoot>1 ); bPartial = 1; if( aRoot[1]!=1 ) bCkFreelist = 0; } |
︙ | ︙ | |||
11033 11034 11035 11036 11037 11038 11039 | ); } } #endif testcase( pBt->db->flags & SQLITE_CellSizeCk ); pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; for(i=0; (int)i<nRoot && sCheck.mxErr; i++){ | > > | < | | | | | > > | 11112 11113 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124 11125 11126 11127 11128 11129 11130 11131 11132 11133 11134 11135 11136 11137 | ); } } #endif testcase( pBt->db->flags & SQLITE_CellSizeCk ); pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; for(i=0; (int)i<nRoot && sCheck.mxErr; i++){ sCheck.nRow = 0; if( aRoot[i] ){ i64 notUsed; #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){ checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); } #endif sCheck.v0 = aRoot[i]; checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); } sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow); } pBt->db->flags = savedDbFlags; /* Make sure every page in the file is referenced */ if( !bPartial ){ for(i=1; i<=sCheck.nCkPage && sCheck.mxErr; i++){ |
︙ | ︙ |
Changes to src/btree.h.
︙ | ︙ | |||
327 328 329 330 331 332 333 334 335 336 337 338 339 340 | u32 sqlite3BtreePayloadSize(BtCursor*); sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); int sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr, /* OUT: Write number of errors seen to this variable */ char **pzOut /* OUT: Write the error message string here */ ); struct Pager *sqlite3BtreePager(Btree*); i64 sqlite3BtreeRowCountEst(BtCursor*); | > | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | u32 sqlite3BtreePayloadSize(BtCursor*); sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); int sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr, /* OUT: Write number of errors seen to this variable */ char **pzOut /* OUT: Write the error message string here */ ); struct Pager *sqlite3BtreePager(Btree*); i64 sqlite3BtreeRowCountEst(BtCursor*); |
︙ | ︙ |
Changes to src/btreeInt.h.
︙ | ︙ | |||
703 704 705 706 707 708 709 710 711 712 713 714 715 716 | const char *zPfx; /* Error message prefix */ Pgno v0; /* Value for first %u substitution in zPfx (root page) */ Pgno v1; /* Value for second %u substitution in zPfx (current pg) */ int v2; /* Value for third %d substitution in zPfx */ StrAccum errMsg; /* Accumulate the error message text here */ u32 *heap; /* Min-heap used for analyzing cell coverage */ sqlite3 *db; /* Database connection running the check */ }; /* ** Routines to read or write a two- and four-byte big-endian integer values. */ #define get2byte(x) ((x)[0]<<8 | (x)[1]) #define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) | > | 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 | const char *zPfx; /* Error message prefix */ Pgno v0; /* Value for first %u substitution in zPfx (root page) */ Pgno v1; /* Value for second %u substitution in zPfx (current pg) */ int v2; /* Value for third %d substitution in zPfx */ StrAccum errMsg; /* Accumulate the error message text here */ u32 *heap; /* Min-heap used for analyzing cell coverage */ sqlite3 *db; /* Database connection running the check */ i64 nRow; /* Number of rows visited in current tree */ }; /* ** Routines to read or write a two- and four-byte big-endian integer values. */ #define get2byte(x) ((x)[0]<<8 | (x)[1]) #define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
185 186 187 188 189 190 191 | sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addrRewind); } } sqlite3VdbeAddOp0(v, OP_Halt); | | | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addrRewind); } } sqlite3VdbeAddOp0(v, OP_Halt); #if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE) if( pParse->nTableLock>0 && db->init.busy==0 ){ sqlite3UserAuthInit(db); if( db->auth.authLevel<UAUTH_User ){ sqlite3ErrorMsg(pParse, "user not authenticated"); pParse->rc = SQLITE_AUTH_USER; return; } |
︙ | ︙ | |||
1651 1652 1653 1654 1655 1656 1657 | char sqlite3AffinityType(const char *zIn, Column *pCol){ u32 h = 0; char aff = SQLITE_AFF_NUMERIC; const char *zChar = 0; assert( zIn!=0 ); while( zIn[0] ){ | > | | 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 | char sqlite3AffinityType(const char *zIn, Column *pCol){ u32 h = 0; char aff = SQLITE_AFF_NUMERIC; const char *zChar = 0; assert( zIn!=0 ); while( zIn[0] ){ u8 x = *(u8*)zIn; h = (h<<8) + sqlite3UpperToLower[x]; zIn++; if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */ aff = SQLITE_AFF_TEXT; zChar = zIn; }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){ /* CLOB */ aff = SQLITE_AFF_TEXT; }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){ /* TEXT */ |
︙ | ︙ | |||
2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 | SelectDest dest; /* Where the SELECT should store results */ int regYield; /* Register holding co-routine entry-point */ int addrTop; /* Top of the co-routine */ int regRec; /* A record to be insert into the new table */ int regRowid; /* Rowid of the next row to insert */ int addrInsLoop; /* Top of the loop for inserting rows */ Table *pSelTab; /* A table that describes the SELECT results */ if( IN_SPECIAL_PARSE ){ pParse->rc = SQLITE_ERROR; pParse->nErr++; return; } regYield = ++pParse->nMem; regRec = ++pParse->nMem; regRowid = ++pParse->nMem; | > > < | < | | | | 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 | SelectDest dest; /* Where the SELECT should store results */ int regYield; /* Register holding co-routine entry-point */ int addrTop; /* Top of the co-routine */ int regRec; /* A record to be insert into the new table */ int regRowid; /* Rowid of the next row to insert */ int addrInsLoop; /* Top of the loop for inserting rows */ Table *pSelTab; /* A table that describes the SELECT results */ int iCsr; /* Write cursor on the new table */ if( IN_SPECIAL_PARSE ){ pParse->rc = SQLITE_ERROR; pParse->nErr++; return; } iCsr = pParse->nTab++; regYield = ++pParse->nMem; regRec = ++pParse->nMem; regRowid = ++pParse->nMem; sqlite3MayAbort(pParse); sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->regRoot, iDb); sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG); addrTop = sqlite3VdbeCurrentAddr(v) + 1; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); if( pParse->nErr ) return; pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect, SQLITE_AFF_BLOB); if( pSelTab==0 ) return; assert( p->aCol==0 ); p->nCol = p->nNVCol = pSelTab->nCol; p->aCol = pSelTab->aCol; pSelTab->nCol = 0; pSelTab->aCol = 0; sqlite3DeleteTable(db, pSelTab); sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); sqlite3Select(pParse, pSelect, &dest); if( pParse->nErr ) return; sqlite3VdbeEndCoroutine(v, regYield); sqlite3VdbeJumpHere(v, addrTop - 1); addrInsLoop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec); sqlite3TableAffinity(v, p, 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iCsr, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, iCsr, regRec, regRowid); sqlite3VdbeGoto(v, addrInsLoop); sqlite3VdbeJumpHere(v, addrInsLoop); sqlite3VdbeAddOp1(v, OP_Close, iCsr); } /* Compute the complete text of the CREATE statement */ if( pSelect ){ zStmt = createTableStmt(db, p); }else{ Token *pEnd2 = tabOpts ? &pParse->sLastToken : pEnd; |
︙ | ︙ | |||
2918 2919 2920 2921 2922 2923 2924 | /* Reparse everything to update our internal data structures */ sqlite3VdbeAddParseSchemaOp(v, iDb, sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0); /* Test for cycles in generated columns and illegal expressions ** in CHECK constraints and in DEFAULT clauses. */ if( p->tabFlags & TF_HasGenerated ){ | | < < < | 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 | /* Reparse everything to update our internal data structures */ sqlite3VdbeAddParseSchemaOp(v, iDb, sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0); /* Test for cycles in generated columns and illegal expressions ** in CHECK constraints and in DEFAULT clauses. */ if( p->tabFlags & TF_HasGenerated ){ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } } /* Add the table to the in-memory representation of the database. */ if( db->init.busy ){ Table *pOld; Schema *pSchema = p->pSchema; |
︙ | ︙ | |||
3001 3002 3003 3004 3005 3006 3007 | p = pParse->pNewTable; if( p==0 || pParse->nErr ) goto create_view_fail; /* Legacy versions of SQLite allowed the use of the magic "rowid" column ** on a view, even though views do not have rowids. The following flag ** setting fixes this problem. But the fix can be disabled by compiling ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that | | > | > > | | 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 | p = pParse->pNewTable; if( p==0 || pParse->nErr ) goto create_view_fail; /* Legacy versions of SQLite allowed the use of the magic "rowid" column ** on a view, even though views do not have rowids. The following flag ** setting fixes this problem. But the fix can be disabled by compiling ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that ** depend upon the old buggy behavior. The ability can also be toggled ** using sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW,...) */ #ifdef SQLITE_ALLOW_ROWID_IN_VIEW p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */ #else p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */ #endif sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = sqlite3SchemaToIndex(db, p->pSchema); sqlite3FixInit(&sFix, pParse, iDb, "view", pName); if( sqlite3FixSelect(&sFix, pSelect) ) goto create_view_fail; |
︙ | ︙ | |||
5516 5517 5518 5519 5520 5521 5522 | } sqlite3DbFree(db, zColl); } iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); if( iDb<0 ) return; z = sqlite3NameFromToken(db, pObjName); if( z==0 ) return; | | > | 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 | } sqlite3DbFree(db, zColl); } iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); if( iDb<0 ) return; z = sqlite3NameFromToken(db, pObjName); if( z==0 ) return; zDb = pName2->n ? db->aDb[iDb].zDbSName : 0; pTab = sqlite3FindTable(db, z, zDb); if( pTab ){ reindexTable(pParse, pTab, 0); sqlite3DbFree(db, z); return; } pIndex = sqlite3FindIndex(db, z, zDb); sqlite3DbFree(db, z); if( pIndex ){ iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema); sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3RefillIndex(pParse, pIndex, -1); return; } sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); } #endif |
︙ | ︙ |
Changes to src/ctime.c.
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC "4_BYTE_ALIGNED_MALLOC", #endif #ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN # if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1 "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), # endif #endif #ifdef SQLITE_ALLOW_URI_AUTHORITY "ALLOW_URI_AUTHORITY", #endif #ifdef SQLITE_ATOMIC_INTRINSICS "ATOMIC_INTRINSICS=" CTIMEOPT_VAL(SQLITE_ATOMIC_INTRINSICS), #endif | > > > | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC "4_BYTE_ALIGNED_MALLOC", #endif #ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN # if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1 "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), # endif #endif #ifdef SQLITE_ALLOW_ROWID_IN_VIEW "ALLOW_ROWID_IN_VIEW", #endif #ifdef SQLITE_ALLOW_URI_AUTHORITY "ALLOW_URI_AUTHORITY", #endif #ifdef SQLITE_ATOMIC_INTRINSICS "ATOMIC_INTRINSICS=" CTIMEOPT_VAL(SQLITE_ATOMIC_INTRINSICS), #endif |
︙ | ︙ |
Changes to src/date.c.
︙ | ︙ | |||
67 68 69 70 71 72 73 | struct DateTime { sqlite3_int64 iJD; /* The julian day number times 86400000 */ int Y, M, D; /* Year, month, and day */ int h, m; /* Hour and minutes */ int tz; /* Timezone offset in minutes */ double s; /* Seconds */ char validJD; /* True (1) if iJD is valid */ | < < | > | | > > | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | struct DateTime { sqlite3_int64 iJD; /* The julian day number times 86400000 */ int Y, M, D; /* Year, month, and day */ int h, m; /* Hour and minutes */ int tz; /* Timezone offset in minutes */ double s; /* Seconds */ char validJD; /* True (1) if iJD is valid */ char validYMD; /* True (1) if Y,M,D are valid */ char validHMS; /* True (1) if h,m,s are valid */ char nFloor; /* Days to implement "floor" */ unsigned rawS : 1; /* Raw numeric value stored in s */ unsigned isError : 1; /* An overflow has occurred */ unsigned useSubsec : 1; /* Display subsecond precision */ unsigned isUtc : 1; /* Time is known to be UTC */ unsigned isLocal : 1; /* Time is known to be localtime */ }; /* ** Convert zDate into one or more integers according to the conversion ** specifier zFormat. ** |
︙ | ︙ | |||
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | c = *zDate; if( c=='-' ){ sgn = -1; }else if( c=='+' ){ sgn = +1; }else if( c=='Z' || c=='z' ){ zDate++; goto zulu_time; }else{ return c!=0; } zDate++; if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){ return 1; } zDate += 5; p->tz = sgn*(nMn + nHr*60); zulu_time: while( sqlite3Isspace(*zDate) ){ zDate++; } | > > < | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | c = *zDate; if( c=='-' ){ sgn = -1; }else if( c=='+' ){ sgn = +1; }else if( c=='Z' || c=='z' ){ zDate++; p->isLocal = 0; p->isUtc = 1; goto zulu_time; }else{ return c!=0; } zDate++; if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){ return 1; } zDate += 5; p->tz = sgn*(nMn + nHr*60); zulu_time: while( sqlite3Isspace(*zDate) ){ zDate++; } return *zDate!=0; } /* ** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF. ** The HH, MM, and SS must each be exactly 2 digits. The ** fractional seconds FFFF can be one or more digits. |
︙ | ︙ | |||
227 228 229 230 231 232 233 | p->validJD = 0; p->rawS = 0; p->validHMS = 1; p->h = h; p->m = m; p->s = s + ms; if( parseTimezone(zDate, p) ) return 1; | < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | p->validJD = 0; p->rawS = 0; p->validHMS = 1; p->h = h; p->m = m; p->s = s + ms; if( parseTimezone(zDate, p) ) return 1; return 0; } /* ** Put the DateTime object into its error state. */ static void datetimeError(DateTime *p){ |
︙ | ︙ | |||
274 275 276 277 278 279 280 | B = 2 - A + (A/4); X1 = 36525*(Y+4716)/100; X2 = 306001*(M+1)/10000; p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); p->validJD = 1; if( p->validHMS ){ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5); | | | > > > > > > > > > > > > > > > > > > > > > > > > > | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | B = 2 - A + (A/4); X1 = 36525*(Y+4716)/100; X2 = 306001*(M+1)/10000; p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); p->validJD = 1; if( p->validHMS ){ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5); if( p->tz ){ p->iJD -= p->tz*60000; p->validYMD = 0; p->validHMS = 0; p->tz = 0; p->isUtc = 1; p->isLocal = 0; } } } /* ** Given the YYYY-MM-DD information current in p, determine if there ** is day-of-month overflow and set nFloor to the number of days that ** would need to be subtracted from the date in order to bring the ** date back to the end of the month. */ static void computeFloor(DateTime *p){ assert( p->validYMD || p->isError ); assert( p->D>=0 && p->D<=31 ); assert( p->M>=0 && p->M<=12 ); if( p->D<=28 ){ p->nFloor = 0; }else if( (1<<p->M) & 0x15aa ){ p->nFloor = 0; }else if( p->M!=2 ){ p->nFloor = (p->D==31); }else if( p->Y%4!=0 || (p->Y%100==0 && p->Y%400!=0) ){ p->nFloor = p->D - 28; }else{ p->nFloor = p->D - 29; } } /* ** Parse dates of the form ** ** YYYY-MM-DD HH:MM:SS.FFF ** YYYY-MM-DD HH:MM:SS ** YYYY-MM-DD HH:MM |
︙ | ︙ | |||
321 322 323 324 325 326 327 | return 1; } p->validJD = 0; p->validYMD = 1; p->Y = neg ? -Y : Y; p->M = M; p->D = D; | > | > > > > > > | 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | return 1; } p->validJD = 0; p->validYMD = 1; p->Y = neg ? -Y : Y; p->M = M; p->D = D; computeFloor(p); if( p->tz ){ computeJD(p); } return 0; } static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */ /* ** Set the time to the current time reported by the VFS. ** ** Return the number of errors. */ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ p->iJD = sqlite3StmtCurrentTime(context); if( p->iJD>0 ){ p->validJD = 1; p->isUtc = 1; p->isLocal = 0; clearYMD_HMS_TZ(p); return 0; }else{ return 1; } } /* |
︙ | ︙ | |||
474 475 476 477 478 479 480 | /* ** Clear the YMD and HMS and the TZ */ static void clearYMD_HMS_TZ(DateTime *p){ p->validYMD = 0; p->validHMS = 0; | | | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | /* ** Clear the YMD and HMS and the TZ */ static void clearYMD_HMS_TZ(DateTime *p){ p->validYMD = 0; p->validHMS = 0; p->tz = 0; } #ifndef SQLITE_OMIT_LOCALTIME /* ** On recent Windows platforms, the localtime_s() function is available ** as part of the "Secure CRT". It is essentially equivalent to ** localtime_r() available under most POSIX platforms, except that the |
︙ | ︙ | |||
606 607 608 609 610 611 612 | p->h = sLocal.tm_hour; p->m = sLocal.tm_min; p->s = sLocal.tm_sec + (p->iJD%1000)*0.001; p->validYMD = 1; p->validHMS = 1; p->validJD = 0; p->rawS = 0; | | | | | | | | | 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 | p->h = sLocal.tm_hour; p->m = sLocal.tm_min; p->s = sLocal.tm_sec + (p->iJD%1000)*0.001; p->validYMD = 1; p->validHMS = 1; p->validJD = 0; p->rawS = 0; p->tz = 0; p->isError = 0; return SQLITE_OK; } #endif /* SQLITE_OMIT_LOCALTIME */ /* ** The following table defines various date transformations of the form ** ** 'NNN days' ** ** Where NNN is an arbitrary floating-point number and "days" can be one ** of several units of time. */ static const struct { u8 nName; /* Length of the name */ char zName[7]; /* Name of the transformation */ float rLimit; /* Maximum NNN value for this transform */ float rXform; /* Constant used for this transform */ } aXformType[] = { /* 0 */ { 6, "second", 4.6427e+14, 1.0 }, /* 1 */ { 6, "minute", 7.7379e+12, 60.0 }, /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 }, /* 3 */ { 3, "day", 5373485.0, 86400.0 }, /* 4 */ { 5, "month", 176546.0, 30.0*86400.0 }, /* 5 */ { 4, "year", 14713.0, 365.0*86400.0 }, }; /* ** If the DateTime p is raw number, try to figure out if it is ** a julian day number of a unix timestamp. Set the p value ** appropriately. */ |
︙ | ︙ | |||
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | ** ** NNN days ** NNN hours ** NNN minutes ** NNN.NNNN seconds ** NNN months ** NNN years ** start of month ** start of year ** start of week ** start of day ** weekday N ** unixepoch ** localtime ** utc ** ** Return 0 on success and 1 if there is any kind of error. If the error ** is in a system call (i.e. localtime()), then an error message is written ** to context pCtx. If the error is an unrecognized modifier, no error is ** written to pCtx. */ static int parseModifier( | > > > > > > | 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 | ** ** NNN days ** NNN hours ** NNN minutes ** NNN.NNNN seconds ** NNN months ** NNN years ** +/-YYYY-MM-DD HH:MM:SS.SSS ** ceiling ** floor ** start of month ** start of year ** start of week ** start of day ** weekday N ** unixepoch ** auto ** localtime ** utc ** subsec ** subsecond ** ** Return 0 on success and 1 if there is any kind of error. If the error ** is in a system call (i.e. localtime()), then an error message is written ** to context pCtx. If the error is an unrecognized modifier, no error is ** written to pCtx. */ static int parseModifier( |
︙ | ︙ | |||
700 701 702 703 704 705 706 707 708 709 710 711 712 713 | */ if( sqlite3_stricmp(z, "auto")==0 ){ if( idx>1 ) return 1; /* IMP: R-33611-57934 */ autoAdjustDate(p); rc = 0; } break; } case 'j': { /* ** julianday ** ** Always interpret the prior number as a julian-day value. If this ** is not the first modifier, or if the prior argument is not a numeric | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | */ if( sqlite3_stricmp(z, "auto")==0 ){ if( idx>1 ) return 1; /* IMP: R-33611-57934 */ autoAdjustDate(p); rc = 0; } break; } case 'c': { /* ** ceiling ** ** Resolve day-of-month overflow by rolling forward into the next ** month. As this is the default action, this modifier is really ** a no-op that is only included for symmetry. See "floor". */ if( sqlite3_stricmp(z, "ceiling")==0 ){ computeJD(p); clearYMD_HMS_TZ(p); rc = 0; p->nFloor = 0; } break; } case 'f': { /* ** floor ** ** Resolve day-of-month overflow by rolling back to the end of the ** previous month. */ if( sqlite3_stricmp(z, "floor")==0 ){ computeJD(p); p->iJD -= p->nFloor*86400000; clearYMD_HMS_TZ(p); rc = 0; } break; } case 'j': { /* ** julianday ** ** Always interpret the prior number as a julian-day value. If this ** is not the first modifier, or if the prior argument is not a numeric |
︙ | ︙ | |||
727 728 729 730 731 732 733 | case 'l': { /* localtime ** ** Assuming the current time value is UTC (a.k.a. GMT), shift it to ** show local time. */ if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){ | | > > | 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 | case 'l': { /* localtime ** ** Assuming the current time value is UTC (a.k.a. GMT), shift it to ** show local time. */ if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){ rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx); p->isUtc = 0; p->isLocal = 1; } break; } #endif case 'u': { /* ** unixepoch |
︙ | ︙ | |||
752 753 754 755 756 757 758 | p->validJD = 1; p->rawS = 0; rc = 0; } } #ifndef SQLITE_OMIT_LOCALTIME else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ | | | 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 | p->validJD = 1; p->rawS = 0; rc = 0; } } #ifndef SQLITE_OMIT_LOCALTIME else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ if( p->isUtc==0 ){ i64 iOrigJD; /* Original localtime */ i64 iGuess; /* Guess at the corresponding utc time */ int cnt = 0; /* Safety to prevent infinite loop */ i64 iErr; /* Guess is off by this much */ computeJD(p); iGuess = iOrigJD = p->iJD; |
︙ | ︙ | |||
775 776 777 778 779 780 781 | if( rc ) return rc; computeJD(&new); iErr = new.iJD - iOrigJD; }while( iErr && cnt++<3 ); memset(p, 0, sizeof(*p)); p->iJD = iGuess; p->validJD = 1; | | > | | 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 | if( rc ) return rc; computeJD(&new); iErr = new.iJD - iOrigJD; }while( iErr && cnt++<3 ); memset(p, 0, sizeof(*p)); p->iJD = iGuess; p->validJD = 1; p->isUtc = 1; p->isLocal = 0; } rc = SQLITE_OK; } #endif break; } case 'w': { /* ** weekday N ** ** Move the date to the same time on the next occurrence of ** weekday N where 0==Sunday, 1==Monday, and so forth. If the ** date is already on the appropriate weekday, this is a no-op. */ if( sqlite3_strnicmp(z, "weekday ", 8)==0 && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0 && r>=0.0 && r<7.0 && (n=(int)r)==r ){ sqlite3_int64 Z; computeYMD_HMS(p); p->tz = 0; p->validJD = 0; computeJD(p); Z = ((p->iJD + 129600000)/86400000) % 7; if( Z>n ) Z -= 7; p->iJD += (n - Z)*86400000; clearYMD_HMS_TZ(p); rc = 0; |
︙ | ︙ | |||
835 836 837 838 839 840 841 | if( !p->validJD && !p->validYMD && !p->validHMS ) break; z += 9; computeYMD(p); p->validHMS = 1; p->h = p->m = 0; p->s = 0.0; p->rawS = 0; | | | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 | if( !p->validJD && !p->validYMD && !p->validHMS ) break; z += 9; computeYMD(p); p->validHMS = 1; p->h = p->m = 0; p->s = 0.0; p->rawS = 0; p->tz = 0; p->validJD = 0; if( sqlite3_stricmp(z,"month")==0 ){ p->D = 1; rc = 0; }else if( sqlite3_stricmp(z,"year")==0 ){ p->M = 1; p->D = 1; |
︙ | ︙ | |||
906 907 908 909 910 911 912 913 914 915 916 917 918 919 | }else{ p->Y += Y; p->M += M; } x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; p->Y += x; p->M -= x*12; computeJD(p); p->validHMS = 0; p->validYMD = 0; p->iJD += (i64)D*86400000; if( z[11]==0 ){ rc = 0; break; | > | 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 | }else{ p->Y += Y; p->M += M; } x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; p->Y += x; p->M -= x*12; computeFloor(p); computeJD(p); p->validHMS = 0; p->validYMD = 0; p->iJD += (i64)D*86400000; if( z[11]==0 ){ rc = 0; break; |
︙ | ︙ | |||
952 953 954 955 956 957 958 | } /* If control reaches this point, it means the transformation is ** one of the forms like "+NNN days". */ z += n; while( sqlite3Isspace(*z) ) z++; n = sqlite3Strlen30(z); | | > | > | > > | 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 | } /* If control reaches this point, it means the transformation is ** one of the forms like "+NNN days". */ z += n; while( sqlite3Isspace(*z) ) z++; n = sqlite3Strlen30(z); if( n<3 || n>10 ) break; if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; computeJD(p); assert( rc==1 ); rRounder = r<0 ? -0.5 : +0.5; p->nFloor = 0; for(i=0; i<ArraySize(aXformType); i++){ if( aXformType[i].nName==n && sqlite3_strnicmp(aXformType[i].zName, z, n)==0 && r>-aXformType[i].rLimit && r<aXformType[i].rLimit ){ switch( i ){ case 4: { /* Special processing to add months */ assert( strcmp(aXformType[4].zName,"month")==0 ); computeYMD_HMS(p); p->M += (int)r; x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; p->Y += x; p->M -= x*12; computeFloor(p); p->validJD = 0; r -= (int)r; break; } case 5: { /* Special processing to add years */ int y = (int)r; assert( strcmp(aXformType[5].zName,"year")==0 ); computeYMD_HMS(p); assert( p->M>=0 && p->M<=12 ); p->Y += y; computeFloor(p); p->validJD = 0; r -= (int)r; break; } } computeJD(p); p->iJD += (sqlite3_int64)(r*1000.0*aXformType[i].rXform + rRounder); |
︙ | ︙ | |||
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | zBuf[0] = '-'; sqlite3_result_text(context, zBuf, 11, SQLITE_TRANSIENT); }else{ sqlite3_result_text(context, &zBuf[1], 10, SQLITE_TRANSIENT); } } } /* ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) ** ** Return a string described by FORMAT. Conversions as follows: ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > > > > > | > > | | 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 | zBuf[0] = '-'; sqlite3_result_text(context, zBuf, 11, SQLITE_TRANSIENT); }else{ sqlite3_result_text(context, &zBuf[1], 10, SQLITE_TRANSIENT); } } } /* ** Compute the number of days after the most recent January 1. ** ** In other words, compute the zero-based day number for the ** current year: ** ** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ... ** Dec31 = 364 or 365. */ static int daysAfterJan01(DateTime *pDate){ DateTime jan01 = *pDate; assert( jan01.validYMD ); assert( jan01.validHMS ); assert( pDate->validJD ); jan01.validJD = 0; jan01.M = 1; jan01.D = 1; computeJD(&jan01); return (int)((pDate->iJD-jan01.iJD+43200000)/86400000); } /* ** Return the number of days after the most recent Monday. ** ** In other words, return the day of the week according ** to this code: ** ** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday. */ static int daysAfterMonday(DateTime *pDate){ assert( pDate->validJD ); return (int)((pDate->iJD+43200000)/86400000) % 7; } /* ** Return the number of days after the most recent Sunday. ** ** In other words, return the day of the week according ** to this code: ** ** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday */ static int daysAfterSunday(DateTime *pDate){ assert( pDate->validJD ); return (int)((pDate->iJD+129600000)/86400000) % 7; } /* ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) ** ** Return a string described by FORMAT. Conversions as follows: ** ** %d day of month 01-31 ** %e day of month 1-31 ** %f ** fractional seconds SS.SSS ** %F ISO date. YYYY-MM-DD ** %G ISO year corresponding to %V 0000-9999. ** %g 2-digit ISO year corresponding to %V 00-99 ** %H hour 00-24 ** %k hour 0-24 (leading zero converted to space) ** %I hour 01-12 ** %j day of year 001-366 ** %J ** julian day number ** %l hour 1-12 (leading zero converted to space) ** %m month 01-12 ** %M minute 00-59 ** %p "am" or "pm" ** %P "AM" or "PM" ** %R time as HH:MM ** %s seconds since 1970-01-01 ** %S seconds 00-59 ** %T time as HH:MM:SS ** %u day of week 1-7 Monday==1, Sunday==7 ** %w day of week 0-6 Sunday==0, Monday==1 ** %U week of year 00-53 (First Sunday is start of week 01) ** %V week of year 01-53 (First week containing Thursday is week 01) ** %W week of year 00-53 (First Monday is start of week 01) ** %Y year 0000-9999 ** %% % */ static void strftimeFunc( sqlite3_context *context, int argc, sqlite3_value **argv |
︙ | ︙ | |||
1284 1285 1286 1287 1288 1289 1290 | cf = zFmt[i]; switch( cf ){ case 'd': /* Fall thru */ case 'e': { sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D); break; } | | > > > > > > > > > > > > > > > < | < < < < < < < < < < < < | < | | 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 | cf = zFmt[i]; switch( cf ){ case 'd': /* Fall thru */ case 'e': { sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D); break; } case 'f': { /* Fractional seconds. (Non-standard) */ double s = x.s; if( s>59.999 ) s = 59.999; sqlite3_str_appendf(&sRes, "%06.3f", s); break; } case 'F': { sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D); break; } case 'G': /* Fall thru */ case 'g': { DateTime y = x; assert( y.validJD ); /* Move y so that it is the Thursday in the same week as x */ y.iJD += (3 - daysAfterMonday(&x))*86400000; y.validYMD = 0; computeYMD(&y); if( cf=='g' ){ sqlite3_str_appendf(&sRes, "%02d", y.Y%100); }else{ sqlite3_str_appendf(&sRes, "%04d", y.Y); } break; } case 'H': case 'k': { sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h); break; } case 'I': /* Fall thru */ case 'l': { int h = x.h; if( h>12 ) h -= 12; if( h==0 ) h = 12; sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); break; } case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */ sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1); break; } case 'J': { /* Julian day number. (Non-standard) */ sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); break; } case 'm': { sqlite3_str_appendf(&sRes,"%02d",x.M); break; } |
︙ | ︙ | |||
1368 1369 1370 1371 1372 1373 1374 | sqlite3_str_appendf(&sRes,"%02d",(int)x.s); break; } case 'T': { sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); break; } | | | | > > > > > > > > > > > > > > > > > > > > | 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 | sqlite3_str_appendf(&sRes,"%02d",(int)x.s); break; } case 'T': { sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); break; } case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */ case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */ char c = (char)daysAfterSunday(&x) + '0'; if( c=='0' && cf=='u' ) c = '7'; sqlite3_str_appendchar(&sRes, 1, c); break; } case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */ sqlite3_str_appendf(&sRes,"%02d", (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7); break; } case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */ DateTime y = x; /* Adjust y so that is the Thursday in the same week as x */ assert( y.validJD ); y.iJD += (3 - daysAfterMonday(&x))*86400000; y.validYMD = 0; computeYMD(&y); sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1); break; } case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */ sqlite3_str_appendf(&sRes,"%02d", (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7); break; } case 'Y': { sqlite3_str_appendf(&sRes,"%04d",x.Y); break; } case '%': { sqlite3_str_appendchar(&sRes, 1, '%'); |
︙ | ︙ | |||
1521 1522 1523 1524 1525 1526 1527 | } d2.validJD = 0; computeJD(&d2); } d1.iJD = d2.iJD - d1.iJD; d1.iJD += (u64)1486995408 * (u64)100000; } | | < < | 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 | } d2.validJD = 0; computeJD(&d2); } d1.iJD = d2.iJD - d1.iJD; d1.iJD += (u64)1486995408 * (u64)100000; } clearYMD_HMS_TZ(&d1); computeYMD_HMS(&d1); sqlite3StrAccumInit(&sRes, 0, 0, 0, 100); sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f", sign, Y, M, d1.D-1, d1.h, d1.m, d1.s); sqlite3ResultStrAccum(context, &sRes); } |
︙ | ︙ | |||
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 | #endif if( pTm ){ strftime(zBuf, 20, zFormat, &sNow); sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); } } #endif /* ** This function registered all of the above C functions as SQL ** functions. This should be the only routine in this file with ** external linkage. */ void sqlite3RegisterDateTimeFunctions(void){ static FuncDef aDateTimeFuncs[] = { #ifndef SQLITE_OMIT_DATETIME_FUNCS PURE_DATE(julianday, -1, 0, 0, juliandayFunc ), PURE_DATE(unixepoch, -1, 0, 0, unixepochFunc ), PURE_DATE(date, -1, 0, 0, dateFunc ), PURE_DATE(time, -1, 0, 0, timeFunc ), PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), PURE_DATE(timediff, 2, 0, 0, timediffFunc ), DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), DFUNCTION(current_date, 0, 0, 0, cdateFunc ), #else STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc), STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc), STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc), #endif }; sqlite3InsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs)); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 | #endif if( pTm ){ strftime(zBuf, 20, zFormat, &sNow); sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); } } #endif #if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG) /* ** datedebug(...) ** ** This routine returns JSON that describes the internal DateTime object. ** Used for debugging and testing only. Subject to change. */ static void datedebugFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ char *zJson; zJson = sqlite3_mprintf( "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d," "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d," "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d," "isUtc:%d,isLocal:%d}", x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz, x.s, x.validJD, x.validYMD, x.validHMS, x.nFloor, x.rawS, x.isError, x.useSubsec, x.isUtc, x.isLocal); sqlite3_result_text(context, zJson, -1, sqlite3_free); } } #endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */ /* ** This function registered all of the above C functions as SQL ** functions. This should be the only routine in this file with ** external linkage. */ void sqlite3RegisterDateTimeFunctions(void){ static FuncDef aDateTimeFuncs[] = { #ifndef SQLITE_OMIT_DATETIME_FUNCS PURE_DATE(julianday, -1, 0, 0, juliandayFunc ), PURE_DATE(unixepoch, -1, 0, 0, unixepochFunc ), PURE_DATE(date, -1, 0, 0, dateFunc ), PURE_DATE(time, -1, 0, 0, timeFunc ), PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), PURE_DATE(timediff, 2, 0, 0, timediffFunc ), #ifdef SQLITE_DEBUG PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ), #endif DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), DFUNCTION(current_date, 0, 0, 0, cdateFunc ), #else STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc), STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc), STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc), #endif }; sqlite3InsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs)); } |
Changes to src/expr.c.
︙ | ︙ | |||
214 215 216 217 218 219 220 | Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){ while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){ if( ExprHasProperty(pExpr, EP_Unlikely) ){ assert( ExprUseXList(pExpr) ); assert( pExpr->x.pList->nExpr>0 ); assert( pExpr->op==TK_FUNCTION ); pExpr = pExpr->x.pList->a[0].pExpr; | < | > > | 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){ while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){ if( ExprHasProperty(pExpr, EP_Unlikely) ){ assert( ExprUseXList(pExpr) ); assert( pExpr->x.pList->nExpr>0 ); assert( pExpr->op==TK_FUNCTION ); pExpr = pExpr->x.pList->a[0].pExpr; }else if( pExpr->op==TK_COLLATE ){ pExpr = pExpr->pLeft; }else{ break; } } return pExpr; } /* ** Return the collation sequence for the expression pExpr. If |
︙ | ︙ | |||
910 911 912 913 914 915 916 | ** ** If dequote is true, then the token (if it exists) is dequoted. ** If dequote is false, no dequoting is performed. The deQuote ** parameter is ignored if pToken is NULL or if the token does not ** appear to be quoted. If the quotes were of the form "..." (double-quotes) ** then the EP_DblQuoted flag is set on the expression node. ** | | | | | > | | 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 | ** ** If dequote is true, then the token (if it exists) is dequoted. ** If dequote is false, no dequoting is performed. The deQuote ** parameter is ignored if pToken is NULL or if the token does not ** appear to be quoted. If the quotes were of the form "..." (double-quotes) ** then the EP_DblQuoted flag is set on the expression node. ** ** Special case (tag-20240227-a): If op==TK_INTEGER and pToken points to ** a string that can be translated into a 32-bit integer, then the token is ** not stored in u.zToken. Instead, the integer values is written ** into u.iValue and the EP_IntValue flag is set. No extra storage ** is allocated to hold the integer text and the dequote flag is ignored. ** See also tag-20240227-b. */ Expr *sqlite3ExprAlloc( sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */ int op, /* Expression opcode */ const Token *pToken, /* Token argument. Might be NULL */ int dequote /* True to dequote */ ){ Expr *pNew; int nExtra = 0; int iValue = 0; assert( db!=0 ); if( pToken ){ if( op!=TK_INTEGER || pToken->z==0 || sqlite3GetInt32(pToken->z, &iValue)==0 ){ nExtra = pToken->n+1; /* tag-20240227-a */ assert( iValue>=0 ); } } pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra); if( pNew ){ memset(pNew, 0, sizeof(Expr)); pNew->op = (u8)op; |
︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 | /* ** Recursively delete an expression tree. */ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ assert( p!=0 ); assert( db!=0 ); assert( !ExprUseUValue(p) || p->u.iValue>=0 ); assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); #ifdef SQLITE_DEBUG if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ assert( p->pLeft==0 ); assert( p->pRight==0 ); assert( !ExprUseXSelect(p) || p->x.pSelect==0 ); assert( !ExprUseXList(p) || p->x.pList==0 ); } #endif if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); | > < > > > > > > > > > > > > > | 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 | /* ** Recursively delete an expression tree. */ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ assert( p!=0 ); assert( db!=0 ); exprDeleteRestart: assert( !ExprUseUValue(p) || p->u.iValue>=0 ); assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); #ifdef SQLITE_DEBUG if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ assert( p->pLeft==0 ); assert( p->pRight==0 ); assert( !ExprUseXSelect(p) || p->x.pSelect==0 ); assert( !ExprUseXList(p) || p->x.pList==0 ); } #endif if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); if( p->pRight ){ assert( !ExprHasProperty(p, EP_WinFunc) ); sqlite3ExprDeleteNN(db, p->pRight); }else if( ExprUseXSelect(p) ){ assert( !ExprHasProperty(p, EP_WinFunc) ); sqlite3SelectDelete(db, p->x.pSelect); }else{ sqlite3ExprListDelete(db, p->x.pList); #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(p, EP_WinFunc) ){ sqlite3WindowDelete(db, p->y.pWin); } #endif } if( p->pLeft && p->op!=TK_SELECT_COLUMN ){ Expr *pLeft = p->pLeft; if( !ExprHasProperty(p, EP_Static) && !ExprHasProperty(pLeft, EP_Static) ){ /* Avoid unnecessary recursion on unary operators */ sqlite3DbNNFreeNN(db, p); p = pLeft; goto exprDeleteRestart; }else{ sqlite3ExprDeleteNN(db, pLeft); } } } if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbNNFreeNN(db, p); } } void sqlite3ExprDelete(sqlite3 *db, Expr *p){ |
︙ | ︙ | |||
1424 1425 1426 1427 1428 1429 1430 | /* ** Arrange to cause pExpr to be deleted when the pParse is deleted. ** This is similar to sqlite3ExprDelete() except that the delete is ** deferred until the pParse is deleted. ** ** The pExpr might be deleted immediately on an OOM error. ** | | | | | | 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 | /* ** Arrange to cause pExpr to be deleted when the pParse is deleted. ** This is similar to sqlite3ExprDelete() except that the delete is ** deferred until the pParse is deleted. ** ** The pExpr might be deleted immediately on an OOM error. ** ** Return 0 if the delete was successfully deferred. Return non-zero ** if the delete happened immediately because of an OOM. */ int sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ return 0==sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); } /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the ** expression. */ void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){ if( p ){ |
︙ | ︙ | |||
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 | pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); pNewItem->fg = pOldItem->fg; pNewItem->iCursor = pOldItem->iCursor; pNewItem->addrFillSub = pOldItem->addrFillSub; pNewItem->regReturn = pOldItem->regReturn; if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); } pNewItem->u2 = pOldItem->u2; if( pNewItem->fg.isCte ){ pNewItem->u2.pCteUse->nUse++; } | > > > > > > < < < < | 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 | pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); pNewItem->fg = pOldItem->fg; pNewItem->iCursor = pOldItem->iCursor; pNewItem->addrFillSub = pOldItem->addrFillSub; pNewItem->regReturn = pOldItem->regReturn; pNewItem->regResult = pOldItem->regResult; if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); }else if( pNewItem->fg.isTabFunc ){ pNewItem->u1.pFuncArg = sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); }else{ pNewItem->u1.nRow = pOldItem->u1.nRow; } pNewItem->u2 = pOldItem->u2; if( pNewItem->fg.isCte ){ pNewItem->u2.pCteUse->nUse++; } pTab = pNewItem->pTab = pOldItem->pTab; if( pTab ){ pTab->nTabRef++; } pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); if( pOldItem->fg.isUsing ){ assert( pNewItem->fg.isUsing ); |
︙ | ︙ | |||
2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 | }else if( ExprAlwaysTrue(pRight) || ExprAlwaysFalse(pLeft) ){ pExpr = pExpr->op==TK_AND ? pLeft : pRight; } } return pExpr; } /* ** These routines are Walker callbacks used to check expressions to ** see if they are "constant" for some definition of constant. The ** Walker.eCode value determines the type of "constant" we are looking ** for. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 | }else if( ExprAlwaysTrue(pRight) || ExprAlwaysFalse(pLeft) ){ pExpr = pExpr->op==TK_AND ? pLeft : pRight; } } return pExpr; } /* ** pExpr is a TK_FUNCTION node. Try to determine whether or not the ** function is a constant function. A function is constant if all of ** the following are true: ** ** (1) It is a scalar function (not an aggregate or window function) ** (2) It has either the SQLITE_FUNC_CONSTANT or SQLITE_FUNC_SLOCHNG ** property. ** (3) All of its arguments are constants ** ** This routine sets pWalker->eCode to 0 if pExpr is not a constant. ** It makes no changes to pWalker->eCode if pExpr is constant. In ** every case, it returns WRC_Abort. ** ** Called as a service subroutine from exprNodeIsConstant(). */ static SQLITE_NOINLINE int exprNodeIsConstantFunction( Walker *pWalker, Expr *pExpr ){ int n; /* Number of arguments */ ExprList *pList; /* List of arguments */ FuncDef *pDef; /* The function */ sqlite3 *db; /* The database */ assert( pExpr->op==TK_FUNCTION ); if( ExprHasProperty(pExpr, EP_TokenOnly) || (pList = pExpr->x.pList)==0 ){; n = 0; }else{ n = pList->nExpr; sqlite3WalkExprList(pWalker, pList); if( pWalker->eCode==0 ) return WRC_Abort; } db = pWalker->pParse->db; pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); if( pDef==0 || pDef->xFinalize!=0 || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 || ExprHasProperty(pExpr, EP_WinFunc) ){ pWalker->eCode = 0; return WRC_Abort; } return WRC_Prune; } /* ** These routines are Walker callbacks used to check expressions to ** see if they are "constant" for some definition of constant. The ** Walker.eCode value determines the type of "constant" we are looking ** for. ** |
︙ | ︙ | |||
2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 | ** an error for new statements, but is silently converted ** to NULL for existing schemas. This allows sqlite_schema tables that ** contain a bound parameter because they were generated by older versions ** of SQLite to be parsed by newer versions of SQLite without raising a ** malformed schema error. */ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ /* If pWalker->eCode is 2 then any term of the expression that comes from ** the ON or USING clauses of an outer join disqualifies the expression ** from being considered constant. */ if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){ pWalker->eCode = 0; return WRC_Abort; } switch( pExpr->op ){ /* Consider functions to be constant if all their arguments are constant ** and either pWalker->eCode==4 or 5 or the function has the ** SQLITE_FUNC_CONST flag. */ case TK_FUNCTION: if( (pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc)) && !ExprHasProperty(pExpr, EP_WinFunc) ){ if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL); return WRC_Continue; }else{ pWalker->eCode = 0; return WRC_Abort; } case TK_ID: /* Convert "true" or "false" in a DEFAULT clause into the ** appropriate TK_TRUEFALSE operator */ | > > > | 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 | ** an error for new statements, but is silently converted ** to NULL for existing schemas. This allows sqlite_schema tables that ** contain a bound parameter because they were generated by older versions ** of SQLite to be parsed by newer versions of SQLite without raising a ** malformed schema error. */ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ assert( pWalker->eCode>0 ); /* If pWalker->eCode is 2 then any term of the expression that comes from ** the ON or USING clauses of an outer join disqualifies the expression ** from being considered constant. */ if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){ pWalker->eCode = 0; return WRC_Abort; } switch( pExpr->op ){ /* Consider functions to be constant if all their arguments are constant ** and either pWalker->eCode==4 or 5 or the function has the ** SQLITE_FUNC_CONST flag. */ case TK_FUNCTION: if( (pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc)) && !ExprHasProperty(pExpr, EP_WinFunc) ){ if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL); return WRC_Continue; }else if( pWalker->pParse ){ return exprNodeIsConstantFunction(pWalker, pExpr); }else{ pWalker->eCode = 0; return WRC_Abort; } case TK_ID: /* Convert "true" or "false" in a DEFAULT clause into the ** appropriate TK_TRUEFALSE operator */ |
︙ | ︙ | |||
2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 | if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){ return WRC_Continue; } /* no break */ deliberate_fall_through case TK_IF_NULL_ROW: case TK_REGISTER: case TK_DOT: testcase( pExpr->op==TK_REGISTER ); testcase( pExpr->op==TK_IF_NULL_ROW ); testcase( pExpr->op==TK_DOT ); pWalker->eCode = 0; return WRC_Abort; case TK_VARIABLE: if( pWalker->eCode==5 ){ /* Silently convert bound parameters that appear inside of CREATE ** statements into a NULL when parsing the CREATE statement text out ** of the sqlite_schema table */ | > > | 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 | if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){ return WRC_Continue; } /* no break */ deliberate_fall_through case TK_IF_NULL_ROW: case TK_REGISTER: case TK_DOT: case TK_RAISE: testcase( pExpr->op==TK_REGISTER ); testcase( pExpr->op==TK_IF_NULL_ROW ); testcase( pExpr->op==TK_DOT ); testcase( pExpr->op==TK_RAISE ); pWalker->eCode = 0; return WRC_Abort; case TK_VARIABLE: if( pWalker->eCode==5 ){ /* Silently convert bound parameters that appear inside of CREATE ** statements into a NULL when parsing the CREATE statement text out ** of the sqlite_schema table */ |
︙ | ︙ | |||
2439 2440 2441 2442 2443 2444 2445 | /* no break */ deliberate_fall_through default: testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */ testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */ return WRC_Continue; } } | | > < > > > > > > | | | | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | > > > | | 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 | /* no break */ deliberate_fall_through default: testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */ testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */ return WRC_Continue; } } static int exprIsConst(Parse *pParse, Expr *p, int initFlag){ Walker w; w.eCode = initFlag; w.pParse = pParse; w.xExprCallback = exprNodeIsConstant; w.xSelectCallback = sqlite3SelectWalkFail; #ifdef SQLITE_DEBUG w.xSelectCallback2 = sqlite3SelectWalkAssert2; #endif sqlite3WalkExpr(&w, p); return w.eCode; } /* ** Walk an expression tree. Return non-zero if the expression is constant ** and 0 if it involves variables or function calls. ** ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is ** a constant. ** ** The pParse parameter may be NULL. But if it is NULL, there is no way ** to determine if function calls are constant or not, and hence all ** function calls will be considered to be non-constant. If pParse is ** not NULL, then a function call might be constant, depending on the ** function and on its parameters. */ int sqlite3ExprIsConstant(Parse *pParse, Expr *p){ return exprIsConst(pParse, p, 1); } /* ** Walk an expression tree. Return non-zero if ** ** (1) the expression is constant, and ** (2) the expression does originate in the ON or USING clause ** of a LEFT JOIN, and ** (3) the expression does not contain any EP_FixedCol TK_COLUMN ** operands created by the constant propagation optimization. ** ** When this routine returns true, it indicates that the expression ** can be added to the pParse->pConstExpr list and evaluated once when ** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce(). */ static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){ return exprIsConst(pParse, p, 2); } /* ** This routine examines sub-SELECT statements as an expression is being ** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered ** constant as long as they are uncorrelated - meaning that they do not ** contain any terms from outer contexts. */ static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ assert( pSelect!=0 ); assert( pWalker->eCode==3 || pWalker->eCode==0 ); if( (pSelect->selFlags & SF_Correlated)!=0 ){ pWalker->eCode = 0; return WRC_Abort; } return WRC_Prune; } /* ** Walk an expression tree. Return non-zero if the expression is constant ** for any single row of the table with cursor iCur. In other words, the ** expression must not refer to any non-deterministic function nor any ** table other than iCur. ** ** Consider uncorrelated subqueries to be constants if the bAllowSubq ** parameter is true. */ static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){ Walker w; w.eCode = 3; w.pParse = 0; w.xExprCallback = exprNodeIsConstant; if( bAllowSubq ){ w.xSelectCallback = exprSelectWalkTableConstant; }else{ w.xSelectCallback = sqlite3SelectWalkFail; #ifdef SQLITE_DEBUG w.xSelectCallback2 = sqlite3SelectWalkAssert2; #endif } w.u.iCur = iCur; sqlite3WalkExpr(&w, p); return w.eCode; } /* ** Check pExpr to see if it is an constraint on the single data source ** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr ** constrains pSrc but does not depend on any other tables or data ** sources anywhere else in the query. Return true (non-zero) if pExpr ** is a constraint on pSrc only. ** ** This is an optimization. False negatives will perhaps cause slower ** queries, but false positives will yield incorrect answers. So when in ** doubt, return 0. ** ** To be an single-source constraint, the following must be true: ** ** (1) pExpr cannot refer to any table other than pSrc->iCursor. ** ** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is ** true and the subquery is non-correlated ** ** (2b) pExpr cannot use non-deterministic functions. ** ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ** (Is there some way to relax this constraint?) ** ** (4) If pSrc is the right operand of a LEFT JOIN, then... ** (4a) pExpr must come from an ON clause.. ** (4b) and specifically the ON clause associated with the LEFT JOIN. |
︙ | ︙ | |||
2535 2536 2537 2538 2539 2540 2541 | ** from the left side of a RIGHT JOIN over to the right side, ** which leads to incorrect answers. See also restriction (9) ** on push-down. */ int sqlite3ExprIsSingleTableConstraint( Expr *pExpr, /* The constraint */ const SrcList *pSrcList, /* Complete FROM clause */ | | > | 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 | ** from the left side of a RIGHT JOIN over to the right side, ** which leads to incorrect answers. See also restriction (9) ** on push-down. */ int sqlite3ExprIsSingleTableConstraint( Expr *pExpr, /* The constraint */ const SrcList *pSrcList, /* Complete FROM clause */ int iSrc, /* Which element of pSrcList to use */ int bAllowSubq /* Allow non-correlated subqueries */ ){ const SrcItem *pSrc = &pSrcList->a[iSrc]; if( pSrc->fg.jointype & JT_LTORJ ){ return 0; /* rule (3) */ } if( pSrc->fg.jointype & JT_LEFT ){ if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ |
︙ | ︙ | |||
2560 2561 2562 2563 2564 2565 2566 | if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){ return 0; /* restriction (6) */ } break; } } } | > | | 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 | if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){ return 0; /* restriction (6) */ } break; } } } /* Rules (1), (2a), and (2b) handled by the following: */ return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq); } /* ** sqlite3WalkExpr() callback used by sqlite3ExprIsConstantOrGroupBy(). */ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){ |
︙ | ︙ | |||
2645 2646 2647 2648 2649 2650 2651 | ** ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is ** a constant. */ int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){ assert( isInit==0 || isInit==1 ); | | | 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 | ** ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is ** a constant. */ int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){ assert( isInit==0 || isInit==1 ); return exprIsConst(0, p, 4+isInit); } #ifdef SQLITE_ENABLE_CURSOR_HINTS /* ** Walk an expression tree. Return 1 if the expression contains a ** subquery of some kind. Return 0 if there are no subqueries. */ |
︙ | ︙ | |||
2735 2736 2737 2738 2739 2740 2741 | case TK_INTEGER: case TK_STRING: case TK_FLOAT: case TK_BLOB: return 0; case TK_COLUMN: assert( ExprUseYTab(p) ); | | | > > > | | 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 | case TK_INTEGER: case TK_STRING: case TK_FLOAT: case TK_BLOB: return 0; case TK_COLUMN: assert( ExprUseYTab(p) ); return ExprHasProperty(p, EP_CanBeNull) || NEVER(p->y.pTab==0) /* Reference to column of index on expr */ #ifdef SQLITE_ALLOW_ROWID_IN_VIEW || (p->iColumn==XN_ROWID && IsView(p->y.pTab)) #endif || (p->iColumn>=0 && p->y.pTab->aCol!=0 /* Possible due to prior error */ && ALWAYS(p->iColumn<p->y.pTab->nCol) && p->y.pTab->aCol[p->iColumn].notNull==0); default: return 1; } } |
︙ | ︙ | |||
2890 2891 2892 2893 2894 2895 2896 | #ifndef SQLITE_OMIT_SUBQUERY /* ** The argument is an IN operator with a list (not a subquery) on the ** right-hand side. Return TRUE if that list is constant. */ | | | | 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 | #ifndef SQLITE_OMIT_SUBQUERY /* ** The argument is an IN operator with a list (not a subquery) on the ** right-hand side. Return TRUE if that list is constant. */ static int sqlite3InRhsIsConstant(Parse *pParse, Expr *pIn){ Expr *pLHS; int res; assert( !ExprHasProperty(pIn, EP_xIsSelect) ); pLHS = pIn->pLeft; pIn->pLeft = 0; res = sqlite3ExprIsConstant(pParse, pIn); pIn->pLeft = pLHS; return res; } #endif /* ** This function is used by the implementation of the IN (...) operator. |
︙ | ︙ | |||
3165 3166 3167 3168 3169 3170 3171 | ** and the RHS is not constant or has two or fewer terms, ** then it is not worth creating an ephemeral table to evaluate ** the IN operator so return IN_INDEX_NOOP. */ if( eType==0 && (inFlags & IN_INDEX_NOOP_OK) && ExprUseXList(pX) | | | 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 | ** and the RHS is not constant or has two or fewer terms, ** then it is not worth creating an ephemeral table to evaluate ** the IN operator so return IN_INDEX_NOOP. */ if( eType==0 && (inFlags & IN_INDEX_NOOP_OK) && ExprUseXList(pX) && (!sqlite3InRhsIsConstant(pParse,pX) || pX->x.pList->nExpr<=2) ){ pParse->nTab--; /* Back out the allocation of the unused cursor */ iTab = -1; /* Cursor is not allocated */ eType = IN_INDEX_NOOP; } if( eType==0 ){ |
︙ | ︙ | |||
3448 3449 3450 3451 3452 3453 3454 | Expr *pE2 = pItem->pExpr; /* If the expression is not constant then we will need to ** disable the test that was generated above that makes sure ** this code only executes once. Because for a non-constant ** expression we need to rerun this code each time. */ | | | 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 | Expr *pE2 = pItem->pExpr; /* If the expression is not constant then we will need to ** disable the test that was generated above that makes sure ** this code only executes once. Because for a non-constant ** expression we need to rerun this code each time. */ if( addrOnce && !sqlite3ExprIsConstant(pParse, pE2) ){ sqlite3VdbeChangeToNoop(v, addrOnce-1); sqlite3VdbeChangeToNoop(v, addrOnce); ExprClearProperty(pExpr, EP_Subrtn); addrOnce = 0; } /* Evaluate the expression and insert it into the temp table */ |
︙ | ︙ | |||
4612 4613 4614 4615 4616 4617 4618 | } #endif case TK_VARIABLE: { assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( pExpr->u.zToken!=0 ); assert( pExpr->u.zToken[0]!=0 ); sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); | < < < < < < | 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 | } #endif case TK_VARIABLE: { assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( pExpr->u.zToken!=0 ); assert( pExpr->u.zToken[0]!=0 ); sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); return target; } case TK_REGISTER: { return pExpr->iTable; } #ifndef SQLITE_OMIT_CAST case TK_CAST: { |
︙ | ︙ | |||
4791 4792 4793 4794 4795 4796 4797 | #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ return pExpr->y.pWin->regResult; } #endif | | > > | 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 | #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ return pExpr->y.pWin->regResult; } #endif if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ /* SQL functions can be expensive. So try to avoid running them ** multiple times if we know they always give the same result */ return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); } assert( !ExprHasProperty(pExpr, EP_TokenOnly) ); assert( ExprUseXList(pExpr) ); pFarg = pExpr->x.pList; |
︙ | ︙ | |||
4822 4823 4824 4825 4826 4827 4828 | return exprCodeInlineFunction(pParse, pFarg, SQLITE_PTR_TO_INT(pDef->pUserData), target); }else if( pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE) ){ sqlite3ExprFunctionUsable(pParse, pExpr, pDef); } for(i=0; i<nFarg; i++){ | | | 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 | return exprCodeInlineFunction(pParse, pFarg, SQLITE_PTR_TO_INT(pDef->pUserData), target); }else if( pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE) ){ sqlite3ExprFunctionUsable(pParse, pExpr, pDef); } for(i=0; i<nFarg; i++){ if( i<32 && sqlite3ExprIsConstant(pParse, pFarg->a[i].pExpr) ){ testcase( i==31 ); constMask |= MASKBIT32(i); } if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr); } } |
︙ | ︙ | |||
4964 4965 4966 4967 4968 4969 4970 | exprCodeBetween(pParse, pExpr, target, 0, 0); return target; } case TK_COLLATE: { if( !ExprHasProperty(pExpr, EP_Collate) ){ /* A TK_COLLATE Expr node without the EP_Collate tag is a so-called ** "SOFT-COLLATE" that is added to constraints that are pushed down | | | > | 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 | exprCodeBetween(pParse, pExpr, target, 0, 0); return target; } case TK_COLLATE: { if( !ExprHasProperty(pExpr, EP_Collate) ){ /* A TK_COLLATE Expr node without the EP_Collate tag is a so-called ** "SOFT-COLLATE" that is added to constraints that are pushed down ** from outer queries into sub-queries by the WHERE-clause push-down ** optimization. Clear subtypes as subtypes may not cross a subquery ** boundary. */ assert( pExpr->pLeft ); sqlite3ExprCode(pParse, pExpr->pLeft, target); sqlite3VdbeAddOp1(v, OP_ClrSubtype, target); return target; }else{ pExpr = pExpr->pLeft; |
︙ | ︙ | |||
5289 5290 5291 5292 5293 5294 5295 | */ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ int r2; pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); if( ConstFactorOk(pParse) && ALWAYS(pExpr!=0) && pExpr->op!=TK_REGISTER | | | 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 | */ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ int r2; pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); if( ConstFactorOk(pParse) && ALWAYS(pExpr!=0) && pExpr->op!=TK_REGISTER && sqlite3ExprIsConstantNotJoin(pParse, pExpr) ){ *pReg = 0; r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); }else{ int r1 = sqlite3GetTempReg(pParse); r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); if( r2==r1 ){ |
︙ | ︙ | |||
5353 5354 5355 5356 5357 5358 5359 | /* ** Generate code that will evaluate expression pExpr and store the ** results in register target. The results are guaranteed to appear ** in register target. If the expression is constant, then this routine ** might choose to code the expression at initialization time. */ void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ | | | 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 | /* ** Generate code that will evaluate expression pExpr and store the ** results in register target. The results are guaranteed to appear ** in register target. If the expression is constant, then this routine ** might choose to code the expression at initialization time. */ void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ sqlite3ExprCodeRunJustOnce(pParse, pExpr, target); }else{ sqlite3ExprCodeCopy(pParse, pExpr, target); } } /* |
︙ | ︙ | |||
5412 5413 5414 5415 5416 5417 5418 | if( flags & SQLITE_ECEL_OMITREF ){ i--; n--; }else{ sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i); } }else if( (flags & SQLITE_ECEL_FACTOR)!=0 | | | 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 | if( flags & SQLITE_ECEL_OMITREF ){ i--; n--; }else{ sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i); } }else if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i); }else{ int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); if( inReg!=target+i ){ VdbeOp *pOp; if( copyOp==OP_Copy |
︙ | ︙ | |||
6563 6564 6565 6566 6567 6568 6569 | sqlite3 *db = pParse->db; assert( iAgg>=0 ); if( pExpr->op!=TK_AGG_FUNCTION ){ if( iAgg<pAggInfo->nColumn && pAggInfo->aCol[iAgg].pCExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); | | < | < | 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 | sqlite3 *db = pParse->db; assert( iAgg>=0 ); if( pExpr->op!=TK_AGG_FUNCTION ){ if( iAgg<pAggInfo->nColumn && pAggInfo->aCol[iAgg].pCExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aCol[iAgg].pCExpr = pExpr; } } }else{ assert( pExpr->op==TK_AGG_FUNCTION ); if( ALWAYS(iAgg<pAggInfo->nFunc) && pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aFunc[iAgg].pFExpr = pExpr; } } } } return WRC_Continue; } |
︙ | ︙ |
Changes to src/func.c.
︙ | ︙ | |||
1097 1098 1099 1100 1101 1102 1103 | assert( pStr!=0 && pStr->nChar==0 ); switch( sqlite3_value_type(pValue) ){ case SQLITE_FLOAT: { double r1, r2; const char *zVal; r1 = sqlite3_value_double(pValue); | | | | 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 | assert( pStr!=0 && pStr->nChar==0 ); switch( sqlite3_value_type(pValue) ){ case SQLITE_FLOAT: { double r1, r2; const char *zVal; r1 = sqlite3_value_double(pValue); sqlite3_str_appendf(pStr, "%!0.15g", r1); zVal = sqlite3_str_value(pStr); if( zVal ){ sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8); if( r1!=r2 ){ sqlite3_str_reset(pStr); sqlite3_str_appendf(pStr, "%!0.20e", r1); } } break; } case SQLITE_INTEGER: { sqlite3_str_appendf(pStr, "%lld", sqlite3_value_int64(pValue)); break; |
︙ | ︙ | |||
1405 1406 1407 1408 1409 1410 1411 | if( zPattern==0 ){ assert( sqlite3_value_type(argv[1])==SQLITE_NULL || sqlite3_context_db_handle(context)->mallocFailed ); return; } if( zPattern[0]==0 ){ assert( sqlite3_value_type(argv[1])!=SQLITE_NULL ); | | | 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 | if( zPattern==0 ){ assert( sqlite3_value_type(argv[1])==SQLITE_NULL || sqlite3_context_db_handle(context)->mallocFailed ); return; } if( zPattern[0]==0 ){ assert( sqlite3_value_type(argv[1])!=SQLITE_NULL ); sqlite3_result_text(context, (const char*)zStr, nStr, SQLITE_TRANSIENT); return; } nPattern = sqlite3_value_bytes(argv[1]); assert( zPattern==sqlite3_value_text(argv[1]) ); /* No encoding change */ zRep = sqlite3_value_text(argv[2]); if( zRep==0 ) return; nRep = sqlite3_value_bytes(argv[2]); |
︙ | ︙ | |||
1888 1889 1890 1891 1892 1893 1894 | static void sumFinalize(sqlite3_context *context){ SumCtx *p; p = sqlite3_aggregate_context(context, 0); if( p && p->cnt>0 ){ if( p->approx ){ if( p->ovrfl ){ sqlite3_result_error(context,"integer overflow",-1); | | | | | 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 | static void sumFinalize(sqlite3_context *context){ SumCtx *p; p = sqlite3_aggregate_context(context, 0); if( p && p->cnt>0 ){ if( p->approx ){ if( p->ovrfl ){ sqlite3_result_error(context,"integer overflow",-1); }else if( !sqlite3IsOverflow(p->rErr) ){ sqlite3_result_double(context, p->rSum+p->rErr); }else{ sqlite3_result_double(context, p->rSum); } }else{ sqlite3_result_int64(context, p->iSum); } } } static void avgFinalize(sqlite3_context *context){ SumCtx *p; p = sqlite3_aggregate_context(context, 0); if( p && p->cnt>0 ){ double r; if( p->approx ){ r = p->rSum; if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr; }else{ r = (double)(p->iSum); } sqlite3_result_double(context, r/(double)p->cnt); } } static void totalFinalize(sqlite3_context *context){ SumCtx *p; double r = 0.0; p = sqlite3_aggregate_context(context, 0); if( p ){ if( p->approx ){ r = p->rSum; if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr; }else{ r = (double)(p->iSum); } } sqlite3_result_double(context, r); } |
︙ | ︙ | |||
2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 | = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0); if( pGCC ){ StrAccum *pAccum = &pGCC->str; if( pAccum->accError==SQLITE_TOOBIG ){ sqlite3_result_error_toobig(context); }else if( pAccum->accError==SQLITE_NOMEM ){ sqlite3_result_error_nomem(context); }else{ const char *zText = sqlite3_str_value(pAccum); sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT); } } } #else | > > | 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 | = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0); if( pGCC ){ StrAccum *pAccum = &pGCC->str; if( pAccum->accError==SQLITE_TOOBIG ){ sqlite3_result_error_toobig(context); }else if( pAccum->accError==SQLITE_NOMEM ){ sqlite3_result_error_nomem(context); }else if( pGCC->nAccum>0 && pAccum->nChar==0 ){ sqlite3_result_text(context, "", 1, SQLITE_STATIC); }else{ const char *zText = sqlite3_str_value(pAccum); sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT); } } } #else |
︙ | ︙ |
Changes to src/global.c.
︙ | ︙ | |||
285 286 287 288 289 290 291 292 293 294 295 296 297 298 | #endif #ifndef SQLITE_OMIT_DESERIALIZE SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ #endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ #endif 0, /* bLocaltimeFault */ 0, /* xAltLocaltime */ 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ #ifdef SQLITE_DEBUG {0,0,0,0,0,0}, /* aTune */ | > > > | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | #endif #ifndef SQLITE_OMIT_DESERIALIZE SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ #endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ #endif #ifdef SQLITE_ALLOW_ROWID_IN_VIEW 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */ #endif 0, /* bLocaltimeFault */ 0, /* xAltLocaltime */ 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ #ifdef SQLITE_DEBUG {0,0,0,0,0,0}, /* aTune */ |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
573 574 575 576 577 578 579 580 581 582 583 584 585 586 | ** If SQLITE_OMIT_AUTOINCREMENT is defined, then the three routines ** above are all no-ops */ # define autoIncBegin(A,B,C) (0) # define autoIncStep(A,B,C) #endif /* SQLITE_OMIT_AUTOINCREMENT */ /* Forward declaration */ static int xferOptimization( Parse *pParse, /* Parser context */ Table *pDest, /* The table we are inserting into */ Select *pSelect, /* A SELECT statement to use as the data source */ int onError, /* How to handle constraint errors */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 | ** If SQLITE_OMIT_AUTOINCREMENT is defined, then the three routines ** above are all no-ops */ # define autoIncBegin(A,B,C) (0) # define autoIncStep(A,B,C) #endif /* SQLITE_OMIT_AUTOINCREMENT */ /* ** If argument pVal is a Select object returned by an sqlite3MultiValues() ** that was able to use the co-routine optimization, finish coding the ** co-routine. */ void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){ SrcItem *pItem = &pVal->pSrc->a[0]; sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn); sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1); } } /* ** Return true if all expressions in the expression-list passed as the ** only argument are constant. */ static int exprListIsConstant(Parse *pParse, ExprList *pRow){ int ii; for(ii=0; ii<pRow->nExpr; ii++){ if( 0==sqlite3ExprIsConstant(pParse, pRow->a[ii].pExpr) ) return 0; } return 1; } /* ** Return true if all expressions in the expression-list passed as the ** only argument are both constant and have no affinity. */ static int exprListIsNoAffinity(Parse *pParse, ExprList *pRow){ int ii; if( exprListIsConstant(pParse,pRow)==0 ) return 0; for(ii=0; ii<pRow->nExpr; ii++){ Expr *pExpr = pRow->a[ii].pExpr; assert( pExpr->op!=TK_RAISE ); assert( pExpr->affExpr==0 ); if( 0!=sqlite3ExprAffinity(pExpr) ) return 0; } return 1; } /* ** This function is called by the parser for the second and subsequent ** rows of a multi-row VALUES clause. Argument pLeft is the part of ** the VALUES clause already parsed, argument pRow is the vector of values ** for the new row. The Select object returned represents the complete ** VALUES clause, including the new row. ** ** There are two ways in which this may be achieved - by incremental ** coding of a co-routine (the "co-routine" method) or by returning a ** Select object equivalent to the following (the "UNION ALL" method): ** ** "pLeft UNION ALL SELECT pRow" ** ** If the VALUES clause contains a lot of rows, this compound Select ** object may consume a lot of memory. ** ** When the co-routine method is used, each row that will be returned ** by the VALUES clause is coded into part of a co-routine as it is ** passed to this function. The returned Select object is equivalent to: ** ** SELECT * FROM ( ** Select object to read co-routine ** ) ** ** The co-routine method is used in most cases. Exceptions are: ** ** a) If the current statement has a WITH clause. This is to avoid ** statements like: ** ** WITH cte AS ( VALUES('x'), ('y') ... ) ** SELECT * FROM cte AS a, cte AS b; ** ** This will not work, as the co-routine uses a hard-coded register ** for its OP_Yield instructions, and so it is not possible for two ** cursors to iterate through it concurrently. ** ** b) The schema is currently being parsed (i.e. the VALUES clause is part ** of a schema item like a VIEW or TRIGGER). In this case there is no VM ** being generated when parsing is taking place, and so generating ** a co-routine is not possible. ** ** c) There are non-constant expressions in the VALUES clause (e.g. ** the VALUES clause is part of a correlated sub-query). ** ** d) One or more of the values in the first row of the VALUES clause ** has an affinity (i.e. is a CAST expression). This causes problems ** because the complex rules SQLite uses (see function ** sqlite3SubqueryColumnTypes() in select.c) to determine the effective ** affinity of such a column for all rows require access to all values in ** the column simultaneously. */ Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){ if( pParse->bHasWith /* condition (a) above */ || pParse->db->init.busy /* condition (b) above */ || exprListIsConstant(pParse,pRow)==0 /* condition (c) above */ || (pLeft->pSrc->nSrc==0 && exprListIsNoAffinity(pParse,pLeft->pEList)==0) /* condition (d) above */ || IN_SPECIAL_PARSE ){ /* The co-routine method cannot be used. Fall back to UNION ALL. */ Select *pSelect = 0; int f = SF_Values | SF_MultiValue; if( pLeft->pSrc->nSrc ){ sqlite3MultiValuesEnd(pParse, pLeft); f = SF_Values; }else if( pLeft->pPrior ){ /* In this case set the SF_MultiValue flag only if it was set on pLeft */ f = (f & pLeft->selFlags); } pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0); pLeft->selFlags &= ~SF_MultiValue; if( pSelect ){ pSelect->op = TK_ALL; pSelect->pPrior = pLeft; pLeft = pSelect; } }else{ SrcItem *p = 0; /* SrcItem that reads from co-routine */ if( pLeft->pSrc->nSrc==0 ){ /* Co-routine has not yet been started and the special Select object ** that accesses the co-routine has not yet been created. This block ** does both those things. */ Vdbe *v = sqlite3GetVdbe(pParse); Select *pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0); /* Ensure the database schema has been read. This is to ensure we have ** the correct text encoding. */ if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){ sqlite3ReadSchema(pParse); } if( pRet ){ SelectDest dest; pRet->pSrc->nSrc = 1; pRet->pPrior = pLeft->pPrior; pRet->op = pLeft->op; pLeft->pPrior = 0; pLeft->op = TK_SELECT; assert( pLeft->pNext==0 ); assert( pRet->pNext==0 ); p = &pRet->pSrc->a[0]; p->pSelect = pLeft; p->fg.viaCoroutine = 1; p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1; p->regReturn = ++pParse->nMem; p->iCursor = -1; p->u1.nRow = 2; sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub); sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn); /* Allocate registers for the output of the co-routine. Do so so ** that there are two unused registers immediately before those ** used by the co-routine. This allows the code in sqlite3Insert() ** to use these registers directly, instead of copying the output ** of the co-routine to a separate array for processing. */ dest.iSdst = pParse->nMem + 3; dest.nSdst = pLeft->pEList->nExpr; pParse->nMem += 2 + dest.nSdst; pLeft->selFlags |= SF_MultiValue; sqlite3Select(pParse, pLeft, &dest); p->regResult = dest.iSdst; assert( pParse->nErr || dest.iSdst>0 ); pLeft = pRet; } }else{ p = &pLeft->pSrc->a[0]; assert( !p->fg.isTabFunc && !p->fg.isIndexedBy ); p->u1.nRow++; } if( pParse->nErr==0 ){ assert( p!=0 ); if( p->pSelect->pEList->nExpr!=pRow->nExpr ){ sqlite3SelectWrongNumTermsError(pParse, p->pSelect); }else{ sqlite3ExprCodeExprList(pParse, pRow, p->regResult, 0, 0); sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn); } } sqlite3ExprListDelete(pParse->db, pRow); } return pLeft; } /* Forward declaration */ static int xferOptimization( Parse *pParse, /* Parser context */ Table *pDest, /* The table we are inserting into */ Select *pSelect, /* A SELECT statement to use as the data source */ int onError, /* How to handle constraint errors */ |
︙ | ︙ | |||
909 910 911 912 913 914 915 | ** is coming from a SELECT statement, then generate a co-routine that ** produces a single row of the SELECT on each invocation. The ** co-routine is the common header to the 3rd and 4th templates. */ if( pSelect ){ /* Data is coming from a SELECT or from a multi-row VALUES clause. ** Generate a co-routine to run the SELECT. */ | < < > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | > | 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 | ** is coming from a SELECT statement, then generate a co-routine that ** produces a single row of the SELECT on each invocation. The ** co-routine is the common header to the 3rd and 4th templates. */ if( pSelect ){ /* Data is coming from a SELECT or from a multi-row VALUES clause. ** Generate a co-routine to run the SELECT. */ int rc; /* Result code */ if( pSelect->pSrc->nSrc==1 && pSelect->pSrc->a[0].fg.viaCoroutine && pSelect->pPrior==0 ){ SrcItem *pItem = &pSelect->pSrc->a[0]; dest.iSDParm = pItem->regReturn; regFromSelect = pItem->regResult; nColumn = pItem->pSelect->pEList->nExpr; ExplainQueryPlan((pParse, 0, "SCAN %S", pItem)); if( bIdListInOrder && nColumn==pTab->nCol ){ regData = regFromSelect; regRowid = regData - 1; regIns = regRowid - (IsVirtual(pTab) ? 1 : 0); } }else{ int addrTop; /* Top of the co-routine */ int regYield = ++pParse->nMem; addrTop = sqlite3VdbeCurrentAddr(v) + 1; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); dest.iSdst = bIdListInOrder ? regData : 0; dest.nSdst = pTab->nCol; rc = sqlite3Select(pParse, pSelect, &dest); regFromSelect = dest.iSdst; assert( db->pParse==pParse ); if( rc || pParse->nErr ) goto insert_cleanup; assert( db->mallocFailed==0 ); sqlite3VdbeEndCoroutine(v, regYield); sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ assert( pSelect->pEList ); nColumn = pSelect->pEList->nExpr; } /* Set useTempTable to TRUE if the result of the SELECT statement ** should be written into a temporary table (template 4). Set to ** FALSE if each output row of the SELECT can be written directly into ** the destination table (template 3). ** ** A temp table must be used if the table being updated is also one |
︙ | ︙ | |||
1082 1083 1084 1085 1086 1087 1088 | pNx = pUpsert; do{ pNx->pUpsertSrc = pTabList; pNx->regData = regData; pNx->iDataCur = iDataCur; pNx->iIdxCur = iIdxCur; if( pNx->pUpsertTarget ){ | | | 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 | pNx = pUpsert; do{ pNx->pUpsertSrc = pTabList; pNx->regData = regData; pNx->iDataCur = iDataCur; pNx->iIdxCur = iIdxCur; if( pNx->pUpsertTarget ){ if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx, pUpsert) ){ goto insert_cleanup; } } pNx = pNx->pNextUpsert; }while( pNx!=0 ); } #endif |
︙ | ︙ | |||
2974 2975 2976 2977 2978 2979 2980 | /* The sqlite3FaultSim() call allows this corruption test to be ** bypassed during testing, in order to exercise other corruption tests ** further downstream. */ return 0; /* Corrupt schema - two indexes on the same btree */ } } #ifndef SQLITE_OMIT_CHECK | > > | > | 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 | /* The sqlite3FaultSim() call allows this corruption test to be ** bypassed during testing, in order to exercise other corruption tests ** further downstream. */ return 0; /* Corrupt schema - two indexes on the same btree */ } } #ifndef SQLITE_OMIT_CHECK if( pDest->pCheck && (db->mDbFlags & DBFLAG_Vacuum)==0 && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){ return 0; /* Tables have different CHECK constraints. Ticket #2252 */ } #endif #ifndef SQLITE_OMIT_FOREIGN_KEY /* Disallow the transfer optimization if the destination table contains ** any foreign key constraints. This is more restrictive than necessary. ** But the main beneficiary of the transfer optimization is the VACUUM |
︙ | ︙ |
Changes to src/json.c.
︙ | ︙ | |||
294 295 296 297 298 299 300 301 302 303 304 305 306 307 | ** returns JSONB instead of text JSON. */ struct JsonParse { u8 *aBlob; /* JSONB representation of JSON value */ u32 nBlob; /* Bytes of aBlob[] actually used */ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */ char *zJson; /* Json text used for parsing */ int nJson; /* Length of the zJson string in bytes */ u32 nJPRef; /* Number of references to this object */ u32 iErr; /* Error location in zJson[] */ u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ u8 oom; /* Set to true if out of memory */ u8 bJsonIsRCStr; /* True if zJson is an RCStr */ | > | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | ** returns JSONB instead of text JSON. */ struct JsonParse { u8 *aBlob; /* JSONB representation of JSON value */ u32 nBlob; /* Bytes of aBlob[] actually used */ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */ char *zJson; /* Json text used for parsing */ sqlite3 *db; /* The database connection to which this object belongs */ int nJson; /* Length of the zJson string in bytes */ u32 nJPRef; /* Number of references to this object */ u32 iErr; /* Error location in zJson[] */ u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ u8 oom; /* Set to true if out of memory */ u8 bJsonIsRCStr; /* True if zJson is an RCStr */ |
︙ | ︙ | |||
558 559 560 561 562 563 564 | jsonStringExpandAndAppend(p,zIn,N); }else{ memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; } } | < | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | jsonStringExpandAndAppend(p,zIn,N); }else{ memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; } } /* Append formatted text (not to exceed N bytes) to the JsonString. */ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ va_list ap; if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return; va_start(ap, zFormat); sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap); |
︙ | ︙ | |||
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 | static void jsonAppendChar(JsonString *p, char c){ if( p->nUsed>=p->nAlloc ){ jsonAppendCharExpand(p,c); }else{ p->zBuf[p->nUsed++] = c; } } /* Make sure there is a zero terminator on p->zBuf[] ** ** Return true on success. Return false if an OOM prevents this ** from happening. */ static int jsonStringTerminate(JsonString *p){ jsonAppendChar(p, 0); | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | static void jsonAppendChar(JsonString *p, char c){ if( p->nUsed>=p->nAlloc ){ jsonAppendCharExpand(p,c); }else{ p->zBuf[p->nUsed++] = c; } } /* Remove a single character from the end of the string */ static void jsonStringTrimOneChar(JsonString *p){ if( p->eErr==0 ){ assert( p->nUsed>0 ); p->nUsed--; } } /* Make sure there is a zero terminator on p->zBuf[] ** ** Return true on success. Return false if an OOM prevents this ** from happening. */ static int jsonStringTerminate(JsonString *p){ jsonAppendChar(p, 0); jsonStringTrimOneChar(p); return p->eErr==0; } /* Append a comma separator to the output buffer, if the previous ** character is not '[' or '{'. */ static void jsonAppendSeparator(JsonString *p){ char c; if( p->nUsed==0 ) return; c = p->zBuf[p->nUsed-1]; if( c=='[' || c=='{' ) return; jsonAppendChar(p, ','); } /* c is a control character. Append the canonical JSON representation ** of that control character to p. ** ** This routine assumes that the output buffer has already been enlarged ** sufficiently to hold the worst-case encoding plus a nul terminator. */ static void jsonAppendControlChar(JsonString *p, u8 c){ static const char aSpecial[] = { 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; assert( sizeof(aSpecial)==32 ); assert( aSpecial['\b']=='b' ); assert( aSpecial['\f']=='f' ); assert( aSpecial['\n']=='n' ); assert( aSpecial['\r']=='r' ); assert( aSpecial['\t']=='t' ); assert( c>=0 && c<sizeof(aSpecial) ); assert( p->nUsed+7 <= p->nAlloc ); if( aSpecial[c] ){ p->zBuf[p->nUsed] = '\\'; p->zBuf[p->nUsed+1] = aSpecial[c]; p->nUsed += 2; }else{ p->zBuf[p->nUsed] = '\\'; p->zBuf[p->nUsed+1] = 'u'; p->zBuf[p->nUsed+2] = '0'; p->zBuf[p->nUsed+3] = '0'; p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4]; p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf]; p->nUsed += 6; } } /* Append the N-byte string in zIn to the end of the JsonString string ** under construction. Enclose the string in double-quotes ("...") and ** escape any double-quotes or backslash characters contained within the ** string. ** ** This routine is a high-runner. There is a measurable performance |
︙ | ︙ | |||
665 666 667 668 669 670 671 | memcpy(&p->zBuf[p->nUsed], z, k); p->nUsed += k; z += k; N -= k; } c = z[0]; if( c=='"' || c=='\\' ){ | < < < < < < < < < < < < < < < < < < < < < < > | 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 | memcpy(&p->zBuf[p->nUsed], z, k); p->nUsed += k; z += k; N -= k; } c = z[0]; if( c=='"' || c=='\\' ){ if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = c; }else if( c=='\'' ){ p->zBuf[p->nUsed++] = c; }else{ if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return; jsonAppendControlChar(p, c); } z++; N--; } p->zBuf[p->nUsed++] = '"'; assert( p->nUsed<p->nAlloc ); } |
︙ | ︙ | |||
819 820 821 822 823 824 825 | if( pParse->bJsonIsRCStr ){ sqlite3RCStrUnref(pParse->zJson); pParse->zJson = 0; pParse->nJson = 0; pParse->bJsonIsRCStr = 0; } if( pParse->nBlobAlloc ){ | | | | 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 | if( pParse->bJsonIsRCStr ){ sqlite3RCStrUnref(pParse->zJson); pParse->zJson = 0; pParse->nJson = 0; pParse->bJsonIsRCStr = 0; } if( pParse->nBlobAlloc ){ sqlite3DbFree(pParse->db, pParse->aBlob); pParse->aBlob = 0; pParse->nBlob = 0; pParse->nBlobAlloc = 0; } } /* ** Decrement the reference count on the JsonParse object. When the ** count reaches zero, free the object. */ static void jsonParseFree(JsonParse *pParse){ if( pParse ){ if( pParse->nJPRef>1 ){ pParse->nJPRef--; }else{ jsonParseReset(pParse); sqlite3DbFree(pParse->db, pParse); } } } /************************************************************************** ** Utility routines for the JSON text parser **************************************************************************/ |
︙ | ︙ | |||
1067 1068 1069 1070 1071 1072 1073 | assert( N>pParse->nBlobAlloc ); if( pParse->nBlobAlloc==0 ){ t = 100; }else{ t = pParse->nBlobAlloc*2; } if( t<N ) t = N+100; | | | 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 | assert( N>pParse->nBlobAlloc ); if( pParse->nBlobAlloc==0 ){ t = 100; }else{ t = pParse->nBlobAlloc*2; } if( t<N ) t = N+100; aNew = sqlite3DbRealloc(pParse->db, pParse->aBlob, t); if( aNew==0 ){ pParse->oom = 1; return 1; } pParse->aBlob = aNew; pParse->nBlobAlloc = t; return 0; } /* |
︙ | ︙ | |||
1394 1395 1396 1397 1398 1399 1400 | case JSONB_TEXT5: { j = i+n; k = j+sz; while( j<k ){ if( !jsonIsOk[z[j]] && z[j]!='\'' ){ if( z[j]=='"' ){ if( x==JSONB_TEXTJ ) return j+1; | | > > > | 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 | case JSONB_TEXT5: { j = i+n; k = j+sz; while( j<k ){ if( !jsonIsOk[z[j]] && z[j]!='\'' ){ if( z[j]=='"' ){ if( x==JSONB_TEXTJ ) return j+1; }else if( z[j]<=0x1f ){ /* Control characters in JSON5 string literals are ok */ if( x==JSONB_TEXTJ ) return j+1; }else if( NEVER(z[j]!='\\') || j+1>=k ){ return j+1; }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){ j++; }else if( z[j+1]=='u' ){ if( j+5>=k ) return j+1; if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1; j++; |
︙ | ︙ | |||
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 | jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); pParse->iDepth--; return j+1; } case '[': { /* Parse array */ iThis = pParse->nBlob; jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0); iStart = pParse->nBlob; if( pParse->oom ) return -1; if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; return -1; } | > | 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 | jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); pParse->iDepth--; return j+1; } case '[': { /* Parse array */ iThis = pParse->nBlob; assert( i<=(u32)pParse->nJson ); jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0); iStart = pParse->nBlob; if( pParse->oom ) return -1; if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; return -1; } |
︙ | ︙ | |||
1688 1689 1690 1691 1692 1693 1694 | opcode = JSONB_TEXT5; pParse->hasNonstd = 1; }else{ pParse->iErr = j; return -1; } }else if( c<=0x1f ){ | | | | > > > > > | 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 | opcode = JSONB_TEXT5; pParse->hasNonstd = 1; }else{ pParse->iErr = j; return -1; } }else if( c<=0x1f ){ if( c==0 ){ pParse->iErr = j; return -1; } /* Control characters are not allowed in canonical JSON string ** literals, but are allowed in JSON5 string literals. */ opcode = JSONB_TEXT5; pParse->hasNonstd = 1; }else if( c=='"' ){ opcode = JSONB_TEXT5; } j++; } jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]); return j+1; |
︙ | ︙ | |||
1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 | } case 'n': { if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ jsonBlobAppendOneByte(pParse, JSONB_NULL); return i+4; } /* fall-through into the default case that checks for NaN */ } default: { u32 k; int nn; c = z[i]; for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){ if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue; | > | 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 | } case 'n': { if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ jsonBlobAppendOneByte(pParse, JSONB_NULL); return i+4; } /* fall-through into the default case that checks for NaN */ /* no break */ deliberate_fall_through } default: { u32 k; int nn; c = z[i]; for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){ if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue; |
︙ | ︙ | |||
1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 | ** this into the JSONB format and make it the return value of the ** SQL function. */ static void jsonReturnStringAsBlob(JsonString *pStr){ JsonParse px; memset(&px, 0, sizeof(px)); jsonStringTerminate(pStr); px.zJson = pStr->zBuf; px.nJson = pStr->nUsed; (void)jsonTranslateTextToBlob(&px, 0); if( px.oom ){ | > > > > > | | | 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 | ** this into the JSONB format and make it the return value of the ** SQL function. */ static void jsonReturnStringAsBlob(JsonString *pStr){ JsonParse px; memset(&px, 0, sizeof(px)); jsonStringTerminate(pStr); if( pStr->eErr ){ sqlite3_result_error_nomem(pStr->pCtx); return; } px.zJson = pStr->zBuf; px.nJson = pStr->nUsed; px.db = sqlite3_context_db_handle(pStr->pCtx); (void)jsonTranslateTextToBlob(&px, 0); if( px.oom ){ sqlite3DbFree(px.db, px.aBlob); sqlite3_result_error_nomem(pStr->pCtx); }else{ assert( px.nBlobAlloc>0 ); assert( !px.bReadOnly ); sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC); } } /* The byte at index i is a node type-code. This routine ** determines the payload size for that node and writes that ** payload size in to *pSz. It returns the offset from i to the ** beginning of the payload. Return 0 on error. |
︙ | ︙ | |||
2056 2057 2058 2059 2060 2061 2062 | *pSz = 0; return 0; } sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) + (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8]; n = 9; } | | | | 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 | *pSz = 0; return 0; } sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) + (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8]; n = 9; } if( (i64)i+sz+n > pParse->nBlob && (i64)i+sz+n > pParse->nBlob-pParse->delta ){ sz = 0; n = 0; } *pSz = sz; return n; } |
︙ | ︙ | |||
2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 | } case JSONB_FALSE: { jsonAppendRawNZ(pOut, "false", 5); return i+1; } case JSONB_INT: case JSONB_FLOAT: { jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); break; } case JSONB_INT5: { /* Integer literal in hexadecimal notation */ u32 k = 2; sqlite3_uint64 u = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; int bOverflow = 0; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); k++; }else if( zIn[0]=='+' ){ k++; } for(; k<sz; k++){ | > > | 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 | } case JSONB_FALSE: { jsonAppendRawNZ(pOut, "false", 5); return i+1; } case JSONB_INT: case JSONB_FLOAT: { if( sz==0 ) goto malformed_jsonb; jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); break; } case JSONB_INT5: { /* Integer literal in hexadecimal notation */ u32 k = 2; sqlite3_uint64 u = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; int bOverflow = 0; if( sz==0 ) goto malformed_jsonb; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); k++; }else if( zIn[0]=='+' ){ k++; } for(; k<sz; k++){ |
︙ | ︙ | |||
2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 | } jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u); break; } case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ u32 k = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); k++; } if( zIn[k]=='.' ){ jsonAppendChar(pOut, '0'); } | > | 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 | } jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u); break; } case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ u32 k = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; if( sz==0 ) goto malformed_jsonb; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); k++; } if( zIn[k]=='.' ){ jsonAppendChar(pOut, '0'); } |
︙ | ︙ | |||
2166 2167 2168 2169 2170 2171 2172 | case JSONB_TEXT5: { const char *zIn; u32 k; u32 sz2 = sz; zIn = (const char*)&pParse->aBlob[i+n]; jsonAppendChar(pOut, '"'); while( sz2>0 ){ | | > > > > > > > | 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 | case JSONB_TEXT5: { const char *zIn; u32 k; u32 sz2 = sz; zIn = (const char*)&pParse->aBlob[i+n]; jsonAppendChar(pOut, '"'); while( sz2>0 ){ for(k=0; k<sz2 && (jsonIsOk[(u8)zIn[k]] || zIn[k]=='\''); k++){} if( k>0 ){ jsonAppendRawNZ(pOut, zIn, k); if( k>=sz2 ){ break; } zIn += k; sz2 -= k; } if( zIn[0]=='"' ){ jsonAppendRawNZ(pOut, "\\\"", 2); zIn++; sz2--; continue; } if( zIn[0]<=0x1f ){ if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break; jsonAppendControlChar(pOut, zIn[0]); zIn++; sz2--; continue; } assert( zIn[0]=='\\' ); assert( sz2>=1 ); if( sz2<2 ){ pOut->eErr |= JSTRING_MALFORMED; |
︙ | ︙ | |||
2250 2251 2252 2253 2254 2255 2256 | jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz); break; } case JSONB_ARRAY: { jsonAppendChar(pOut, '['); j = i+n; iEnd = j+sz; | | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 | jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz); break; } case JSONB_ARRAY: { jsonAppendChar(pOut, '['); j = i+n; iEnd = j+sz; while( j<iEnd && pOut->eErr==0 ){ j = jsonTranslateBlobToText(pParse, j, pOut); jsonAppendChar(pOut, ','); } if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; if( sz>0 ) jsonStringTrimOneChar(pOut); jsonAppendChar(pOut, ']'); break; } case JSONB_OBJECT: { int x = 0; jsonAppendChar(pOut, '{'); j = i+n; iEnd = j+sz; while( j<iEnd && pOut->eErr==0 ){ j = jsonTranslateBlobToText(pParse, j, pOut); jsonAppendChar(pOut, (x++ & 1) ? ',' : ':'); } if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; if( sz>0 ) jsonStringTrimOneChar(pOut); jsonAppendChar(pOut, '}'); break; } default: { malformed_jsonb: pOut->eErr |= JSTRING_MALFORMED; break; } } return i+n+sz; } /* Context for recursion of json_pretty() */ typedef struct JsonPretty JsonPretty; struct JsonPretty { JsonParse *pParse; /* The BLOB being rendered */ JsonString *pOut; /* Generate pretty output into this string */ const char *zIndent; /* Use this text for indentation */ u32 szIndent; /* Bytes in zIndent[] */ u32 nIndent; /* Current level of indentation */ }; /* Append indentation to the pretty JSON under construction */ static void jsonPrettyIndent(JsonPretty *pPretty){ u32 jj; for(jj=0; jj<pPretty->nIndent; jj++){ jsonAppendRaw(pPretty->pOut, pPretty->zIndent, pPretty->szIndent); } } /* ** Translate the binary JSONB representation of JSON beginning at ** pParse->aBlob[i] into a JSON text string. Append the JSON ** text onto the end of pOut. Return the index in pParse->aBlob[] ** of the first byte past the end of the element that is translated. ** ** This is a variant of jsonTranslateBlobToText() that "pretty-prints" ** the output. Extra whitespace is inserted to make the JSON easier ** for humans to read. ** ** If an error is detected in the BLOB input, the pOut->eErr flag ** might get set to JSTRING_MALFORMED. But not all BLOB input errors ** are detected. So a malformed JSONB input might either result ** in an error, or in incorrect JSON. ** ** The pOut->eErr JSTRING_OOM flag is set on a OOM. */ static u32 jsonTranslateBlobToPrettyText( JsonPretty *pPretty, /* Pretty-printing context */ u32 i /* Start rendering at this index */ ){ u32 sz, n, j, iEnd; const JsonParse *pParse = pPretty->pParse; JsonString *pOut = pPretty->pOut; n = jsonbPayloadSize(pParse, i, &sz); if( n==0 ){ pOut->eErr |= JSTRING_MALFORMED; return pParse->nBlob+1; } switch( pParse->aBlob[i] & 0x0f ){ case JSONB_ARRAY: { j = i+n; iEnd = j+sz; jsonAppendChar(pOut, '['); if( j<iEnd ){ jsonAppendChar(pOut, '\n'); pPretty->nIndent++; while( pOut->eErr==0 ){ jsonPrettyIndent(pPretty); j = jsonTranslateBlobToPrettyText(pPretty, j); if( j>=iEnd ) break; jsonAppendRawNZ(pOut, ",\n", 2); } jsonAppendChar(pOut, '\n'); pPretty->nIndent--; jsonPrettyIndent(pPretty); } jsonAppendChar(pOut, ']'); i = iEnd; break; } case JSONB_OBJECT: { j = i+n; iEnd = j+sz; jsonAppendChar(pOut, '{'); if( j<iEnd ){ jsonAppendChar(pOut, '\n'); pPretty->nIndent++; while( pOut->eErr==0 ){ jsonPrettyIndent(pPretty); j = jsonTranslateBlobToText(pParse, j, pOut); if( j>iEnd ){ pOut->eErr |= JSTRING_MALFORMED; break; } jsonAppendRawNZ(pOut, ": ", 2); j = jsonTranslateBlobToPrettyText(pPretty, j); if( j>=iEnd ) break; jsonAppendRawNZ(pOut, ",\n", 2); } jsonAppendChar(pOut, '\n'); pPretty->nIndent--; jsonPrettyIndent(pPretty); } jsonAppendChar(pOut, '}'); i = iEnd; break; } default: { i = jsonTranslateBlobToText(pParse, i, pOut); break; } } return i; } /* Return true if the input pJson ** ** For performance reasons, this routine does not do a detailed check of the ** input BLOB to ensure that it is well-formed. Hence, false positives are ** possible. False negatives should never occur, however. */ |
︙ | ︙ | |||
2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 | JsonParse *pParse, /* The original JSONB that is being edited */ JsonParse *pIns, /* Populate this with the blob data to insert */ const char *zTail /* Tail of the path that determins substructure */ ){ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; int rc; memset(pIns, 0, sizeof(*pIns)); if( zTail[0]==0 ){ /* No substructure. Just insert what is given in pParse. */ pIns->aBlob = pParse->aIns; pIns->nBlob = pParse->nIns; rc = 0; }else{ /* Construct the binary substructure */ | > | 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 | JsonParse *pParse, /* The original JSONB that is being edited */ JsonParse *pIns, /* Populate this with the blob data to insert */ const char *zTail /* Tail of the path that determins substructure */ ){ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; int rc; memset(pIns, 0, sizeof(*pIns)); pIns->db = pParse->db; if( zTail[0]==0 ){ /* No substructure. Just insert what is given in pParse. */ pIns->aBlob = pParse->aIns; pIns->nBlob = pParse->nIns; rc = 0; }else{ /* Construct the binary substructure */ |
︙ | ︙ | |||
2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 | if( pParse->eEdit>=JEDIT_INS ){ u32 nIns; /* Total bytes to insert (label+value) */ JsonParse v; /* BLOB encoding of the value to be inserted */ JsonParse ix; /* Header of the label to be inserted */ testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); memset(&ix, 0, sizeof(ix)); jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); pParse->oom |= ix.oom; rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); if( !JSON_LOOKUP_ISERROR(rc) && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) ){ assert( !pParse->oom ); | > | 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 | if( pParse->eEdit>=JEDIT_INS ){ u32 nIns; /* Total bytes to insert (label+value) */ JsonParse v; /* BLOB encoding of the value to be inserted */ JsonParse ix; /* Header of the label to be inserted */ testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); memset(&ix, 0, sizeof(ix)); ix.db = pParse->db; jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); pParse->oom |= ix.oom; rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); if( !JSON_LOOKUP_ISERROR(rc) && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) ){ assert( !pParse->oom ); |
︙ | ︙ | |||
2952 2953 2954 2955 2956 2957 2958 | case JSONB_TEXTJ: { /* Translate JSON formatted string into raw text */ u32 iIn, iOut; const char *z; char *zOut; u32 nOut = sz; z = (const char*)&pParse->aBlob[i+n]; | | | 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 | case JSONB_TEXTJ: { /* Translate JSON formatted string into raw text */ u32 iIn, iOut; const char *z; char *zOut; u32 nOut = sz; z = (const char*)&pParse->aBlob[i+n]; zOut = sqlite3DbMallocRaw(db, nOut+1); if( zOut==0 ) goto returnfromblob_oom; for(iIn=iOut=0; iIn<sz; iIn++){ char c = z[iIn]; if( c=='\\' ){ u32 v; u32 szEscape = jsonUnescapeOneChar(&z[iIn], sz-iIn, &v); if( v<=0x7f ){ |
︙ | ︙ | |||
2986 2987 2988 2989 2990 2991 2992 | iIn += szEscape - 1; }else{ zOut[iOut++] = c; } } /* end for() */ assert( iOut<=nOut ); zOut[iOut] = 0; | | | 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 | iIn += szEscape - 1; }else{ zOut[iOut++] = c; } } /* end for() */ assert( iOut<=nOut ); zOut[iOut] = 0; sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC); break; } case JSONB_ARRAY: case JSONB_OBJECT: { int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); if( flags & JSON_BLOB ){ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT); |
︙ | ︙ | |||
3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 | sqlite3_context *ctx, sqlite3_value *pArg, JsonParse *pParse ){ int eType = sqlite3_value_type(pArg); static u8 aNull[] = { 0x00 }; memset(pParse, 0, sizeof(pParse[0])); switch( eType ){ default: { pParse->aBlob = aNull; pParse->nBlob = 1; return 0; } case SQLITE_BLOB: { | > | 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 | sqlite3_context *ctx, sqlite3_value *pArg, JsonParse *pParse ){ int eType = sqlite3_value_type(pArg); static u8 aNull[] = { 0x00 }; memset(pParse, 0, sizeof(pParse[0])); pParse->db = sqlite3_context_db_handle(ctx); switch( eType ){ default: { pParse->aBlob = aNull; pParse->nBlob = 1; return 0; } case SQLITE_BLOB: { |
︙ | ︙ | |||
3064 3065 3066 3067 3068 3069 3070 | int nJson = sqlite3_value_bytes(pArg); if( zJson==0 ) return 1; if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){ pParse->zJson = (char*)zJson; pParse->nJson = nJson; if( jsonConvertTextToBlob(pParse, ctx) ){ sqlite3_result_error(ctx, "malformed JSON", -1); | | | 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 | int nJson = sqlite3_value_bytes(pArg); if( zJson==0 ) return 1; if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){ pParse->zJson = (char*)zJson; pParse->nJson = nJson; if( jsonConvertTextToBlob(pParse, ctx) ){ sqlite3_result_error(ctx, "malformed JSON", -1); sqlite3DbFree(pParse->db, pParse->aBlob); memset(pParse, 0, sizeof(pParse[0])); return 1; } }else{ jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson); } break; |
︙ | ︙ | |||
3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 | if( rc==JSON_LOOKUP_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else{ jsonBadPathError(ctx, zPath); } return; } /* ** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, ** from the SQL function argument pArg. Return a pointer to the new ** JsonParse object. ** ** Ownership of the new JsonParse object is passed to the caller. The | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 | if( rc==JSON_LOOKUP_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else{ jsonBadPathError(ctx, zPath); } return; } /* ** If pArg is a blob that seems like a JSONB blob, then initialize ** p to point to that JSONB and return TRUE. If pArg does not seem like ** a JSONB blob, then return FALSE; ** ** This routine is only called if it is already known that pArg is a ** blob. The only open question is whether or not the blob appears ** to be a JSONB blob. */ static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){ u32 n, sz = 0; p->aBlob = (u8*)sqlite3_value_blob(pArg); p->nBlob = (u32)sqlite3_value_bytes(pArg); if( p->nBlob==0 ){ p->aBlob = 0; return 0; } if( NEVER(p->aBlob==0) ){ return 0; } if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT && (n = jsonbPayloadSize(p, 0, &sz))>0 && sz+n==p->nBlob && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0) ){ return 1; } p->aBlob = 0; p->nBlob = 0; return 0; } /* ** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, ** from the SQL function argument pArg. Return a pointer to the new ** JsonParse object. ** ** Ownership of the new JsonParse object is passed to the caller. The |
︙ | ︙ | |||
3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 | sqlite3_context *ctx, sqlite3_value *pArg, u32 flgs ){ int eType; /* Datatype of pArg */ JsonParse *p = 0; /* Value to be returned */ JsonParse *pFromCache = 0; /* Value taken from cache */ assert( ctx!=0 ); eType = sqlite3_value_type(pArg); if( eType==SQLITE_NULL ){ return 0; } pFromCache = jsonCacheSearch(ctx, pArg); if( pFromCache ){ pFromCache->nJPRef++; if( (flgs & JSON_EDITABLE)==0 ){ return pFromCache; } } rebuild_from_cache: | > > | > | < < < < < < < < < < < < < | < < < < < | | | | | > > > > > > > > > > > > > > | | 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 | sqlite3_context *ctx, sqlite3_value *pArg, u32 flgs ){ int eType; /* Datatype of pArg */ JsonParse *p = 0; /* Value to be returned */ JsonParse *pFromCache = 0; /* Value taken from cache */ sqlite3 *db; /* The database connection */ assert( ctx!=0 ); eType = sqlite3_value_type(pArg); if( eType==SQLITE_NULL ){ return 0; } pFromCache = jsonCacheSearch(ctx, pArg); if( pFromCache ){ pFromCache->nJPRef++; if( (flgs & JSON_EDITABLE)==0 ){ return pFromCache; } } db = sqlite3_context_db_handle(ctx); rebuild_from_cache: p = sqlite3DbMallocZero(db, sizeof(*p)); if( p==0 ) goto json_pfa_oom; memset(p, 0, sizeof(*p)); p->db = db; p->nJPRef = 1; if( pFromCache!=0 ){ u32 nBlob = pFromCache->nBlob; p->aBlob = sqlite3DbMallocRaw(db, nBlob); if( p->aBlob==0 ) goto json_pfa_oom; memcpy(p->aBlob, pFromCache->aBlob, nBlob); p->nBlobAlloc = p->nBlob = nBlob; p->hasNonstd = pFromCache->hasNonstd; jsonParseFree(pFromCache); return p; } if( eType==SQLITE_BLOB ){ if( jsonArgIsJsonb(pArg,p) ){ if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){ goto json_pfa_oom; } return p; } /* If the blob is not valid JSONB, fall through into trying to cast ** the blob into text which is then interpreted as JSON. (tag-20240123-a) ** ** This goes against all historical documentation about how the SQLite ** JSON functions were suppose to work. From the beginning, blob was ** reserved for expansion and a blob value should have raised an error. ** But it did not, due to a bug. And many applications came to depend ** upon this buggy behavior, espeically when using the CLI and reading ** JSON text using readfile(), which returns a blob. For this reason ** we will continue to support the bug moving forward. ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d */ } p->zJson = (char*)sqlite3_value_text(pArg); p->nJson = sqlite3_value_bytes(pArg); if( db->mallocFailed ) goto json_pfa_oom; if( p->nJson==0 ) goto json_pfa_malformed; assert( p->zJson!=0 ); if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){ if( flgs & JSON_KEEPERROR ){ p->nErr = 1; return p; }else{ jsonParseFree(p); return 0; |
︙ | ︙ | |||
3443 3444 3445 3446 3447 3448 3449 | break; } } if( showContent ){ if( sz==0 && x<=JSONB_FALSE ){ sqlite3_str_append(pOut, "\n", 1); }else{ | | | | | 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 | break; } } if( showContent ){ if( sz==0 && x<=JSONB_FALSE ){ sqlite3_str_append(pOut, "\n", 1); }else{ u32 j; sqlite3_str_appendall(pOut, ": \""); for(j=iStart+n; j<iStart+n+sz; j++){ u8 c = pParse->aBlob[j]; if( c<0x20 || c>=0x7f ) c = '.'; sqlite3_str_append(pOut, (char*)&c, 1); } sqlite3_str_append(pOut, "\"\n", 2); } } iStart += n + sz; |
︙ | ︙ | |||
3497 3498 3499 3500 3501 3502 3503 | assert( argc>=1 ); sqlite3StrAccumInit(&out, 0, 0, 0, 1000000); p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; if( argc==1 ){ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out); | | > | 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 | assert( argc>=1 ); sqlite3StrAccumInit(&out, 0, 0, 0, 1000000); p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; if( argc==1 ){ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out); sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8); }else{ jsonShowParse(p); } jsonParseFree(p); sqlite3_str_reset(&out); } #endif /* SQLITE_DEBUG */ /**************************************************************************** ** Scalar SQL function implementations ****************************************************************************/ |
︙ | ︙ | |||
3600 3601 3602 3603 3604 3605 3606 | if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ cnt = jsonbArrayCount(p, i); } if( !eErr ) sqlite3_result_int64(ctx, cnt); jsonParseFree(p); } | < < < < < < < | 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 | if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ cnt = jsonbArrayCount(p, i); } if( !eErr ) sqlite3_result_int64(ctx, cnt); jsonParseFree(p); } /* True if the string is all alphanumerics and underscores */ static int jsonAllAlphanum(const char *z, int n){ int i; for(i=0; i<n && (sqlite3Isalnum(z[i]) || z[i]=='_'); i++){} return i==n; } |
︙ | ︙ | |||
3671 3672 3673 3674 3675 3676 3677 | ** convenience. ** ** NUMBER ==> $[NUMBER] // PG compatible ** LABEL ==> $.LABEL // PG compatible ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience */ jsonStringInit(&jx, ctx); | | | 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 | ** convenience. ** ** NUMBER ==> $[NUMBER] // PG compatible ** LABEL ==> $.LABEL // PG compatible ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience */ jsonStringInit(&jx, ctx); if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ jsonAppendRawNZ(&jx, "[", 1); jsonAppendRaw(&jx, zPath, nPath); jsonAppendRawNZ(&jx, "]", 2); }else if( jsonAllAlphanum(zPath, nPath) ){ jsonAppendRawNZ(&jx, ".", 1); jsonAppendRaw(&jx, zPath, nPath); }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){ |
︙ | ︙ | |||
4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 | }else{ i = 0; } sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC); json_type_done: jsonParseFree(p); } /* ** json_valid(JSON) ** json_valid(JSON, FLAGS) ** ** Check the JSON argument to see if it is well-formed. The FLAGS argument ** encodes the various constraints on what is meant by "well-formed": | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 | }else{ i = 0; } sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC); json_type_done: jsonParseFree(p); } /* ** json_pretty(JSON) ** json_pretty(JSON, INDENT) ** ** Return text that is a pretty-printed rendering of the input JSON. ** If the argument is not valid JSON, return NULL. ** ** The INDENT argument is text that is used for indentation. If omitted, ** it defaults to four spaces (the same as PostgreSQL). */ static void jsonPrettyFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonString s; /* The output string */ JsonPretty x; /* Pretty printing context */ memset(&x, 0, sizeof(x)); x.pParse = jsonParseFuncArg(ctx, argv[0], 0); if( x.pParse==0 ) return; x.pOut = &s; jsonStringInit(&s, ctx); if( argc==1 || (x.zIndent = (const char*)sqlite3_value_text(argv[1]))==0 ){ x.zIndent = " "; x.szIndent = 4; }else{ x.szIndent = (u32)strlen(x.zIndent); } jsonTranslateBlobToPrettyText(&x, 0); jsonReturnString(&s, 0, 0); jsonParseFree(x.pParse); } /* ** json_valid(JSON) ** json_valid(JSON, FLAGS) ** ** Check the JSON argument to see if it is well-formed. The FLAGS argument ** encodes the various constraints on what is meant by "well-formed": |
︙ | ︙ | |||
4248 4249 4250 4251 4252 4253 4254 | #ifdef SQLITE_LEGACY_JSON_VALID /* Incorrect legacy behavior was to return FALSE for a NULL input */ sqlite3_result_int(ctx, 0); #endif return; } case SQLITE_BLOB: { | | | > > > | | 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 | #ifdef SQLITE_LEGACY_JSON_VALID /* Incorrect legacy behavior was to return FALSE for a NULL input */ sqlite3_result_int(ctx, 0); #endif return; } case SQLITE_BLOB: { if( jsonFuncArgMightBeBinary(argv[0]) ){ if( flags & 0x04 ){ /* Superficial checking only - accomplished by the ** jsonFuncArgMightBeBinary() call above. */ res = 1; }else if( flags & 0x08 ){ /* Strict checking. Check by translating BLOB->TEXT->BLOB. If ** no errors occur, call that a "strict check". */ JsonParse px; u32 iErr; memset(&px, 0, sizeof(px)); px.aBlob = (u8*)sqlite3_value_blob(argv[0]); px.nBlob = sqlite3_value_bytes(argv[0]); iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1); res = iErr==0; } break; } /* Fall through into interpreting the input as text. See note ** above at tag-20240123-a. */ /* no break */ deliberate_fall_through } default: { JsonParse px; if( (flags & 0x3)==0 ) break; memset(&px, 0, sizeof(px)); p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR); |
︙ | ︙ | |||
4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 | ){ i64 iErrPos = 0; /* Error position to be returned */ JsonParse s; assert( argc==1 ); UNUSED_PARAMETER(argc); memset(&s, 0, sizeof(s)); if( jsonFuncArgMightBeBinary(argv[0]) ){ s.aBlob = (u8*)sqlite3_value_blob(argv[0]); s.nBlob = sqlite3_value_bytes(argv[0]); iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1); }else{ s.zJson = (char*)sqlite3_value_text(argv[0]); if( s.zJson==0 ) return; /* NULL input or OOM */ | > | 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 | ){ i64 iErrPos = 0; /* Error position to be returned */ JsonParse s; assert( argc==1 ); UNUSED_PARAMETER(argc); memset(&s, 0, sizeof(s)); s.db = sqlite3_context_db_handle(ctx); if( jsonFuncArgMightBeBinary(argv[0]) ){ s.aBlob = (u8*)sqlite3_value_blob(argv[0]); s.nBlob = sqlite3_value_bytes(argv[0]); iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1); }else{ s.zJson = (char*)sqlite3_value_text(argv[0]); if( s.zJson==0 ) return; /* NULL input or OOM */ |
︙ | ︙ | |||
4389 4390 4391 4392 4393 4394 4395 | jsonReturnString(pStr, 0, 0); return; }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); if( isFinal ){ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); }else{ | | | | 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 | jsonReturnString(pStr, 0, 0); return; }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); if( isFinal ){ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); }else{ jsonStringTrimOneChar(pStr); } return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, pStr->bStatic ? SQLITE_TRANSIENT : sqlite3RCStrUnref); pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); jsonStringTrimOneChar(pStr); } }else{ sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC); } sqlite3_result_subtype(ctx, JSON_SUBTYPE); } static void jsonArrayValue(sqlite3_context *ctx){ |
︙ | ︙ | |||
4509 4510 4511 4512 4513 4514 4515 | jsonReturnString(pStr, 0, 0); return; }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); if( isFinal ){ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); }else{ | | | | 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 | jsonReturnString(pStr, 0, 0); return; }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); if( isFinal ){ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); }else{ jsonStringTrimOneChar(pStr); } return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, pStr->bStatic ? SQLITE_TRANSIENT : sqlite3RCStrUnref); pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); jsonStringTrimOneChar(pStr); } }else{ sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC); } sqlite3_result_subtype(ctx, JSON_SUBTYPE); } static void jsonObjectValue(sqlite3_context *ctx){ |
︙ | ︙ | |||
4605 4606 4607 4608 4609 4610 4611 | UNUSED_PARAMETER(argv); UNUSED_PARAMETER(argc); UNUSED_PARAMETER(pAux); rc = sqlite3_declare_vtab(db, "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path," "json HIDDEN,root HIDDEN)"); if( rc==SQLITE_OK ){ | | > < > | | < | 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 | UNUSED_PARAMETER(argv); UNUSED_PARAMETER(argc); UNUSED_PARAMETER(pAux); rc = sqlite3_declare_vtab(db, "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path," "json HIDDEN,root HIDDEN)"); if( rc==SQLITE_OK ){ pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew)); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); pNew->db = db; } return rc; } /* destructor for json_each virtual table */ static int jsonEachDisconnect(sqlite3_vtab *pVtab){ JsonEachConnection *p = (JsonEachConnection*)pVtab; sqlite3DbFree(p->db, pVtab); return SQLITE_OK; } /* constructor for a JsonEachCursor object for json_each(). */ static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ JsonEachConnection *pVtab = (JsonEachConnection*)p; JsonEachCursor *pCur; UNUSED_PARAMETER(p); pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur)); if( pCur==0 ) return SQLITE_NOMEM; pCur->db = pVtab->db; jsonStringZero(&pCur->path); *ppCursor = &pCur->base; return SQLITE_OK; } /* constructor for a JsonEachCursor object for json_tree(). */ |
︙ | ︙ | |||
4665 4666 4667 4668 4669 4670 4671 | } /* Destructor for a jsonEachCursor object */ static int jsonEachClose(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; jsonEachCursorReset(p); | | | 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 | } /* Destructor for a jsonEachCursor object */ static int jsonEachClose(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; jsonEachCursorReset(p); sqlite3DbFree(p->db, cur); return SQLITE_OK; } /* Return TRUE if the jsonEachCursor object has been advanced off the end ** of the JSON object */ static int jsonEachEof(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; |
︙ | ︙ | |||
4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 | sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey); } break; } case JEACH_VALUE: { u32 i = jsonSkipLabel(p); jsonReturnFromBlob(&p->sParse, i, ctx, 1); break; } case JEACH_TYPE: { u32 i = jsonSkipLabel(p); u8 eType = p->sParse.aBlob[i] & 0x0f; sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); break; | > > > | 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 | sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey); } break; } case JEACH_VALUE: { u32 i = jsonSkipLabel(p); jsonReturnFromBlob(&p->sParse, i, ctx, 1); if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){ sqlite3_result_subtype(ctx, JSON_SUBTYPE); } break; } case JEACH_TYPE: { u32 i = jsonSkipLabel(p); u8 eType = p->sParse.aBlob[i] & 0x0f; sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); break; |
︙ | ︙ | |||
4896 4897 4898 4899 4900 4901 4902 | default: { sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC); break; } case JEACH_JSON: { if( p->sParse.zJson==0 ){ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, | | | | 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 | default: { sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC); break; } case JEACH_JSON: { if( p->sParse.zJson==0 ){ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, SQLITE_TRANSIENT); }else{ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_TRANSIENT); } break; } } return SQLITE_OK; } |
︙ | ︙ | |||
4999 5000 5001 5002 5003 5004 5005 | UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(argc); jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; memset(&p->sParse, 0, sizeof(p->sParse)); p->sParse.nJPRef = 1; | | | | | < < < | 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 | UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(argc); jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; memset(&p->sParse, 0, sizeof(p->sParse)); p->sParse.nJPRef = 1; p->sParse.db = p->db; if( jsonFuncArgMightBeBinary(argv[0]) ){ p->sParse.nBlob = sqlite3_value_bytes(argv[0]); p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); }else{ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); p->sParse.nJson = sqlite3_value_bytes(argv[0]); if( p->sParse.zJson==0 ){ p->i = p->iEnd = 0; return SQLITE_OK; } |
︙ | ︙ | |||
5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 | JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc), JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc), JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc), JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc), JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc), JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc), JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc), JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc), JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc), JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc), JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc), JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc), JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc), JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc), | > > | 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 | JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc), JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc), JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc), JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc), JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc), JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc), JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc), JFUNCTION(json_pretty, 1,1,0, 0,0,0, jsonPrettyFunc), JFUNCTION(json_pretty, 2,1,0, 0,0,0, jsonPrettyFunc), JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc), JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc), JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc), JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc), JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc), JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc), JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc), |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
760 761 762 763 764 765 766 767 768 769 770 771 772 773 | #ifndef SQLITE_OMIT_DESERIALIZE case SQLITE_CONFIG_MEMDB_MAXSIZE: { sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, sqlite3_int64); break; } #endif /* SQLITE_OMIT_DESERIALIZE */ default: { rc = SQLITE_ERROR; break; } } va_end(ap); | > > > > > > > > > > > > | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | #ifndef SQLITE_OMIT_DESERIALIZE case SQLITE_CONFIG_MEMDB_MAXSIZE: { sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, sqlite3_int64); break; } #endif /* SQLITE_OMIT_DESERIALIZE */ case SQLITE_CONFIG_ROWID_IN_VIEW: { int *pVal = va_arg(ap,int*); #ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( 0==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = TF_NoVisibleRowid; if( 1==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = 0; *pVal = (sqlite3GlobalConfig.mNoVisibleRowid==0); #else *pVal = 0; #endif break; } default: { rc = SQLITE_ERROR; break; } } va_end(ap); |
︙ | ︙ | |||
4667 4668 4669 4670 4671 4672 4673 | ** only available if compiled using SQLITE_DEBUG. ** ** If onOff is initially 1, then turn it on. If onOff is initially ** off, turn it off. If onOff is initially -1, then change onOff ** to be the current setting. */ case SQLITE_TESTCTRL_JSON_SELFCHECK: { | | | 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 | ** only available if compiled using SQLITE_DEBUG. ** ** If onOff is initially 1, then turn it on. If onOff is initially ** off, turn it off. If onOff is initially -1, then change onOff ** to be the current setting. */ case SQLITE_TESTCTRL_JSON_SELFCHECK: { #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) int *pOnOff = va_arg(ap, int*); if( *pOnOff<0 ){ *pOnOff = sqlite3Config.bJsonSelfcheck; }else{ sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff); } #endif |
︙ | ︙ |
Changes to src/malloc.c.
︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 229 230 | static void sqlite3MallocAlarm(int nByte){ if( mem0.alarmThreshold<=0 ) return; sqlite3_mutex_leave(mem0.mutex); sqlite3_release_memory(nByte); sqlite3_mutex_enter(mem0.mutex); } /* ** Do a memory allocation with statistics and alarms. Assume the ** lock is already held. */ static void mallocWithAlarm(int n, void **pp){ void *p; int nFull; | > > > > > > > > > > > > > > > > > > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | static void sqlite3MallocAlarm(int nByte){ if( mem0.alarmThreshold<=0 ) return; sqlite3_mutex_leave(mem0.mutex); sqlite3_release_memory(nByte); sqlite3_mutex_enter(mem0.mutex); } #ifdef SQLITE_DEBUG /* ** This routine is called whenever an out-of-memory condition is seen, ** It's only purpose to to serve as a breakpoint for gdb or similar ** code debuggers when working on out-of-memory conditions, for example ** caused by PRAGMA hard_heap_limit=N. */ static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){ static u64 nOomFault = 0; nOomFault += n; /* The assert() is never reached in a human lifetime. It is here mostly ** to prevent code optimizers from optimizing out this function. */ assert( (nOomFault>>32) < 0xffffffff ); } #else # define test_oom_breakpoint(X) /* No-op for production builds */ #endif /* ** Do a memory allocation with statistics and alarms. Assume the ** lock is already held. */ static void mallocWithAlarm(int n, void **pp){ void *p; int nFull; |
︙ | ︙ | |||
243 244 245 246 247 248 249 250 251 252 253 254 255 256 | sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.alarmThreshold - nFull ){ AtomicStore(&mem0.nearlyFull, 1); sqlite3MallocAlarm(nFull); if( mem0.hardLimit ){ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.hardLimit - nFull ){ *pp = 0; return; } } }else{ AtomicStore(&mem0.nearlyFull, 0); } | > | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.alarmThreshold - nFull ){ AtomicStore(&mem0.nearlyFull, 1); sqlite3MallocAlarm(nFull); if( mem0.hardLimit ){ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.hardLimit - nFull ){ test_oom_breakpoint(1); *pp = 0; return; } } }else{ AtomicStore(&mem0.nearlyFull, 0); } |
︙ | ︙ | |||
531 532 533 534 535 536 537 538 539 540 541 542 543 544 | sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); nDiff = nNew - nOld; if( nDiff>0 && (nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >= mem0.alarmThreshold-nDiff ){ sqlite3MallocAlarm(nDiff); if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ sqlite3_mutex_leave(mem0.mutex); return 0; } } pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT if( pNew==0 && mem0.alarmThreshold>0 ){ sqlite3MallocAlarm((int)nBytes); | > | 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); nDiff = nNew - nOld; if( nDiff>0 && (nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >= mem0.alarmThreshold-nDiff ){ sqlite3MallocAlarm(nDiff); if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ sqlite3_mutex_leave(mem0.mutex); test_oom_breakpoint(1); return 0; } } pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT if( pNew==0 && mem0.alarmThreshold>0 ){ sqlite3MallocAlarm((int)nBytes); |
︙ | ︙ |
Changes to src/memdb.c.
︙ | ︙ | |||
795 796 797 798 799 800 801 802 803 804 805 806 807 808 | sqlite3_free(zSql); if( rc ) return 0; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ){ pOut = 0; }else{ sz = sqlite3_column_int64(pStmt, 0)*szPage; if( piSize ) *piSize = sz; if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ pOut = 0; }else{ pOut = sqlite3_malloc64( sz ); if( pOut ){ int nPage = sqlite3_column_int(pStmt, 0); | > > > > > > > > | 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 | sqlite3_free(zSql); if( rc ) return 0; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ){ pOut = 0; }else{ sz = sqlite3_column_int64(pStmt, 0)*szPage; if( sz==0 ){ sqlite3_reset(pStmt); sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0); rc = sqlite3_step(pStmt); if( rc==SQLITE_ROW ){ sz = sqlite3_column_int64(pStmt, 0)*szPage; } } if( piSize ) *piSize = sz; if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ pOut = 0; }else{ pOut = sqlite3_malloc64( sz ); if( pOut ){ int nPage = sqlite3_column_int(pStmt, 0); |
︙ | ︙ |
Changes to src/os_unix.c.
︙ | ︙ | |||
1291 1292 1293 1294 1295 1296 1297 1298 | ** Otherwise, assume that the system provides the POSIX version of ** strerror_r(), which always writes an error message into aErr[]. ** ** If the code incorrectly assumes that it is the POSIX version that is ** available, the error message will often be an empty string. Not a ** huge problem. Incorrectly concluding that the GNU version is available ** could lead to a segfault though. */ | > > > | > | 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 | ** Otherwise, assume that the system provides the POSIX version of ** strerror_r(), which always writes an error message into aErr[]. ** ** If the code incorrectly assumes that it is the POSIX version that is ** available, the error message will often be an empty string. Not a ** huge problem. Incorrectly concluding that the GNU version is available ** could lead to a segfault though. ** ** Forum post 3f13857fa4062301 reports that the Android SDK may use ** int-type return, depending on its version. */ #if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \ && !defined(ANDROID) && !defined(__ANDROID__) zErr = # endif strerror_r(iErrno, aErr, sizeof(aErr)-1); #elif SQLITE_THREADSAFE /* This is a threadsafe build, but strerror_r() is not available. */ zErr = ""; |
︙ | ︙ | |||
5091 5092 5093 5094 5095 5096 5097 | ** a lock on a read-locking slot at this point, this breaks the ** anti-deadlock rules (see above). */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT int iMutex; for(iMutex=ofst; iMutex<ofst+n; iMutex++){ if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){ rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]); | | | | 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 | ** a lock on a read-locking slot at this point, this breaks the ** anti-deadlock rules (see above). */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT int iMutex; for(iMutex=ofst; iMutex<ofst+n; iMutex++){ if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){ rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]); if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes; }else{ sqlite3_mutex_enter(pShmNode->aMutex[iMutex]); } } #else sqlite3_mutex_enter(pShmNode->pShmMutex); #endif if( ALWAYS(rc==SQLITE_OK) ){ if( flags & SQLITE_SHM_UNLOCK ){ /* Case (a) - unlock. */ int bUnlock = 1; assert( (p->exclMask & p->sharedMask)==0 ); assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); |
︙ | ︙ | |||
5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 | } } assert( assertLockingArrayOk(pShmNode) ); } /* Drop the mutexes acquired above. */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT for(iMutex--; iMutex>=ofst; iMutex--){ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]); } #else sqlite3_mutex_leave(pShmNode->pShmMutex); #endif } | > | 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 | } } assert( assertLockingArrayOk(pShmNode) ); } /* Drop the mutexes acquired above. */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT leave_shmnode_mutexes: for(iMutex--; iMutex>=ofst; iMutex--){ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]); } #else sqlite3_mutex_leave(pShmNode->pShmMutex); #endif } |
︙ | ︙ | |||
5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 | #if SQLITE_MAX_MMAP_SIZE>0 unixFile *pFd = (unixFile *)fd; /* The underlying database file */ #endif *pp = 0; #if SQLITE_MAX_MMAP_SIZE>0 if( pFd->mmapSizeMax>0 ){ if( pFd->pMapRegion==0 ){ int rc = unixMapfile(pFd, -1); if( rc!=SQLITE_OK ) return rc; } | > > > > > | | 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 | #if SQLITE_MAX_MMAP_SIZE>0 unixFile *pFd = (unixFile *)fd; /* The underlying database file */ #endif *pp = 0; #if SQLITE_MAX_MMAP_SIZE>0 if( pFd->mmapSizeMax>0 ){ /* Ensure that there is always at least a 256 byte buffer of addressable ** memory following the returned page. If the database is corrupt, ** SQLite may overread the page slightly (in practice only a few bytes, ** but 256 is safe, round, number). */ const int nEofBuffer = 256; if( pFd->pMapRegion==0 ){ int rc = unixMapfile(pFd, -1); if( rc!=SQLITE_OK ) return rc; } if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ *pp = &((u8 *)pFd->pMapRegion)[iOff]; pFd->nFetchOut++; } } #endif return SQLITE_OK; } |
︙ | ︙ | |||
6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 | if( fd<0 ){ if( isNewJrnl && errno==EACCES && osAccess(zName, F_OK) ){ /* If unable to create a journal because the directory is not ** writable, change the error code to indicate that. */ rc = SQLITE_READONLY_DIRECTORY; }else if( errno!=EISDIR && isReadWrite ){ /* Failed to open the file for read/write access. Try read-only. */ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); openFlags &= ~(O_RDWR|O_CREAT); flags |= SQLITE_OPEN_READONLY; openFlags |= O_RDONLY; isReadonly = 1; | > > > > > > | > | 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 | if( fd<0 ){ if( isNewJrnl && errno==EACCES && osAccess(zName, F_OK) ){ /* If unable to create a journal because the directory is not ** writable, change the error code to indicate that. */ rc = SQLITE_READONLY_DIRECTORY; }else if( errno!=EISDIR && isReadWrite ){ /* Failed to open the file for read/write access. Try read-only. */ UnixUnusedFd *pReadonly = 0; flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); openFlags &= ~(O_RDWR|O_CREAT); flags |= SQLITE_OPEN_READONLY; openFlags |= O_RDONLY; isReadonly = 1; pReadonly = findReusableFd(zName, flags); if( pReadonly ){ fd = pReadonly->fd; sqlite3_free(pReadonly); }else{ fd = robust_open(zName, openFlags, openMode); } } } if( fd<0 ){ int rc2 = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); if( rc==SQLITE_OK ) rc = rc2; goto open_finished; } |
︙ | ︙ |
Changes to src/os_win.c.
︙ | ︙ | |||
4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 | *pp = 0; OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n", osGetCurrentProcessId(), fd, iOff, nAmt, pp)); #if SQLITE_MAX_MMAP_SIZE>0 if( pFd->mmapSizeMax>0 ){ if( pFd->pMapRegion==0 ){ int rc = winMapfile(pFd, -1); if( rc!=SQLITE_OK ){ OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n", osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); return rc; } } | > > > > > | | 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 | *pp = 0; OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n", osGetCurrentProcessId(), fd, iOff, nAmt, pp)); #if SQLITE_MAX_MMAP_SIZE>0 if( pFd->mmapSizeMax>0 ){ /* Ensure that there is always at least a 256 byte buffer of addressable ** memory following the returned page. If the database is corrupt, ** SQLite may overread the page slightly (in practice only a few bytes, ** but 256 is safe, round, number). */ const int nEofBuffer = 256; if( pFd->pMapRegion==0 ){ int rc = winMapfile(pFd, -1); if( rc!=SQLITE_OK ){ OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n", osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); return rc; } } if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ assert( pFd->pMapRegion!=0 ); *pp = &((u8 *)pFd->pMapRegion)[iOff]; pFd->nFetchOut++; } } #endif |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
814 815 816 817 818 819 820 | */ int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->fd->pMethods==0 ) return 0; if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; | < | | | 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 | */ int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->fd->pMethods==0 ) return 0; if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); return iRead==0; } #endif return 1; } #endif #ifndef SQLITE_OMIT_WAL |
︙ | ︙ | |||
7084 7085 7086 7087 7088 7089 7090 | } /* ** Return the file handle for the journal file (if it exists). ** This will be either the rollback journal or the WAL file. */ sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ | | | 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 | } /* ** Return the file handle for the journal file (if it exists). ** This will be either the rollback journal or the WAL file. */ sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ #ifdef SQLITE_OMIT_WAL return pPager->jfd; #else return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd; #endif } /* |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | ** file that specifies the input grammar and actions to take while parsing. ** That input file is processed by Lemon to generate a C-language ** implementation of a parser for the given grammar. You might be reading ** this comment as part of the translated C-code. Edits should be made ** to the original parse.y sources. */ } // All token codes are small integers with #defines that begin with "TK_" %token_prefix TK_ // The type of the data attached to each token is Token. This is also the // default type for non-terminals. // | > > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** file that specifies the input grammar and actions to take while parsing. ** That input file is processed by Lemon to generate a C-language ** implementation of a parser for the given grammar. You might be reading ** this comment as part of the translated C-code. Edits should be made ** to the original parse.y sources. */ } // Function used to enlarge the parser stack, if needed %realloc parserStackRealloc %free sqlite3_free // All token codes are small integers with #defines that begin with "TK_" %token_prefix TK_ // The type of the data attached to each token is Token. This is also the // default type for non-terminals. // |
︙ | ︙ | |||
41 42 43 44 45 46 47 | if( TOKEN.z[0] ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); }else{ sqlite3ErrorMsg(pParse, "incomplete input"); } } %stack_overflow { | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | if( TOKEN.z[0] ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); }else{ sqlite3ErrorMsg(pParse, "incomplete input"); } } %stack_overflow { sqlite3OomFault(pParse->db); } // The name of the generated procedure that implements the parser // is as follows: %name sqlite3Parser // The following text is included near the beginning of the C source |
︙ | ︙ | |||
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 | pSelect->pWith = pWith; parserDoubleLinkSelect(pParse, pSelect); }else{ sqlite3WithDelete(pParse->db, pWith); } return pSelect; } } %ifndef SQLITE_OMIT_CTE select(A) ::= WITH wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);} select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);} %endif /* SQLITE_OMIT_CTE */ select(A) ::= selectnowith(A). { Select *p = A; if( p ){ parserDoubleLinkSelect(pParse, p); } } | > > > > > > > > > | 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | pSelect->pWith = pWith; parserDoubleLinkSelect(pParse, pSelect); }else{ sqlite3WithDelete(pParse->db, pWith); } return pSelect; } /* Memory allocator for parser stack resizing. This is a thin wrapper around ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate ** testing. */ static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){ return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize); } } %ifndef SQLITE_OMIT_CTE select(A) ::= WITH wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);} select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);} %endif /* SQLITE_OMIT_CTE */ select(A) ::= selectnowith(A). { Select *p = A; if( p ){ parserDoubleLinkSelect(pParse, p); } } |
︙ | ︙ | |||
606 607 608 609 610 611 612 | }else{ sqlite3WindowListDelete(pParse->db, R); } } %endif | | | > > > > > > > > > | < < | < < < < < < | > > | 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | }else{ sqlite3WindowListDelete(pParse->db, R); } } %endif // Single row VALUES clause. // %type values {Select*} oneselect(A) ::= values(A). %destructor values {sqlite3SelectDelete(pParse->db, $$);} values(A) ::= VALUES LP nexprlist(X) RP. { A = sqlite3SelectNew(pParse,X,0,0,0,0,0,SF_Values,0); } // Multiple row VALUES clause. // %type mvalues {Select*} oneselect(A) ::= mvalues(A). { sqlite3MultiValuesEnd(pParse, A); } %destructor mvalues {sqlite3SelectDelete(pParse->db, $$);} mvalues(A) ::= values(A) COMMA LP nexprlist(Y) RP. { A = sqlite3MultiValues(pParse, A, Y); } mvalues(A) ::= mvalues(A) COMMA LP nexprlist(Y) RP. { A = sqlite3MultiValues(pParse, A, Y); } // The "distinct" nonterminal is true (1) if the DISTINCT keyword is // present and false (0) if it is not. // %type distinct {int} distinct(A) ::= DISTINCT. {A = SF_Distinct;} |
︙ | ︙ | |||
1262 1263 1264 1265 1266 1267 1268 | } expr(A) ::= NOT(B) expr(X). {A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/} expr(A) ::= BITNOT(B) expr(X). {A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/} expr(A) ::= PLUS|MINUS(B) expr(X). [BITNOT] { | > > > > > > > > | | > | 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 | } expr(A) ::= NOT(B) expr(X). {A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/} expr(A) ::= BITNOT(B) expr(X). {A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/} expr(A) ::= PLUS|MINUS(B) expr(X). [BITNOT] { Expr *p = X; u8 op = @B + (TK_UPLUS-TK_PLUS); assert( TK_UPLUS>TK_PLUS ); assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); if( p && p->op==TK_UPLUS ){ p->op = op; A = p; }else{ A = sqlite3PExpr(pParse, op, p, 0); /*A-overwrites-B*/ } } expr(A) ::= expr(B) PTR(C) expr(D). { ExprList *pList = sqlite3ExprListAppend(pParse, 0, B); pList = sqlite3ExprListAppend(pParse, pList, D); A = sqlite3ExprFunction(pParse, pList, &C, 0); } |
︙ | ︙ | |||
1305 1306 1307 1308 1309 1310 1311 | ** regardless of the value of expr1. */ sqlite3ExprUnmapAndDelete(pParse, A); A = sqlite3Expr(pParse->db, TK_STRING, N ? "true" : "false"); if( A ) sqlite3ExprIdToTrueFalse(A); }else{ Expr *pRHS = Y->a[0].pExpr; | | | 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 | ** regardless of the value of expr1. */ sqlite3ExprUnmapAndDelete(pParse, A); A = sqlite3Expr(pParse->db, TK_STRING, N ? "true" : "false"); if( A ) sqlite3ExprIdToTrueFalse(A); }else{ Expr *pRHS = Y->a[0].pExpr; if( Y->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && A->op!=TK_VECTOR ){ Y->a[0].pExpr = 0; sqlite3ExprListDelete(pParse->db, Y); pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); A = sqlite3PExpr(pParse, TK_EQ, A, pRHS); }else if( Y->nExpr==1 && pRHS->op==TK_SELECT ){ A = sqlite3PExpr(pParse, TK_IN, A, 0); sqlite3PExprAddSelect(pParse, A, pRHS->x.pSelect); |
︙ | ︙ | |||
1745 1746 1747 1748 1749 1750 1751 | with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); } with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); } %type wqas {u8} wqas(A) ::= AS. {A = M10d_Any;} wqas(A) ::= AS MATERIALIZED. {A = M10d_Yes;} wqas(A) ::= AS NOT MATERIALIZED. {A = M10d_No;} | | > | 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 | with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); } with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); } %type wqas {u8} wqas(A) ::= AS. {A = M10d_Any;} wqas(A) ::= AS MATERIALIZED. {A = M10d_Yes;} wqas(A) ::= AS NOT MATERIALIZED. {A = M10d_No;} wqitem(A) ::= withnm(X) eidlist_opt(Y) wqas(M) LP select(Z) RP. { A = sqlite3CteNew(pParse, &X, Y, Z, M); /*A-overwrites-X*/ } withnm(A) ::= nm(A). {pParse->bHasWith = 1;} wqlist(A) ::= wqitem(X). { A = sqlite3WithAdd(pParse, 0, X); /*A-overwrites-X*/ } wqlist(A) ::= wqlist(A) COMMA wqitem(X). { A = sqlite3WithAdd(pParse, A, X); } %endif SQLITE_OMIT_CTE |
︙ | ︙ | |||
1908 1909 1910 1911 1912 1913 1914 | %token COLUMN /* Reference to a table column */ AGG_FUNCTION /* An aggregate function */ AGG_COLUMN /* An aggregated column */ TRUEFALSE /* True or false keyword */ ISNOT /* Combination of IS and NOT */ FUNCTION /* A function invocation */ | < > > > > > > > | 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 | %token COLUMN /* Reference to a table column */ AGG_FUNCTION /* An aggregate function */ AGG_COLUMN /* An aggregated column */ TRUEFALSE /* True or false keyword */ ISNOT /* Combination of IS and NOT */ FUNCTION /* A function invocation */ UPLUS /* Unary plus */ UMINUS /* Unary minus */ TRUTH /* IS TRUE or IS FALSE or IS NOT TRUE or IS NOT FALSE */ REGISTER /* Reference to a VDBE register */ VECTOR /* Vector */ SELECT_COLUMN /* Choose a single column from a multi-column SELECT */ IF_NULL_ROW /* the if-null-row operator */ ASTERISK /* The "*" in count(*) and similar */ SPAN /* The span operator */ ERROR /* An expression containing an error */ . term(A) ::= QNUMBER(X). { A=tokenExpr(pParse,@X,X); sqlite3DequoteNumber(pParse, A); } /* There must be no more than 255 tokens defined above. If this grammar ** is extended with new rules and tokens, they must either be so few in ** number that TK_SPAN is no more than 255, or else the new tokens must ** appear after this line. */ %include { #if TK_SPAN>255 |
︙ | ︙ |
Changes to src/pragma.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 | ** that includes the PragType_XXXX macro definitions and the aPragmaName[] ** object. This ensures that the aPragmaName[] table is arranged in ** lexicographical order to facility a binary search of the pragma name. ** Do not edit pragma.h directly. Edit and rerun the script in at ** ../tool/mkpragmatab.tcl. */ #include "pragma.h" /* ** Interpret the given string as a safety level. Return 0 for OFF, ** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or ** unrecognized string argument. The FULL and EXTRA option is disallowed ** if the omitFull parameter it 1. ** ** Note that the values returned are one less that the values that | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | ** that includes the PragType_XXXX macro definitions and the aPragmaName[] ** object. This ensures that the aPragmaName[] table is arranged in ** lexicographical order to facility a binary search of the pragma name. ** Do not edit pragma.h directly. Edit and rerun the script in at ** ../tool/mkpragmatab.tcl. */ #include "pragma.h" /* ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands ** will be run with an analysis_limit set to the lessor of the value of ** the following macro or to the actual analysis_limit if it is non-zero, ** in order to prevent PRAGMA optimize from running for too long. ** ** The value of 2000 is chosen emperically so that the worst-case run-time ** for PRAGMA optimize does not exceed 100 milliseconds against a variety ** of test databases on a RaspberryPI-4 compiled using -Os and without ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of ** this paragraph, "worst-case" means that ANALYZE ends up being ** run on every table in the database. The worst case typically only ** happens if PRAGMA optimize is run on a database file for which ANALYZE ** has not been previously run and the 0x10000 flag is included so that ** all tables are analyzed. The usual case for PRAGMA optimize is that ** no ANALYZE commands will be run at all, or if any ANALYZE happens it ** will be against a single table, so that expected timing for PRAGMA ** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000 ** flag or less than 100 microseconds without the 0x10000 flag. ** ** An analysis limit of 2000 is almost always sufficient for the query ** planner to fully characterize an index. The additional accuracy from ** a larger analysis is not usually helpful. */ #ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT # define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000 #endif /* ** Interpret the given string as a safety level. Return 0 for OFF, ** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or ** unrecognized string argument. The FULL and EXTRA option is disallowed ** if the omitFull parameter it 1. ** ** Note that the values returned are one less that the values that |
︙ | ︙ | |||
1671 1672 1673 1674 1675 1676 1677 | /* Initialize the VDBE program */ pParse->nMem = 6; /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ | | < | 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 | /* Initialize the VDBE program */ pParse->nMem = 6; /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ if( sqlite3GetInt32(pValue->z, &mxErr) ){ if( mxErr<=0 ){ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; } }else{ pObjTab = sqlite3LocateTable(pParse, 0, zRight, iDb>=0 ? db->aDb[iDb].zDbSName : 0); } } sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */ /* Do an integrity check on each database file */ for(i=0; i<db->nDb; i++){ HashElem *x; /* For looping over tables in the schema */ Hash *pTbls; /* Set of all tables in the schema */ int *aRoot; /* Array of root page numbers of all btrees */ int cnt = 0; /* Number of entries in aRoot[] */ if( OMIT_TEMPDB && i==1 ) continue; if( iDb>=0 && i!=iDb ) continue; sqlite3CodeVerifySchema(pParse, i); pParse->okConstFactor = 0; /* tag-20230327-1 */ |
︙ | ︙ | |||
1710 1711 1712 1713 1714 1715 1716 | for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); /* Current table */ Index *pIdx; /* An index on pTab */ int nIdx; /* Number of indexes on pTab */ if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) cnt++; for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < < < < | 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 | for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); /* Current table */ Index *pIdx; /* An index on pTab */ int nIdx; /* Number of indexes on pTab */ if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) cnt++; for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } } if( cnt==0 ) continue; if( pObjTab ) cnt++; aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1)); if( aRoot==0 ) break; cnt = 0; if( pObjTab ) aRoot[++cnt] = 0; for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx; if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ aRoot[++cnt] = pIdx->tnum; } } aRoot[0] = cnt; /* Make sure sufficient number of registers have been allocated */ sqlite3TouchRegister(pParse, 8+cnt); sqlite3ClearTempRegCache(pParse); /* Do the b-tree integrity checks */ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); sqlite3VdbeChangeP5(v, (u8)i); addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName), P4_DYNAMIC); sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3); integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, addr); /* Check that the indexes all have the right number of rows */ cnt = pObjTab ? 1 : 0; sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ int iTab = 0; Table *pTab = sqliteHashData(x); Index *pIdx; if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ){ iTab = cnt++; }else{ iTab = cnt; for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){ if( IsPrimaryKeyIndex(pIdx) ) break; iTab++; } } for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->pPartIdxWhere==0 ){ addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+cnt, 0, 8+iTab); VdbeCoverageNeverNull(v); sqlite3VdbeLoadString(v, 4, pIdx->zName); sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, addr); } cnt++; } } /* Make sure all the indices are constructed correctly. */ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx, *pPk; Index *pPrior = 0; /* Previous index */ int loopTop; int iDataCur, iIdxCur; int r1 = -1; int bStrict; /* True for a STRICT table */ int r2; /* Previous key for WITHOUT ROWID tables */ int mxCol; /* Maximum non-virtual column number */ if( pObjTab && pObjTab!=pTab ) continue; if( !IsOrdinaryTable(pTab) ) continue; if( isQuick || HasRowid(pTab) ){ pPk = 0; r2 = 0; }else{ pPk = sqlite3PrimaryKeyIndex(pTab); r2 = sqlite3GetTempRange(pParse, pPk->nKeyCol); sqlite3VdbeAddOp3(v, OP_Null, 1, r2, r2+pPk->nKeyCol-1); |
︙ | ︙ | |||
1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 | }else{ sqlite3VdbeChangeP5(v, 0x0d); /* INT, TEXT, or BLOB */ /* OP_IsType does not detect NaN values in the database file ** which should be treated as a NULL. So if the header type ** is REAL, we have to load the actual data using OP_Column ** to reliably determine if the value is a NULL. */ sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3); jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk); VdbeCoverage(v); } zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, pCol->zCnName); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); if( doTypeCheck ){ | > | 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 | }else{ sqlite3VdbeChangeP5(v, 0x0d); /* INT, TEXT, or BLOB */ /* OP_IsType does not detect NaN values in the database file ** which should be treated as a NULL. So if the header type ** is REAL, we have to load the actual data using OP_Column ** to reliably determine if the value is a NULL. */ sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3); sqlite3ColumnDefault(v, pTab, j, 3); jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk); VdbeCoverage(v); } zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, pCol->zCnName); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); if( doTypeCheck ){ |
︙ | ︙ | |||
2090 2091 2092 2093 2094 2095 2096 | } sqlite3VdbeJumpHere(v, jmp4); sqlite3ResolvePartIdxLabel(pParse, jmp3); } } sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, loopTop-1); | > | | > > | > > > > > > > > | > > > > > > | > > > > > > | > | < | | | > | | < < < < | 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 | } sqlite3VdbeJumpHere(v, jmp4); sqlite3ResolvePartIdxLabel(pParse, jmp3); } } sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, loopTop-1); if( pPk ){ assert( !isQuick ); sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); } } #ifndef SQLITE_OMIT_VIRTUALTABLE /* Second pass to invoke the xIntegrity method on all virtual ** tables. */ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); sqlite3_vtab *pVTab; int a1; if( pObjTab && pObjTab!=pTab ) continue; if( IsOrdinaryTable(pTab) ) continue; if( !IsVirtual(pTab) ) continue; if( pTab->nCol<=0 ){ const char *zMod = pTab->u.vtab.azArg[0]; if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue; } sqlite3ViewGetColumnNames(pParse, pTab); if( pTab->u.vtab.p==0 ) continue; pVTab = pTab->u.vtab.p->pVtab; if( NEVER(pVTab==0) ) continue; if( NEVER(pVTab->pModule==0) ) continue; if( pVTab->pModule->iVersion<4 ) continue; if( pVTab->pModule->xIntegrity==0 ) continue; sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick); pTab->nTabRef++; sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF); a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v); integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, a1); continue; } #endif } { static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList endCode[] = { { OP_AddImm, 1, 0, 0}, /* 0 */ { OP_IfNotZero, 1, 4, 0}, /* 1 */ { OP_String8, 0, 3, 0}, /* 2 */ |
︙ | ︙ | |||
2370 2371 2372 2373 2374 2375 2376 | ** ** The details of optimizations performed by this pragma are expected ** to change and improve over time. Applications should anticipate that ** this pragma will perform new optimizations in future releases. ** ** The optional argument is a bitmask of optimizations to perform: ** | | | | | | | | | > > > > | | | > | > | | < | | > > > > > > > > | | | > | > | | > > | > > > > > > > > > > > > > > | > | | | | > > > | > > | > > | > > > > > > > > > > > > | | > > > > > > > | > > > | > > > > > > > > | | | > > > > > > > > > > > > > > > > | 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 | ** ** The details of optimizations performed by this pragma are expected ** to change and improve over time. Applications should anticipate that ** this pragma will perform new optimizations in future releases. ** ** The optional argument is a bitmask of optimizations to perform: ** ** 0x00001 Debugging mode. Do not actually perform any optimizations ** but instead return one line of text for each optimization ** that would have been done. Off by default. ** ** 0x00002 Run ANALYZE on tables that might benefit. On by default. ** See below for additional information. ** ** 0x00010 Run all ANALYZE operations using an analysis_limit that ** is the lessor of the current analysis_limit and the ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option. ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is ** currently (2024-02-19) set to 2000, which is such that ** the worst case run-time for PRAGMA optimize on a 100MB ** database will usually be less than 100 milliseconds on ** a RaspberryPI-4 class machine. On by default. ** ** 0x10000 Look at tables to see if they need to be reanalyzed ** due to growth or shrinkage even if they have not been ** queried during the current connection. Off by default. ** ** The default MASK is and always shall be 0x0fffe. In the current ** implementation, the default mask only covers the 0x00002 optimization, ** though additional optimizations that are covered by 0x0fffe might be ** added in the future. Optimizations that are off by default and must ** be explicitly requested have masks of 0x10000 or greater. ** ** DETERMINATION OF WHEN TO RUN ANALYZE ** ** In the current implementation, a table is analyzed if only if all of ** the following are true: ** ** (1) MASK bit 0x00002 is set. ** ** (2) The table is an ordinary table, not a virtual table or view. ** ** (3) The table name does not begin with "sqlite_". ** ** (4) One or more of the following is true: ** (4a) The 0x10000 MASK bit is set. ** (4b) One or more indexes on the table lacks an entry ** in the sqlite_stat1 table. ** (4c) The query planner used sqlite_stat1-style statistics for one ** or more indexes of the table at some point during the lifetime ** of the current connection. ** ** (5) One or more of the following is true: ** (5a) One or more indexes on the table lacks an entry ** in the sqlite_stat1 table. (Same as 4a) ** (5b) The number of rows in the table has increased or decreased by ** 10-fold. In other words, the current size of the table is ** 10 times larger than the size in sqlite_stat1 or else the ** current size is less than 1/10th the size in sqlite_stat1. ** ** The rules for when tables are analyzed are likely to change in ** future releases. Future versions of SQLite might accept a string ** literal argument to this pragma that contains a mnemonic description ** of the options rather than a bitmap. */ case PragTyp_OPTIMIZE: { int iDbLast; /* Loop termination point for the schema loop */ int iTabCur; /* Cursor for a table whose size needs checking */ HashElem *k; /* Loop over tables of a schema */ Schema *pSchema; /* The current schema */ Table *pTab; /* A table in the schema */ Index *pIdx; /* An index of the table */ LogEst szThreshold; /* Size threshold above which reanalysis needed */ char *zSubSql; /* SQL statement for the OP_SqlExec opcode */ u32 opMask; /* Mask of operations to perform */ int nLimit; /* Analysis limit to use */ int nCheck = 0; /* Number of tables to be optimized */ int nBtree = 0; /* Number of btrees to scan */ int nIndex; /* Number of indexes on the current table */ if( zRight ){ opMask = (u32)sqlite3Atoi(zRight); if( (opMask & 0x02)==0 ) break; }else{ opMask = 0xfffe; } if( (opMask & 0x10)==0 ){ nLimit = 0; }else if( db->nAnalysisLimit>0 && db->nAnalysisLimit<SQLITE_DEFAULT_OPTIMIZE_LIMIT ){ nLimit = 0; }else{ nLimit = SQLITE_DEFAULT_OPTIMIZE_LIMIT; } iTabCur = pParse->nTab++; for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){ if( iDb==1 ) continue; sqlite3CodeVerifySchema(pParse, iDb); pSchema = db->aDb[iDb].pSchema; for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ pTab = (Table*)sqliteHashData(k); /* This only works for ordinary tables */ if( !IsOrdinaryTable(pTab) ) continue; /* Do not scan system tables */ if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue; /* Find the size of the table as last recorded in sqlite_stat1. ** If any index is unanalyzed, then the threshold is -1 to ** indicate a new, unanalyzed index */ szThreshold = pTab->nRowLogEst; nIndex = 0; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ nIndex++; if( !pIdx->hasStat1 ){ szThreshold = -1; /* Always analyze if any index lacks statistics */ } } /* If table pTab has not been used in a way that would benefit from ** having analysis statistics during the current session, then skip it, ** unless the 0x10000 MASK bit is set. */ if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){ /* Check for size change if stat1 has been used for a query */ }else if( opMask & 0x10000 ){ /* Check for size change if 0x10000 is set */ }else if( pTab->pIndex!=0 && szThreshold<0 ){ /* Do analysis if unanalyzed indexes exists */ }else{ /* Otherwise, we can skip this table */ continue; } nCheck++; if( nCheck==2 ){ /* If ANALYZE might be invoked two or more times, hold a write ** transaction for efficiency */ sqlite3BeginWriteOperation(pParse, 0, iDb); } nBtree += nIndex+1; /* Reanalyze if the table is 10 times larger or smaller than ** the last analysis. Unconditional reanalysis if there are ** unanalyzed indexes. */ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); if( szThreshold>=0 ){ const LogEst iRange = 33; /* 10x size change */ sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur, sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold>=iRange ? szThreshold-iRange : -1, szThreshold+iRange); VdbeCoverage(v); }else{ sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur, sqlite3VdbeCurrentAddr(v)+2+(opMask&1)); VdbeCoverage(v); } zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"", db->aDb[iDb].zDbSName, pTab->zName); if( opMask & 0x01 ){ int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC); sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1); }else{ sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0, zSubSql, P4_DYNAMIC); } } } sqlite3VdbeAddOp0(v, OP_Expire); /* In a schema with a large number of tables and indexes, scale back ** the analysis_limit to avoid excess run-time in the worst case. */ if( !db->mallocFailed && nLimit>0 && nBtree>100 ){ int iAddr, iEnd; VdbeOp *aOp; nLimit = 100*nLimit/nBtree; if( nLimit<100 ) nLimit = 100; aOp = sqlite3VdbeGetOp(v, 0); iEnd = sqlite3VdbeCurrentAddr(v); for(iAddr=0; iAddr<iEnd; iAddr++){ if( aOp[iAddr].opcode==OP_SqlExec ) aOp[iAddr].p2 = nLimit; } } break; } /* ** PRAGMA busy_timeout ** PRAGMA busy_timeout = N ** |
︙ | ︙ | |||
2728 2729 2730 2731 2732 2733 2734 | pIdxInfo->estimatedCost = (double)1; if( pTab->nHidden==0 ){ return SQLITE_OK; } pConstraint = pIdxInfo->aConstraint; seen[0] = 0; seen[1] = 0; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ | | | < > | | | > > | 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 | pIdxInfo->estimatedCost = (double)1; if( pTab->nHidden==0 ){ return SQLITE_OK; } pConstraint = pIdxInfo->aConstraint; seen[0] = 0; seen[1] = 0; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->iColumn < pTab->iHidden ) continue; if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( pConstraint->usable==0 ) return SQLITE_CONSTRAINT; j = pConstraint->iColumn - pTab->iHidden; assert( j < 2 ); seen[j] = i+1; } if( seen[0]==0 ){ pIdxInfo->estimatedCost = (double)2147483647; pIdxInfo->estimatedRows = 2147483647; return SQLITE_OK; } j = seen[0]-1; pIdxInfo->aConstraintUsage[j].argvIndex = 1; pIdxInfo->aConstraintUsage[j].omit = 1; pIdxInfo->estimatedCost = (double)20; pIdxInfo->estimatedRows = 20; if( seen[1] ){ j = seen[1]-1; pIdxInfo->aConstraintUsage[j].argvIndex = 2; pIdxInfo->aConstraintUsage[j].omit = 1; } return SQLITE_OK; } /* Create a new cursor for the pragma virtual table */ static int pragmaVtabOpen(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){ PragmaVtabCursor *pCsr; pCsr = (PragmaVtabCursor*)sqlite3_malloc(sizeof(*pCsr)); if( pCsr==0 ) return SQLITE_NOMEM; memset(pCsr, 0, sizeof(PragmaVtabCursor)); pCsr->base.pVtab = pVtab; *ppCursor = &pCsr->base; return SQLITE_OK; } /* Clear all content from pragma virtual table cursor. */ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ int i; sqlite3_finalize(pCsr->pPragma); pCsr->pPragma = 0; pCsr->iRowid = 0; for(i=0; i<ArraySize(pCsr->azArg); i++){ sqlite3_free(pCsr->azArg[i]); pCsr->azArg[i] = 0; } } /* Close a pragma virtual table cursor */ |
︙ | ︙ |
Changes to src/prepare.c.
︙ | ︙ | |||
629 630 631 632 633 634 635 | ** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj); */ void *sqlite3ParserAddCleanup( Parse *pParse, /* Destroy when this Parser finishes */ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ void *pPtr /* Pointer to object to be cleaned up */ ){ | > > > > > | > | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | ** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj); */ void *sqlite3ParserAddCleanup( Parse *pParse, /* Destroy when this Parser finishes */ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ void *pPtr /* Pointer to object to be cleaned up */ ){ ParseCleanup *pCleanup; if( sqlite3FaultSim(300) ){ pCleanup = 0; sqlite3OomFault(pParse->db); }else{ pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); } if( pCleanup ){ pCleanup->pNext = pParse->pCleanup; pParse->pCleanup = pCleanup; pCleanup->pPtr = pPtr; pCleanup->xCleanup = xCleanup; }else{ xCleanup(pParse->db, pPtr); |
︙ | ︙ |
Changes to src/printf.c.
︙ | ︙ | |||
494 495 496 497 498 499 500 501 502 503 504 505 506 507 | if( precision>SQLITE_FP_PRECISION_LIMIT ){ precision = SQLITE_FP_PRECISION_LIMIT; } #endif if( xtype==etFLOAT ){ iRound = -precision; }else if( xtype==etGENERIC ){ iRound = precision; }else{ iRound = precision+1; } sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); if( s.isSpecial ){ if( s.isSpecial==2 ){ | > | 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 | if( precision>SQLITE_FP_PRECISION_LIMIT ){ precision = SQLITE_FP_PRECISION_LIMIT; } #endif if( xtype==etFLOAT ){ iRound = -precision; }else if( xtype==etGENERIC ){ if( precision==0 ) precision = 1; iRound = precision; }else{ iRound = precision+1; } sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); if( s.isSpecial ){ if( s.isSpecial==2 ){ |
︙ | ︙ | |||
529 530 531 532 533 534 535 | if( s.sign=='-' ){ prefix = '-'; }else{ prefix = flag_prefix; } exp = s.iDP-1; | < > > | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | if( s.sign=='-' ){ prefix = '-'; }else{ prefix = flag_prefix; } exp = s.iDP-1; /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ if( xtype==etGENERIC ){ assert( precision>0 ); precision--; flag_rtz = !flag_alternateform; if( exp<-4 || exp>precision ){ xtype = etEXP; }else{ precision = precision - exp; xtype = etFLOAT; } |
︙ | ︙ | |||
851 852 853 854 855 856 857 | sqlite3_str_append(pAccum, ".", 1); } sqlite3_str_appendall(pAccum, pItem->zName); }else if( pItem->zAlias ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else{ Select *pSel = pItem->pSelect; | | > > > > | 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 | sqlite3_str_append(pAccum, ".", 1); } sqlite3_str_appendall(pAccum, pItem->zName); }else if( pItem->zAlias ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else{ Select *pSel = pItem->pSelect; assert( pSel!=0 ); /* Because of tag-20240424-1 */ if( pSel->selFlags & SF_NestedFrom ){ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); }else if( pSel->selFlags & SF_MultiValue ){ assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy ); sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE", pItem->u1.nRow); }else{ sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId); } } length = width = 0; break; } |
︙ | ︙ |
Changes to src/resolve.c.
︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 84 85 86 87 88 | Expr *pOrig; /* The iCol-th column of the result set */ Expr *pDup; /* Copy of pOrig */ sqlite3 *db; /* The database connection */ assert( iCol>=0 && iCol<pEList->nExpr ); pOrig = pEList->a[iCol].pExpr; assert( pOrig!=0 ); db = pParse->db; pDup = sqlite3ExprDup(db, pOrig, 0); if( db->mallocFailed ){ sqlite3ExprDelete(db, pDup); pDup = 0; }else{ Expr temp; | > > | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | Expr *pOrig; /* The iCol-th column of the result set */ Expr *pDup; /* Copy of pOrig */ sqlite3 *db; /* The database connection */ assert( iCol>=0 && iCol<pEList->nExpr ); pOrig = pEList->a[iCol].pExpr; assert( pOrig!=0 ); assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); if( pExpr->pAggInfo ) return; db = pParse->db; pDup = sqlite3ExprDup(db, pOrig, 0); if( db->mallocFailed ){ sqlite3ExprDelete(db, pDup); pDup = 0; }else{ Expr temp; |
︙ | ︙ | |||
273 274 275 276 277 278 279 | ** If the name cannot be resolved unambiguously, leave an error message ** in pParse and return WRC_Abort. Return WRC_Prune on success. */ static int lookupName( Parse *pParse, /* The parsing context */ const char *zDb, /* Name of the database containing table, or NULL */ const char *zTab, /* Name of table containing column, or NULL */ | | > | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | ** If the name cannot be resolved unambiguously, leave an error message ** in pParse and return WRC_Abort. Return WRC_Prune on success. */ static int lookupName( Parse *pParse, /* The parsing context */ const char *zDb, /* Name of the database containing table, or NULL */ const char *zTab, /* Name of table containing column, or NULL */ const Expr *pRight, /* Name of the column. */ NameContext *pNC, /* The name context used to resolve the name */ Expr *pExpr /* Make this EXPR node point to the selected column */ ){ int i, j; /* Loop counters */ int cnt = 0; /* Number of matching column names */ int cntTab = 0; /* Number of potential "rowid" matches */ int nSubquery = 0; /* How many levels of subquery */ sqlite3 *db = pParse->db; /* The database connection */ SrcItem *pItem; /* Use for looping over pSrcList items */ SrcItem *pMatch = 0; /* The matching pSrcList item */ NameContext *pTopNC = pNC; /* First namecontext in the list */ Schema *pSchema = 0; /* Schema of the expression */ int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */ Table *pTab = 0; /* Table holding the row */ Column *pCol; /* A column of pTab */ ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ const char *zCol = pRight->u.zToken; assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ assert( zDb==0 || zTab!=0 ); assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); /* Initialize the node to no-match */ |
︙ | ︙ | |||
462 463 464 465 466 467 468 | if( pItem->fg.isNestedFrom ){ sqlite3SrcItemColumnUsed(pItem, j); } break; } } if( 0==cnt && VisibleRowid(pTab) ){ | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | if( pItem->fg.isNestedFrom ){ sqlite3SrcItemColumnUsed(pItem, j); } break; } } if( 0==cnt && VisibleRowid(pTab) ){ /* pTab is a potential ROWID match. Keep track of it and match ** the ROWID later if that seems appropriate. (Search for "cntTab" ** to find related code.) Only allow a ROWID match if there is ** a single ROWID match candidate. */ #ifdef SQLITE_ALLOW_ROWID_IN_VIEW /* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match ** if there is a single VIEW candidate or if there is a single ** non-VIEW candidate plus multiple VIEW candidates. In other ** words non-VIEW candidate terms take precedence over VIEWs. */ if( cntTab==0 || (cntTab==1 && ALWAYS(pMatch!=0) && ALWAYS(pMatch->pTab!=0) && (pMatch->pTab->tabFlags & TF_Ephemeral)!=0 && (pTab->tabFlags & TF_Ephemeral)==0) ){ cntTab = 1; pMatch = pItem; }else{ cntTab++; } #else /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is ** simpler since we require exactly one candidate, which will ** always be a non-VIEW */ cntTab++; pMatch = pItem; #endif } } if( pMatch ){ pExpr->iTable = pMatch->iCursor; assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pMatch->pTab; if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){ |
︙ | ︙ | |||
492 493 494 495 496 497 498 | #ifndef SQLITE_OMIT_TRIGGER if( pParse->pTriggerTab!=0 ){ int op = pParse->eTriggerOp; assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); if( pParse->bReturning ){ if( (pNC->ncFlags & NC_UBaseReg)!=0 && ALWAYS(zTab==0 | | > | 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 | #ifndef SQLITE_OMIT_TRIGGER if( pParse->pTriggerTab!=0 ){ int op = pParse->eTriggerOp; assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); if( pParse->bReturning ){ if( (pNC->ncFlags & NC_UBaseReg)!=0 && ALWAYS(zTab==0 || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0 || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0)) ){ pExpr->iTable = op!=TK_DELETE; pTab = pParse->pTriggerTab; } }else if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){ pExpr->iTable = 1; pTab = pParse->pTriggerTab; |
︙ | ︙ | |||
589 590 591 592 593 594 595 | } #endif /* !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT) */ /* ** Perhaps the name is a reference to the ROWID */ if( cnt==0 | | | > > > > > | 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | } #endif /* !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT) */ /* ** Perhaps the name is a reference to the ROWID */ if( cnt==0 && cntTab>=1 && pMatch && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 && sqlite3IsRowid(zCol) && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom) ){ cnt = cntTab; #if SQLITE_ALLOW_ROWID_IN_VIEW+0==2 if( pMatch->pTab!=0 && IsView(pMatch->pTab) ){ eNewExprOp = TK_NULL; } #endif if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1; pExpr->affExpr = SQLITE_AFF_INTEGER; } /* ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z ** might refer to an result-set alias. This happens, for example, when |
︙ | ︙ | |||
749 750 751 752 753 754 755 756 757 758 759 760 761 762 | } } zErr = cnt==0 ? "no such column" : "ambiguous column name"; if( zDb ){ sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); }else if( zTab ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); pParse->checkSchema = 1; pTopNC->nNcErr++; eNewExprOp = TK_NULL; | > > > > | 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 | } } zErr = cnt==0 ? "no such column" : "ambiguous column name"; if( zDb ){ sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); }else if( zTab ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){ sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a" " string literal in single-quotes?", zErr, zCol); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); pParse->checkSchema = 1; pTopNC->nNcErr++; eNewExprOp = TK_NULL; |
︙ | ︙ | |||
782 783 784 785 786 787 788 | ** if the mask contains extra set bits. However, it is important to ** avoid setting bits beyond the maximum column number of the table. ** (See ticket [b92e5e8ec2cdbaa1]). ** ** If a generated column is referenced, set bits for every column ** of the table. */ | > | | > > > | 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 | ** if the mask contains extra set bits. However, it is important to ** avoid setting bits beyond the maximum column number of the table. ** (See ticket [b92e5e8ec2cdbaa1]). ** ** If a generated column is referenced, set bits for every column ** of the table. */ if( pMatch ){ if( pExpr->iColumn>=0 ){ pMatch->colUsed |= sqlite3ExprColUsed(pExpr); }else{ pMatch->fg.rowidUsed = 1; } } pExpr->op = eNewExprOp; lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); #ifndef SQLITE_OMIT_AUTHORIZATION |
︙ | ︙ | |||
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 | ** "NOT NULL strength reduction optimization". ** ** If this optimization occurs, also restore the NameContext ref-counts ** to the state they where in before the "column" LHS expression was ** resolved. This prevents "column" from being counted as having been ** referenced, which might prevent a SELECT from being erroneously ** marked as correlated. */ case TK_NOTNULL: case TK_ISNULL: { int anRef[8]; NameContext *p; int i; for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){ anRef[i] = p->nRef; } sqlite3WalkExpr(pWalker, pExpr->pLeft); | > > > > > > > > > > > > > > | > > > > > > > > > | | > > > > > > > > > | | | < | | | | | < < | < | | 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 | ** "NOT NULL strength reduction optimization". ** ** If this optimization occurs, also restore the NameContext ref-counts ** to the state they where in before the "column" LHS expression was ** resolved. This prevents "column" from being counted as having been ** referenced, which might prevent a SELECT from being erroneously ** marked as correlated. ** ** 2024-03-28: Beware of aggregates. A bare column of aggregated table ** can still evaluate to NULL even though it is marked as NOT NULL. ** Example: ** ** CREATE TABLE t1(a INT NOT NULL); ** SELECT a, a IS NULL, a IS NOT NULL, count(*) FROM t1; ** ** The "a IS NULL" and "a IS NOT NULL" expressions cannot be optimized ** here because at the time this case is hit, we do not yet know whether ** or not t1 is being aggregated. We have to assume the worst and omit ** the optimization. The only time it is safe to apply this optimization ** is within the WHERE clause. */ case TK_NOTNULL: case TK_ISNULL: { int anRef[8]; NameContext *p; int i; for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){ anRef[i] = p->nRef; } sqlite3WalkExpr(pWalker, pExpr->pLeft); if( IN_RENAME_OBJECT ) return WRC_Prune; if( sqlite3ExprCanBeNull(pExpr->pLeft) ){ /* The expression can be NULL. So the optimization does not apply */ return WRC_Prune; } for(i=0, p=pNC; p; p=p->pNext, i++){ if( (p->ncFlags & NC_Where)==0 ){ return WRC_Prune; /* Not in a WHERE clause. Unsafe to optimize. */ } } testcase( ExprHasProperty(pExpr, EP_OuterON) ); assert( !ExprHasProperty(pExpr, EP_IntValue) ); #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x80000 ){ sqlite3DebugPrintf( "NOT NULL strength reduction converts the following to %d:\n", pExpr->op==TK_NOTNULL ); sqlite3ShowExpr(pExpr); } #endif /* TREETRACE_ENABLED */ pExpr->u.iValue = (pExpr->op==TK_NOTNULL); pExpr->flags |= EP_IntValue; pExpr->op = TK_INTEGER; for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){ p->nRef = anRef[i]; } sqlite3ExprDelete(pParse->db, pExpr->pLeft); pExpr->pLeft = 0; return WRC_Prune; } /* A column name: ID ** Or table name and column name: ID.ID ** Or a database, table and column: ID.ID.ID ** ** The TK_ID and TK_OUT cases are combined so that there will only ** be one call to lookupName(). Then the compiler will in-line ** lookupName() for a size reduction and performance increase. */ case TK_ID: case TK_DOT: { const char *zTable; const char *zDb; Expr *pRight; if( pExpr->op==TK_ID ){ zDb = 0; zTable = 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); pRight = pExpr; }else{ Expr *pLeft = pExpr->pLeft; testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); sqlite3ResolveNotValid(pParse, pNC, "the \".\" operator", NC_IdxExpr|NC_GenCol, 0, pExpr); pRight = pExpr->pRight; if( pRight->op==TK_ID ){ zDb = 0; }else{ assert( pRight->op==TK_DOT ); assert( !ExprHasProperty(pRight, EP_IntValue) ); zDb = pLeft->u.zToken; pLeft = pRight->pLeft; pRight = pRight->pRight; } assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) ); zTable = pLeft->u.zToken; assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); } } return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr); } /* Resolve function names */ case TK_FUNCTION: { ExprList *pList = pExpr->x.pList; /* The argument list */ int n = pList ? pList->nExpr : 0; /* Number of arguments */ |
︙ | ︙ | |||
1207 1208 1209 1210 1211 1212 1213 | #ifndef SQLITE_OMIT_WINDOWFUNC pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0)); #else pNC->ncFlags &= ~NC_AllowAgg; #endif } } | < | < | 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 | #ifndef SQLITE_OMIT_WINDOWFUNC pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0)); #else pNC->ncFlags &= ~NC_AllowAgg; #endif } } else if( ExprHasProperty(pExpr, EP_WinFunc) || pExpr->pLeft ){ is_agg = 1; } sqlite3WalkExprList(pWalker, pList); if( is_agg ){ if( pExpr->pLeft ){ assert( pExpr->pLeft->op==TK_ORDER ); assert( ExprUseXList(pExpr->pLeft) ); sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList); } |
︙ | ︙ | |||
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 | testcase( pExpr->op==TK_IN ); if( ExprUseXSelect(pExpr) ){ int nRef = pNC->nRef; testcase( pNC->ncFlags & NC_IsCheck ); testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); if( pNC->ncFlags & NC_SelfRef ){ notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); }else{ sqlite3WalkSelect(pWalker, pExpr->x.pSelect); } assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); } pNC->ncFlags |= NC_Subquery; } break; } case TK_VARIABLE: { testcase( pNC->ncFlags & NC_IsCheck ); | > > | 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 | testcase( pExpr->op==TK_IN ); if( ExprUseXSelect(pExpr) ){ int nRef = pNC->nRef; testcase( pNC->ncFlags & NC_IsCheck ); testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); assert( pExpr->x.pSelect ); if( pNC->ncFlags & NC_SelfRef ){ notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); }else{ sqlite3WalkSelect(pWalker, pExpr->x.pSelect); } assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); pExpr->x.pSelect->selFlags |= SF_Correlated; } pNC->ncFlags |= NC_Subquery; } break; } case TK_VARIABLE: { testcase( pNC->ncFlags & NC_IsCheck ); |
︙ | ︙ | |||
1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 | } /* Recursively resolve names in all subqueries in the FROM clause */ if( pOuterNC ) pOuterNC->nNestedSelect++; for(i=0; i<p->pSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ int nRef = pOuterNC ? pOuterNC->nRef : 0; const char *zSavedContext = pParse->zAuthContext; if( pItem->zName ) pParse->zAuthContext = pItem->zName; sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; | > | 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 | } /* Recursively resolve names in all subqueries in the FROM clause */ if( pOuterNC ) pOuterNC->nNestedSelect++; for(i=0; i<p->pSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/ if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ int nRef = pOuterNC ? pOuterNC->nRef : 0; const char *zSavedContext = pParse->zAuthContext; if( pItem->zName ) pParse->zAuthContext = pItem->zName; sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; |
︙ | ︙ | |||
1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 | if( p->pHaving ){ if( (p->selFlags & SF_Aggregate)==0 ){ sqlite3ErrorMsg(pParse, "HAVING clause on a non-aggregate query"); return WRC_Abort; } if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; } if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; /* Resolve names in table-valued-function arguments */ for(i=0; i<p->pSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; if( pItem->fg.isTabFunc && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg) ){ | > > | 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 | if( p->pHaving ){ if( (p->selFlags & SF_Aggregate)==0 ){ sqlite3ErrorMsg(pParse, "HAVING clause on a non-aggregate query"); return WRC_Abort; } if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; } sNC.ncFlags |= NC_Where; if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; sNC.ncFlags &= ~NC_Where; /* Resolve names in table-valued-function arguments */ for(i=0; i<p->pSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; if( pItem->fg.isTabFunc && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg) ){ |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
1669 1670 1671 1672 1673 1674 1675 | int bSeq; /* True if sorter record includes seq. no. */ int nRefKey = 0; struct ExprList_item *aOutEx = p->pEList->a; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS int addrExplain; /* Address of OP_Explain instruction */ #endif | > > | | | > > > > > | 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 | int bSeq; /* True if sorter record includes seq. no. */ int nRefKey = 0; struct ExprList_item *aOutEx = p->pEList->a; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS int addrExplain; /* Address of OP_Explain instruction */ #endif nKey = pOrderBy->nExpr - pSort->nOBSat; if( pSort->nOBSat==0 || nKey==1 ){ ExplainQueryPlan2(addrExplain, (pParse, 0, "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat?"LAST TERM OF ":"" )); }else{ ExplainQueryPlan2(addrExplain, (pParse, 0, "USE TEMP B-TREE FOR LAST %d TERMS OF ORDER BY", nKey )); } sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd); sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush); assert( addrBreak<0 ); if( pSort->labelBkOut ){ sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
︙ | ︙ | |||
1709 1710 1711 1712 1713 1714 1715 | if( eDest==SRT_EphemTab || eDest==SRT_Table ){ regRow = sqlite3GetTempReg(pParse); nColumn = 0; }else{ regRow = sqlite3GetTempRange(pParse, nColumn); } } | < | 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 | if( eDest==SRT_EphemTab || eDest==SRT_Table ){ regRow = sqlite3GetTempReg(pParse); nColumn = 0; }else{ regRow = sqlite3GetTempRange(pParse, nColumn); } } if( pSort->sortFlags & SORTFLAG_UseSorter ){ int regSortOut = ++pParse->nMem; iSortTab = pParse->nTab++; if( pSort->labelBkOut ){ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, |
︙ | ︙ | |||
1949 1950 1951 1952 1953 1954 1955 | assert( pTab && ExprUseYTab(pExpr) && pExpr->y.pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ if( iCol<pS->pEList->nExpr | < | < < < | 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 | assert( pTab && ExprUseYTab(pExpr) && pExpr->y.pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ if( iCol<pS->pEList->nExpr && (!ViewCanHaveRowid || iCol>=0) ){ /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. */ NameContext sNC; Expr *p = pS->pEList->a[iCol].pExpr; |
︙ | ︙ | |||
2318 2319 2320 2321 2322 2323 2324 | CollSeq *pColl; int i,j; Expr *p; struct ExprList_item *a; NameContext sNC; assert( pSelect!=0 ); | < | > > > > > > > | < < | | 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 | CollSeq *pColl; int i,j; Expr *p; struct ExprList_item *a; NameContext sNC; assert( pSelect!=0 ); assert( (pSelect->selFlags & SF_Resolved)!=0 ); assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); if( db->mallocFailed || IN_RENAME_OBJECT ) return; while( pSelect->pPrior ) pSelect = pSelect->pPrior; a = pSelect->pEList->a; memset(&sNC, 0, sizeof(sNC)); sNC.pSrcList = pSelect->pSrc; for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ const char *zType; i64 n; int m = 0; Select *pS2 = pSelect; pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); p = a[i].pExpr; /* pCol->szEst = ... // Column size est for SELECT tables never used */ pCol->affinity = sqlite3ExprAffinity(p); while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); pS2 = pS2->pNext; pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr); } if( pCol->affinity<=SQLITE_AFF_NONE ){ pCol->affinity = aff; } if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){ for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); } if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){ pCol->affinity = SQLITE_AFF_BLOB; }else if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){ pCol->affinity = SQLITE_AFF_BLOB; |
︙ | ︙ | |||
2370 2371 2372 2373 2374 2375 2376 | zType = sqlite3StdType[j]; break; } } } } if( zType ){ | | | | | 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 | zType = sqlite3StdType[j]; break; } } } } if( zType ){ const i64 k = sqlite3Strlen30(zType); n = sqlite3Strlen30(pCol->zCnName); pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2); pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); if( pCol->zCnName ){ memcpy(&pCol->zCnName[n+1], zType, k+1); pCol->colFlags |= COLFLAG_HASTYPE; } } pColl = sqlite3ExprCollSeq(pParse, p); if( pColl ){ assert( pTab->pIndex==0 ); sqlite3ColumnSetColl(db, pCol, pColl->zName); |
︙ | ︙ | |||
4772 4773 4774 4775 4776 4777 4778 | WhereConst *pConst, /* The WhereConst into which we are inserting */ Expr *pColumn, /* The COLUMN part of the constraint */ Expr *pValue, /* The VALUE part of the constraint */ Expr *pExpr /* Overall expression: COLUMN=VALUE or VALUE=COLUMN */ ){ int i; assert( pColumn->op==TK_COLUMN ); | | | 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 | WhereConst *pConst, /* The WhereConst into which we are inserting */ Expr *pColumn, /* The COLUMN part of the constraint */ Expr *pValue, /* The VALUE part of the constraint */ Expr *pExpr /* Overall expression: COLUMN=VALUE or VALUE=COLUMN */ ){ int i; assert( pColumn->op==TK_COLUMN ); assert( sqlite3ExprIsConstant(pConst->pParse, pValue) ); if( ExprHasProperty(pColumn, EP_FixedCol) ) return; if( sqlite3ExprAffinity(pValue)!=0 ) return; if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pConst->pParse,pExpr)) ){ return; } |
︙ | ︙ | |||
4830 4831 4832 4833 4834 4835 4836 | return; } if( pExpr->op!=TK_EQ ) return; pRight = pExpr->pRight; pLeft = pExpr->pLeft; assert( pRight!=0 ); assert( pLeft!=0 ); | | | | 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 | return; } if( pExpr->op!=TK_EQ ) return; pRight = pExpr->pRight; pLeft = pExpr->pLeft; assert( pRight!=0 ); assert( pLeft!=0 ); if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pLeft) ){ constInsert(pConst,pRight,pLeft,pExpr); } if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pRight) ){ constInsert(pConst,pLeft,pRight,pExpr); } } /* ** This is a helper function for Walker callback propagateConstantExprRewrite(). ** |
︙ | ︙ | |||
5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 | ** Transformed into: ** ** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1 WHERE a=5 AND c-d=10) ** WHERE x=5 AND y=10; ** ** The hope is that the terms added to the inner query will make it more ** efficient. ** ** Do not attempt this optimization if: ** ** (1) (** This restriction was removed on 2017-09-29. We used to ** disallow this optimization for aggregate subqueries, but now ** it is allowed by putting the extra terms on the HAVING clause. ** The added HAVING clause is pointless if the subquery lacks | > > > > > > > > > > > > | 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 | ** Transformed into: ** ** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1 WHERE a=5 AND c-d=10) ** WHERE x=5 AND y=10; ** ** The hope is that the terms added to the inner query will make it more ** efficient. ** ** NAME AMBIGUITY ** ** This optimization is called the "WHERE-clause push-down optimization". ** ** Do not confuse this optimization with another unrelated optimization ** with a similar name: The "MySQL push-down optimization" causes WHERE ** clause terms that can be evaluated using only the index and without ** reference to the table are run first, so that if they are false, ** unnecessary table seeks are avoided. ** ** RULES ** ** Do not attempt this optimization if: ** ** (1) (** This restriction was removed on 2017-09-29. We used to ** disallow this optimization for aggregate subqueries, but now ** it is allowed by putting the extra terms on the HAVING clause. ** The added HAVING clause is pointless if the subquery lacks |
︙ | ︙ | |||
5119 5120 5121 5122 5123 5124 5125 | ** of a join (either an INNER or an OUTER join), and ** ** (9b) The subquery is to the right of the ON/USING clause ** ** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING ** clause and the subquery. ** | | | | | > > > > | 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 | ** of a join (either an INNER or an OUTER join), and ** ** (9b) The subquery is to the right of the ON/USING clause ** ** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING ** clause and the subquery. ** ** Without this restriction, the WHERE-clause push-down optimization ** might move the ON/USING filter expression from the left side of a ** RIGHT JOIN over to the right side, which leads to incorrect answers. ** See also restriction (6) in sqlite3ExprIsSingleTableConstraint(). ** ** (10) The inner query is not the right-hand table of a RIGHT JOIN. ** ** (11) The subquery is not a VALUES clause ** ** (12) The WHERE clause is not "rowid ISNULL" or the equivalent. This ** case only comes up if SQLite is compiled using ** SQLITE_ALLOW_ROWID_IN_VIEW. ** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. */ static int pushDownWhereTerms( Parse *pParse, /* Parse context (for malloc() and error reporting) */ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ |
︙ | ︙ | |||
5238 5239 5240 5241 5242 5243 5244 | if( ExprHasProperty(pWhere,EP_OuterON) && pWhere->w.iJoin!=iCursor ){ return 0; /* restriction (5) */ } #endif | > > > > > > > > > > > > | | 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 | if( ExprHasProperty(pWhere,EP_OuterON) && pWhere->w.iJoin!=iCursor ){ return 0; /* restriction (5) */ } #endif #ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( ViewCanHaveRowid && (pWhere->op==TK_ISNULL || pWhere->op==TK_NOTNULL) ){ Expr *pLeft = pWhere->pLeft; if( ALWAYS(pLeft) && pLeft->op==TK_COLUMN && pLeft->iColumn < 0 ){ return 0; /* Restriction (12) */ } } #endif if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){ nChng++; pSubq->selFlags |= SF_PushDown; while( pSubq ){ SubstContext x; pNew = sqlite3ExprDup(pParse->db, pWhere, 0); unsetJoinExpr(pNew, -1, 1); x.pParse = pParse; |
︙ | ︙ | |||
5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 | pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias); }else{ pTab->zName = sqlite3MPrintf(pParse->db, "%!S", pFrom); } while( pSel->pPrior ){ pSel = pSel->pPrior; } sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); #ifndef SQLITE_ALLOW_ROWID_IN_VIEW /* The usual case - do not allow ROWID on a subquery */ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; #else | > | > | 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 | pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias); }else{ pTab->zName = sqlite3MPrintf(pParse->db, "%!S", pFrom); } while( pSel->pPrior ){ pSel = pSel->pPrior; } sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; pTab->eTabType = TABTYP_VIEW; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); #ifndef SQLITE_ALLOW_ROWID_IN_VIEW /* The usual case - do not allow ROWID on a subquery */ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; #else /* Legacy compatibility mode */ pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid; #endif return pParse->nErr ? SQLITE_ERROR : SQLITE_OK; } /* ** Check the N SrcItem objects to the right of pBase. (N might be zero!) |
︙ | ︙ | |||
6138 6139 6140 6141 6142 6143 6144 | if( db->mallocFailed ) break; assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom->pSelect) ); if( pFrom->fg.isNestedFrom ){ assert( pFrom->pSelect!=0 ); pNestedFrom = pFrom->pSelect->pEList; assert( pNestedFrom!=0 ); assert( pNestedFrom->nExpr==pTab->nCol ); | | | 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 | if( db->mallocFailed ) break; assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom->pSelect) ); if( pFrom->fg.isNestedFrom ){ assert( pFrom->pSelect!=0 ); pNestedFrom = pFrom->pSelect->pEList; assert( pNestedFrom!=0 ); assert( pNestedFrom->nExpr==pTab->nCol ); assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid ); }else{ if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ continue; } pNestedFrom = 0; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*"; |
︙ | ︙ | |||
6170 6171 6172 6173 6174 6175 6176 | pX->fg.bUsingTerm = 1; } } }else{ pUsing = 0; } | | > | 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 | pX->fg.bUsingTerm = 1; } } }else{ pUsing = 0; } nAdd = pTab->nCol; if( VisibleRowid(pTab) && (selFlags & SF_NestedFrom)!=0 ) nAdd++; for(j=0; j<nAdd; j++){ const char *zName; struct ExprList_item *pX; /* Newly added ExprList term */ if( j==pTab->nCol ){ zName = sqlite3RowidAlias(pTab); if( zName==0 ) continue; |
︙ | ︙ | |||
6252 6253 6254 6255 6256 6257 6258 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); if( pNew==0 ){ break; /* OOM */ } pX = &pNew->a[pNew->nExpr-1]; assert( pX->zEName==0 ); if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ | | > | 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); if( pNew==0 ){ break; /* OOM */ } pX = &pNew->a[pNew->nExpr-1]; assert( pX->zEName==0 ); if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ if( pNestedFrom && (!ViewCanHaveRowid || j<pNestedFrom->nExpr) ){ assert( j<pNestedFrom->nExpr ); pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName); testcase( pX->zEName==0 ); }else{ pX->zEName = sqlite3MPrintf(db, "%s.%s.%s", zSchemaName, zTabName, zName); testcase( pX->zEName==0 ); } |
︙ | ︙ | |||
6369 6370 6371 6372 6373 6374 6375 | int i; SrcList *pTabList; SrcItem *pFrom; if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; | < | | 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 | int i; SrcList *pTabList; SrcItem *pFrom; if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; assert( (p->selFlags & SF_Resolved) ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; assert( pTab!=0 ); if( (pTab->tabFlags & TF_Ephemeral)!=0 ){ /* A sub-query in the FROM clause of a SELECT */ Select *pSel = pFrom->pSelect; |
︙ | ︙ | |||
6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 | #if TREETRACE_ENABLED /* ** Display all information about an AggInfo object */ static void printAggInfo(AggInfo *pAggInfo){ int ii; for(ii=0; ii<pAggInfo->nColumn; ii++){ struct AggInfo_col *pCol = &pAggInfo->aCol[ii]; sqlite3DebugPrintf( "agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d" " iSorterColumn=%d %s\n", ii, pCol->pTab ? pCol->pTab->zName : "NULL", pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii, | > > | 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 | #if TREETRACE_ENABLED /* ** Display all information about an AggInfo object */ static void printAggInfo(AggInfo *pAggInfo){ int ii; sqlite3DebugPrintf("AggInfo %d/%p:\n", pAggInfo->selId, pAggInfo); for(ii=0; ii<pAggInfo->nColumn; ii++){ struct AggInfo_col *pCol = &pAggInfo->aCol[ii]; sqlite3DebugPrintf( "agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d" " iSorterColumn=%d %s\n", ii, pCol->pTab ? pCol->pTab->zName : "NULL", pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii, |
︙ | ︙ | |||
7630 7631 7632 7633 7634 7635 7636 | sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase); } #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* Generate code for all sub-queries in the FROM clause */ pSub = pItem->pSelect; | | | 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 | sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase); } #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* Generate code for all sub-queries in the FROM clause */ pSub = pItem->pSelect; if( pSub==0 || pItem->addrFillSub!=0 ) continue; /* The code for a subquery should only be generated once. */ assert( pItem->addrFillSub==0 ); /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select ** may contain expression trees of at most |
︙ | ︙ | |||
7661 7662 7663 7664 7665 7666 7667 | TREETRACE(0x4000,pParse,p, ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); sqlite3TreeViewSelect(0, p, 0); } #endif assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 ); }else{ | | | 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 | TREETRACE(0x4000,pParse,p, ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); sqlite3TreeViewSelect(0, p, 0); } #endif assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 ); }else{ TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n")); } /* Convert unused result columns of the subquery into simple NULL ** expressions, to avoid unneeded searching and computation. */ if( OptimizationEnabled(db, SQLITE_NullUnusedCols) && disableUnusedSubqueryResultColumns(pItem) |
︙ | ︙ | |||
8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 | */ select_end: assert( db->mallocFailed==0 || db->mallocFailed==1 ); assert( db->mallocFailed==0 || pParse->nErr!=0 ); sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ for(i=0; i<pAggInfo->nColumn; i++){ Expr *pExpr = pAggInfo->aCol[i].pCExpr; if( pExpr==0 ) continue; assert( pExpr->pAggInfo==pAggInfo ); assert( pExpr->iAgg==i ); } for(i=0; i<pAggInfo->nFunc; i++){ | > > > > > > | 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 | */ select_end: assert( db->mallocFailed==0 || db->mallocFailed==1 ); assert( db->mallocFailed==0 || pParse->nErr!=0 ); sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x20 ){ TREETRACE(0x20,pParse,p,("Finished with AggInfo\n")); printAggInfo(pAggInfo); } #endif for(i=0; i<pAggInfo->nColumn; i++){ Expr *pExpr = pAggInfo->aCol[i].pCExpr; if( pExpr==0 ) continue; assert( pExpr->pAggInfo==pAggInfo ); assert( pExpr->iAgg==i ); } for(i=0; i<pAggInfo->nFunc; i++){ |
︙ | ︙ |
Changes to src/shell.c.in.
︙ | ︙ | |||
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | * eputf(f, ...) => emit varargs per format f to error stream * oputb(b, n) => emit char buffer b[0..n-1] to default stream * * Note that the default stream is whatever has been last set via: * setOutputStream(FILE *pf) * This is normally the stream that CLI normal output goes to. * For the stand-alone CLI, it is stdout with no .output redirect. */ # define sputz(s,z) fPutsUtf8(z,s) # define sputf fPrintfUtf8 # define oputz(z) oPutsUtf8(z) # define oputf oPrintfUtf8 # define eputz(z) ePutsUtf8(z) # define eputf ePrintfUtf8 # define oputb(buf,na) oPutbUtf8(buf,na) #else /* For Fiddle, all console handling and emit redirection is omitted. */ | > > > > > > > > | > > < | | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | * eputf(f, ...) => emit varargs per format f to error stream * oputb(b, n) => emit char buffer b[0..n-1] to default stream * * Note that the default stream is whatever has been last set via: * setOutputStream(FILE *pf) * This is normally the stream that CLI normal output goes to. * For the stand-alone CLI, it is stdout with no .output redirect. * * The ?putz(z) forms are required for the Fiddle builds for string literal * output, in aid of enforcing format string to argument correspondence. */ # define sputz(s,z) fPutsUtf8(z,s) # define sputf fPrintfUtf8 # define oputz(z) oPutsUtf8(z) # define oputf oPrintfUtf8 # define eputz(z) ePutsUtf8(z) # define eputf ePrintfUtf8 # define oputb(buf,na) oPutbUtf8(buf,na) #else /* For Fiddle, all console handling and emit redirection is omitted. */ /* These next 3 macros are for emitting formatted output. When complaints * from the WASM build are issued for non-formatted output, (when a mere * string literal is to be emitted, the ?putz(z) forms should be used. * (This permits compile-time checking of format string / argument mismatch.) */ # define oputf(fmt, ...) printf(fmt,__VA_ARGS__) # define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) # define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) /* These next 3 macros are for emitting simple string literals. */ # define oputz(z) fputs(z,stdout) # define eputz(z) fputs(z,stderr) # define sputz(fp,z) fputs(z,fp) # define oputb(buf,na) fwrite(buf,1,na,stdout) #endif /* True if the timer is enabled */ static int enableTimer = 0; /* A version of strcmp() that works with NULL values */ |
︙ | ︙ | |||
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 | #endif #ifdef SQLITE_HAVE_ZLIB INCLUDE ../ext/misc/zipfile.c INCLUDE ../ext/misc/sqlar.c #endif INCLUDE ../ext/expert/sqlite3expert.h INCLUDE ../ext/expert/sqlite3expert.c #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) #define SQLITE_SHELL_HAVE_RECOVER 1 #else #define SQLITE_SHELL_HAVE_RECOVER 0 #endif #if SQLITE_SHELL_HAVE_RECOVER | > > > | 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 | #endif #ifdef SQLITE_HAVE_ZLIB INCLUDE ../ext/misc/zipfile.c INCLUDE ../ext/misc/sqlar.c #endif INCLUDE ../ext/expert/sqlite3expert.h INCLUDE ../ext/expert/sqlite3expert.c INCLUDE ../ext/intck/sqlite3intck.h INCLUDE ../ext/intck/sqlite3intck.c #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) #define SQLITE_SHELL_HAVE_RECOVER 1 #else #define SQLITE_SHELL_HAVE_RECOVER 0 #endif #if SQLITE_SHELL_HAVE_RECOVER |
︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 | u8 scanstatsOn; /* True to display scan stats before each finalize */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ | > | 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 | u8 scanstatsOn; /* True to display scan stats before each finalize */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ u8 eRestoreState; /* See comments above doAutoDetectRestore() */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ |
︙ | ︙ | |||
3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 | int bw = p->cmOpts.bWordWrap; const char *zEmpty = ""; const char *zShowNull = p->nullValue; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) return; nColumn = sqlite3_column_count(pStmt); nAlloc = nColumn*4; if( nAlloc<=0 ) nAlloc = 1; azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); shell_check_oom(azData); azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom(azNextLine); memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); | > | 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 | int bw = p->cmOpts.bWordWrap; const char *zEmpty = ""; const char *zShowNull = p->nullValue; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) return; nColumn = sqlite3_column_count(pStmt); if( nColumn==0 ) goto columnar_end; nAlloc = nColumn*4; if( nAlloc<=0 ) nAlloc = 1; azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); shell_check_oom(azData); azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom(azNextLine); memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); |
︙ | ︙ | |||
3844 3845 3846 3847 3848 3849 3850 | z = azData[i]; if( z==0 ) z = (char*)zEmpty; n = strlenChar(z); j = i%nColumn; if( n>p->actualWidth[j] ) p->actualWidth[j] = n; } if( seenInterrupt ) goto columnar_end; | < | 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 | z = azData[i]; if( z==0 ) z = (char*)zEmpty; n = strlenChar(z); j = i%nColumn; if( n>p->actualWidth[j] ) p->actualWidth[j] = n; } if( seenInterrupt ) goto columnar_end; switch( p->cMode ){ case MODE_Column: { colSep = " "; rowSep = "\n"; if( p->showHeader ){ for(i=0; i<nColumn; i++){ w = p->actualWidth[i]; |
︙ | ︙ | |||
4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 | #endif #ifndef SQLITE_OMIT_TEST_CONTROL ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", #ifdef SQLITE_ENABLE_IOTRACE ",iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", " Options:", " fkey-indexes Find missing foreign key indexes", | > | 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 | #endif #ifndef SQLITE_OMIT_TEST_CONTROL ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", ".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db", #ifdef SQLITE_ENABLE_IOTRACE ",iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", " Options:", " fkey-indexes Find missing foreign key indexes", |
︙ | ︙ | |||
6727 6728 6729 6730 6731 6732 6733 | usage: eputf("Usage %s sub-command ?switches...?\n", azArg[0]); eputz("Where sub-commands are:\n"); eputz(" fkey-indexes\n"); return SQLITE_ERROR; } | < < < < < | | 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 | usage: eputf("Usage %s sub-command ?switches...?\n", azArg[0]); eputz("Where sub-commands are:\n"); eputz(" fkey-indexes\n"); return SQLITE_ERROR; } static void shellPrepare( sqlite3 *db, int *pRc, const char *zSql, sqlite3_stmt **ppStmt ){ *ppStmt = 0; if( *pRc==SQLITE_OK ){ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); if( rc!=SQLITE_OK ){ eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); *pRc = rc; } } } /* ** Create a prepared statement using printf-style arguments for the SQL. */ static void shellPreparePrintf( sqlite3 *db, int *pRc, sqlite3_stmt **ppStmt, const char *zFmt, ... ){ *ppStmt = 0; |
︙ | ︙ | |||
6774 6775 6776 6777 6778 6779 6780 | }else{ shellPrepare(db, pRc, z, ppStmt); sqlite3_free(z); } } } | > | < < < < | > | 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 | }else{ shellPrepare(db, pRc, z, ppStmt); sqlite3_free(z); } } } /* ** Finalize the prepared statement created using shellPreparePrintf(). */ static void shellFinalize( int *pRc, sqlite3_stmt *pStmt ){ if( pStmt ){ sqlite3 *db = sqlite3_db_handle(pStmt); int rc = sqlite3_finalize(pStmt); if( *pRc==SQLITE_OK ){ if( rc!=SQLITE_OK ){ eputf("SQL error: %s\n", sqlite3_errmsg(db)); } *pRc = rc; } } } #if !defined SQLITE_OMIT_VIRTUALTABLE /* Reset the prepared statement created using shellPreparePrintf(). ** ** This routine is could be marked "static". But it is not always used, ** depending on compile-time options. By omitting the "static", we avoid ** nuisance compiler warnings about "defined but not used". */ void shellReset( |
︙ | ︙ | |||
7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 | eputf("sql error: %s (%d)\n", zErr, errCode); } rc = sqlite3_recover_finish(p); return rc; } #endif /* SQLITE_SHELL_HAVE_RECOVER */ /* * zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it. * zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE, * close db and set it to 0, and return the columns spec, to later * be sqlite3_free()'ed by the caller. * The return is 0 when either: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 | eputf("sql error: %s (%d)\n", zErr, errCode); } rc = sqlite3_recover_finish(p); return rc; } #endif /* SQLITE_SHELL_HAVE_RECOVER */ /* ** Implementation of ".intck STEPS_PER_UNLOCK" command. */ static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){ sqlite3_intck *p = 0; int rc = SQLITE_OK; rc = sqlite3_intck_open(pState->db, "main", &p); if( rc==SQLITE_OK ){ i64 nStep = 0; i64 nError = 0; const char *zErr = 0; while( SQLITE_OK==sqlite3_intck_step(p) ){ const char *zMsg = sqlite3_intck_message(p); if( zMsg ){ oputf("%s\n", zMsg); nError++; } nStep++; if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){ sqlite3_intck_unlock(p); } } rc = sqlite3_intck_error(p, &zErr); if( zErr ){ eputf("%s\n", zErr); } sqlite3_intck_close(p); oputf("%lld steps, %lld errors\n", nStep, nError); } return rc; } /* * zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it. * zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE, * close db and set it to 0, and return the columns spec, to later * be sqlite3_free()'ed by the caller. * The return is 0 when either: |
︙ | ︙ | |||
7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 | } sqlite3_finalize(pStmt); sqlite3_close(*pDb); *pDb = 0; return zColsSpec; } } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 | } sqlite3_finalize(pStmt); sqlite3_close(*pDb); *pDb = 0; return zColsSpec; } } /* ** Check if the sqlite_schema table contains one or more virtual tables. If ** parameter zLike is not NULL, then it is an SQL expression that the ** sqlite_schema row must also match. If one or more such rows are found, ** print the following warning to the output: ** ** WARNING: Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */ static int outputDumpWarning(ShellState *p, const char *zLike){ int rc = SQLITE_OK; sqlite3_stmt *pStmt = 0; shellPreparePrintf(p->db, &rc, &pStmt, "SELECT 1 FROM sqlite_schema o WHERE " "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true" ); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ oputz("/* WARNING: " "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n" ); } shellFinalize(&rc, pStmt); return rc; } /* ** Fault-Simulator state and logic. */ static struct { int iId; /* ID that triggers a simulated fault. -1 means "any" */ int iErr; /* The error code to return on a fault */ int iCnt; /* Trigger the fault only if iCnt is already zero */ int iInterval; /* Reset iCnt to this value after each fault */ int eVerbose; /* When to print output */ int nHit; /* Number of hits seen so far */ int nRepeat; /* Turn off after this many hits. 0 for never */ int nSkip; /* Skip this many before first fault */ } faultsim_state = {-1, 0, 0, 0, 0, 0, 0, 0}; /* ** This is the fault-sim callback */ static int faultsim_callback(int iArg){ if( faultsim_state.iId>0 && faultsim_state.iId!=iArg ){ return SQLITE_OK; } if( faultsim_state.iCnt ){ if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--; if( faultsim_state.eVerbose>=2 ){ oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); } return SQLITE_OK; } if( faultsim_state.eVerbose>=1 ){ oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); } faultsim_state.iCnt = faultsim_state.iInterval; faultsim_state.nHit++; if( faultsim_state.nRepeat>0 && faultsim_state.nRepeat<=faultsim_state.nHit ){ faultsim_state.iCnt = -1; } return faultsim_state.iErr; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ |
︙ | ︙ | |||
8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 | zLike = zExpr; } } } open_db(p, 0); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ oputz("PRAGMA foreign_keys=OFF;\n"); oputz("BEGIN TRANSACTION;\n"); } | > | 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 | zLike = zExpr; } } } open_db(p, 0); outputDumpWarning(p, zLike); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ oputz("PRAGMA foreign_keys=OFF;\n"); oputz("BEGIN TRANSACTION;\n"); } |
︙ | ︙ | |||
8352 8353 8354 8355 8356 8357 8358 | ); run_schema_dump_query(p,zSql); sqlite3_free(zSql); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ zSql = sqlite3_mprintf( "SELECT sql FROM sqlite_schema AS o " "WHERE (%s) AND sql NOT NULL" | | > | 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 | ); run_schema_dump_query(p,zSql); sqlite3_free(zSql); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ zSql = sqlite3_mprintf( "SELECT sql FROM sqlite_schema AS o " "WHERE (%s) AND sql NOT NULL" " AND type IN ('index','trigger','view') " "ORDER BY type COLLATE NOCASE DESC", zLike ); run_table_dump_query(p, zSql); sqlite3_free(zSql); } sqlite3_free(zLike); if( p->writableSchema ){ |
︙ | ︙ | |||
8675 8676 8677 8678 8679 8680 8681 | showHelp(p->out, 0); } }else #ifndef SQLITE_SHELL_FIDDLE if( c=='i' && cli_strncmp(azArg[0], "import", n)==0 ){ char *zTable = 0; /* Insert data into this table */ | | | | < | 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 | showHelp(p->out, 0); } }else #ifndef SQLITE_SHELL_FIDDLE if( c=='i' && cli_strncmp(azArg[0], "import", n)==0 ){ char *zTable = 0; /* Insert data into this table */ char *zSchema = 0; /* Schema of zTable */ char *zFile = 0; /* Name of file to extra content from */ sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ i64 nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int needCommit; /* True to COMMIT or ROLLBACK at end */ int nSep; /* Number of bytes in p->colSeparator[] */ char *zSql = 0; /* An SQL statement */ ImportCtx sCtx; /* Reader context */ char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ int eVerbose = 0; /* Larger for more console output */ int nSkip = 0; /* Initial lines to skip */ int useOutputMode = 1; /* Use output mode to determine separators */ char *zCreate = 0; /* CREATE TABLE statement text */ |
︙ | ︙ | |||
8818 8819 8820 8821 8822 8823 8824 | import_cleanup(&sCtx); shell_out_of_memory(); } /* Below, resources must be freed before exit. */ while( (nSkip--)>0 ){ while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} } | < < < < < < < < < < < < | > | > < < < < > > > > > > | > > > > > > > > > > > | | < < | > > | | > > > > | > > > > > > | > > < < | 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 | import_cleanup(&sCtx); shell_out_of_memory(); } /* Below, resources must be freed before exit. */ while( (nSkip--)>0 ){ while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} } import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) ){ /* Table does not exist. Create it. */ sqlite3 *dbCols = 0; char *zRenames = 0; char *zColDefs; zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", zSchema ? zSchema : "main", zTable); while( xRead(&sCtx) ){ zAutoColumn(sCtx.z, &dbCols, 0); if( sCtx.cTerm!=sCtx.cColSep ) break; } zColDefs = zAutoColumn(0, &dbCols, &zRenames); if( zRenames!=0 ){ sputf((stdin_is_interactive && p->in==stdin)? p->out : stderr, "Columns renamed during .import %s due to duplicates:\n" "%s\n", sCtx.zFile, zRenames); sqlite3_free(zRenames); } assert(dbCols==0); if( zColDefs==0 ){ eputf("%s: empty file\n", sCtx.zFile); import_cleanup(&sCtx); rc = 1; goto meta_command_exit; } zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs); if( zCreate==0 ){ import_cleanup(&sCtx); shell_out_of_memory(); } if( eVerbose>=1 ){ oputf("%s\n", zCreate); } rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); sqlite3_free(zCreate); zCreate = 0; if( rc ){ eputf("%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); import_cleanup(&sCtx); rc = 1; goto meta_command_exit; } } zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);", zTable, zSchema); if( zSql==0 ){ import_cleanup(&sCtx); shell_out_of_memory(); } nByte = strlen(zSql); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); zSql = 0; if( rc ){ if (pStmt) sqlite3_finalize(pStmt); eputf("Error: %s\n", sqlite3_errmsg(p->db)); import_cleanup(&sCtx); rc = 1; goto meta_command_exit; } if( sqlite3_step(pStmt)==SQLITE_ROW ){ nCol = sqlite3_column_int(pStmt, 0); }else{ nCol = 0; } sqlite3_finalize(pStmt); pStmt = 0; if( nCol==0 ) return 0; /* no columns, no error */ zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ import_cleanup(&sCtx); shell_out_of_memory(); } if( zSchema ){ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?", zSchema, zTable); }else{ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); } j = strlen30(zSql); for(i=1; i<nCol; i++){ zSql[j++] = ','; zSql[j++] = '?'; } zSql[j++] = ')'; zSql[j] = 0; if( eVerbose>=2 ){ oputf("Insert using: %s\n", zSql); } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); zSql = 0; if( rc ){ eputf("Error: %s\n", sqlite3_errmsg(p->db)); if (pStmt) sqlite3_finalize(pStmt); import_cleanup(&sCtx); rc = 1; goto meta_command_exit; } needCommit = sqlite3_get_autocommit(p->db); if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0); do{ int startLine = sCtx.nLine; for(i=0; i<nCol; i++){ char *z = xRead(&sCtx); /* |
︙ | ︙ | |||
9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 | }else{ eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); rc = 1; } sqlite3_free(zSql); }else #endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */ #ifdef SQLITE_ENABLE_IOTRACE if( c=='i' && cli_strncmp(azArg[0], "iotrace", n)==0 ){ SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...); if( iotrace && iotrace!=stdout ) fclose(iotrace); iotrace = 0; if( nArg<2 ){ | > > > > > > > > > > > > > > > | 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 | }else{ eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); rc = 1; } sqlite3_free(zSql); }else #endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */ if( c=='i' && cli_strncmp(azArg[0], "intck", n)==0 ){ i64 iArg = 0; if( nArg==2 ){ iArg = integerValue(azArg[1]); if( iArg==0 ) iArg = -1; } if( (nArg!=1 && nArg!=2) || iArg<0 ){ eputf("%s","Usage: .intck STEPS_PER_UNLOCK\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); rc = intckDatabaseCmd(p, iArg); }else #ifdef SQLITE_ENABLE_IOTRACE if( c=='i' && cli_strncmp(azArg[0], "iotrace", n)==0 ){ SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...); if( iotrace && iotrace!=stdout ) fclose(iotrace); iotrace = 0; if( nArg<2 ){ |
︙ | ︙ | |||
10748 10749 10750 10751 10752 10753 10754 | } aCtrl[] = { {"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" }, {"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" }, /*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/ /*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/ {"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" }, {"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" }, | | | 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 10895 | } aCtrl[] = { {"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" }, {"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" }, /*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/ /*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/ {"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" }, {"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" }, {"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." }, {"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" }, {"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"}, {"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" }, {"json_selfcheck", SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN" }, {"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, {"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" }, {"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" }, |
︙ | ︙ | |||
10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 10992 10993 10994 | isOk = 1; }else{ rc2 = booleanValue(azArg[2]); isOk = 3; } sqlite3_test_control(testctrl, &rc2); break; } } if( isOk==0 && iCtrl>=0 ){ oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ oputf("%d\n", rc2); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124 11125 11126 11127 11128 11129 11130 11131 11132 11133 11134 11135 11136 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 11175 11176 11177 11178 11179 11180 11181 11182 11183 11184 11185 11186 11187 11188 11189 11190 11191 11192 11193 11194 11195 11196 11197 | isOk = 1; }else{ rc2 = booleanValue(azArg[2]); isOk = 3; } sqlite3_test_control(testctrl, &rc2); break; case SQLITE_TESTCTRL_FAULT_INSTALL: { int kk; int bShowHelp = nArg<=2; isOk = 3; for(kk=2; kk<nArg; kk++){ const char *z = azArg[kk]; if( z[0]=='-' && z[1]=='-' ) z++; if( cli_strcmp(z,"off")==0 ){ sqlite3_test_control(testctrl, 0); }else if( cli_strcmp(z,"on")==0 ){ faultsim_state.iCnt = faultsim_state.nSkip; if( faultsim_state.iErr==0 ) faultsim_state.iErr = 1; faultsim_state.nHit = 0; sqlite3_test_control(testctrl, faultsim_callback); }else if( cli_strcmp(z,"reset")==0 ){ faultsim_state.iCnt = faultsim_state.nSkip; faultsim_state.nHit = 0; sqlite3_test_control(testctrl, faultsim_callback); }else if( cli_strcmp(z,"status")==0 ){ oputf("faultsim.iId: %d\n", faultsim_state.iId); oputf("faultsim.iErr: %d\n", faultsim_state.iErr); oputf("faultsim.iCnt: %d\n", faultsim_state.iCnt); oputf("faultsim.nHit: %d\n", faultsim_state.nHit); oputf("faultsim.iInterval: %d\n", faultsim_state.iInterval); oputf("faultsim.eVerbose: %d\n", faultsim_state.eVerbose); oputf("faultsim.nRepeat: %d\n", faultsim_state.nRepeat); oputf("faultsim.nSkip: %d\n", faultsim_state.nSkip); }else if( cli_strcmp(z,"-v")==0 ){ if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++; }else if( cli_strcmp(z,"-q")==0 ){ if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--; }else if( cli_strcmp(z,"-id")==0 && kk+1<nArg ){ faultsim_state.iId = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-errcode")==0 && kk+1<nArg ){ faultsim_state.iErr = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-interval")==0 && kk+1<nArg ){ faultsim_state.iInterval = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-repeat")==0 && kk+1<nArg ){ faultsim_state.nRepeat = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-skip")==0 && kk+1<nArg ){ faultsim_state.nSkip = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){ bShowHelp = 1; }else{ eputf("Unrecognized fault_install argument: \"%s\"\n", azArg[kk]); rc = 1; bShowHelp = 1; break; } } if( bShowHelp ){ oputz( "Usage: .testctrl fault_install ARGS\n" "Possible arguments:\n" " off Disable faultsim\n" " on Activate faultsim\n" " reset Reset the trigger counter\n" " status Show current status\n" " -v Increase verbosity\n" " -q Decrease verbosity\n" " --errcode N When triggered, return N as error code\n" " --id ID Trigger only for the ID specified\n" " --interval N Trigger only after every N-th call\n" " --repeat N Turn off after N hits. 0 means never\n" " --skip N Skip the first N encounters\n" ); } break; } } } if( isOk==0 && iCtrl>=0 ){ oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ oputf("%d\n", rc2); |
︙ | ︙ | |||
11386 11387 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 | if( zSql==0 ) return 1; zSql[nSql] = ';'; zSql[nSql+1] = 0; rc = sqlite3_complete(zSql); zSql[nSql] = 0; return rc; } /* ** Run a single line of SQL. Return the number of errors. */ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ int rc; char *zErrMsg = 0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 11624 11625 11626 11627 11628 11629 11630 11631 11632 11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 11684 | if( zSql==0 ) return 1; zSql[nSql] = ';'; zSql[nSql+1] = 0; rc = sqlite3_complete(zSql); zSql[nSql] = 0; return rc; } /* ** This function is called after processing each line of SQL in the ** runOneSqlLine() function. Its purpose is to detect scenarios where ** defensive mode should be automatically turned off. Specifically, when ** ** 1. The first line of input is "PRAGMA foreign_keys=OFF;", ** 2. The second line of input is "BEGIN TRANSACTION;", ** 3. The database is empty, and ** 4. The shell is not running in --safe mode. ** ** The implementation uses the ShellState.eRestoreState to maintain state: ** ** 0: Have not seen any SQL. ** 1: Have seen "PRAGMA foreign_keys=OFF;". ** 2-6: Currently running .dump transaction. If the "2" bit is set, ** disable DEFENSIVE when done. If "4" is set, disable DQS_DDL. ** 7: Nothing left to do. This function becomes a no-op. */ static int doAutoDetectRestore(ShellState *p, const char *zSql){ int rc = SQLITE_OK; if( p->eRestoreState<7 ){ switch( p->eRestoreState ){ case 0: { const char *zExpect = "PRAGMA foreign_keys=OFF;"; assert( strlen(zExpect)==24 ); if( p->bSafeMode==0 && memcmp(zSql, zExpect, 25)==0 ){ p->eRestoreState = 1; }else{ p->eRestoreState = 7; } break; }; case 1: { int bIsDump = 0; const char *zExpect = "BEGIN TRANSACTION;"; assert( strlen(zExpect)==18 ); if( memcmp(zSql, zExpect, 19)==0 ){ /* Now check if the database is empty. */ const char *zQuery = "SELECT 1 FROM sqlite_schema LIMIT 1"; sqlite3_stmt *pStmt = 0; bIsDump = 1; shellPrepare(p->db, &rc, zQuery, &pStmt); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ bIsDump = 0; } shellFinalize(&rc, pStmt); } if( bIsDump && rc==SQLITE_OK ){ int bDefense = 0; int bDqsDdl = 0; sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDefense); sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, -1, &bDqsDdl); sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0); sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 1, 0); p->eRestoreState = (bDefense ? 2 : 0) + (bDqsDdl ? 4 : 0); }else{ p->eRestoreState = 7; } break; } default: { if( sqlite3_get_autocommit(p->db) ){ if( (p->eRestoreState & 2) ){ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0); } if( (p->eRestoreState & 4) ){ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 0, 0); } p->eRestoreState = 7; } break; } } } return rc; } /* ** Run a single line of SQL. Return the number of errors. */ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ int rc; char *zErrMsg = 0; |
︙ | ︙ | |||
11434 11435 11436 11437 11438 11439 11440 11441 11442 11443 11444 11445 11446 11447 | }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ char zLineBuf[2000]; sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); oputf("%s\n", zLineBuf); } return 0; } static void echo_group_input(ShellState *p, const char *zDo){ if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo); } | > > | 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 | }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ char zLineBuf[2000]; sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); oputf("%s\n", zLineBuf); } if( doAutoDetectRestore(p, zSql) ) return 1; return 0; } static void echo_group_input(ShellState *p, const char *zDo){ if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo); } |
︙ | ︙ | |||
11786 11787 11788 11789 11790 11791 11792 11793 11794 11795 11796 11797 11798 11799 | " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nofollow refuse to open symbolic links to database files\n" " -nonce STRING set the safe-mode escape nonce\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -pcachetrace trace all page cache operations\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -safe enable safe-mode\n" " -separator SEP set output column separator. Default: '|'\n" | > | 12073 12074 12075 12076 12077 12078 12079 12080 12081 12082 12083 12084 12085 12086 12087 | " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nofollow refuse to open symbolic links to database files\n" " -nonce STRING set the safe-mode escape nonce\n" " -no-rowid-in-view Disable rowid-in-view using sqlite3_config()\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -pcachetrace trace all page cache operations\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -safe enable safe-mode\n" " -separator SEP set output column separator. Default: '|'\n" |
︙ | ︙ | |||
11818 11819 11820 11821 11822 11823 11824 | "FILENAME is the name of an SQLite database. A new database is created\n" "if the file does not previously exist. Defaults to :memory:.\n", Argv0); if( showDetail ){ eputf("OPTIONS include:\n%s", zOptions); }else{ eputz("Use the -help option for additional information\n"); } | | | 12106 12107 12108 12109 12110 12111 12112 12113 12114 12115 12116 12117 12118 12119 12120 | "FILENAME is the name of an SQLite database. A new database is created\n" "if the file does not previously exist. Defaults to :memory:.\n", Argv0); if( showDetail ){ eputf("OPTIONS include:\n%s", zOptions); }else{ eputz("Use the -help option for additional information\n"); } exit(0); } /* ** Internal check: Verify that the SQLite is uninitialized. Print a ** error message if it is initialized. */ static void verify_uninitialized(void){ |
︙ | ︙ | |||
12076 12077 12078 12079 12080 12081 12082 12083 12084 12085 12086 12087 12088 12089 | /* Need to check for batch mode here to so we can avoid printing ** informational messages (like from process_sqliterc) before ** we do the actual processing of arguments later in a second pass. */ stdin_is_interactive = 0; }else if( cli_strcmp(z,"-utf8")==0 ){ }else if( cli_strcmp(z,"-no-utf8")==0 ){ }else if( cli_strcmp(z,"-heap")==0 ){ #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) const char *zSize; sqlite3_int64 szHeap; zSize = cmdline_option_value(argc, argv, ++i); szHeap = integerValue(zSize); | > > > > | 12364 12365 12366 12367 12368 12369 12370 12371 12372 12373 12374 12375 12376 12377 12378 12379 12380 12381 | /* Need to check for batch mode here to so we can avoid printing ** informational messages (like from process_sqliterc) before ** we do the actual processing of arguments later in a second pass. */ stdin_is_interactive = 0; }else if( cli_strcmp(z,"-utf8")==0 ){ }else if( cli_strcmp(z,"-no-utf8")==0 ){ }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ int val = 0; sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW, &val); assert( val==0 ); }else if( cli_strcmp(z,"-heap")==0 ){ #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) const char *zSize; sqlite3_int64 szHeap; zSize = cmdline_option_value(argc, argv, ++i); szHeap = integerValue(zSize); |
︙ | ︙ | |||
12351 12352 12353 12354 12355 12356 12357 12358 12359 12360 12361 12362 12363 12364 | stdin_is_interactive = 1; }else if( cli_strcmp(z,"-batch")==0 ){ /* already handled */ }else if( cli_strcmp(z,"-utf8")==0 ){ /* already handled */ }else if( cli_strcmp(z,"-no-utf8")==0 ){ /* already handled */ }else if( cli_strcmp(z,"-heap")==0 ){ i++; }else if( cli_strcmp(z,"-pagecache")==0 ){ i+=2; }else if( cli_strcmp(z,"-lookaside")==0 ){ i+=2; }else if( cli_strcmp(z,"-threadsafe")==0 ){ | > > | 12643 12644 12645 12646 12647 12648 12649 12650 12651 12652 12653 12654 12655 12656 12657 12658 | stdin_is_interactive = 1; }else if( cli_strcmp(z,"-batch")==0 ){ /* already handled */ }else if( cli_strcmp(z,"-utf8")==0 ){ /* already handled */ }else if( cli_strcmp(z,"-no-utf8")==0 ){ /* already handled */ }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ /* already handled */ }else if( cli_strcmp(z,"-heap")==0 ){ i++; }else if( cli_strcmp(z,"-pagecache")==0 ){ i+=2; }else if( cli_strcmp(z,"-lookaside")==0 ){ i+=2; }else if( cli_strcmp(z,"-threadsafe")==0 ){ |
︙ | ︙ | |||
12512 12513 12514 12515 12516 12517 12518 12519 12520 12521 12522 12523 12524 12525 | data.in = stdin; rc = process_input(&data); } } #ifndef SQLITE_SHELL_FIDDLE /* In WASM mode we have to leave the db state in place so that ** client code can "push" SQL into it after this call returns. */ free(azCmd); set_table_name(&data, 0); if( data.db ){ session_close_all(&data, -1); close_db(data.db); } for(i=0; i<ArraySize(data.aAuxDb); i++){ | > > > > > | 12806 12807 12808 12809 12810 12811 12812 12813 12814 12815 12816 12817 12818 12819 12820 12821 12822 12823 12824 | data.in = stdin; rc = process_input(&data); } } #ifndef SQLITE_SHELL_FIDDLE /* In WASM mode we have to leave the db state in place so that ** client code can "push" SQL into it after this call returns. */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( data.expert.pExpert ){ expertFinish(&data, 1, 0); } #endif free(azCmd); set_table_name(&data, 0); if( data.db ){ session_close_all(&data, -1); close_db(data.db); } for(i=0; i<ArraySize(data.aAuxDb); i++){ |
︙ | ︙ | |||
12578 12579 12580 12581 12582 12583 12584 | SQLITE_FCNTL_VFS_POINTER, &pVfs); } return pVfs; } /* Only for emcc experimentation purposes. */ sqlite3 * fiddle_db_arg(sqlite3 *arg){ | | | 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889 12890 12891 | SQLITE_FCNTL_VFS_POINTER, &pVfs); } return pVfs; } /* Only for emcc experimentation purposes. */ sqlite3 * fiddle_db_arg(sqlite3 *arg){ oputf("fiddle_db_arg(%p)\n", (const void*)arg); return arg; } /* ** Intended to be called via a SharedWorker() while a separate ** SharedWorker() (which manages the wasm module) is performing work ** which should be interrupted. Unfortunately, SharedWorker is not |
︙ | ︙ | |||
12604 12605 12606 12607 12608 12609 12610 | return globalDb ? sqlite3_db_filename(globalDb, zDbName ? zDbName : "main") : NULL; } /* ** Completely wipes out the contents of the currently-opened database | | > > > > > > > > > > | | | 12903 12904 12905 12906 12907 12908 12909 12910 12911 12912 12913 12914 12915 12916 12917 12918 12919 12920 12921 12922 12923 12924 12925 12926 12927 12928 12929 12930 12931 12932 | return globalDb ? sqlite3_db_filename(globalDb, zDbName ? zDbName : "main") : NULL; } /* ** Completely wipes out the contents of the currently-opened database ** but leaves its storage intact for reuse. If any transactions are ** active, they are forcibly rolled back. */ void fiddle_reset_db(void){ if( globalDb ){ int rc; while( sqlite3_txn_state(globalDb,0)>0 ){ /* ** Resolve problem reported in ** https://sqlite.org/forum/forumpost/0b41a25d65 */ oputz("Rolling back in-progress transaction.\n"); sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0); } rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); if( 0==rc ) sqlite3_exec(globalDb, "VACUUM", 0, 0, 0); sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); } } /* ** Uses the current database's VFS xRead to stream the db file's ** contents out to the given callback. The callback gets a single |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
416 417 418 419 420 421 422 423 424 425 426 427 428 429 | ** <ul> ** <li> The application must ensure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. ** <li> The application must not close the [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. ** <li> The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** </ul> */ int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ void *, /* 1st argument to callback */ | > > | 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | ** <ul> ** <li> The application must ensure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. ** <li> The application must not close the [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. ** <li> The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** <li> The application must not dereference the arrays or string pointers ** passed as the 3rd and 4th callback parameters after it returns. ** </ul> */ int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ void *, /* 1st argument to callback */ |
︙ | ︙ | |||
758 759 760 761 762 763 764 | ** <li> [SQLITE_LOCK_SHARED], ** <li> [SQLITE_LOCK_RESERVED], ** <li> [SQLITE_LOCK_PENDING], or ** <li> [SQLITE_LOCK_EXCLUSIVE]. ** </ul> ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to | | | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | ** <li> [SQLITE_LOCK_SHARED], ** <li> [SQLITE_LOCK_RESERVED], ** <li> [SQLITE_LOCK_PENDING], or ** <li> [SQLITE_LOCK_EXCLUSIVE]. ** </ul> ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to ** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ** SQLITE_LOCK_NONE. If the database file lock is already at or above the ** requested lock, then the call to xLock() is a no-op. ** xUnlock() downgrades the database file lock to either SHARED or NONE. ** If the lock is already at or below the requested lock state, then the call ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, ** PENDING, or EXCLUSIVE lock on the file. It returns true ** if such a lock exists and false otherwise. ** ** The xFileControl() method is a generic interface that allows custom |
︙ | ︙ | |||
2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 | ** [sqlite3_int64] parameter which is the default maximum size for an in-memory ** database created using [sqlite3_deserialize()]. This default maximum ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | > > > > > > > > > > > > > > > > | 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 | ** [sqlite3_int64] parameter which is the default maximum size for an in-memory ** database created using [sqlite3_deserialize()]. This default maximum ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** ** [[SQLITE_CONFIG_ROWID_IN_VIEW]] ** <dt>SQLITE_CONFIG_ROWID_IN_VIEW ** <dd>The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability ** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is ** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability ** defaults to on. This configuration option queries the current setting or ** changes the setting to off or on. The argument is a pointer to an integer. ** If that integer initially holds a value of 1, then the ability for VIEWs to ** have ROWIDs is activated. If the integer initially holds zero, then the ** ability is deactivated. Any other initial value for the integer leaves the ** setting unchanged. After changes, if any, the integer is written with ** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite ** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and ** recommended case) then the integer is always filled with zero, regardless ** if its initial value. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
︙ | ︙ | |||
2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** | > | 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ #define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** |
︙ | ︙ | |||
3282 3283 3284 3285 3286 3287 3288 | #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* | | | | 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 | #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* ** CAPI3REF: Deprecated Tracing And Profiling Functions ** DEPRECATED ** ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface ** instead of the routines described here. ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. ** |
︙ | ︙ | |||
6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 | ** ** ^In the current implementation, the update hook ** is not invoked when conflicting rows are deleted because of an ** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the update hook. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. | > > > > > > | 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 | ** ** ^In the current implementation, the update hook ** is not invoked when conflicting rows are deleted because of an ** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** ** Whether the update hook is invoked before or after the ** corresponding change is currently unspecified and may differ ** depending on the type of change. Do not rely on the order of the ** hook call with regards to the final result of the operation which ** triggers the hook. ** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the update hook. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. |
︙ | ︙ | |||
8334 8335 8336 8337 8338 8339 8340 | ** recognized by SQLite. Applications can uses these routines to determine ** whether or not a specific identifier needs to be escaped (for example, ** by enclosing in double-quotes) so as not to confuse the parser. ** ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** | | | 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 | ** recognized by SQLite. Applications can uses these routines to determine ** whether or not a specific identifier needs to be escaped (for example, ** by enclosing in double-quotes) so as not to confuse the parser. ** ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** ** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and ** makes *Z point to that keyword expressed as UTF8 and writes the number ** of bytes in the keyword into *L. The string that *Z points to is not ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns ** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z ** or L are NULL or invalid pointers then calls to ** sqlite3_keyword_name(N,Z,L) result in undefined behavior. ** |
︙ | ︙ | |||
9913 9914 9915 9916 9917 9918 9919 | ** that the query planner does not need the rows to be returned in sorted order ** as long as all rows with the same values in all columns identified by the ** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ** is doing a GROUP BY. ** <li value="2"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular | | | | < | | < > | | < | > > > > | > > | > > > > > > > > > > > > > > > > > | 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 | ** that the query planner does not need the rows to be returned in sorted order ** as long as all rows with the same values in all columns identified by the ** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ** is doing a GROUP BY. ** <li value="2"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular ** order, as long as rows with the same values in all columns identified ** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows ** contain the same values for all columns identified by "colUsed", all but ** one such row may optionally be omitted from the result.)^ ** The virtual table is not required to omit rows that are duplicates ** over the "colUsed" columns, but if the virtual table can do that without ** too much extra effort, it could potentially help the query to run faster. ** This mode is used for a DISTINCT query. ** <li value="3"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the ** virtual table must return rows in the order defined by "aOrderBy" as ** if the sqlite3_vtab_distinct() interface had returned 0. However if ** two or more rows in the result have the same values for all columns ** identified by "colUsed", then all but one such row may optionally be ** omitted.)^ Like when the return value is 2, the virtual table ** is not required to omit rows that are duplicates over the "colUsed" ** columns, but if the virtual table can do that without ** too much extra effort, it could potentially help the query to run faster. ** This mode is used for queries ** that have both DISTINCT and ORDER BY clauses. ** </ol> ** ** <p>The following table summarizes the conditions under which the ** virtual table is allowed to set the "orderByConsumed" flag based on ** the value returned by sqlite3_vtab_distinct(). This table is a ** restatement of the previous four paragraphs: ** ** <table border=1 cellspacing=0 cellpadding=10 width="90%"> ** <tr> ** <td valign="top">sqlite3_vtab_distinct() return value ** <td valign="top">Rows are returned in aOrderBy order ** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent ** <td valign="top">Duplicates over all colUsed columns may be omitted ** <tr><td>0<td>yes<td>yes<td>no ** <tr><td>1<td>no<td>yes<td>no ** <tr><td>2<td>no<td>yes<td>yes ** <tr><td>3<td>yes<td>yes<td>yes ** </table> ** ** ^For the purposes of comparing virtual table output values to see if the ** values are same value for sorting purposes, two NULL values are considered ** to be the same. In other words, the comparison operator is "IS" ** (or "IS NOT DISTINCT FROM") and not "==". ** ** If a virtual table implementation is unable to meet the requirements |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
323 324 325 326 327 328 329 330 331 332 333 334 335 336 | ** SEH support if the -DSQLITE_OMIT_SEH option is given. */ #if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH) # define SQLITE_USE_SEH 1 #else # undef SQLITE_USE_SEH #endif /* ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. ** 0 means mutexes are permanently disable and the library is never ** threadsafe. 1 means the library is serialized which is the highest ** level of threadsafety. 2 means the library is multithreaded - multiple ** threads can use SQLite as long as no two threads try to use the same | > > > > > > > > > > > > > | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | ** SEH support if the -DSQLITE_OMIT_SEH option is given. */ #if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH) # define SQLITE_USE_SEH 1 #else # undef SQLITE_USE_SEH #endif /* ** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly ** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0 */ #if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1 /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */ # undef SQLITE_DIRECT_OVERFLOW_READ #else /* In all other cases, enable */ # define SQLITE_DIRECT_OVERFLOW_READ 1 #endif /* ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. ** 0 means mutexes are permanently disable and the library is never ** threadsafe. 1 means the library is serialized which is the highest ** level of threadsafety. 2 means the library is multithreaded - multiple ** threads can use SQLite as long as no two threads try to use the same |
︙ | ︙ | |||
591 592 593 594 595 596 597 598 599 600 601 602 603 604 | /* ** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE */ #if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) # define SQLITE_OMIT_ALTERTABLE #endif /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() ** macros to verify that we have tested SQLite for large-file support. */ #define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0) | > > | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 | /* ** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE */ #if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) # define SQLITE_OMIT_ALTERTABLE #endif #define SQLITE_DIGIT_SEPARATOR '_' /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() ** macros to verify that we have tested SQLite for large-file support. */ #define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0) |
︙ | ︙ | |||
869 870 871 872 873 874 875 | ** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer */ #ifndef SQLITE_PTRSIZE # if defined(__SIZEOF_POINTER__) # define SQLITE_PTRSIZE __SIZEOF_POINTER__ # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ | | | 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 | ** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer */ #ifndef SQLITE_PTRSIZE # if defined(__SIZEOF_POINTER__) # define SQLITE_PTRSIZE __SIZEOF_POINTER__ # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ (defined(__APPLE__) && defined(__ppc__)) || \ (defined(__TOS_AIX__) && !defined(__64BIT__)) # define SQLITE_PTRSIZE 4 # else # define SQLITE_PTRSIZE 8 # endif #endif |
︙ | ︙ | |||
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 | ** 0x00001000 LEFT JOIN simplifies to JOIN ** 0x00002000 Constant propagation ** 0x00004000 Push-down optimization ** 0x00008000 After all FROM-clause analysis ** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing ** 0x00020000 Transform DISTINCT into GROUP BY ** 0x00040000 SELECT tree dump after all code has been generated */ /* ** Macros for "wheretrace" */ extern u32 sqlite3WhereTrace; #if defined(SQLITE_DEBUG) \ | > | 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 | ** 0x00001000 LEFT JOIN simplifies to JOIN ** 0x00002000 Constant propagation ** 0x00004000 Push-down optimization ** 0x00008000 After all FROM-clause analysis ** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing ** 0x00020000 Transform DISTINCT into GROUP BY ** 0x00040000 SELECT tree dump after all code has been generated ** 0x00080000 NOT NULL strength reduction */ /* ** Macros for "wheretrace" */ extern u32 sqlite3WhereTrace; #if defined(SQLITE_DEBUG) \ |
︙ | ︙ | |||
1136 1137 1138 1139 1140 1141 1142 | ** 0x00000002 Solver ** 0x00000004 Solver costs ** 0x00000008 WhereLoop inserts ** ** 0x00000010 Display sqlite3_index_info xBestIndex calls ** 0x00000020 Range an equality scan metrics ** 0x00000040 IN operator decisions | | | 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 | ** 0x00000002 Solver ** 0x00000004 Solver costs ** 0x00000008 WhereLoop inserts ** ** 0x00000010 Display sqlite3_index_info xBestIndex calls ** 0x00000020 Range an equality scan metrics ** 0x00000040 IN operator decisions ** 0x00000080 WhereLoop cost adjustments ** 0x00000100 ** 0x00000200 Covering index decisions ** 0x00000400 OR optimization ** 0x00000800 Index scanner ** 0x00001000 More details associated with code generation ** 0x00002000 ** 0x00004000 Show all WHERE terms at key points |
︙ | ︙ | |||
1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 | */ #define SQLITE_FUNC_HASH_SZ 23 struct FuncDefHash { FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */ }; #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) #ifdef SQLITE_USER_AUTHENTICATION /* ** Information held in the "sqlite3" database connection object and used ** to manage user authentication. */ typedef struct sqlite3_userauth sqlite3_userauth; struct sqlite3_userauth { | > > > > | 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 | */ #define SQLITE_FUNC_HASH_SZ 23 struct FuncDefHash { FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */ }; #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) #if defined(SQLITE_USER_AUTHENTICATION) # warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \ See ext/userauth/user-auth.txt for details." #endif #ifdef SQLITE_USER_AUTHENTICATION /* ** Information held in the "sqlite3" database connection object and used ** to manage user authentication. */ typedef struct sqlite3_userauth sqlite3_userauth; struct sqlite3_userauth { |
︙ | ︙ | |||
1882 1883 1884 1885 1886 1887 1888 | #define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */ #define SQLITE_Transitive 0x00000080 /* Transitive constraints */ #define SQLITE_OmitNoopJoin 0x00000100 /* Omit unused tables in joins */ #define SQLITE_CountOfView 0x00000200 /* The count-of-view optimization */ #define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */ #define SQLITE_Stat4 0x00000800 /* Use STAT4 data */ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */ | | | 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 | #define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */ #define SQLITE_Transitive 0x00000080 /* Transitive constraints */ #define SQLITE_OmitNoopJoin 0x00000100 /* Omit unused tables in joins */ #define SQLITE_CountOfView 0x00000200 /* The count-of-view optimization */ #define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */ #define SQLITE_Stat4 0x00000800 /* Use STAT4 data */ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */ #define SQLITE_PushDown 0x00001000 /* WHERE-clause push-down opt */ #define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */ #define SQLITE_SkipScan 0x00004000 /* Skip-scans */ #define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */ #define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */ #define SQLITE_SeekScan 0x00020000 /* The OP_SeekScan optimization */ #define SQLITE_OmitOrderBy 0x00040000 /* Omit pointless ORDER BY */ /* TH3 expects this value ^^^^^^^^^^ to be 0x40000. Coordinate any change */ |
︙ | ︙ | |||
2455 2456 2457 2458 2459 2460 2461 | #define TF_HasPrimaryKey 0x00000004 /* Table has a primary key */ #define TF_Autoincrement 0x00000008 /* Integer primary key is autoincrement */ #define TF_HasStat1 0x00000010 /* nRowLogEst set from sqlite_stat1 */ #define TF_HasVirtual 0x00000020 /* Has one or more VIRTUAL columns */ #define TF_HasStored 0x00000040 /* Has one or more STORED columns */ #define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ #define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ | | < | 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 | #define TF_HasPrimaryKey 0x00000004 /* Table has a primary key */ #define TF_Autoincrement 0x00000008 /* Integer primary key is autoincrement */ #define TF_HasStat1 0x00000010 /* nRowLogEst set from sqlite_stat1 */ #define TF_HasVirtual 0x00000020 /* Has one or more VIRTUAL columns */ #define TF_HasStored 0x00000040 /* Has one or more STORED columns */ #define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ #define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ #define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */ #define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ #define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ #define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ #define TF_Shadow 0x00001000 /* True for a shadow table */ #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ #define TF_Ephemeral 0x00004000 /* An ephemeral table */ #define TF_Eponymous 0x00008000 /* An eponymous virtual table */ |
︙ | ︙ | |||
2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 | # define IsOrdinaryHiddenColumn(X) 0 #endif /* Does the table have a rowid */ #define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0) #define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0) /* ** Each foreign key constraint is an instance of the following structure. ** ** A foreign key is associated with two tables. The "from" table is ** the table that contains the REFERENCES clause that creates the foreign ** key. The "to" table is the table that is named in the REFERENCES clause. | > > > > > > > > > | 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 | # define IsOrdinaryHiddenColumn(X) 0 #endif /* Does the table have a rowid */ #define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0) #define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0) /* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is ** available. By default, this macro is false */ #ifndef SQLITE_ALLOW_ROWID_IN_VIEW # define ViewCanHaveRowid 0 #else # define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0) #endif /* ** Each foreign key constraint is an instance of the following structure. ** ** A foreign key is associated with two tables. The "from" table is ** the table that contains the REFERENCES clause that creates the foreign ** key. The "to" table is the table that is named in the REFERENCES clause. |
︙ | ︙ | |||
2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 | unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ unsigned isResized:1; /* True if resizeIndexObject() has been called */ unsigned isCovering:1; /* True if this is a covering index */ unsigned noSkipScan:1; /* Do not try to use skip-scan if true */ unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */ unsigned bNoQuery:1; /* Do not use this index to optimize queries */ unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */ unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ unsigned bHasExpr:1; /* Index contains an expression, either a literal ** expression, or a reference to a VIRTUAL column */ #ifdef SQLITE_ENABLE_STAT4 int nSample; /* Number of elements in aSample[] */ | > | 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 | unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ unsigned isResized:1; /* True if resizeIndexObject() has been called */ unsigned isCovering:1; /* True if this is a covering index */ unsigned noSkipScan:1; /* Do not try to use skip-scan if true */ unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */ unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */ unsigned bNoQuery:1; /* Do not use this index to optimize queries */ unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */ unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ unsigned bHasExpr:1; /* Index contains an expression, either a literal ** expression, or a reference to a VIRTUAL column */ #ifdef SQLITE_ENABLE_STAT4 int nSample; /* Number of elements in aSample[] */ |
︙ | ︙ | |||
3246 3247 3248 3249 3250 3251 3252 | ** jointype expresses the join between the table and the previous table. ** ** In the colUsed field, the high-order bit (bit 63) is set if the table ** contains more than 63 columns and the 64-th or later column is used. ** ** Union member validity: ** | | | > > | | | 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 | ** jointype expresses the join between the table and the previous table. ** ** In the colUsed field, the high-order bit (bit 63) is set if the table ** contains more than 63 columns and the 64-th or later column is used. ** ** Union member validity: ** ** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc ** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy ** u1.nRow !fg.isTabFunc && !fg.isIndexedBy ** ** u2.pIBIndex fg.isIndexedBy && !fg.isCte ** u2.pCteUse fg.isCte && !fg.isIndexedBy */ struct SrcItem { Schema *pSchema; /* Schema to which this item is fixed */ char *zDatabase; /* Name of database holding this table */ char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ Table *pTab; /* An SQL table corresponding to zName */ |
︙ | ︙ | |||
3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 | unsigned fromDDL :1; /* Comes from sqlite_schema */ unsigned isCte :1; /* This is a CTE */ unsigned notCte :1; /* This item may not match a CTE */ unsigned isUsing :1; /* u3.pUsing is valid */ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ union { Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ } u3; Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ union { char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ } u1; union { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ } u2; }; | > > | 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 | unsigned fromDDL :1; /* Comes from sqlite_schema */ unsigned isCte :1; /* This is a CTE */ unsigned notCte :1; /* This item may not match a CTE */ unsigned isUsing :1; /* u3.pUsing is valid */ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ unsigned rowidUsed :1; /* The ROWID of this table is referenced */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ union { Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ } u3; Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ union { char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ u32 nRow; /* Number of rows in a VALUES clause */ } u1; union { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ } u2; }; |
︙ | ︙ | |||
3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 | #define NC_Complex 0x002000 /* True if a function or subquery seen */ #define NC_AllowWin 0x004000 /* Window functions are allowed here */ #define NC_HasWin 0x008000 /* One or more window functions seen */ #define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */ #define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */ #define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */ #define NC_NoSelect 0x080000 /* Do not descend into sub-selects */ #define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */ /* ** An instance of the following object describes a single ON CONFLICT ** clause in an upsert. ** ** The pUpsertTarget field is only set if the ON CONFLICT clause includes | > | 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 | #define NC_Complex 0x002000 /* True if a function or subquery seen */ #define NC_AllowWin 0x004000 /* Window functions are allowed here */ #define NC_HasWin 0x008000 /* One or more window functions seen */ #define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */ #define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */ #define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */ #define NC_NoSelect 0x080000 /* Do not descend into sub-selects */ #define NC_Where 0x100000 /* Processing WHERE clause of a SELECT */ #define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */ /* ** An instance of the following object describes a single ON CONFLICT ** clause in an upsert. ** ** The pUpsertTarget field is only set if the ON CONFLICT clause includes |
︙ | ︙ | |||
3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 | struct Upsert { ExprList *pUpsertTarget; /* Optional description of conflict target */ Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */ /* Above this point is the parse tree for the ON CONFLICT clauses. ** The next group of fields stores intermediate data. */ void *pToFree; /* Free memory when deleting the Upsert object */ /* All fields above are owned by the Upsert object and must be freed ** when the Upsert is destroyed. The fields below are used to transfer ** information from the INSERT processing down into the UPDATE processing ** while generating code. The fields below are owned by the INSERT | > | 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 | struct Upsert { ExprList *pUpsertTarget; /* Optional description of conflict target */ Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */ u8 isDup; /* True if 2nd or later with same pUpsertIdx */ /* Above this point is the parse tree for the ON CONFLICT clauses. ** The next group of fields stores intermediate data. */ void *pToFree; /* Free memory when deleting the Upsert object */ /* All fields above are owned by the Upsert object and must be freed ** when the Upsert is destroyed. The fields below are used to transfer ** information from the INSERT processing down into the UPDATE processing ** while generating code. The fields below are owned by the INSERT |
︙ | ︙ | |||
3542 3543 3544 3545 3546 3547 3548 | #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */ #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */ #define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */ #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ #define SF_View 0x0200000 /* SELECT statement is a view */ #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ #define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ | | > | 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 | #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */ #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */ #define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */ #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ #define SF_View 0x0200000 /* SELECT statement is a view */ #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ #define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ #define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ #define SF_Correlated 0x20000000 /* True if references the outer context */ /* True if S exists and has SF_NestedFrom */ #define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) /* ** The results of a SELECT can be distributed in several ways, as defined ** by one of the following macros. The "SRT" prefix means "SELECT Result |
︙ | ︙ | |||
3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 | u8 isMultiWrite; /* True if statement may modify/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif #ifdef SQLITE_DEBUG u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ #endif int nRangeReg; /* Size of the temporary register block */ | > | 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 | u8 isMultiWrite; /* True if statement may modify/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ u8 bHasWith; /* True if statement contains WITH */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif #ifdef SQLITE_DEBUG u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ #endif int nRangeReg; /* Size of the temporary register block */ |
︙ | ︙ | |||
4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 | #endif #ifndef SQLITE_OMIT_DESERIALIZE sqlite3_int64 mxMemdbSize; /* Default max memdb size */ #endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */ int iOnceResetThreshold; /* When to reset OP_Once counters */ u32 szSorterRef; /* Min size in bytes to use sorter-refs */ unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */ /* vvvv--- must be last ---vvv */ #ifdef SQLITE_DEBUG | > > > > > | 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 | #endif #ifndef SQLITE_OMIT_DESERIALIZE sqlite3_int64 mxMemdbSize; /* Default max memdb size */ #endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ #endif #ifdef SQLITE_ALLOW_ROWID_IN_VIEW u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW ** feature is disabled. 0 if rowids can ** occur in views. */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */ int iOnceResetThreshold; /* When to reset OP_Once counters */ u32 szSorterRef; /* Min size in bytes to use sorter-refs */ unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */ /* vvvv--- must be last ---vvv */ #ifdef SQLITE_DEBUG |
︙ | ︙ | |||
4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 | int regOne; /* Register containing constant value 1 */ int regStartRowid; int regEndRowid; u8 bExprArgs; /* Defer evaluation of window function arguments ** due to the SQLITE_SUBTYPE flag */ }; #ifndef SQLITE_OMIT_WINDOWFUNC void sqlite3WindowDelete(sqlite3*, Window*); void sqlite3WindowUnlinkFromSelect(Window*); void sqlite3WindowListDelete(sqlite3 *db, Window *p); Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); void sqlite3WindowAttach(Parse*, Expr*, Window*); void sqlite3WindowLink(Select *pSel, Window *pWin); | > > > | 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 | int regOne; /* Register containing constant value 1 */ int regStartRowid; int regEndRowid; u8 bExprArgs; /* Defer evaluation of window function arguments ** due to the SQLITE_SUBTYPE flag */ }; Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow); void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal); #ifndef SQLITE_OMIT_WINDOWFUNC void sqlite3WindowDelete(sqlite3*, Window*); void sqlite3WindowUnlinkFromSelect(Window*); void sqlite3WindowListDelete(sqlite3 *db, Window *p); Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); void sqlite3WindowAttach(Parse*, Expr*, Window*); void sqlite3WindowLink(Select *pSel, Window *pWin); |
︙ | ︙ | |||
4679 4680 4681 4682 4683 4684 4685 4686 4687 | # define sqlite3MutexWarnOnContention(x) #endif #ifndef SQLITE_OMIT_FLOATING_POINT # define EXP754 (((u64)0x7ff)<<52) # define MAN754 ((((u64)1)<<52)-1) # define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0) int sqlite3IsNaN(double); #else | > > | | > | 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 | # define sqlite3MutexWarnOnContention(x) #endif #ifndef SQLITE_OMIT_FLOATING_POINT # define EXP754 (((u64)0x7ff)<<52) # define MAN754 ((((u64)1)<<52)-1) # define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0) # define IsOvfl(X) (((X)&EXP754)==EXP754) int sqlite3IsNaN(double); int sqlite3IsOverflow(double); #else # define IsNaN(X) 0 # define sqlite3IsNaN(X) 0 # define sqlite3IsOVerflow(X) 0 #endif /* ** An instance of the following structure holds information about SQL ** functions arguments that are the parameters to the printf() function. */ struct PrintfArguments { |
︙ | ︙ | |||
4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 | void sqlite3SetString(char **, sqlite3*, const char*); void sqlite3ProgressCheck(Parse*); void sqlite3ErrorMsg(Parse*, const char*, ...); int sqlite3ErrorToParser(sqlite3*,int); void sqlite3Dequote(char*); void sqlite3DequoteExpr(Expr*); void sqlite3DequoteToken(Token*); void sqlite3TokenInit(Token*,char*); int sqlite3KeywordCode(const unsigned char*, int); int sqlite3RunParser(Parse*, const char*); void sqlite3FinishCoding(Parse*); int sqlite3GetTempReg(Parse*); void sqlite3ReleaseTempReg(Parse*,int); int sqlite3GetTempRange(Parse*,int); | > | 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 | void sqlite3SetString(char **, sqlite3*, const char*); void sqlite3ProgressCheck(Parse*); void sqlite3ErrorMsg(Parse*, const char*, ...); int sqlite3ErrorToParser(sqlite3*,int); void sqlite3Dequote(char*); void sqlite3DequoteExpr(Expr*); void sqlite3DequoteToken(Token*); void sqlite3DequoteNumber(Parse*, Expr*); void sqlite3TokenInit(Token*,char*); int sqlite3KeywordCode(const unsigned char*, int); int sqlite3RunParser(Parse*, const char*); void sqlite3FinishCoding(Parse*); int sqlite3GetTempReg(Parse*); void sqlite3ReleaseTempReg(Parse*,int); int sqlite3GetTempRange(Parse*,int); |
︙ | ︙ | |||
4804 4805 4806 4807 4808 4809 4810 | Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*); void sqlite3ExprOrderByAggregateError(Parse*,Expr*); void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); void sqlite3ExprDelete(sqlite3*, Expr*); void sqlite3ExprDeleteGeneric(sqlite3*,void*); | | | 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 | Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*); void sqlite3ExprOrderByAggregateError(Parse*,Expr*); void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); void sqlite3ExprDelete(sqlite3*, Expr*); void sqlite3ExprDeleteGeneric(sqlite3*,void*); int sqlite3ExprDeferredDelete(Parse*, Expr*); void sqlite3ExprUnmapAndDelete(Parse*, Expr*); ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); Select *sqlite3ExprListToValues(Parse*, int, ExprList*); void sqlite3ExprListSetSortOrder(ExprList*,int,int); void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); |
︙ | ︙ | |||
5027 5028 5029 5030 5031 5032 5033 | void sqlite3EndTransaction(Parse*,int); void sqlite3Savepoint(Parse*, int, Token*); void sqlite3CloseSavepoints(sqlite3 *); void sqlite3LeaveMutexAndCloseZombie(sqlite3*); u32 sqlite3IsTrueOrFalse(const char*); int sqlite3ExprIdToTrueFalse(Expr*); int sqlite3ExprTruthValue(const Expr*); | | < < | | 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 | void sqlite3EndTransaction(Parse*,int); void sqlite3Savepoint(Parse*, int, Token*); void sqlite3CloseSavepoints(sqlite3 *); void sqlite3LeaveMutexAndCloseZombie(sqlite3*); u32 sqlite3IsTrueOrFalse(const char*); int sqlite3ExprIdToTrueFalse(Expr*); int sqlite3ExprTruthValue(const Expr*); int sqlite3ExprIsConstant(Parse*,Expr*); int sqlite3ExprIsConstantOrFunction(Expr*, u8); int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS int sqlite3ExprContainsSubquery(Expr*); #endif int sqlite3ExprIsInteger(const Expr*, int*); int sqlite3ExprCanBeNull(const Expr*); int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); int sqlite3IsRowid(const char*); |
︙ | ︙ | |||
5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 | int sqlite3ExprDataType(const Expr *pExpr); int sqlite3Atoi64(const char*, i64*, int, u8); int sqlite3DecOrHexToI64(const char*, i64*); void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); void sqlite3Error(sqlite3*,int); void sqlite3ErrorClear(sqlite3*); void sqlite3SystemError(sqlite3*,int); void *sqlite3HexToBlob(sqlite3*, const char *z, int n); u8 sqlite3HexToInt(int h); int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); #if defined(SQLITE_NEED_ERR_NAME) const char *sqlite3ErrName(int); #endif | > > | 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 | int sqlite3ExprDataType(const Expr *pExpr); int sqlite3Atoi64(const char*, i64*, int, u8); int sqlite3DecOrHexToI64(const char*, i64*); void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); void sqlite3Error(sqlite3*,int); void sqlite3ErrorClear(sqlite3*); void sqlite3SystemError(sqlite3*,int); #if !defined(SQLITE_OMIT_BLOB_LITERAL) void *sqlite3HexToBlob(sqlite3*, const char *z, int n); #endif u8 sqlite3HexToInt(int h); int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); #if defined(SQLITE_NEED_ERR_NAME) const char *sqlite3ErrName(int); #endif |
︙ | ︙ | |||
5528 5529 5530 5531 5532 5533 5534 | # define sqlite3WithDelete(x,y) # define sqlite3WithPush(x,y,z) ((void*)0) #endif #ifndef SQLITE_OMIT_UPSERT Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); void sqlite3UpsertDelete(sqlite3*,Upsert*); Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); | | | 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 | # define sqlite3WithDelete(x,y) # define sqlite3WithPush(x,y,z) ((void*)0) #endif #ifndef SQLITE_OMIT_UPSERT Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); void sqlite3UpsertDelete(sqlite3*,Upsert*); Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*); void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int); Upsert *sqlite3UpsertOfIndex(Upsert*,Index*); int sqlite3UpsertNextIsIPK(Upsert*); #else #define sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0) #define sqlite3UpsertDelete(x,y) #define sqlite3UpsertDup(x,y) ((Upsert*)0) |
︙ | ︙ |
Changes to src/sqliteLimit.h.
︙ | ︙ | |||
183 184 185 186 187 188 189 | ** Maximum number of pages in one database file. ** ** This is really just the default value for the max_page_count pragma. ** This value can be lowered (or raised) at run-time using that the ** max_page_count macro. */ #ifndef SQLITE_MAX_PAGE_COUNT | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | ** Maximum number of pages in one database file. ** ** This is really just the default value for the max_page_count pragma. ** This value can be lowered (or raised) at run-time using that the ** max_page_count macro. */ #ifndef SQLITE_MAX_PAGE_COUNT # define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */ #endif /* ** Maximum length (in bytes) of the pattern in a LIKE or GLOB ** operator. */ #ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
986 987 988 989 990 991 992 993 994 995 996 997 998 999 | int argc, sqlite3_value **argv ){ sqlite3_int64 v = sqlite3_value_int64(argv[0]); sqlite3_result_int64(context, v); sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, context); } /* ** SQL function: strtod(X) ** ** Use the C-library strtod() function to convert string X into a double. ** Used for comparing the accuracy of SQLite's internal text-to-float conversion ** routines against the C-library. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 | int argc, sqlite3_value **argv ){ sqlite3_int64 v = sqlite3_value_int64(argv[0]); sqlite3_result_int64(context, v); sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, context); } /* ** These SQL functions attempt to return a value (their first argument) ** that has been modified to have multiple datatypes. For example both ** TEXT and INTEGER. */ static void addTextTypeFunction( sqlite3_context *context, int argc, sqlite3_value **argv ){ (void)sqlite3_value_text(argv[0]); (void)argc; sqlite3_result_value(context, argv[0]); } static void addIntTypeFunction( sqlite3_context *context, int argc, sqlite3_value **argv ){ (void)sqlite3_value_int64(argv[0]); (void)argc; sqlite3_result_value(context, argv[0]); } static void addRealTypeFunction( sqlite3_context *context, int argc, sqlite3_value **argv ){ (void)sqlite3_value_double(argv[0]); (void)argc; sqlite3_result_value(context, argv[0]); } /* ** SQL function: strtod(X) ** ** Use the C-library strtod() function to convert string X into a double. ** Used for comparing the accuracy of SQLite's internal text-to-float conversion ** routines against the C-library. |
︙ | ︙ | |||
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 | /* The intreal() function converts its argument to an integer and returns ** it as a MEM_IntReal. */ if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "intreal", 1, SQLITE_UTF8, 0, intrealFunction, 0, 0); } /* Functions strtod() and dtostr() work as in the shell. These routines ** use the standard C library to convert between floating point and ** text. This is used to compare SQLite's internal conversion routines ** against the standard library conversion routines. ** ** Both routines copy/pasted from the shell.c.in implementation | > > > > > > > > > > > > > > > > | 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 | /* The intreal() function converts its argument to an integer and returns ** it as a MEM_IntReal. */ if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "intreal", 1, SQLITE_UTF8, 0, intrealFunction, 0, 0); } /* The add_text_type(), add_int_type(), and add_real_type() functions ** attempt to return a value that has multiple datatypes. */ if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "add_text_type", 1, SQLITE_UTF8, 0, addTextTypeFunction, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "add_int_type", 1, SQLITE_UTF8, 0, addIntTypeFunction, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "add_real_type", 1, SQLITE_UTF8, 0, addRealTypeFunction, 0, 0); } /* Functions strtod() and dtostr() work as in the shell. These routines ** use the standard C library to convert between floating point and ** text. This is used to compare SQLite's internal conversion routines ** against the standard library conversion routines. ** ** Both routines copy/pasted from the shell.c.in implementation |
︙ | ︙ |
Changes to src/test_bestindex.c.
︙ | ︙ | |||
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp)); rc = SQLITE_ERROR; }else{ rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp)); } if( rc!=SQLITE_OK ){ sqlite3_free(pTab); pTab = 0; } }else{ rc = SQLITE_NOMEM; } sqlite3_free(zCmd); | > > > | | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp)); rc = SQLITE_ERROR; }else{ rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp)); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("declare_vtab: %s", sqlite3_errmsg(db)); } } if( rc!=SQLITE_OK ){ sqlite3_free(pTab); pTab = 0; } }else{ rc = SQLITE_NOMEM; } sqlite3_free(zCmd); *ppVtab = pTab ? &pTab->base : 0; return rc; } /* The xDisconnect and xDestroy methods are also the same */ static int tclDisconnect(sqlite3_vtab *pVtab){ tcl_vtab *pTab = (tcl_vtab*)pVtab; while( pTab->pFindFunctionList ){ |
︙ | ︙ |
Changes to src/test_config.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #endif #ifdef SQLITE_32BIT_ROWID Tcl_SetVar2(interp, "sqlite_options", "rowid32", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "rowid32", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_CASE_SENSITIVE_LIKE Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","1",TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","0",TCL_GLOBAL_ONLY); #endif | > > > > > > > > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | #endif #ifdef SQLITE_32BIT_ROWID Tcl_SetVar2(interp, "sqlite_options", "rowid32", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "rowid32", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_ALLOW_ROWID_IN_VIEW Tcl_SetVar2( interp, "sqlite_options", "allow_rowid_in_view", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2( interp, "sqlite_options", "allow_rowid_in_view", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_CASE_SENSITIVE_LIKE Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","1",TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","0",TCL_GLOBAL_ONLY); #endif |
︙ | ︙ |
Changes to src/test_tclsh.c.
︙ | ︙ | |||
104 105 106 107 108 109 110 111 112 113 114 115 116 117 | #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); #endif extern int TestExpert_Init(Tcl_Interp*); extern int Sqlitetest_window_Init(Tcl_Interp *); extern int Sqlitetestvdbecov_Init(Tcl_Interp *); extern int TestRecover_Init(Tcl_Interp*); Tcl_CmdInfo cmdInfo; /* Since the primary use case for this binary is testing of SQLite, ** be sure to generate core files if we crash */ #if defined(unix) { struct rlimit x; | > | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); #endif extern int TestExpert_Init(Tcl_Interp*); extern int Sqlitetest_window_Init(Tcl_Interp *); extern int Sqlitetestvdbecov_Init(Tcl_Interp *); extern int TestRecover_Init(Tcl_Interp*); extern int Sqlitetestintck_Init(Tcl_Interp*); Tcl_CmdInfo cmdInfo; /* Since the primary use case for this binary is testing of SQLite, ** be sure to generate core files if we crash */ #if defined(unix) { struct rlimit x; |
︙ | ︙ | |||
171 172 173 174 175 176 177 178 179 180 181 182 183 184 | #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); #endif TestExpert_Init(interp); Sqlitetest_window_Init(interp); Sqlitetestvdbecov_Init(interp); TestRecover_Init(interp); Tcl_CreateObjCommand( interp, "load_testfixture_extensions", load_testfixture_extensions,0,0 ); return 0; } | > | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); #endif TestExpert_Init(interp); Sqlitetest_window_Init(interp); Sqlitetestvdbecov_Init(interp); TestRecover_Init(interp); Sqlitetestintck_Init(interp); Tcl_CreateObjCommand( interp, "load_testfixture_extensions", load_testfixture_extensions,0,0 ); return 0; } |
︙ | ︙ |
Changes to src/tokenize.c.
︙ | ︙ | |||
433 434 435 436 437 438 439 | testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); testcase( z[0]=='9' ); testcase( z[0]=='.' ); *tokenType = TK_INTEGER; #ifndef SQLITE_OMIT_HEX_INTEGER if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ | > | > > > | | > > > > > | > > > > > > > | > | | > | > > | > > > | | | | | > | | > | > > | > > > > | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); testcase( z[0]=='9' ); testcase( z[0]=='.' ); *tokenType = TK_INTEGER; #ifndef SQLITE_OMIT_HEX_INTEGER if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ for(i=3; 1; i++){ if( sqlite3Isxdigit(z[i])==0 ){ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ *tokenType = TK_QNUMBER; }else{ break; } } } }else #endif { for(i=0; 1; i++){ if( sqlite3Isdigit(z[i])==0 ){ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ *tokenType = TK_QNUMBER; }else{ break; } } } #ifndef SQLITE_OMIT_FLOATING_POINT if( z[i]=='.' ){ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; for(i++; 1; i++){ if( sqlite3Isdigit(z[i])==0 ){ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ *tokenType = TK_QNUMBER; }else{ break; } } } } if( (z[i]=='e' || z[i]=='E') && ( sqlite3Isdigit(z[i+1]) || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) ) ){ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; for(i+=2; 1; i++){ if( sqlite3Isdigit(z[i])==0 ){ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ *tokenType = TK_QNUMBER; }else{ break; } } } } #endif } while( IdChar(z[i]) ){ *tokenType = TK_ILLEGAL; i++; } return i; } case CC_QUOTE2: { |
︙ | ︙ | |||
618 619 620 621 622 623 624 625 626 627 | pParse->nErr++; break; } #ifndef SQLITE_OMIT_WINDOWFUNC if( tokenType>=TK_WINDOW ){ assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW ); #else if( tokenType>=TK_SPACE ){ | > | > > | 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 | pParse->nErr++; break; } #ifndef SQLITE_OMIT_WINDOWFUNC if( tokenType>=TK_WINDOW ){ assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW || tokenType==TK_QNUMBER ); #else if( tokenType>=TK_SPACE ){ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL || tokenType==TK_QNUMBER ); #endif /* SQLITE_OMIT_WINDOWFUNC */ if( AtomicLoad(&db->u1.isInterrupted) ){ pParse->rc = SQLITE_INTERRUPT; pParse->nErr++; break; } if( tokenType==TK_SPACE ){ |
︙ | ︙ | |||
654 655 656 657 658 659 660 | }else if( tokenType==TK_OVER ){ assert( n==4 ); tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed); }else if( tokenType==TK_FILTER ){ assert( n==6 ); tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ | | | 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 | }else if( tokenType==TK_OVER ){ assert( n==4 ); tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed); }else if( tokenType==TK_FILTER ){ assert( n==6 ); tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ }else if( tokenType!=TK_QNUMBER ){ Token x; x.z = zSql; x.n = n; sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x); break; } } |
︙ | ︙ |
Changes to src/treeview.c.
︙ | ︙ | |||
190 191 192 193 194 195 196 | StrAccum x; int n = 0; char zLine[1000]; sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); if( pItem->pTab ){ | | | > > | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | StrAccum x; int n = 0; char zLine[1000]; sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); if( pItem->pTab ){ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s", pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed, pItem->fg.rowidUsed ? "+rowid" : ""); } if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); }else if( pItem->fg.jointype & JT_LEFT ){ sqlite3_str_appendf(&x, " LEFT-JOIN"); }else if( pItem->fg.jointype & JT_RIGHT ){ sqlite3_str_appendf(&x, " RIGHT-JOIN"); |
︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | if( pItem->pSelect ) n++; if( pItem->fg.isTabFunc ) n++; if( pItem->fg.isUsing ) n++; if( pItem->fg.isUsing ){ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); } if( pItem->pSelect ){ if( pItem->pTab ){ Table *pTab = pItem->pTab; sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); } sqlite3TreeViewPop(&pView); } } | > > | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | if( pItem->pSelect ) n++; if( pItem->fg.isTabFunc ) n++; if( pItem->fg.isUsing ) n++; if( pItem->fg.isUsing ){ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); } if( pItem->pSelect ){ sqlite3TreeViewPush(&pView, i+1<pSrc->nSrc); if( pItem->pTab ){ Table *pTab = pItem->pTab; sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); sqlite3TreeViewPop(&pView); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); } sqlite3TreeViewPop(&pView); } } |
︙ | ︙ | |||
340 341 342 343 344 345 346 | if( p->pOrderBy ){ sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY"); } if( p->pLimit ){ sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); if( p->pLimit->pRight ){ | | | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | if( p->pOrderBy ){ sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY"); } if( p->pLimit ){ sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); if( p->pLimit->pRight ){ sqlite3TreeViewItem(pView, "OFFSET", 0); sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); sqlite3TreeViewPop(&pView); } sqlite3TreeViewPop(&pView); } if( p->pPrior ){ const char *zOp = "UNION"; |
︙ | ︙ |
Changes to src/trigger.c.
︙ | ︙ | |||
946 947 948 949 950 951 952 953 954 955 956 957 958 959 | pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName); pItem->fg.eEName = pList->a[i].fg.eEName; } } } return pNew; } /* ** Generate code for the RETURNING trigger. Unlike other triggers ** that invoke a subprogram in the bytecode, the code for RETURNING ** is generated in-line. */ static void codeReturningTrigger( | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 | pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName); pItem->fg.eEName = pList->a[i].fg.eEName; } } } return pNew; } /* If the Expr node is a subquery or an EXISTS operator or an IN operator that ** uses a subquery, and if the subquery is SF_Correlated, then mark the ** expression as EP_VarSelect. */ static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){ UNUSED_PARAMETER(NotUsed); if( ExprUseXSelect(pExpr) && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0 ){ testcase( ExprHasProperty(pExpr, EP_VarSelect) ); ExprSetProperty(pExpr, EP_VarSelect); } return WRC_Continue; } /* ** If the SELECT references the table pWalker->u.pTab, then do two things: ** ** (1) Mark the SELECT as as SF_Correlated. ** (2) Set pWalker->eCode to non-zero so that the caller will know ** that (1) has happened. */ static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ int i; SrcList *pSrc; assert( pSelect!=0 ); pSrc = pSelect->pSrc; assert( pSrc!=0 ); for(i=0; i<pSrc->nSrc; i++){ if( pSrc->a[i].pTab==pWalker->u.pTab ){ testcase( pSelect->selFlags & SF_Correlated ); pSelect->selFlags |= SF_Correlated; pWalker->eCode = 1; break; } } return WRC_Continue; } /* ** Scan the expression list that is the argument to RETURNING looking ** for subqueries that depend on the table which is being modified in the ** statement that is hosting the RETURNING clause (pTab). Mark all such ** subqueries as SF_Correlated. If the subqueries are part of an ** expression, mark the expression as EP_VarSelect. ** ** https://sqlite.org/forum/forumpost/2c83569ce8945d39 */ static void sqlite3ProcessReturningSubqueries( ExprList *pEList, Table *pTab ){ Walker w; memset(&w, 0, sizeof(w)); w.xExprCallback = sqlite3ExprWalkNoop; w.xSelectCallback = sqlite3ReturningSubqueryCorrelated; w.u.pTab = pTab; sqlite3WalkExprList(&w, pEList); if( w.eCode ){ w.xExprCallback = sqlite3ReturningSubqueryVarSelect; w.xSelectCallback = sqlite3SelectWalkNoop; sqlite3WalkExprList(&w, pEList); } } /* ** Generate code for the RETURNING trigger. Unlike other triggers ** that invoke a subprogram in the bytecode, the code for RETURNING ** is generated in-line. */ static void codeReturningTrigger( |
︙ | ︙ | |||
983 984 985 986 987 988 989 990 991 992 993 994 995 996 | } memset(&sSelect, 0, sizeof(sSelect)); memset(&sFrom, 0, sizeof(sFrom)); sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); sSelect.pSrc = &sFrom; sFrom.nSrc = 1; sFrom.a[0].pTab = pTab; sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); if( pParse->nErr==0 ){ assert( db->mallocFailed==0 ); sqlite3GenerateColumnNames(pParse, &sSelect); } sqlite3ExprListDelete(db, sSelect.pEList); | > | 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 | } memset(&sSelect, 0, sizeof(sSelect)); memset(&sFrom, 0, sizeof(sFrom)); sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); sSelect.pSrc = &sFrom; sFrom.nSrc = 1; sFrom.a[0].pTab = pTab; sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); if( pParse->nErr==0 ){ assert( db->mallocFailed==0 ); sqlite3GenerateColumnNames(pParse, &sSelect); } sqlite3ExprListDelete(db, sSelect.pEList); |
︙ | ︙ | |||
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 | pParse->pTriggerTab = pTab; if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK && ALWAYS(!db->mallocFailed) ){ int i; int nCol = pNew->nExpr; int reg = pParse->nMem+1; pParse->nMem += nCol+2; pReturning->iRetReg = reg; for(i=0; i<nCol; i++){ Expr *pCol = pNew->a[i].pExpr; assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ sqlite3ExprCodeFactorable(pParse, pCol, reg+i); if( sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){ | > | 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 | pParse->pTriggerTab = pTab; if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK && ALWAYS(!db->mallocFailed) ){ int i; int nCol = pNew->nExpr; int reg = pParse->nMem+1; sqlite3ProcessReturningSubqueries(pNew, pTab); pParse->nMem += nCol+2; pReturning->iRetReg = reg; for(i=0; i<nCol; i++){ Expr *pCol = pNew->a[i].pExpr; assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ sqlite3ExprCodeFactorable(pParse, pCol, reg+i); if( sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){ |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
917 918 919 920 921 922 923 924 925 926 927 928 929 930 | testcase( oldmask!=0xffffffff && i==31 ); sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, k); } } if( chngRowid==0 && pPk==0 ){ sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); } } /* Populate the array of registers beginning at regNew with the new ** row data. This array is used to check constants, create the new ** table and index records, and as the values for any new.* references | > > > | 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 | testcase( oldmask!=0xffffffff && i==31 ); sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, k); } } if( chngRowid==0 && pPk==0 ){ #ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( isView ) sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); #endif sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); } } /* Populate the array of registers beginning at regNew with the new ** row data. This array is used to check constants, create the new ** table and index records, and as the values for any new.* references |
︙ | ︙ |
Changes to src/upsert.c.
︙ | ︙ | |||
86 87 88 89 90 91 92 | ** ** Return SQLITE_OK if everything works, or an error code is something ** is wrong. */ int sqlite3UpsertAnalyzeTarget( Parse *pParse, /* The parsing context */ SrcList *pTabList, /* Table into which we are inserting */ | | > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | ** ** Return SQLITE_OK if everything works, or an error code is something ** is wrong. */ int sqlite3UpsertAnalyzeTarget( Parse *pParse, /* The parsing context */ SrcList *pTabList, /* Table into which we are inserting */ Upsert *pUpsert, /* The ON CONFLICT clauses */ Upsert *pAll /* Complete list of all ON CONFLICT clauses */ ){ Table *pTab; /* That table into which we are inserting */ int rc; /* Result code */ int iCursor; /* Cursor used by pTab */ Index *pIdx; /* One of the indexes of pTab */ ExprList *pTarget; /* The conflict-target clause */ Expr *pTerm; /* One term of the conflict-target clause */ |
︙ | ︙ | |||
189 190 191 192 193 194 195 196 197 198 199 200 201 202 | } if( ii<nn ){ /* Column ii of the index did not match any term of the conflict target. ** Continue the search with the next index. */ continue; } pUpsert->pUpsertIdx = pIdx; break; } if( pUpsert->pUpsertIdx==0 ){ char zWhich[16]; if( nClause==0 && pUpsert->pNextUpsert==0 ){ zWhich[0] = 0; }else{ | > > > > > > > > | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | } if( ii<nn ){ /* Column ii of the index did not match any term of the conflict target. ** Continue the search with the next index. */ continue; } pUpsert->pUpsertIdx = pIdx; if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){ /* Really this should be an error. The isDup ON CONFLICT clause will ** never fire. But this problem was not discovered until three years ** after multi-CONFLICT upsert was added, and so we silently ignore ** the problem to prevent breaking applications that might actually ** have redundant ON CONFLICT clauses. */ pUpsert->isDup = 1; } break; } if( pUpsert->pUpsertIdx==0 ){ char zWhich[16]; if( nClause==0 && pUpsert->pNextUpsert==0 ){ zWhich[0] = 0; }else{ |
︙ | ︙ | |||
215 216 217 218 219 220 221 | ** conflict target, or if pUpsert is followed by another ON CONFLICT ** clause that targets the INTEGER PRIMARY KEY. */ int sqlite3UpsertNextIsIPK(Upsert *pUpsert){ Upsert *pNext; if( NEVER(pUpsert==0) ) return 0; pNext = pUpsert->pNextUpsert; | > | | | > > > | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | ** conflict target, or if pUpsert is followed by another ON CONFLICT ** clause that targets the INTEGER PRIMARY KEY. */ int sqlite3UpsertNextIsIPK(Upsert *pUpsert){ Upsert *pNext; if( NEVER(pUpsert==0) ) return 0; pNext = pUpsert->pNextUpsert; while( 1 /*exit-by-return*/ ){ if( pNext==0 ) return 1; if( pNext->pUpsertTarget==0 ) return 1; if( pNext->pUpsertIdx==0 ) return 1; if( !pNext->isDup ) return 0; pNext = pNext->pNextUpsert; } return 0; } /* ** Given the list of ON CONFLICT clauses described by pUpsert, and ** a particular index pIdx, return a pointer to the particular ON CONFLICT ** clause that applies to the index. Or, if the index is not subject to |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
64 65 66 67 68 69 70 71 72 73 74 75 76 77 | rc = isnan(x); #endif /* HAVE_ISNAN */ testcase( rc ); return rc; } #endif /* SQLITE_OMIT_FLOATING_POINT */ /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. ** ** The value returned will never be negative. Nor will it ever be greater ** than the actual length of the string. For very long strings (greater ** than 1GiB) the value returned might be less than the true string length. | > > > > > > > > > > > > > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | rc = isnan(x); #endif /* HAVE_ISNAN */ testcase( rc ); return rc; } #endif /* SQLITE_OMIT_FLOATING_POINT */ #ifndef SQLITE_OMIT_FLOATING_POINT /* ** Return true if the floating point value is NaN or +Inf or -Inf. */ int sqlite3IsOverflow(double x){ int rc; /* The value return */ u64 y; memcpy(&y,&x,sizeof(y)); rc = IsOvfl(y); return rc; } #endif /* SQLITE_OMIT_FLOATING_POINT */ /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. ** ** The value returned will never be negative. Nor will it ever be greater ** than the actual length of the string. For very long strings (greater ** than 1GiB) the value returned might be less than the true string length. |
︙ | ︙ | |||
306 307 308 309 310 311 312 313 314 315 316 317 318 319 | } void sqlite3DequoteExpr(Expr *p){ assert( !ExprHasProperty(p, EP_IntValue) ); assert( sqlite3Isquote(p->u.zToken[0]) ); p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; sqlite3Dequote(p->u.zToken); } /* ** If the input token p is quoted, try to adjust the token to remove ** the quotes. This is not always possible: ** ** "abc" -> abc ** "ab""cd" -> (not possible because of the interior "") | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | } void sqlite3DequoteExpr(Expr *p){ assert( !ExprHasProperty(p, EP_IntValue) ); assert( sqlite3Isquote(p->u.zToken[0]) ); p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; sqlite3Dequote(p->u.zToken); } /* ** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken ** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those ** that contain '_' characters that must be removed before further processing. */ void sqlite3DequoteNumber(Parse *pParse, Expr *p){ assert( p!=0 || pParse->db->mallocFailed ); if( p ){ const char *pIn = p->u.zToken; char *pOut = p->u.zToken; int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X')); int iValue; assert( p->op==TK_QNUMBER ); p->op = TK_INTEGER; do { if( *pIn!=SQLITE_DIGIT_SEPARATOR ){ *pOut++ = *pIn; if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT; }else{ if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1]))) || (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1]))) ){ sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken); } } }while( *pIn++ ); if( bHex ) p->op = TK_INTEGER; /* tag-20240227-a: If after dequoting, the number is an integer that ** fits in 32 bits, then it must be converted into EP_IntValue. Other ** parts of the code expect this. See also tag-20240227-b. */ if( p->op==TK_INTEGER && sqlite3GetInt32(p->u.zToken, &iValue) ){ p->u.iValue = iValue; p->flags |= EP_IntValue; } } } /* ** If the input token p is quoted, try to adjust the token to remove ** the quotes. This is not always possible: ** ** "abc" -> abc ** "ab""cd" -> (not possible because of the interior "") |
︙ | ︙ | |||
623 624 625 626 627 628 629 630 631 632 633 634 635 636 | *pResult = (double)r; } }else{ double rr[2]; u64 s2; rr[0] = (double)s; s2 = (u64)rr[0]; rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); if( e>0 ){ while( e>=100 ){ e -= 100; dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); } while( e>=10 ){ | > > > | 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 | *pResult = (double)r; } }else{ double rr[2]; u64 s2; rr[0] = (double)s; s2 = (u64)rr[0]; #if defined(_MSC_VER) && _MSC_VER<1700 if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); } #endif rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); if( e>0 ){ while( e>=100 ){ e -= 100; dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); } while( e>=10 ){ |
︙ | ︙ | |||
1065 1066 1067 1068 1069 1070 1071 | assert( v>0 ); while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } assert( i>=0 && i<sizeof(p->zBuf)-1 ); p->n = sizeof(p->zBuf) - 1 - i; assert( p->n>0 ); assert( p->n<sizeof(p->zBuf) ); p->iDP = p->n + exp; | | | 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 | assert( v>0 ); while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } assert( i>=0 && i<sizeof(p->zBuf)-1 ); p->n = sizeof(p->zBuf) - 1 - i; assert( p->n>0 ); assert( p->n<sizeof(p->zBuf) ); p->iDP = p->n + exp; if( iRound<=0 ){ iRound = p->iDP - iRound; if( iRound==0 && p->zBuf[i+1]>='5' ){ iRound = 1; p->zBuf[i--] = '0'; p->n++; p->iDP++; } |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
1125 1126 1127 1128 1129 1130 1131 | ** ** If P2!=0 then the coroutine implementation immediately follows ** this opcode. So jump over the coroutine implementation to ** address P2. ** ** See also: EndCoroutine */ | | | 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 | ** ** If P2!=0 then the coroutine implementation immediately follows ** this opcode. So jump over the coroutine implementation to ** address P2. ** ** See also: EndCoroutine */ case OP_InitCoroutine: { /* jump0 */ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); assert( pOp->p2>=0 && pOp->p2<p->nOp ); assert( pOp->p3>=0 && pOp->p3<p->nOp ); pOut = &aMem[pOp->p1]; assert( !VdbeMemDynamic(pOut) ); pOut->u.i = pOp->p3 - 1; pOut->flags = MEM_Int; |
︙ | ︙ | |||
1148 1149 1150 1151 1152 1153 1154 | break; } /* Opcode: EndCoroutine P1 * * * * ** ** The instruction at the address in register P1 is a Yield. ** Jump to the P2 parameter of that Yield. | | > > > < | | 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 | break; } /* Opcode: EndCoroutine P1 * * * * ** ** The instruction at the address in register P1 is a Yield. ** Jump to the P2 parameter of that Yield. ** After the jump, the value register P1 is left with a value ** such that subsequent OP_Yields go back to the this same ** OP_EndCoroutine instruction. ** ** See also: InitCoroutine */ case OP_EndCoroutine: { /* in1 */ VdbeOp *pCaller; pIn1 = &aMem[pOp->p1]; assert( pIn1->flags==MEM_Int ); assert( pIn1->u.i>=0 && pIn1->u.i<p->nOp ); pCaller = &aOp[pIn1->u.i]; assert( pCaller->opcode==OP_Yield ); assert( pCaller->p2>=0 && pCaller->p2<p->nOp ); pIn1->u.i = (int)(pOp - p->aOp) - 1; pOp = &aOp[pCaller->p2 - 1]; break; } /* Opcode: Yield P1 P2 * * * ** ** Swap the program counter with the value in register P1. This ** has the effect of yielding to a coroutine. ** ** If the coroutine that is launched by this instruction ends with ** Yield or Return then continue to the next instruction. But if ** the coroutine launched by this instruction ends with ** EndCoroutine, then jump to P2 rather than continuing with the ** next instruction. ** ** See also: InitCoroutine */ case OP_Yield: { /* in1, jump0 */ int pcDest; pIn1 = &aMem[pOp->p1]; assert( VdbeMemDynamic(pIn1)==0 ); pIn1->flags = MEM_Int; pcDest = (int)pIn1->u.i; pIn1->u.i = (int)(pOp - aOp); REGISTER_TRACE(pOp->p1, pIn1); |
︙ | ︙ | |||
1508 1509 1510 1511 1512 1513 1514 | sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); } pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } | | | < < < < | 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 | sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); } pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Variable P1 P2 * * * ** Synopsis: r[P2]=parameter(P1) ** ** Transfer the values of bound parameter P1 into register P2 */ case OP_Variable: { /* out2 */ Mem *pVar; /* Value being transferred */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); pVar = &p->aVar[pOp->p1 - 1]; if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; } pOut = &aMem[pOp->p2]; if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut); memcpy(pOut, pVar, MEMCELLSIZE); |
︙ | ︙ | |||
2041 2042 2043 2044 2045 2046 2047 | /* Opcode: MustBeInt P1 P2 * * * ** ** Force the value in register P1 to be an integer. If the value ** in P1 is not an integer and cannot be converted into an integer ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ | | | 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 | /* Opcode: MustBeInt P1 P2 * * * ** ** Force the value in register P1 to be an integer. If the value ** in P1 is not an integer and cannot be converted into an integer ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ case OP_MustBeInt: { /* jump0, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Int)==0 ){ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ VdbeBranchTaken(1, 2); if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; |
︙ | ︙ | |||
2082 2083 2084 2085 2086 2087 2088 | sqlite3VdbeMemRealify(pIn1); REGISTER_TRACE(pOp->p1, pIn1); } break; } #endif | | | 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 | sqlite3VdbeMemRealify(pIn1); REGISTER_TRACE(pOp->p1, pIn1); } break; } #endif #if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE) /* Opcode: Cast P1 P2 * * * ** Synopsis: affinity(r[P1]) ** ** Force the value in register P1 to be the type defined by P2. ** ** <ul> ** <li> P2=='A' → BLOB |
︙ | ︙ | |||
2297 2298 2299 2300 2301 2302 2303 | flags3 = pIn3->flags; } if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn3,0); } } }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){ | > > | > > | | 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 | flags3 = pIn3->flags; } if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn3,0); } } }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){ if( (flags1 & MEM_Str)!=0 ){ pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ testcase( pIn1->flags & MEM_Int ); testcase( pIn1->flags & MEM_Real ); testcase( pIn1->flags & MEM_IntReal ); sqlite3VdbeMemStringify(pIn1, encoding, 1); testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) ); flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str; } if( (flags3 & MEM_Str)!=0 ){ pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ testcase( pIn3->flags & MEM_Int ); testcase( pIn3->flags & MEM_Real ); testcase( pIn3->flags & MEM_IntReal ); sqlite3VdbeMemStringify(pIn3, encoding, 1); testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) ); flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask); } |
︙ | ︙ | |||
3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 | v = pRec->u.i; } len = sqlite3SmallTypeSizes[serial_type]; assert( len>=1 && len<=8 && len!=5 && len!=7 ); switch( len ){ default: zPayload[7] = (u8)(v&0xff); v >>= 8; zPayload[6] = (u8)(v&0xff); v >>= 8; case 6: zPayload[5] = (u8)(v&0xff); v >>= 8; zPayload[4] = (u8)(v&0xff); v >>= 8; case 4: zPayload[3] = (u8)(v&0xff); v >>= 8; case 3: zPayload[2] = (u8)(v&0xff); v >>= 8; case 2: zPayload[1] = (u8)(v&0xff); v >>= 8; case 1: zPayload[0] = (u8)(v&0xff); } zPayload += len; } }else if( serial_type<0x80 ){ *(zHdr++) = serial_type; if( serial_type>=14 && pRec->n>0 ){ | > > > > > | 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 | v = pRec->u.i; } len = sqlite3SmallTypeSizes[serial_type]; assert( len>=1 && len<=8 && len!=5 && len!=7 ); switch( len ){ default: zPayload[7] = (u8)(v&0xff); v >>= 8; zPayload[6] = (u8)(v&0xff); v >>= 8; /* no break */ deliberate_fall_through case 6: zPayload[5] = (u8)(v&0xff); v >>= 8; zPayload[4] = (u8)(v&0xff); v >>= 8; /* no break */ deliberate_fall_through case 4: zPayload[3] = (u8)(v&0xff); v >>= 8; /* no break */ deliberate_fall_through case 3: zPayload[2] = (u8)(v&0xff); v >>= 8; /* no break */ deliberate_fall_through case 2: zPayload[1] = (u8)(v&0xff); v >>= 8; /* no break */ deliberate_fall_through case 1: zPayload[0] = (u8)(v&0xff); } zPayload += len; } }else if( serial_type<0x80 ){ *(zHdr++) = serial_type; if( serial_type>=14 && pRec->n>0 ){ |
︙ | ︙ | |||
4573 4574 4575 4576 4577 4578 4579 | ** ** A pseudo-table created by this opcode is used to hold a single ** row output from the sorter so that the row can be decomposed into ** individual columns using the OP_Column opcode. The OP_Column opcode ** is the only cursor opcode that works with a pseudo-table. ** ** P3 is the number of fields in the records that will be stored by | | > | 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 | ** ** A pseudo-table created by this opcode is used to hold a single ** row output from the sorter so that the row can be decomposed into ** individual columns using the OP_Column opcode. The OP_Column opcode ** is the only cursor opcode that works with a pseudo-table. ** ** P3 is the number of fields in the records that will be stored by ** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor ** will return NULL for every column. */ case OP_OpenPseudo: { VdbeCursor *pCx; assert( pOp->p1>=0 ); assert( pOp->p3>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p3, CURTYPE_PSEUDO); |
︙ | ︙ | |||
4716 4717 4718 4719 4720 4721 4722 | ** The IdxGE opcode will be skipped if this opcode succeeds, but the ** IdxGE opcode will be used on subsequent loop iterations. The ** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this ** is an equality search. ** ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ | | | | | | 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 | ** The IdxGE opcode will be skipped if this opcode succeeds, but the ** IdxGE opcode will be used on subsequent loop iterations. The ** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this ** is an equality search. ** ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ case OP_SeekLT: /* jump0, in3, group, ncycle */ case OP_SeekLE: /* jump0, in3, group, ncycle */ case OP_SeekGE: /* jump0, in3, group, ncycle */ case OP_SeekGT: { /* jump0, in3, group, ncycle */ int res; /* Comparison result */ int oc; /* Opcode */ VdbeCursor *pC; /* The cursor to seek */ UnpackedRecord r; /* The key to seek for */ int nField; /* Number of columns or fields in the key */ i64 iKey; /* The rowid we are to seek to */ int eqOnly; /* Only interested in == results */ |
︙ | ︙ | |||
5386 5387 5388 5389 5390 5391 5392 | ** ** This opcode leaves the cursor in a state where it cannot be advanced ** in either direction. In other words, the Next and Prev opcodes will ** not work following this opcode. ** ** See also: Found, NotFound, NoConflict, SeekRowid */ | | | 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 | ** ** This opcode leaves the cursor in a state where it cannot be advanced ** in either direction. In other words, the Next and Prev opcodes will ** not work following this opcode. ** ** See also: Found, NotFound, NoConflict, SeekRowid */ case OP_SeekRowid: { /* jump0, in3, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; u64 iKey; pIn3 = &aMem[pOp->p3]; testcase( pIn3->flags & MEM_Int ); |
︙ | ︙ | |||
6145 6146 6147 6148 6149 6150 6151 | ** to the following instruction. ** ** This opcode leaves the cursor configured to move in reverse order, ** from the end toward the beginning. In other words, the cursor is ** configured to use Prev, not Next. */ case OP_SeekEnd: /* ncycle */ | | | 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 | ** to the following instruction. ** ** This opcode leaves the cursor configured to move in reverse order, ** from the end toward the beginning. In other words, the cursor is ** configured to use Prev, not Next. */ case OP_SeekEnd: /* ncycle */ case OP_Last: { /* jump0, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); |
︙ | ︙ | |||
6179 6180 6181 6182 6183 6184 6185 | if( pOp->p2>0 ){ VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; } break; } | | | | > > > | > > > | > > > | > | 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 | if( pOp->p2>0 ){ VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; } break; } /* Opcode: IfSizeBetween P1 P2 P3 P4 * ** ** Let N be the approximate number of rows in the table or index ** with cursor P1 and let X be 10*log2(N) if N is positive or -1 ** if N is zero. ** ** Jump to P2 if X is in between P3 and P4, inclusive. */ case OP_IfSizeBetween: { /* jump */ VdbeCursor *pC; BtCursor *pCrsr; int res; i64 sz; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pOp->p4type==P4_INT32 ); assert( pOp->p3>=-1 && pOp->p3<=640*2 ); assert( pOp->p4.i>=-1 && pOp->p4.i<=640*2 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); if( rc ) goto abort_due_to_error; if( res!=0 ){ sz = -1; /* -Infinity encoding */ }else{ sz = sqlite3BtreeRowCountEst(pCrsr); assert( sz>0 ); sz = sqlite3LogEst((u64)sz); } res = sz>=pOp->p3 && sz<=pOp->p4.i; VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; break; } /* Opcode: SorterSort P1 P2 * * * |
︙ | ︙ | |||
6253 6254 6255 6256 6257 6258 6259 | ** If P2 is zero, that is an assertion that the P1 table is never ** empty and hence the jump will never be taken. ** ** This opcode leaves the cursor configured to move in forward order, ** from the beginning toward the end. In other words, the cursor is ** configured to use Next, not Prev. */ | | | 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 | ** If P2 is zero, that is an assertion that the P1 table is never ** empty and hence the jump will never be taken. ** ** This opcode leaves the cursor configured to move in forward order, ** from the beginning toward the end. In other words, the cursor is ** configured to use Next, not Prev. */ case OP_Rewind: { /* jump0, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pOp->p5==0 ); assert( pOp->p2>=0 && pOp->p2<p->nOp ); |
︙ | ︙ | |||
6900 6901 6902 6903 6904 6905 6906 | assert( pDb->pBt!=0 ); rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, pOp->p3); if( rc ) goto abort_due_to_error; pOut->u.i = pgno; break; } | | > > > | > | > > > > > | > > > > | 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 | assert( pDb->pBt!=0 ); rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, pOp->p3); if( rc ) goto abort_due_to_error; pOut->u.i = pgno; break; } /* Opcode: SqlExec P1 P2 * P4 * ** ** Run the SQL statement or statements specified in the P4 string. ** ** The P1 parameter is a bitmask of options: ** ** 0x0001 Disable Auth and Trace callbacks while the statements ** in P4 are running. ** ** 0x0002 Set db->nAnalysisLimit to P2 while the statements in ** P4 are running. ** */ case OP_SqlExec: { char *zErr; #ifndef SQLITE_OMIT_AUTHORIZATION sqlite3_xauth xAuth; #endif u8 mTrace; int savedAnalysisLimit; sqlite3VdbeIncrWriteCounter(p, 0); db->nSqlExec++; zErr = 0; #ifndef SQLITE_OMIT_AUTHORIZATION xAuth = db->xAuth; #endif mTrace = db->mTrace; savedAnalysisLimit = db->nAnalysisLimit; if( pOp->p1 & 0x0001 ){ #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = 0; #endif db->mTrace = 0; } if( pOp->p1 & 0x0002 ){ db->nAnalysisLimit = pOp->p2; } rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr); db->nSqlExec--; #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = xAuth; #endif db->mTrace = mTrace; db->nAnalysisLimit = savedAnalysisLimit; if( zErr || rc ){ sqlite3VdbeError(p, "%s", zErr); sqlite3_free(zErr); if( rc==SQLITE_NOMEM ) goto no_mem; goto abort_due_to_error; } break; |
︙ | ︙ | |||
7083 7084 7085 7086 7087 7088 7089 | } #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* Opcode: IntegrityCk P1 P2 P3 P4 P5 ** ** Do an analysis of the currently open database. Store in | | | | | > > | | | | | | 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 | } #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* Opcode: IntegrityCk P1 P2 P3 P4 P5 ** ** Do an analysis of the currently open database. Store in ** register (P1+1) the text of an error message describing any problems. ** If no problems are found, store a NULL in register (P1+1). ** ** The register (P1) contains one less than the maximum number of allowed ** errors. At most reg(P1) errors will be reported. ** In other words, the analysis stops as soon as reg(P1) errors are ** seen. Reg(P1) is updated with the number of errors remaining. ** ** The root page numbers of all tables in the database are integers ** stored in P4_INTARRAY argument. ** ** If P5 is not zero, the check is done on the auxiliary database ** file, not the main database file. ** ** This opcode is used to implement the integrity_check pragma. */ case OP_IntegrityCk: { int nRoot; /* Number of tables to check. (Number of root pages.) */ Pgno *aRoot; /* Array of rootpage numbers for tables to be checked */ int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ assert( p->bIsReader ); assert( pOp->p4type==P4_INTARRAY ); nRoot = pOp->p2; aRoot = pOp->p4.ai; assert( nRoot>0 ); assert( aRoot!=0 ); assert( aRoot[0]==(Pgno)nRoot ); assert( pOp->p1>0 && (pOp->p1+1)<=(p->nMem+1 - p->nCursor) ); pnErr = &aMem[pOp->p1]; assert( (pnErr->flags & MEM_Int)!=0 ); assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); pIn1 = &aMem[pOp->p1+1]; assert( pOp->p5<db->nDb ); assert( DbMaskTest(p->btreeMask, pOp->p5) ); rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], &aMem[pOp->p3], nRoot, (int)pnErr->u.i+1, &nErr, &z); sqlite3VdbeMemSetNull(pIn1); if( nErr==0 ){ assert( z==0 ); }else if( rc ){ sqlite3_free(z); goto abort_due_to_error; }else{ |
︙ | ︙ | |||
7246 7247 7248 7249 7250 7251 7252 | /* Opcode: Program P1 P2 P3 P4 P5 ** ** Execute the trigger program passed as P4 (type P4_SUBPROGRAM). ** ** P1 contains the address of the memory cell that contains the first memory ** cell in an array of values used as arguments to the sub-program. P2 ** contains the address to jump to if the sub-program throws an IGNORE | | > > | | 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 | /* Opcode: Program P1 P2 P3 P4 P5 ** ** Execute the trigger program passed as P4 (type P4_SUBPROGRAM). ** ** P1 contains the address of the memory cell that contains the first memory ** cell in an array of values used as arguments to the sub-program. P2 ** contains the address to jump to if the sub-program throws an IGNORE ** exception using the RAISE() function. P2 might be zero, if there is ** no possibility that an IGNORE exception will be raised. ** Register P3 contains the address ** of a memory cell in this (the parent) VM that is used to allocate the ** memory required by the sub-vdbe at runtime. ** ** P4 is a pointer to the VM containing the trigger program. ** ** If P5 is non-zero, then recursive program invocation is enabled. */ case OP_Program: { /* jump0 */ int nMem; /* Number of memory registers for sub-program */ int nByte; /* Bytes of runtime space required for sub-program */ Mem *pRt; /* Register to allocate runtime space */ Mem *pMem; /* Used to iterate through memory cells */ Mem *pEnd; /* Last memory cell in new array */ VdbeFrame *pFrame; /* New vdbe frame to execute in */ SubProgram *pProgram; /* Sub-program to execute */ |
︙ | ︙ | |||
8803 8804 8805 8806 8807 8808 8809 | ** Increment the value of P1 so that OP_Once opcodes will jump the ** first time they are evaluated for this run. ** ** If P3 is not zero, then it is an address to jump to if an SQLITE_CORRUPT ** error is encountered. */ case OP_Trace: | | | 8838 8839 8840 8841 8842 8843 8844 8845 8846 8847 8848 8849 8850 8851 8852 | ** Increment the value of P1 so that OP_Once opcodes will jump the ** first time they are evaluated for this run. ** ** If P3 is not zero, then it is an address to jump to if an SQLITE_CORRUPT ** error is encountered. */ case OP_Trace: case OP_Init: { /* jump0 */ int i; #ifndef SQLITE_OMIT_TRACE char *zTrace; #endif /* If the P4 argument is not NULL, then it must be an SQL comment string. ** The "--" string is broken up to prevent false-positives with srcck1.c. |
︙ | ︙ |
Changes to src/vdbe.h.
︙ | ︙ | |||
291 292 293 294 295 296 297 298 299 300 301 302 303 304 | UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*); typedef int (*RecordCompare)(int,const void*,UnpackedRecord*); RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*); void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); int sqlite3VdbeHasSubProgram(Vdbe*); int sqlite3NotPureFunc(sqlite3_context*); #ifdef SQLITE_ENABLE_BYTECODE_VTAB int sqlite3VdbeBytecodeVtabInit(sqlite3*); #endif /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on | > > | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*); typedef int (*RecordCompare)(int,const void*,UnpackedRecord*); RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*); void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); int sqlite3VdbeHasSubProgram(Vdbe*); void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val); int sqlite3NotPureFunc(sqlite3_context*); #ifdef SQLITE_ENABLE_BYTECODE_VTAB int sqlite3VdbeBytecodeVtabInit(sqlite3*); #endif /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on |
︙ | ︙ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
2400 2401 2402 2403 2404 2405 2406 | *(i64*)pOut = res; return 0; } return 1; } if( flags & SQLITE_SCANSTAT_COMPLEX ){ idx = iScan; | < > > | 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 | *(i64*)pOut = res; return 0; } return 1; } if( flags & SQLITE_SCANSTAT_COMPLEX ){ idx = iScan; }else{ /* If the COMPLEX flag is clear, then this function must ignore any ** ScanStatus structures with ScanStatus.addrLoop set to 0. */ for(idx=0; idx<p->nScan; idx++){ pScan = &p->aScan[idx]; if( pScan->zName ){ iScan--; if( iScan<0 ) break; } } } if( idx>=p->nScan ) return 1; assert( pScan==0 || pScan==&p->aScan[idx] ); pScan = &p->aScan[idx]; switch( iScanStatusOp ){ case SQLITE_SCANSTAT_NLOOP: { if( pScan->addrLoop>0 ){ *(sqlite3_int64*)pOut = aOp[pScan->addrLoop].nExec; }else{ *(sqlite3_int64*)pOut = -1; |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
935 936 937 938 939 940 941 942 943 944 945 946 947 948 | ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to ** have non-negative values for P2. */ assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); assert( ADDR(pOp->p2)<-pParse->nLabel ); assert( aLabel!=0 ); /* True because of tag-20230419-1 */ pOp->p2 = aLabel[ADDR(pOp->p2)]; } break; } } /* The mkopcodeh.tcl script has so arranged things that the only ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to ** have non-negative values for P2. */ assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0); | > > > > > > > > > | 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to ** have non-negative values for P2. */ assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); assert( ADDR(pOp->p2)<-pParse->nLabel ); assert( aLabel!=0 ); /* True because of tag-20230419-1 */ pOp->p2 = aLabel[ADDR(pOp->p2)]; } /* OPFLG_JUMP opcodes never have P2==0, though OPFLG_JUMP0 opcodes ** might */ assert( pOp->p2>0 || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP0)!=0 ); /* Jumps never go off the end of the bytecode array */ assert( pOp->p2<p->nOp || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)==0 ); break; } } /* The mkopcodeh.tcl script has so arranged things that the only ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to ** have non-negative values for P2. */ assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0); |
︙ | ︙ | |||
3342 3343 3344 3345 3346 3347 3348 | p->nChange = 0; } } } /* Check for immediate foreign key violations. */ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ | | | | 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 | p->nChange = 0; } } } /* Check for immediate foreign key violations. */ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ (void)sqlite3VdbeCheckFk(p, 0); } /* If the auto-commit flag is set and this is the only active writer ** VM, then we do either a commit or rollback of the current transaction. ** ** Note: This block also runs if one of the special errors handled ** above has occurred. */ if( !sqlite3VtabInSync(db) |
︙ | ︙ | |||
4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 | assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); #endif assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); swapMixedEndianFloat(x); memcpy(&pMem->u.r, &x, sizeof(x)); pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; } } void sqlite3VdbeSerialGet( const unsigned char *buf, /* Buffer to deserialize from */ u32 serial_type, /* Serial type to deserialize */ Mem *pMem /* Memory cell to write value into */ ){ switch( serial_type ){ | > > > > > > > > > > > > > > > > > | 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 | assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); #endif assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); swapMixedEndianFloat(x); memcpy(&pMem->u.r, &x, sizeof(x)); pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; } } static int serialGet7( const unsigned char *buf, /* Buffer to deserialize from */ Mem *pMem /* Memory cell to write value into */ ){ u64 x = FOUR_BYTE_UINT(buf); u32 y = FOUR_BYTE_UINT(buf+4); x = (x<<32) + y; assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); swapMixedEndianFloat(x); memcpy(&pMem->u.r, &x, sizeof(x)); if( IsNaN(x) ){ pMem->flags = MEM_Null; return 1; } pMem->flags = MEM_Real; return 0; } void sqlite3VdbeSerialGet( const unsigned char *buf, /* Buffer to deserialize from */ u32 serial_type, /* Serial type to deserialize */ Mem *pMem /* Memory cell to write value into */ ){ switch( serial_type ){ |
︙ | ︙ | |||
4495 4496 4497 4498 4499 4500 4501 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; testcase( x<r ); testcase( x>r ); testcase( x==r ); return (x<r) ? -1 : (x>r); }else{ i64 y; | < < | | | | | 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; testcase( x<r ); testcase( x>r ); testcase( x==r ); return (x<r) ? -1 : (x>r); }else{ i64 y; if( r<-9223372036854775808.0 ) return +1; if( r>=9223372036854775808.0 ) return -1; y = (i64)r; if( i<y ) return -1; if( i>y ) return +1; testcase( doubleLt(((double)i),r) ); testcase( doubleLt(r,((double)i)) ); testcase( doubleEq(r,((double)i)) ); return (((double)i)<r) ? -1 : (((double)i)>r); } } /* ** Compare the values contained by the two memory cells, returning ** negative, zero or positive if pMem1 is less than, equal to, or greater ** than pMem2. Sorting order is NULL's first, followed by numbers (integers |
︙ | ︙ | |||
4735 4736 4737 4738 4739 4740 4741 | serial_type = aKey1[idx1]; testcase( serial_type==12 ); if( serial_type>=10 ){ rc = serial_type==10 ? -1 : +1; }else if( serial_type==0 ){ rc = -1; }else if( serial_type==7 ){ | | | 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 | serial_type = aKey1[idx1]; testcase( serial_type==12 ); if( serial_type>=10 ){ rc = serial_type==10 ? -1 : +1; }else if( serial_type==0 ){ rc = -1; }else if( serial_type==7 ){ serialGet7(&aKey1[d1], &mem1); rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r); }else{ i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); i64 rhs = pRhs->u.i; if( lhs<rhs ){ rc = -1; }else if( lhs>rhs ){ |
︙ | ︙ | |||
4760 4761 4762 4763 4764 4765 4766 | ** numbers). Types 10 and 11 are currently "reserved for future ** use", so it doesn't really matter what the results of comparing ** them to numeric values are. */ rc = serial_type==10 ? -1 : +1; }else if( serial_type==0 ){ rc = -1; }else{ | < > > | > > > | 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 | ** numbers). Types 10 and 11 are currently "reserved for future ** use", so it doesn't really matter what the results of comparing ** them to numeric values are. */ rc = serial_type==10 ? -1 : +1; }else if( serial_type==0 ){ rc = -1; }else{ if( serial_type==7 ){ if( serialGet7(&aKey1[d1], &mem1) ){ rc = -1; /* mem1 is a NaN */ }else if( mem1.u.r<pRhs->u.r ){ rc = -1; }else if( mem1.u.r>pRhs->u.r ){ rc = +1; }else{ assert( rc==0 ); } }else{ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r); } } } /* RHS is a string */ else if( pRhs->flags & MEM_Str ){ |
︙ | ︙ | |||
4837 4838 4839 4840 4841 4842 4843 | } } } /* RHS is null */ else{ serial_type = aKey1[idx1]; | | > > > > > > > | 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 | } } } /* RHS is null */ else{ serial_type = aKey1[idx1]; if( serial_type==0 || serial_type==10 || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0) ){ assert( rc==0 ); }else{ rc = 1; } } if( rc!=0 ){ int sortFlags = pPKey2->pKeyInfo->aSortFlags[i]; if( sortFlags ){ if( (sortFlags & KEYINFO_ORDER_BIGNULL)==0 || ((sortFlags & KEYINFO_ORDER_DESC) |
︙ | ︙ |
Changes to src/vdbemem.c.
︙ | ︙ | |||
938 939 940 941 942 943 944 945 946 947 948 949 950 951 | if( VdbeMemDynamic(pMem) ){ vdbeReleaseAndSetInt64(pMem, val); }else{ pMem->u.i = val; pMem->flags = MEM_Int; } } /* A no-op destructor */ void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); } /* ** Set the value stored in *pMem should already be a NULL. ** Also store a pointer to go with it. | > > > > > > > | 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 | if( VdbeMemDynamic(pMem) ){ vdbeReleaseAndSetInt64(pMem, val); }else{ pMem->u.i = val; pMem->flags = MEM_Int; } } /* ** Set the iIdx'th entry of array aMem[] to contain integer value val. */ void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val){ sqlite3VdbeMemSetInt64(&aMem[iIdx], val); } /* A no-op destructor */ void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); } /* ** Set the value stored in *pMem should already be a NULL. ** Also store a pointer to go with it. |
︙ | ︙ | |||
1627 1628 1629 1630 1631 1632 1633 | sqlite3VdbeMemCast(*ppVal, aff, enc); sqlite3ValueApplyAffinity(*ppVal, affinity, enc); } return rc; } /* Handle negative integers in a single step. This is needed in the | | | | > | > > > | | | | > > > > > > | | | | > | > > > > > > > > | > | 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 | sqlite3VdbeMemCast(*ppVal, aff, enc); sqlite3ValueApplyAffinity(*ppVal, affinity, enc); } return rc; } /* Handle negative integers in a single step. This is needed in the ** case when the value is -9223372036854775808. Except - do not do this ** for hexadecimal literals. */ if( op==TK_UMINUS ){ Expr *pLeft = pExpr->pLeft; if( (pLeft->op==TK_INTEGER || pLeft->op==TK_FLOAT) ){ if( ExprHasProperty(pLeft, EP_IntValue) || pLeft->u.zToken[0]!='0' || (pLeft->u.zToken[1] & ~0x20)!='X' ){ pExpr = pLeft; op = pExpr->op; negInt = -1; zNeg = "-"; } } } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ pVal = valueNew(db, pCtx); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); }else{ i64 iVal; if( op==TK_INTEGER && 0==sqlite3DecOrHexToI64(pExpr->u.zToken, &iVal) ){ sqlite3VdbeMemSetInt64(pVal, iVal*negInt); }else{ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); if( zVal==0 ) goto no_mem; sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); } } if( affinity==SQLITE_AFF_BLOB ){ if( op==TK_FLOAT ){ assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) ); sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8); pVal->flags = MEM_Real; }else if( op==TK_INTEGER ){ /* This case is required by -9223372036854775808 and other strings ** that look like integers but cannot be handled by the ** sqlite3DecOrHexToI64() call above. */ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); } }else{ sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); } assert( (pVal->flags & MEM_IntReal)==0 ); if( pVal->flags & (MEM_Int|MEM_IntReal|MEM_Real) ){ testcase( pVal->flags & MEM_Int ); testcase( pVal->flags & MEM_Real ); |
︙ | ︙ | |||
1918 1919 1920 1921 1922 1923 1924 | sqlite3 *db, /* Database handle */ const void *pRec, /* Pointer to buffer containing record */ int nRec, /* Size of buffer pRec in bytes */ int iCol, /* Column to extract */ sqlite3_value **ppVal /* OUT: Extracted value */ ){ u32 t = 0; /* a column type code */ | | | | | | | 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 | sqlite3 *db, /* Database handle */ const void *pRec, /* Pointer to buffer containing record */ int nRec, /* Size of buffer pRec in bytes */ int iCol, /* Column to extract */ sqlite3_value **ppVal /* OUT: Extracted value */ ){ u32 t = 0; /* a column type code */ u32 nHdr; /* Size of the header in the record */ u32 iHdr; /* Next unread header byte */ i64 iField; /* Next unread data byte */ u32 szField = 0; /* Size of the current data field */ int i; /* Column index */ u8 *a = (u8*)pRec; /* Typecast byte array */ Mem *pMem = *ppVal; /* Write result into this Mem object */ assert( iCol>0 ); iHdr = getVarint32(a, nHdr); if( nHdr>(u32)nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; iField = nHdr; for(i=0; i<=iCol; i++){ iHdr += getVarint32(&a[iHdr], t); testcase( iHdr==nHdr ); testcase( iHdr==nHdr+1 ); if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT; szField = sqlite3VdbeSerialTypeLen(t); |
︙ | ︙ |
Changes to src/vdbevtab.c.
︙ | ︙ | |||
282 283 284 285 286 287 288 | sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); } break; } #ifdef SQLITE_ENABLE_STMT_SCANSTATUS case 9: /* nexec */ | | | | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); } break; } #ifdef SQLITE_ENABLE_STMT_SCANSTATUS case 9: /* nexec */ sqlite3_result_int64(ctx, pOp->nExec); break; case 10: /* ncycle */ sqlite3_result_int64(ctx, pOp->nCycle); break; #else case 9: /* nexec */ case 10: /* ncycle */ sqlite3_result_int(ctx, 0); break; #endif |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
607 608 609 610 611 612 613 614 615 616 617 618 619 620 | sCtx.pTab = pTab; sCtx.pVTable = pVTable; sCtx.pPrior = db->pVtabCtx; sCtx.bDeclared = 0; db->pVtabCtx = &sCtx; pTab->nTabRef++; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); sqlite3DeleteTable(db, pTab); db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); assert( sCtx.pTab==pTab ); if( SQLITE_OK!=rc ){ if( zErr==0 ){ | > > | 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | sCtx.pTab = pTab; sCtx.pVTable = pVTable; sCtx.pPrior = db->pVtabCtx; sCtx.bDeclared = 0; db->pVtabCtx = &sCtx; pTab->nTabRef++; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); assert( pTab!=0 ); assert( pTab->nTabRef>1 || rc!=SQLITE_OK ); sqlite3DeleteTable(db, pTab); db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); assert( sCtx.pTab==pTab ); if( SQLITE_OK!=rc ){ if( zErr==0 ){ |
︙ | ︙ | |||
629 630 631 632 633 634 635 | ** the sqlite3_vtab object if successful. */ memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0])); pVTable->pVtab->pModule = pMod->pModule; pMod->nRefModule++; pVTable->nRef = 1; if( sCtx.bDeclared==0 ){ const char *zFormat = "vtable constructor did not declare schema: %s"; | | | 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 | ** the sqlite3_vtab object if successful. */ memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0])); pVTable->pVtab->pModule = pMod->pModule; pMod->nRefModule++; pVTable->nRef = 1; if( sCtx.bDeclared==0 ){ const char *zFormat = "vtable constructor did not declare schema: %s"; *pzErr = sqlite3MPrintf(db, zFormat, zModuleName); sqlite3VtabUnlock(pVTable); rc = SQLITE_ERROR; }else{ int iCol; u16 oooHidden = 0; /* If everything went according to plan, link the new VTable structure ** into the linked list headed by pTab->u.vtab.p. Then loop through the |
︙ | ︙ | |||
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 | */ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ VtabCtx *pCtx; int rc = SQLITE_OK; Table *pTab; Parse sParse; int initBusy; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ return SQLITE_MISUSE_BKPT; } #endif sqlite3_mutex_enter(db->mutex); pCtx = db->pVtabCtx; if( !pCtx || pCtx->bDeclared ){ sqlite3Error(db, SQLITE_MISUSE_BKPT); sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; } pTab = pCtx->pTab; assert( IsVirtual(pTab) ); sqlite3ParseObjectInit(&sParse, db); sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; sParse.disableTriggers = 1; /* We should never be able to reach this point while loading the ** schema. Nevertheless, defend against that (turn off db->init.busy) ** in case a bug arises. */ assert( db->init.busy==0 ); initBusy = db->init.busy; db->init.busy = 0; sParse.nQueryLoop = 1; | > > > > > > > > > > > > > > > > > > > | | | | < | 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 | */ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ VtabCtx *pCtx; int rc = SQLITE_OK; Table *pTab; Parse sParse; int initBusy; int i; const unsigned char *z; static const u8 aKeyword[] = { TK_CREATE, TK_TABLE, 0 }; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ return SQLITE_MISUSE_BKPT; } #endif /* Verify that the first two keywords in the CREATE TABLE statement ** really are "CREATE" and "TABLE". If this is not the case, then ** sqlite3_declare_vtab() is being misused. */ z = (const unsigned char*)zCreateTable; for(i=0; aKeyword[i]; i++){ int tokenType = 0; do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE ); if( tokenType!=aKeyword[i] ){ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error"); return SQLITE_ERROR; } } sqlite3_mutex_enter(db->mutex); pCtx = db->pVtabCtx; if( !pCtx || pCtx->bDeclared ){ sqlite3Error(db, SQLITE_MISUSE_BKPT); sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; } pTab = pCtx->pTab; assert( IsVirtual(pTab) ); sqlite3ParseObjectInit(&sParse, db); sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; sParse.disableTriggers = 1; /* We should never be able to reach this point while loading the ** schema. Nevertheless, defend against that (turn off db->init.busy) ** in case a bug arises. */ assert( db->init.busy==0 ); initBusy = db->init.busy; db->init.busy = 0; sParse.nQueryLoop = 1; if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) ){ assert( sParse.pNewTable!=0 ); assert( !db->mallocFailed ); assert( IsOrdinaryTable(sParse.pNewTable) ); assert( sParse.zErrMsg==0 ); if( !pTab->aCol ){ Table *pNew = sParse.pNewTable; Index *pIdx; pTab->aCol = pNew->aCol; sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); pTab->nNVCol = pTab->nCol = pNew->nCol; |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 | pWal->bShmUnreliable = 0; sqlite3WalEndReadTransaction(pWal); *pChanged = 1; } return rc; } /* ** Attempt to start a read transaction. This might fail due to a race or ** other transient condition. When that happens, it returns WAL_RETRY to ** indicate to the caller that it is safe to retry immediately. ** ** On success return SQLITE_OK. On a permanent failure (such an ** I/O error or an SQLITE_BUSY because another process is running | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 | pWal->bShmUnreliable = 0; sqlite3WalEndReadTransaction(pWal); *pChanged = 1; } return rc; } /* ** The final argument passed to walTryBeginRead() is of type (int*). The ** caller should invoke walTryBeginRead as follows: ** ** int cnt = 0; ** do { ** rc = walTryBeginRead(..., &cnt); ** }while( rc==WAL_RETRY ); ** ** The final value of "cnt" is of no use to the caller. It is used by ** the implementation of walTryBeginRead() as follows: ** ** + Each time walTryBeginRead() is called, it is incremented. Once ** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead() ** has many times been invoked and failed with WAL_RETRY - walTryBeginRead() ** returns SQLITE_PROTOCOL. ** ** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed ** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS ** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case ** the next invocation of walTryBeginRead() may omit an expected call to ** sqlite3OsSleep(). There has already been a delay when the previous call ** waited on a lock. */ #define WAL_RETRY_PROTOCOL_LIMIT 100 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT # define WAL_RETRY_BLOCKED_MASK 0x10000000 #else # define WAL_RETRY_BLOCKED_MASK 0 #endif /* ** Attempt to start a read transaction. This might fail due to a race or ** other transient condition. When that happens, it returns WAL_RETRY to ** indicate to the caller that it is safe to retry immediately. ** ** On success return SQLITE_OK. On a permanent failure (such an ** I/O error or an SQLITE_BUSY because another process is running |
︙ | ︙ | |||
2948 2949 2950 2951 2952 2953 2954 | ** This routine uses the nBackfill and aReadMark[] fields of the header ** to select a particular WAL_READ_LOCK() that strives to let the ** checkpoint process do as much work as possible. This routine might ** update values of the aReadMark[] array in the header, but if it does ** so it takes care to hold an exclusive lock on the corresponding ** WAL_READ_LOCK() while changing values. */ | | | 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 | ** This routine uses the nBackfill and aReadMark[] fields of the header ** to select a particular WAL_READ_LOCK() that strives to let the ** checkpoint process do as much work as possible. This routine might ** update values of the aReadMark[] array in the header, but if it does ** so it takes care to hold an exclusive lock on the corresponding ** WAL_READ_LOCK() while changing values. */ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ u32 mxReadMark; /* Largest aReadMark[] value */ int mxI; /* Index of largest aReadMark[] value */ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ u32 mxFrame; /* Wal frame to lock to */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
︙ | ︙ | |||
2981 2982 2983 2984 2985 2986 2987 | ** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few ** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this ** is more of a scheduler yield than an actual delay. But on the 10th ** an subsequent retries, the delays start becoming longer and longer, ** so that on the 100th (and last) RETRY we delay for 323 milliseconds. ** The total delay time before giving up is less than 10 seconds. */ | > | > | | | | > > | > > | > | > > > | 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 | ** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few ** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this ** is more of a scheduler yield than an actual delay. But on the 10th ** an subsequent retries, the delays start becoming longer and longer, ** so that on the 100th (and last) RETRY we delay for 323 milliseconds. ** The total delay time before giving up is less than 10 seconds. */ (*pCnt)++; if( *pCnt>5 ){ int nDelay = 1; /* Pause time in microseconds */ int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK); if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){ VVA_ONLY( pWal->lockError = 1; ) return SQLITE_PROTOCOL; } if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; #ifdef SQLITE_ENABLE_SETLK_TIMEOUT /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor ** to block for locks for approximately nDelay us. This affects three ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the ** first attempted read fails, and (c) the shared lock taken on the ** read-mark. ** ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error, ** then sleep for the minimum of 1us. The previous call already provided ** an extra delay while it was blocking on the lock. */ nBlockTmout = (nDelay+998) / 1000; if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){ if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1; } #endif sqlite3OsSleep(pWal->pVfs, nDelay); *pCnt &= ~WAL_RETRY_BLOCKED_MASK; } if( !useWal ){ assert( rc==SQLITE_OK ); if( pWal->bShmUnreliable==0 ){ rc = walIndexReadHdr(pWal, pChanged); } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT walDisableBlocking(pWal); if( rc==SQLITE_BUSY_TIMEOUT ){ rc = SQLITE_BUSY; *pCnt |= WAL_RETRY_BLOCKED_MASK; } #endif if( rc==SQLITE_BUSY ){ /* If there is not a recovery running in another thread or process ** then convert BUSY errors to WAL_RETRY. If recovery is known to ** be running, convert BUSY to BUSY_RECOVERY. There is a race here ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY ** would be technically correct. But the race is benign since with |
︙ | ︙ | |||
3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 | return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; } (void)walEnableBlockingMs(pWal, nBlockTmout); rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); walDisableBlocking(pWal); if( rc ){ assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT ); return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; } /* Now that the read-lock has been obtained, check that neither the ** value in the aReadMark[] array or the contents of the wal-index ** header have changed. ** | > > > > > > > | 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 | return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; } (void)walEnableBlockingMs(pWal, nBlockTmout); rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); walDisableBlocking(pWal); if( rc ){ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT if( rc==SQLITE_BUSY_TIMEOUT ){ *pCnt |= WAL_RETRY_BLOCKED_MASK; } #else assert( rc!=SQLITE_BUSY_TIMEOUT ); #endif assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT ); return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; } /* Now that the read-lock has been obtained, check that neither the ** value in the aReadMark[] array or the contents of the wal-index ** header have changed. ** |
︙ | ︙ | |||
3320 3321 3322 3323 3324 3325 3326 | return rc; } ckptLock = 1; } #endif do{ | | | 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 | return rc; } ckptLock = 1; } #endif do{ rc = walTryBeginRead(pWal, pChanged, 0, &cnt); }while( rc==WAL_RETRY ); testcase( (rc&0xff)==SQLITE_BUSY ); testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); #ifdef SQLITE_ENABLE_SNAPSHOT |
︙ | ︙ | |||
3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 | while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ u32 iFrame = iH + sLoc.iZero; if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){ assert( iFrame>iRead || CORRUPT_DB ); iRead = iFrame; } if( (nCollide--)==0 ){ return SQLITE_CORRUPT_BKPT; } iKey = walNextHash(iKey); } if( iRead ) break; } | > | 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 | while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ u32 iFrame = iH + sLoc.iZero; if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){ assert( iFrame>iRead || CORRUPT_DB ); iRead = iFrame; } if( (nCollide--)==0 ){ *piRead = 0; return SQLITE_CORRUPT_BKPT; } iKey = walNextHash(iKey); } if( iRead ) break; } |
︙ | ︙ | |||
3804 3805 3806 3807 3808 3809 3810 | } } walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; cnt = 0; do{ int notUsed; | | | 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 | } } walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; cnt = 0; do{ int notUsed; rc = walTryBeginRead(pWal, ¬Used, 1, &cnt); }while( rc==WAL_RETRY ); assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); } return rc; |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
297 298 299 300 301 302 303 304 305 306 307 308 309 310 | static Expr *whereRightSubexprIsColumn(Expr *p){ p = sqlite3ExprSkipCollateAndLikely(p->pRight); if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ return p; } return 0; } /* ** Advance to the next WhereTerm that matches according to the criteria ** established when the pScan object was initialized by whereScanInit(). ** Return NULL if there are no more matching WhereTerms. */ static WhereTerm *whereScanNext(WhereScan *pScan){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | static Expr *whereRightSubexprIsColumn(Expr *p){ p = sqlite3ExprSkipCollateAndLikely(p->pRight); if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ return p; } return 0; } /* ** Term pTerm is guaranteed to be a WO_IN term. It may be a component term ** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)". ** This function checks to see if the term is compatible with an index ** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so, ** it returns a pointer to the name of the collation sequence (e.g. "BINARY" ** or "NOCASE") used by the comparison in pTerm. If it is not compatible ** with affinity idxaff, NULL is returned. */ static SQLITE_NOINLINE const char *indexInAffinityOk( Parse *pParse, WhereTerm *pTerm, u8 idxaff ){ Expr *pX = pTerm->pExpr; Expr inexpr; assert( pTerm->eOperator & WO_IN ); if( sqlite3ExprIsVector(pX->pLeft) ){ int iField = pTerm->u.x.iField - 1; inexpr.flags = 0; inexpr.op = TK_EQ; inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr; assert( ExprUseXSelect(pX) ); inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr; pX = &inexpr; } if( sqlite3IndexAffinityOk(pX, idxaff) ){ CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX); return pRet ? pRet->zName : sqlite3StrBINARY; } return 0; } /* ** Advance to the next WhereTerm that matches according to the criteria ** established when the pScan object was initialized by whereScanInit(). ** Return NULL if there are no more matching WhereTerms. */ static WhereTerm *whereScanNext(WhereScan *pScan){ |
︙ | ︙ | |||
348 349 350 351 352 353 354 | pScan->aiColumn[j] = pX->iColumn; pScan->nEquiv++; } } if( (pTerm->eOperator & pScan->opMask)!=0 ){ /* Verify the affinity and collating sequence match */ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ | | > > > > > > | | | | | > > | | | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 | pScan->aiColumn[j] = pX->iColumn; pScan->nEquiv++; } } if( (pTerm->eOperator & pScan->opMask)!=0 ){ /* Verify the affinity and collating sequence match */ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ const char *zCollName; Parse *pParse = pWC->pWInfo->pParse; pX = pTerm->pExpr; if( (pTerm->eOperator & WO_IN) ){ zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff); if( !zCollName ) continue; }else{ CollSeq *pColl; if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ continue; } assert(pX->pLeft); pColl = sqlite3ExprCompareCollSeq(pParse, pX); zCollName = pColl ? pColl->zName : sqlite3StrBINARY; } if( sqlite3StrICmp(zCollName, pScan->zCollName) ){ continue; } } if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0 && (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0)) && pX->op==TK_COLUMN && pX->iTable==pScan->aiCur[0] |
︙ | ︙ | |||
709 710 711 712 713 714 715 | /* ** Two routines for printing the content of an sqlite3_index_info ** structure. Used for testing and debugging only. If neither ** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines ** are no-ops. */ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) | | > > > > | > > > > | | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 | /* ** Two routines for printing the content of an sqlite3_index_info ** structure. Used for testing and debugging only. If neither ** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines ** are no-ops. */ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) static void whereTraceIndexInfoInputs( sqlite3_index_info *p, /* The IndexInfo object */ Table *pTab /* The TABLE that is the virtual table */ ){ int i; if( (sqlite3WhereTrace & 0x10)==0 ) return; sqlite3DebugPrintf("sqlite3_index_info inputs for %s:\n", pTab->zName); for(i=0; i<p->nConstraint; i++){ sqlite3DebugPrintf( " 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)); } for(i=0; i<p->nOrderBy; i++){ sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n", i, p->aOrderBy[i].iColumn, p->aOrderBy[i].desc); } } static void whereTraceIndexInfoOutputs( sqlite3_index_info *p, /* The IndexInfo object */ Table *pTab /* The TABLE that is the virtual table */ ){ int i; if( (sqlite3WhereTrace & 0x10)==0 ) return; sqlite3DebugPrintf("sqlite3_index_info outputs for %s:\n", pTab->zName); for(i=0; i<p->nConstraint; i++){ sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", i, p->aConstraintUsage[i].argvIndex, p->aConstraintUsage[i].omit); } sqlite3DebugPrintf(" idxNum=%d\n", p->idxNum); sqlite3DebugPrintf(" idxStr=%s\n", p->idxStr); sqlite3DebugPrintf(" orderByConsumed=%d\n", p->orderByConsumed); sqlite3DebugPrintf(" estimatedCost=%g\n", p->estimatedCost); sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows); } #else #define whereTraceIndexInfoInputs(A,B) #define whereTraceIndexInfoOutputs(A,B) #endif /* ** We know that pSrc is an operand of an outer join. Return true if ** pTerm is a constraint that is compatible with that join. ** ** pTerm must be EP_OuterON if pSrc is the right operand of an |
︙ | ︙ | |||
930 931 932 933 934 935 936 | idxCols = 0; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ Expr *pExpr = pTerm->pExpr; /* Make the automatic index a partial index if there are terms in the ** WHERE clause (or the ON clause of a LEFT join) that constrain which ** rows of the target table (pSrc) that can be used. */ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 | | | 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 | idxCols = 0; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ Expr *pExpr = pTerm->pExpr; /* Make the automatic index a partial index if there are terms in the ** WHERE clause (or the ON clause of a LEFT join) that constrain which ** rows of the target table (pSrc) that can be used. */ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0) ){ pPartial = sqlite3ExprAnd(pParse, pPartial, sqlite3ExprDup(pParse->db, pExpr, 0)); } if( termCanDriveIndex(pTerm, pSrc, notReady) ){ int iCol; Bitmask cMask; |
︙ | ︙ | |||
972 973 974 975 976 977 978 | ** columns that are needed by the query. With a covering index, the ** original table never needs to be accessed. Automatic indices must ** be a covering index because the index will not be updated if the ** original table changes and the index and table cannot both be used ** if they go out of sync. */ if( IsView(pTable) ){ | | | 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 | ** columns that are needed by the query. With a covering index, the ** original table never needs to be accessed. Automatic indices must ** be a covering index because the index will not be updated if the ** original table changes and the index and table cannot both be used ** if they go out of sync. */ if( IsView(pTable) ){ extraCols = ALLBITS & ~idxCols; }else{ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); } mxBitCol = MIN(BMS-1,pTable->nCol); testcase( pTable->nCol==BMS-1 ); testcase( pTable->nCol==BMS-2 ); for(i=0; i<mxBitCol; i++){ |
︙ | ︙ | |||
1199 1200 1201 1202 1203 1204 1205 | sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter); addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){ Expr *pExpr = pTerm->pExpr; if( (pTerm->wtFlags & TERM_VIRTUAL)==0 | | | 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 | sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter); addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){ Expr *pExpr = pTerm->pExpr; if( (pTerm->wtFlags & TERM_VIRTUAL)==0 && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0) ){ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); } } if( pLoop->wsFlags & WHERE_IPK ){ int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); |
︙ | ︙ | |||
1325 1326 1327 1328 1329 1330 1331 | if( pOrderBy ){ int n = pOrderBy->nExpr; for(i=0; i<n; i++){ Expr *pExpr = pOrderBy->a[i].pExpr; Expr *pE2; /* Skip over constant terms in the ORDER BY clause */ | | | 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 | if( pOrderBy ){ int n = pOrderBy->nExpr; for(i=0; i<n; i++){ Expr *pExpr = pOrderBy->a[i].pExpr; Expr *pE2; /* Skip over constant terms in the ORDER BY clause */ if( sqlite3ExprIsConstant(0, pExpr) ){ continue; } /* Virtual tables are unable to deal with NULLS FIRST */ if( pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) break; /* First case - a direct column references without a COLLATE operator */ |
︙ | ︙ | |||
1360 1361 1362 1363 1364 1365 1366 | } /* No matches cause a break out of the loop */ break; } if( i==n ){ nOrderBy = n; | | | 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 | } /* No matches cause a break out of the loop */ break; } if( i==n ){ nOrderBy = n; if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ eDistinct = 1; } } } |
︙ | ︙ | |||
1437 1438 1439 1440 1441 1442 1443 | j++; } assert( j==nTerm ); pIdxInfo->nConstraint = j; for(i=j=0; i<nOrderBy; i++){ Expr *pExpr = pOrderBy->a[i].pExpr; | | | 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 | j++; } assert( j==nTerm ); pIdxInfo->nConstraint = j; for(i=j=0; i<nOrderBy; i++){ Expr *pExpr = pOrderBy->a[i].pExpr; if( sqlite3ExprIsConstant(0, pExpr) ) continue; assert( pExpr->op==TK_COLUMN || (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN && pExpr->iColumn==pExpr->pLeft->iColumn) ); pIdxOrderBy[j].iColumn = pExpr->iColumn; pIdxOrderBy[j].desc = pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC; j++; } |
︙ | ︙ | |||
1489 1490 1491 1492 1493 1494 1495 | ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates ** that this is required. */ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int rc; | | | | 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 | ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates ** that this is required. */ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int rc; whereTraceIndexInfoInputs(p, pTab); pParse->db->nSchemaLock++; rc = pVtab->pModule->xBestIndex(pVtab, p); pParse->db->nSchemaLock--; whereTraceIndexInfoOutputs(p, pTab); if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ if( rc==SQLITE_NOMEM ){ sqlite3OomFault(pParse->db); }else if( !pVtab->zErrMsg ){ sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc)); }else{ |
︙ | ︙ | |||
2969 2970 2971 2972 2973 2974 2975 | assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); if( pNew->wsFlags & WHERE_BTM_LIMIT ){ opMask = WO_LT|WO_LE; }else{ assert( pNew->u.btree.nBtm==0 ); opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; } | > | > > > > | 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 | assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); if( pNew->wsFlags & WHERE_BTM_LIMIT ){ opMask = WO_LT|WO_LE; }else{ assert( pNew->u.btree.nBtm==0 ); opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; } if( pProbe->bUnordered || pProbe->bLowQual ){ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){ opMask &= ~(WO_EQ|WO_IN|WO_IS); } } assert( pNew->u.btree.nEq<pProbe->nColumn ); assert( pNew->u.btree.nEq<pProbe->nKeyCol || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY ); saved_nEq = pNew->u.btree.nEq; saved_nBtm = pNew->u.btree.nBtm; |
︙ | ︙ | |||
3235 3236 3237 3238 3239 3240 3241 | ** as (col=?). */ pNew->nOut += 10; } } } } | | | | | > > > | > > > > > > > > | 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 | ** as (col=?). */ pNew->nOut += 10; } } } } /* Set rCostIdx to the estimated cost of visiting selected rows in the ** index. The estimate is the sum of two values: ** 1. The cost of doing one search-by-key to find the first matching ** entry ** 2. Stepping forward in the index pNew->nOut times to find all ** additional matching entries. */ assert( pSrc->pTab->szTabRow>0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* The pProbe->szIdxRow is low for an IPK table since the interior ** pages are small. Thus szIdxRow gives a good estimate of seek cost. ** But the leaf pages are full-size, so pProbe->szIdxRow would badly ** under-estimate the scanning cost. */ rCostIdx = pNew->nOut + 16; }else{ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; } rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx); /* Estimate the cost of running the loop. If all data is coming ** from the index, then this is just the cost of doing the index ** lookup and scan. But if some data is coming out of the main table, ** we also have to add in the cost of doing pNew->nOut searches to ** locate the row in the main table that corresponds to the index entry. */ pNew->rRun = rCostIdx; if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){ pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16); } ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult); nOutUnadjusted = pNew->nOut; pNew->rRun += nInMul + nIn; |
︙ | ︙ | |||
3355 3356 3357 3358 3359 3360 3361 | int ii, jj; if( pIndex->bUnordered ) return 0; if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; for(ii=0; ii<pOB->nExpr; ii++){ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); if( NEVER(pExpr==0) ) continue; | > | > | 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 | int ii, jj; if( pIndex->bUnordered ) return 0; if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; for(ii=0; ii<pOB->nExpr; ii++){ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); if( NEVER(pExpr==0) ) continue; if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) && pExpr->iTable==iCursor ){ if( pExpr->iColumn<0 ) return 1; for(jj=0; jj<pIndex->nKeyCol; jj++){ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; } }else if( (aColExpr = pIndex->aColExpr)!=0 ){ for(jj=0; jj<pIndex->nKeyCol; jj++){ if( pIndex->aiColumn[jj]!=XN_EXPR ) continue; |
︙ | ︙ | |||
3612 3613 3614 3615 3616 3617 3618 | if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){ Expr *pLeft = pPart->pLeft; Expr *pRight = pPart->pRight; u8 aff; if( pLeft->op!=TK_COLUMN ) return; | | | 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 | if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){ Expr *pLeft = pPart->pLeft; Expr *pRight = pPart->pRight; u8 aff; if( pLeft->op!=TK_COLUMN ) return; if( !sqlite3ExprIsConstant(0, pRight) ) return; if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return; if( pLeft->iColumn<0 ) return; aff = pIdx->pTable->aCol[pLeft->iColumn].affinity; if( aff>=SQLITE_AFF_TEXT ){ if( pItem ){ sqlite3 *db = pParse->db; IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocRaw(db, sizeof(*p)); |
︙ | ︙ | |||
3961 3962 3963 3964 3965 3966 3967 | pBuilder->bldFlags1 = 0; rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); if( pBuilder->bldFlags1==SQLITE_BLDF1_INDEXED ){ /* If a non-unique index is used, or if a prefix of the key for ** unique index is used (making the index functionally non-unique) ** then the sqlite_stat1 data becomes important for scoring the ** plan */ | | > > > > > > > > > > > > > > > | 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 | pBuilder->bldFlags1 = 0; rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); if( pBuilder->bldFlags1==SQLITE_BLDF1_INDEXED ){ /* If a non-unique index is used, or if a prefix of the key for ** unique index is used (making the index functionally non-unique) ** then the sqlite_stat1 data becomes important for scoring the ** plan */ pTab->tabFlags |= TF_MaybeReanalyze; } #ifdef SQLITE_ENABLE_STAT4 sqlite3Stat4ProbeFree(pBuilder->pRec); pBuilder->nRecValid = 0; pBuilder->pRec = 0; #endif } return rc; } #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Return true if pTerm is a virtual table LIMIT or OFFSET term. */ static int isLimitTerm(WhereTerm *pTerm){ assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; } /* ** Return true if the first nCons constraints in the pUsage array are ** marked as in-use (have argvIndex>0). False otherwise. */ static int allConstraintsUsed( struct sqlite3_index_constraint_usage *aUsage, int nCons ){ int ii; for(ii=0; ii<nCons; ii++){ if( aUsage[ii].argvIndex<=0 ) return 0; } return 1; } /* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This ** function marks a subset of those constraints usable, invokes the ** xBestIndex method and adds the returned plan to pBuilder. ** |
︙ | ︙ | |||
4123 4124 4125 4126 4127 4128 4129 4130 | ** (2) Multiple outputs from a single IN value will not merge ** together. */ pIdxInfo->orderByConsumed = 0; pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } assert( pbRetryLimit || !isLimitTerm(pTerm) ); | > > > > > | | > > | | | 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 | ** (2) Multiple outputs from a single IN value will not merge ** together. */ pIdxInfo->orderByConsumed = 0; pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET ** terms. And if there are any, they should follow all other terms. */ assert( pbRetryLimit || !isLimitTerm(pTerm) ); assert( !isLimitTerm(pTerm) || i>=nConstraint-2 ); assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) ); if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){ /* If there is an IN(...) term handled as an == (separate call to ** xFilter for each value on the RHS of the IN) and a LIMIT or ** OFFSET term handled as well, the plan is unusable. Similarly, ** if there is a LIMIT/OFFSET and there are other unused terms, ** the plan cannot be used. In these cases set variable *pbRetryLimit ** to true to tell the caller to retry with LIMIT and OFFSET ** disabled. */ if( pIdxInfo->needToFreeIdxStr ){ sqlite3_free(pIdxInfo->idxStr); pIdxInfo->idxStr = 0; pIdxInfo->needToFreeIdxStr = 0; } *pbRetryLimit = 1; return SQLITE_OK; |
︙ | ︙ | |||
4986 4987 4988 4989 4990 4991 4992 | orderDistinctMask |= pLoop->maskSelf; for(i=0; i<nOrderBy; i++){ Expr *p; Bitmask mTerm; if( MASKBIT(i) & obSat ) continue; p = pOrderBy->a[i].pExpr; mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p); | | | 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 | orderDistinctMask |= pLoop->maskSelf; for(i=0; i<nOrderBy; i++){ Expr *p; Bitmask mTerm; if( MASKBIT(i) & obSat ) continue; p = pOrderBy->a[i].pExpr; mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p); if( mTerm==0 && !sqlite3ExprIsConstant(0,p) ) continue; if( (mTerm&~orderDistinctMask)==0 ){ obSat |= MASKBIT(i); } } } } /* End the loop over all WhereLoops from outer-most down to inner-most */ if( obSat==obDone ) return (i8)nOrderBy; |
︙ | ︙ | |||
5455 5456 5457 5458 5459 5460 5461 | pWInfo->bOrderedInnerLoop = 0; if( pWInfo->pOrderBy ){ pWInfo->nOBSat = pFrom->isOrdered; if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } | > | < | < | 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 | pWInfo->bOrderedInnerLoop = 0; if( pWInfo->pOrderBy ){ pWInfo->nOBSat = pFrom->isOrdered; if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */ assert( pWInfo->pSelect->pOrderBy==0 || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr ); }else{ pWInfo->revMask = pFrom->revLoop; if( pWInfo->nOBSat<=0 ){ pWInfo->nOBSat = 0; if( nLoop>0 ){ u32 wsFlags = pFrom->aLoop[nLoop-1]->wsFlags; if( (wsFlags & WHERE_ONEROW)==0 |
︙ | ︙ | |||
5501 5502 5503 5504 5505 5506 5507 | if( nOrder==pWInfo->pOrderBy->nExpr ){ pWInfo->sorted = 1; pWInfo->revMask = revMask; } } } | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 | if( nOrder==pWInfo->pOrderBy->nExpr ){ pWInfo->sorted = 1; pWInfo->revMask = revMask; } } } pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ sqlite3StackFreeNN(pParse->db, pSpace); return SQLITE_OK; } /* ** This routine implements a heuristic designed to improve query planning. ** This routine is called in between the first and second call to ** wherePathSolver(). Hence the name "Interstage" "Heuristic". ** ** The first call to wherePathSolver() (hereafter just "solver()") computes ** the best path without regard to the order of the outputs. The second call ** to the solver() builds upon the first call to try to find an alternative ** path that satisfies the ORDER BY clause. ** ** This routine looks at the results of the first solver() run, and for ** every FROM clause term in the resulting query plan that uses an equality ** constraint against an index, disable other WhereLoops for that same ** FROM clause term that would try to do a full-table scan. This prevents ** an index search from being converted into a full-table scan in order to ** satisfy an ORDER BY clause, since even though we might get slightly better ** performance using the full-scan without sorting if the output size ** estimates are very precise, we might also get severe performance ** degradation using the full-scan if the output size estimate is too large. ** It is better to err on the side of caution. ** ** Except, if the first solver() call generated a full-table scan in an outer ** loop then stop this analysis at the first full-scan, since the second ** solver() run might try to swap that full-scan for another in order to ** get the output into the correct order. In other words, we allow a ** rewrite like this: ** ** First Solver() Second Solver() ** |-- SCAN t1 |-- SCAN t2 ** |-- SEARCH t2 `-- SEARCH t1 ** `-- SORT USING B-TREE ** ** The purpose of this routine is to disallow rewrites such as: ** ** First Solver() Second Solver() ** |-- SEARCH t1 |-- SCAN t2 <--- bad! ** |-- SEARCH t2 `-- SEARCH t1 ** `-- SORT USING B-TREE ** ** See test cases in test/whereN.test for the real-world query that ** originally provoked this heuristic. */ static SQLITE_NOINLINE void whereInterstageHeuristic(WhereInfo *pWInfo){ int i; #ifdef WHERETRACE_ENABLED int once = 0; #endif for(i=0; i<pWInfo->nLevel; i++){ WhereLoop *p = pWInfo->a[i].pWLoop; if( p==0 ) break; if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue; if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){ u8 iTab = p->iTab; WhereLoop *pLoop; for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){ if( pLoop->iTab!=iTab ) continue; if( (pLoop->wsFlags & (WHERE_CONSTRAINT|WHERE_AUTO_INDEX))!=0 ){ /* Auto-index and index-constrained loops allowed to remain */ continue; } #ifdef WHERETRACE_ENABLED if( sqlite3WhereTrace & 0x80 ){ if( once==0 ){ sqlite3DebugPrintf("Loops disabled by interstage heuristic:\n"); once = 1; } sqlite3WhereLoopPrint(pLoop, &pWInfo->sWC); } #endif /* WHERETRACE_ENABLED */ pLoop->prereq = ALLBITS; /* Prevent 2nd solver() from using this one */ } }else{ break; } } } /* ** Most queries use only a single table (they are not joins) and have ** simple == constraints against indexed fields. This routine attempts ** to plan those simple cases using much less ceremony than the ** general-purpose query planner, and thereby yield faster sqlite3_prepare() ** times for the common case. |
︙ | ︙ | |||
5797 5798 5799 5800 5801 5802 5803 | assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) ); for(i=0; i<pWInfo->nLevel; i++){ WhereLoop *pLoop = pWInfo->a[i].pWLoop; const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; Table *pTab = pItem->pTab; if( (pTab->tabFlags & TF_HasStat1)==0 ) break; | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 | assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) ); for(i=0; i<pWInfo->nLevel; i++){ WhereLoop *pLoop = pWInfo->a[i].pWLoop; const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; Table *pTab = pItem->pTab; if( (pTab->tabFlags & TF_HasStat1)==0 ) break; pTab->tabFlags |= TF_MaybeReanalyze; if( i>=1 && (pLoop->wsFlags & reqFlags)==reqFlags /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0) ){ if( nSearch > pTab->nRowLogEst ){ testcase( pItem->fg.jointype & JT_LEFT ); pLoop->wsFlags |= WHERE_BLOOMFILTER; pLoop->wsFlags &= ~WHERE_IDX_ONLY; WHERETRACE(0xffffffff, ( "-> use Bloom-filter on loop %c because there are ~%.1e " "lookups into %s which has only ~%.1e rows\n", pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName, (double)sqlite3LogEstToInt(pTab->nRowLogEst))); } } nSearch += pLoop->nOut; } } /* ** Expression Node callback for sqlite3ExprCanReturnSubtype(). ** ** Only a function call is able to return a subtype. So if the node ** is not a function call, return WRC_Prune immediately. ** ** A function call is able to return a subtype if it has the ** SQLITE_RESULT_SUBTYPE property. ** ** Assume that every function is able to pass-through a subtype from ** one of its argument (using sqlite3_result_value()). Most functions ** are not this way, but we don't have a mechanism to distinguish those ** that are from those that are not, so assume they all work this way. ** That means that if one of its arguments is another function and that ** other function is able to return a subtype, then this function is ** able to return a subtype. */ static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ int n; FuncDef *pDef; sqlite3 *db; if( pExpr->op!=TK_FUNCTION ){ return WRC_Prune; } assert( ExprUseXList(pExpr) ); db = pWalker->pParse->db; n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ pWalker->eCode = 1; return WRC_Prune; } return WRC_Continue; } /* ** Return TRUE if expression pExpr is able to return a subtype. ** ** A TRUE return does not guarantee that a subtype will be returned. ** It only indicates that a subtype return is possible. False positives ** are acceptable as they only disable an optimization. False negatives, ** on the other hand, can lead to incorrect answers. */ static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ Walker w; memset(&w, 0, sizeof(w)); w.pParse = pParse; w.xExprCallback = exprNodeCanReturnSubtype; sqlite3WalkExpr(&w, pExpr); return w.eCode; } /* ** The index pIdx is used by a query and contains one or more expressions. ** In other words pIdx is an index on an expression. iIdxCur is the cursor ** number for the index and iDataCur is the cursor number for the corresponding ** table. ** |
︙ | ︙ | |||
5843 5844 5845 5846 5847 5848 5849 | IndexedExpr *p; Table *pTab; assert( pIdx->bHasExpr ); pTab = pIdx->pTable; for(i=0; i<pIdx->nColumn; i++){ Expr *pExpr; int j = pIdx->aiColumn[i]; | < < < < < < | | < < < < < < < | < | | 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 | IndexedExpr *p; Table *pTab; assert( pIdx->bHasExpr ); pTab = pIdx->pTable; for(i=0; i<pIdx->nColumn; i++){ Expr *pExpr; int j = pIdx->aiColumn[i]; if( j==XN_EXPR ){ pExpr = pIdx->aColExpr->a[i].pExpr; }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); }else{ continue; } if( sqlite3ExprIsConstant(0,pExpr) ) continue; if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){ /* Functions that might set a subtype should not be replaced by the ** value taken from an expression index since the index omits the ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */ continue; } p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); if( p==0 ) break; p->pIENext = pParse->pIdxEpr; #ifdef WHERETRACE_ENABLED if( sqlite3WhereTrace & 0x200 ){ sqlite3DebugPrintf("New pParse->pIdxEpr term {%d,%d}\n", iIdxCur, i); if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(pExpr); } #endif p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); p->iDataCur = pTabItem->iCursor; p->iIdxCur = iIdxCur; p->iIdxCol = i; p->bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0; if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){ p->aff = pIdx->zColAff[i]; } #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS p->zIdxName = pIdx->zName; #endif pParse->pIdxEpr = p; |
︙ | ︙ | |||
6049 6050 6051 6052 6053 6054 6055 | /* Variable initialization */ db = pParse->db; memset(&sWLB, 0, sizeof(sWLB)); /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); | | > > > | 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 | /* Variable initialization */ db = pParse->db; memset(&sWLB, 0, sizeof(sWLB)); /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); if( pOrderBy && pOrderBy->nExpr>=BMS ){ pOrderBy = 0; wctrlFlags &= ~WHERE_WANT_DISTINCT; } /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask */ testcase( pTabList->nSrc==BMS ); if( pTabList->nSrc>BMS ){ sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS); |
︙ | ︙ | |||
6131 6132 6133 6134 6135 6136 6137 | if( nTabList==0 ){ if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr; if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0 && OptimizationEnabled(db, SQLITE_DistinctOpt) ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } | > > > | > | 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 | if( nTabList==0 ){ if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr; if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0 && OptimizationEnabled(db, SQLITE_DistinctOpt) ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } if( ALWAYS(pWInfo->pSelect) && (pWInfo->pSelect->selFlags & SF_MultiValue)==0 ){ ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); } }else{ /* Assign a bit from the bitmask to every term in the FROM clause. ** ** The N-th term of the FROM clause is assigned a bitmask of 1<<N. ** ** The rule of the previous sentence ensures that if X is the bitmask for ** a table T, then X-1 is the bitmask for all other tables to the left of T. |
︙ | ︙ | |||
6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 | } #endif WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC); wherePathSolver(pWInfo, 0); if( db->mallocFailed ) goto whereBeginError; if( pWInfo->pOrderBy ){ wherePathSolver(pWInfo, pWInfo->nRowOut+1); if( db->mallocFailed ) goto whereBeginError; } /* TUNING: Assume that a DISTINCT clause on a subquery reduces ** the output size by a factor of 8 (LogEst -30). */ | > | 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 | } #endif WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC); wherePathSolver(pWInfo, 0); if( db->mallocFailed ) goto whereBeginError; if( pWInfo->pOrderBy ){ whereInterstageHeuristic(pWInfo); wherePathSolver(pWInfo, pWInfo->nRowOut+1); if( db->mallocFailed ) goto whereBeginError; } /* TUNING: Assume that a DISTINCT clause on a subquery reduces ** the output size by a factor of 8 (LogEst -30). */ |
︙ | ︙ | |||
6833 6834 6835 6836 6837 6838 6839 | } #endif if( pLevel->iLeftJoin ){ int ws = pLoop->wsFlags; addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); if( (ws & WHERE_IDX_ONLY)==0 ){ | > | > > > > > > > | 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 | } #endif if( pLevel->iLeftJoin ){ int ws = pLoop->wsFlags; addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); if( (ws & WHERE_IDX_ONLY)==0 ){ SrcItem *pSrc = &pTabList->a[pLevel->iFrom]; assert( pLevel->iTabCur==pSrc->iCursor ); if( pSrc->fg.viaCoroutine ){ int m, n; n = pSrc->regResult; assert( pSrc->pTab!=0 ); m = pSrc->pTab->nCol; sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); } sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); } if( (ws & WHERE_INDEXED) || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx) ){ if( ws & WHERE_MULTI_OR ){ Index *pIx = pLevel->u.pCoveringIdx; |
︙ | ︙ | |||
6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 | /* For a co-routine, change all OP_Column references to the table of ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. */ if( pTabItem->fg.viaCoroutine ){ testcase( pParse->db->mallocFailed ); translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, pTabItem->regResult, 0); continue; } /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases | > | 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 | /* For a co-routine, change all OP_Column references to the table of ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. */ if( pTabItem->fg.viaCoroutine ){ testcase( pParse->db->mallocFailed ); assert( pTabItem->regResult>=0 ); translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, pTabItem->regResult, 0); continue; } /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases |
︙ | ︙ |
Changes to src/wherecode.c.
︙ | ︙ | |||
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 | addrNxt, r1, nEq); VdbeCoverage(pParse->pVdbe); } pLevel->regFilter = 0; pLevel->addrBrk = 0; } } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ Bitmask sqlite3WhereCodeOneLoopStart( Parse *pParse, /* Parsing context */ | > > > > > > > > > > > > > > > > > > > > > | 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 | addrNxt, r1, nEq); VdbeCoverage(pParse->pVdbe); } pLevel->regFilter = 0; pLevel->addrBrk = 0; } } /* ** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...) ** operator. Return true if level pLoop is guaranteed to visit only one ** row for each key generated for the index. */ static int whereLoopIsOneRow(WhereLoop *pLoop){ if( pLoop->u.btree.pIndex->onError && pLoop->nSkip==0 && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol ){ int ii; for(ii=0; ii<pLoop->u.btree.nEq; ii++){ if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){ return 0; } } return 1; } return 0; } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ Bitmask sqlite3WhereCodeOneLoopStart( Parse *pParse, /* Parsing context */ |
︙ | ︙ | |||
1411 1412 1413 1414 1415 1416 1417 | */ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0 ); if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); | | | 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 | */ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0 ); if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN match flag")); } /* Compute a safe address to jump to if we discover that the table for ** this loop is empty and can never contribute content. */ for(j=iLevel; j>0; j--){ if( pWInfo->a[j].iLeftJoin ) break; if( pWInfo->a[j].pRJ ) break; |
︙ | ︙ | |||
2080 2081 2082 2083 2084 2085 2086 | /* The following assert() is not a requirement, merely an observation: ** The OR-optimization doesn't work for the right hand table of ** a LEFT JOIN: */ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); } /* Record the instruction used to terminate the loop. */ | | > > | 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 | /* The following assert() is not a requirement, merely an observation: ** The OR-optimization doesn't work for the right hand table of ** a LEFT JOIN: */ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); } /* Record the instruction used to terminate the loop. */ if( (pLoop->wsFlags & WHERE_ONEROW) || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop)) ){ pLevel->op = OP_Noop; }else if( bRev ){ pLevel->op = OP_Prev; }else{ pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; |
︙ | ︙ | |||
2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 | ** ** iLoop==1: Code only expressions that are entirely covered by pIdx. ** iLoop==2: Code remaining expressions that do not contain correlated ** sub-queries. ** iLoop==3: Code all remaining expressions. ** ** An effort is made to skip unnecessary iterations of the loop. */ iLoop = (pIdx ? 1 : 2); do{ int iNext = 0; /* Next value for iLoop */ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; int skipLikeAddr = 0; | > > > > > > | 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 | ** ** iLoop==1: Code only expressions that are entirely covered by pIdx. ** iLoop==2: Code remaining expressions that do not contain correlated ** sub-queries. ** iLoop==3: Code all remaining expressions. ** ** An effort is made to skip unnecessary iterations of the loop. ** ** This optimization of causing simple query restrictions to occur before ** more complex one is call the "push-down" optimization in MySQL. Here ** in SQLite, the name is "MySQL push-down", since there is also another ** totally unrelated optimization called "WHERE-clause push-down". ** Sometimes the qualifier is omitted, resulting in an ambiguity, so beware. */ iLoop = (pIdx ? 1 : 2); do{ int iNext = 0; /* Next value for iLoop */ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; int skipLikeAddr = 0; |
︙ | ︙ | |||
2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 | int k; ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName)); sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, pRJ->regReturn); for(k=0; k<iLevel; k++){ int iIdxCur; mAll |= pWInfo->a[k].pWLoop->maskSelf; sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); iIdxCur = pWInfo->a[k].iIdxCur; if( iIdxCur ){ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); } } if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ | > > > > > > > > > | 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 | int k; ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName)); sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, pRJ->regReturn); for(k=0; k<iLevel; k++){ int iIdxCur; SrcItem *pRight; assert( pWInfo->a[k].pWLoop->iTab == pWInfo->a[k].iFrom ); pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom]; mAll |= pWInfo->a[k].pWLoop->maskSelf; if( pRight->fg.viaCoroutine ){ sqlite3VdbeAddOp3( v, OP_Null, 0, pRight->regResult, pRight->regResult + pRight->pSelect->pEList->nExpr-1 ); } sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); iIdxCur = pWInfo->a[k].iIdxCur; if( iIdxCur ){ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); } } if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ |
︙ | ︙ |
Changes to src/whereexpr.c.
︙ | ︙ | |||
985 986 987 988 989 990 991 | iCur = pFrom->a[j].iCursor; for(pIdx=pFrom->a[j].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->aColExpr==0 ) continue; for(i=0; i<pIdx->nKeyCol; i++){ if( pIdx->aiColumn[i]!=XN_EXPR ) continue; assert( pIdx->bHasExpr ); if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0 | | | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 | iCur = pFrom->a[j].iCursor; for(pIdx=pFrom->a[j].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->aColExpr==0 ) continue; for(i=0; i<pIdx->nKeyCol; i++){ if( pIdx->aiColumn[i]!=XN_EXPR ) continue; assert( pIdx->bHasExpr ); if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0 && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr) ){ aiCurCol[0] = iCur; aiCurCol[1] = XN_EXPR; return 1; } } } |
︙ | ︙ | |||
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 | /* If this term has child terms, then they are also part of the ** pWC->a[] array. So this term can be ignored, as a LIMIT clause ** will only be added if each of the child terms passes the ** (leftCursor==iCsr) test below. */ continue; } if( pWC->a[ii].leftCursor!=iCsr ) return; } /* Check condition (5). Return early if it is not met. */ if( pOrderBy ){ for(ii=0; ii<pOrderBy->nExpr; ii++){ Expr *pExpr = pOrderBy->a[ii].pExpr; if( pExpr->op!=TK_COLUMN ) return; if( pExpr->iTable!=iCsr ) return; if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return; } } /* All conditions are met. Add the terms to the where-clause object. */ assert( p->pLimit->op==TK_LIMIT ); | > < < | > > > > | 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 | /* If this term has child terms, then they are also part of the ** pWC->a[] array. So this term can be ignored, as a LIMIT clause ** will only be added if each of the child terms passes the ** (leftCursor==iCsr) test below. */ continue; } if( pWC->a[ii].leftCursor!=iCsr ) return; if( pWC->a[ii].prereqRight!=0 ) return; } /* Check condition (5). Return early if it is not met. */ if( pOrderBy ){ for(ii=0; ii<pOrderBy->nExpr; ii++){ Expr *pExpr = pOrderBy->a[ii].pExpr; if( pExpr->op!=TK_COLUMN ) return; if( pExpr->iTable!=iCsr ) return; if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return; } } /* All conditions are met. Add the terms to the where-clause object. */ assert( p->pLimit->op==TK_LIMIT ); if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){ whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); } if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){ whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); } } } /* ** Initialize a preallocated WhereClause structure. */ void sqlite3WhereClauseInit( |
︙ | ︙ |
Changes to src/window.c.
︙ | ︙ | |||
1160 1161 1162 1163 1164 1165 1166 | ** The argument expression is an PRECEDING or FOLLOWING offset. The ** value should be a non-negative integer. If the value is not a ** constant, change it to NULL. The fact that it is then a non-negative ** integer will be caught later. But it is important not to leave ** variable values in the expression tree. */ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ | | | 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 | ** The argument expression is an PRECEDING or FOLLOWING offset. The ** value should be a non-negative integer. If the value is not a ** constant, change it to NULL. The fact that it is then a non-negative ** integer will be caught later. But it is important not to leave ** variable values in the expression tree. */ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ if( 0==sqlite3ExprIsConstant(0,pExpr) ){ if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr); sqlite3ExprDelete(pParse->db, pExpr); pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0); } return pExpr; } |
︙ | ︙ |
Changes to test/alter2.test.
︙ | ︙ | |||
367 368 369 370 371 372 373 | } {1 integer 123 text 123 integer} do_test alter2-7.5 { set sql {CREATE TABLE t1(a, b DEFAULT -123.0, c VARCHAR(10) default 5)} alter_table t1 $sql 3 execsql { SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1; } | | | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | } {1 integer 123 text 123 integer} do_test alter2-7.5 { set sql {CREATE TABLE t1(a, b DEFAULT -123.0, c VARCHAR(10) default 5)} alter_table t1 $sql 3 execsql { SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1; } } {1 integer -123.0 real 5 text} #----------------------------------------------------------------------- # Test that UPDATE trigger tables work with default values, and that when # a row is updated the default values are correctly transfered to the # new row. # ifcapable trigger { |
︙ | ︙ | |||
393 394 395 396 397 398 399 | } {} } do_test alter2-8.2 { execsql { UPDATE t1 SET c = 10 WHERE a = 1; SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1; } | | | | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | } {} } do_test alter2-8.2 { execsql { UPDATE t1 SET c = 10 WHERE a = 1; SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1; } } {1 integer -123.0 real 10 text} ifcapable trigger { do_test alter2-8.3 { set ::val } {-123.0 real 5 text -123.0 real 10 text} } #----------------------------------------------------------------------- # Test that DELETE trigger tables work with default values, and that when # a row is updated the default values are correctly transfered to the # new row. # |
︙ | ︙ | |||
421 422 423 424 425 426 427 | list } {} do_test alter2-9.2 { execsql { DELETE FROM t1 WHERE a = 2; } set ::val | | | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | list } {} do_test alter2-9.2 { execsql { DELETE FROM t1 WHERE a = 2; } set ::val } {-123.0 real 5 text} } #----------------------------------------------------------------------- # Test creating an index on a column added with a default value. # ifcapable bloblit { do_test alter2-10.1 { |
︙ | ︙ |
Changes to test/altertab2.test.
︙ | ︙ | |||
356 357 358 359 360 361 362 363 | do_catchsql_test 8.6 { CREATE TABLE t0(c0); CREATE INDEX i0 ON t0(likelihood(1,2) AND 0); ALTER TABLE t0 RENAME TO t1; SELECT sql FROM sqlite_master WHERE name='i0'; } {1 {error in index i0: second argument to likelihood() must be a constant between 0.0 and 1.0}} finish_test | > > > > > > > > > > > > > > > > > > > > > > | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | do_catchsql_test 8.6 { CREATE TABLE t0(c0); CREATE INDEX i0 ON t0(likelihood(1,2) AND 0); ALTER TABLE t0 RENAME TO t1; SELECT sql FROM sqlite_master WHERE name='i0'; } {1 {error in index i0: second argument to likelihood() must be a constant between 0.0 and 1.0}} reset_db do_execsql_test 9.0 { CREATE TABLE t1(a,b,c,d); CREATE TABLE t2(a,b,c,d,x); CREATE TRIGGER AFTER INSERT ON t2 BEGIN SELECT group_conct( 123 ORDER BY ( SELECT 1 FROM ( VALUES(a, 'b'), ('c') ) )) FROM t1; END; } do_catchsql_test 9.1 { ALTER TABLE t2 RENAME TO newname; } {1 {error in trigger AFTER: all VALUES must have the same number of terms}} finish_test |
Changes to test/altertab3.test.
︙ | ︙ | |||
731 732 733 734 735 736 737 738 739 | END} {CREATE TRIGGER tr2 AFTER DELETE ON "t3" BEGIN SELECT z, y FROM ( SELECT "t3".* FROM "t3" ); END} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 | END} {CREATE TRIGGER tr2 AFTER DELETE ON "t3" BEGIN SELECT z, y FROM ( SELECT "t3".* FROM "t3" ); END} } #------------------------------------------------------------------------- reset_db do_execsql_test 30.0 { CREATE TABLE t1(a, b); CREATE VIEW v1 AS SELECT ( VALUES(a), (b) ) FROM ( SELECT a, b FROM t1 ) ; } do_execsql_test 30.1 { SELECT * FROM v1 } do_execsql_test 30.1 { ALTER TABLE t1 RENAME TO t2; } do_execsql_test 30.2 { SELECT sql FROM sqlite_schema WHERE type='view' } { {CREATE VIEW v1 AS SELECT ( VALUES(a), (b) ) FROM ( SELECT a, b FROM "t2" )} } #------------------------------------------------------------------------- reset_db do_execsql_test 31.0 { CREATE TABLE t1(ii INTEGER PRIMARY KEY, tt INTEGER, rr REAL); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000 ) INSERT INTO t1 SELECT NULL, i, 5.0 FROM s; } do_test 31.1 { set pg [db one {PRAGMA page_count}] execsql { ALTER TABLE t1 DROP COLUMN tt; } set pg2 [db one {PRAGMA page_count}] expr $pg==$pg2 } {1} do_execsql_test 31.2 { SELECT rr FROM t1 LIMIT 1 } {5.0} finish_test |
Changes to test/avfs.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2021-03-06 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests for the appendvfs extension. # # Tests performed: # avfs-1.0. Test that an appendvfs DB can be added to an empty (ZLF) file. # avfs-1.1. Test that the DB can be read with correct content upon reopen. # avfs-1.2. Test that an appendvfs DB can be added to a simple text file. | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2021-03-06 # # 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. # #*********************************************************************** # TESTRUNNER: shell # # This file implements tests for the appendvfs extension. # # Tests performed: # avfs-1.0. Test that an appendvfs DB can be added to an empty (ZLF) file. # avfs-1.1. Test that the DB can be read with correct content upon reopen. # avfs-1.2. Test that an appendvfs DB can be added to a simple text file. |
︙ | ︙ |
Changes to test/bestindex8.test.
︙ | ︙ | |||
154 155 156 157 158 159 160 | set ::lFilterArgs } {10} do_test 2.2 { set ::lFilterArgs [list] execsql { SELECT * FROM vt1 LIMIT 5 OFFSET 50 } set ::lFilterArgs | | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | set ::lFilterArgs } {10} do_test 2.2 { set ::lFilterArgs [list] execsql { SELECT * FROM vt1 LIMIT 5 OFFSET 50 } set ::lFilterArgs } {{50 5}} do_test 2.3 { set ::lFilterArgs [list] execsql { SELECT * FROM vt1 ORDER BY a, b LIMIT 1 OFFSET 1 } set ::lFilterArgs } {{1 1}} |
︙ | ︙ |
Added test/bestindexC.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | # 2024-04-26 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix bestindexC ifcapable !vtab { finish_test return } register_tcl_module db proc vtab_command {lVal method args} { switch -- $method { xConnect { return "CREATE TABLE t1(a)" } xBestIndex { set hdl [lindex $args 0] set clist [$hdl constraints] set orderby [$hdl orderby] set idxstr [list] set res [list] set idx 0 foreach c $clist { array set a $c if {$a(usable)==0} continue if {$a(op)=="limit" && ![info exists ::do_not_use_limit]} { lappend idxstr limit lappend res omit $idx } if {$a(op)=="offset" && ![info exists ::do_not_use_offset]} { lappend idxstr offset lappend res omit $idx } incr idx } return "cost 1000000 rows 1000000 idxnum 0 idxstr {$idxstr} $res" } xFilter { set idxstr [lindex $args 1] set LIMIT "" foreach a $idxstr b [lindex $args 2] { set x($a) $b } if {![info exists x(limit)]} { set x(limit) -1 } if {![info exists x(offset)]} { set x(offset) -1 } set LIMIT " LIMIT $x(limit) OFFSET $x(offset)" set idx 1 foreach v $lVal { lappend lRow "($idx, '$v')" incr idx } return [list sql " SELECT * FROM ( VALUES [join $lRow ,]) $LIMIT "] } } return {} } do_execsql_test 1.0 { CREATE VIRTUAL TABLE x1 USING tcl(vtab_command "a b c d e f"); CREATE VIRTUAL TABLE x2 USING tcl(vtab_command "A B C D E F a b"); } {} do_execsql_test 1.1 { CREATE TEMP TABLE t_unionall AS SELECT * FROM x1 UNION ALL SELECT * FROM x2; CREATE TEMP TABLE t_intersect AS SELECT * FROM x1 INTERSECT SELECT * FROM x2; CREATE TEMP TABLE t_union AS SELECT * FROM x1 UNION SELECT * FROM x2; CREATE TEMP TABLE t_except AS SELECT * FROM x1 EXCEPT SELECT * FROM x2; } foreach {tn limit} { 1 "LIMIT 8" 2 "LIMIT 4" 3 "LIMIT 4 OFFSET 2" 4 "LIMIT 8 OFFSET 4" } { foreach {op tbl} { "UNION ALL" t_unionall "UNION" t_union "INTERSECT" t_intersect "EXCEPT" t_except } { set expect [execsql "SELECT * FROM $tbl $limit"] do_execsql_test 1.2.$tbl.$tn "SELECT * FROM ( SELECT * FROM x1 $op SELECT * FROM x2 ) $limit" $expect } } #------------------------------------------------------------------------- reset_db register_tcl_module db do_execsql_test 2.0 { CREATE VIRTUAL TABLE x1 USING tcl(vtab_command "a b c d e f"); CREATE VIRTUAL TABLE x2 USING tcl(vtab_command "a b e f"); } {} do_execsql_test 2.1 { SELECT * FROM x1 EXCEPT SELECT * FROM x2 LIMIT 3 } {c d} #------------------------------------------------------------------------- reset_db register_tcl_module db do_execsql_test 3.0 { CREATE VIRTUAL TABLE y1 USING tcl(vtab_command "1 2 3 4 5 6 7 8 9 10"); } {} do_execsql_test 3.1 { SELECT * FROM y1 WHERE a = COALESCE('8', a) LIMIT 3 } {8} do_execsql_test 3.2 { SELECT * FROM y1 WHERE a = '2' LIMIT 3 } {2} load_static_extension db series do_execsql_test 3.3 { SELECT * FROM generate_series(1, 5) WHERE value = (value & 14) LIMIT 3 } {2 4} do_execsql_test 3.4 { SELECT value FROM generate_series(1,10) WHERE value>2 LIMIT 4 OFFSET 1; } {4 5 6 7} set ::do_not_use_limit 1 do_execsql_test 3.5 { SELECT * FROM y1 LIMIT 5 OFFSET 3 } {4 5 6 7 8} unset ::do_not_use_limit set ::do_not_use_offset 1 do_execsql_test 3.6 { SELECT * FROM y1 LIMIT 5 OFFSET 3 } {4 5 6 7 8} unset ::do_not_use_offset #------------------------------------------------------------------------- reset_db proc vtab_command {lVal method args} { switch -- $method { xConnect { error "not happy!" } } return {} } register_tcl_module db do_catchsql_test 4.0 { CREATE VIRTUAL TABLE y1 USING tcl(vtab_command 1); } {1 {not happy!}} do_test 4.1 { sqlite3_errcode db } SQLITE_ERROR proc vtab_command {lVal method args} { switch -- $method { xConnect { return $lVal } } return {} } do_catchsql_test 4.2 { CREATE VIRTUAL TABLE y1 USING tcl(vtab_command "PRAGMA page_size=1024"); } {1 {declare_vtab: syntax error}} do_catchsql_test 4.3 { CREATE VIRTUAL TABLE y1 USING tcl(vtab_command "CREATE TABLE x1("); } {1 {declare_vtab: incomplete input}} do_catchsql_test 4.4 { CREATE VIRTUAL TABLE y1 USING tcl(vtab_command "CREATE TABLE x1(insert)"); } {1 {declare_vtab: near "insert": syntax error}} finish_test |
Changes to test/busy.test.
︙ | ︙ | |||
102 103 104 105 106 107 108 | SELECT count(*) FROM sqlite_master; } db2 } {6} proc busy_handler {n} { return 1 } do_test 3.5 { catchsql { PRAGMA optimize } | | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | SELECT count(*) FROM sqlite_master; } db2 } {6} proc busy_handler {n} { return 1 } do_test 3.5 { catchsql { PRAGMA optimize } } {1 {database is locked}} do_test 3.6 { execsql { COMMIT } db2 execsql { WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 ) |
︙ | ︙ |
Changes to test/capi3.test.
︙ | ︙ | |||
177 178 179 180 181 182 183 | } {SQLITE_CANTOPEN 1} do_test capi3-3.4 { sqlite3_errmsg $db2 } {unable to open database file} do_test capi3-3.5 { list [sqlite3_system_errno $db2] [sqlite3_close $db2] } [list $::capi3_errno SQLITE_OK] | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | } {SQLITE_CANTOPEN 1} do_test capi3-3.4 { sqlite3_errmsg $db2 } {unable to open database file} do_test capi3-3.5 { list [sqlite3_system_errno $db2] [sqlite3_close $db2] } [list $::capi3_errno SQLITE_OK] if {[clang_sanitize_address]==0 && 0} { do_test capi3-3.6.1-misuse { sqlite3_close $db2 } {SQLITE_MISUSE} do_test capi3-3.6.2-misuse { sqlite3_errmsg $db2 } {bad parameter or other API misuse} ifcapable {utf16} { |
︙ | ︙ |
Added test/cksumvfs.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # 2024 March 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix cksumvfs sqlite3_register_cksumvfs db close sqlite3 db test.db file_control_reservebytes db 8 set text [db one "SELECT hex(randomblob(5000))"] do_execsql_test 1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(1, $text); } do_execsql_test 1.1 { SELECT * FROM t1; } [list 1 $text] finish_test |
Changes to test/corruptC.test.
︙ | ︙ | |||
94 95 96 97 98 99 100 | # insert corrupt byte(s) hexio_write test.db 2053 [format %02x 0x04] sqlite3 db test.db catchsql {PRAGMA integrity_check} } {0 {{*** in database main *** | | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | # insert corrupt byte(s) hexio_write test.db 2053 [format %02x 0x04] sqlite3 db test.db catchsql {PRAGMA integrity_check} } {0 {{*** in database main *** Tree 3 page 3: free space corruption} {wrong # of entries in index t1i1}}} # test that a corrupt content offset size is handled (seed 5649) # # Update 2016-12-27: As of check-in [0b86fbca66] "In sqlite3BtreeInsert() when # replacing a re-existing row, try to overwrite the cell directly rather than # deallocate and reallocate the cell" on 2016-12-09, this test case no longer # detects the offset size problem during the UPDATE. We have to run a subsequent |
︙ | ︙ |
Changes to test/corruptD.test.
︙ | ︙ | |||
109 110 111 112 113 114 115 | # containing the offset of the first free block in a page. # do_test corruptD-1.1.1 { incr_change_counter hexio_write test.db [expr 1024+1] FFFF catchsql { PRAGMA quick_check } } {0 {{*** in database main *** | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | # containing the offset of the first free block in a page. # do_test corruptD-1.1.1 { incr_change_counter hexio_write test.db [expr 1024+1] FFFF catchsql { PRAGMA quick_check } } {0 {{*** in database main *** Tree 2 page 2: free space corruption} {wrong # of entries in index i1}}} do_test corruptD-1.1.2 { incr_change_counter hexio_write test.db [expr 1024+1] [hexio_render_int32 1021] catchsql { SELECT * FROM t1 ORDER BY rowid } } {1 {database disk image is malformed}} #------------------------------------------------------------------------- |
︙ | ︙ |
Changes to test/corruptL.test.
︙ | ︙ | |||
1500 1501 1502 1503 1504 1505 1506 1507 1508 | WHERE name='i1'; } db close sqlite3 db test.db do_catchsql_test 19.4 { PRAGMA integrity_check; } {1 {database disk image is malformed}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 | WHERE name='i1'; } db close sqlite3 db test.db do_catchsql_test 19.4 { PRAGMA integrity_check; } {1 {database disk image is malformed}} #------------------------------------------------------------------------- reset_db do_test 18.0 { sqlite3 db {} db deserialize [decode_hexdb { .open --hexdb | size 20480 pagesize 4096 filename crash-a4150b729051e4.db | page 1 offset 0 | 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. | 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 05 .....@ ........ | 32: 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 04 ................ | 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................ | 64: 00 00 00 00 00 00 00 00 00 00 ff f0 00 00 00 00 ................ | 96: 00 00 00 00 0d 00 00 00 04 0e e5 00 0f c2 0f 75 ...............u | 112: 0f 19 0e e5 00 00 00 00 00 00 00 01 00 00 00 00 ................ | 3808: 00 00 00 00 00 32 04 06 17 17 11 01 4b 69 6e 64 .....2......Kind | 3824: 65 78 74 31 61 62 63 74 31 05 43 52 45 41 54 45 ext1abct1.CREATE | 3840: 20 49 4e 44 45 58 20 74 31 61 62 63 20 4f 4e 20 INDEX t1abc ON | 3856: 74 31 28 61 2c 62 2c 63 29 5a 03 06 17 25 25 01 t1(a,b,c)Z...%%. | 3872: 79 74 61 62 6c 65 73 71 6c 69 74 65 5f 73 74 61 ytablesqlite_sta | 3888: 74 34 73 71 6c 69 74 65 5f 73 74 61 74 34 04 43 t4sqlite_stat4.C | 3904: 52 45 41 54 45 20 54 41 42 4c 45 20 73 71 6c 69 REATE TABLE sqli | 3920: 74 65 5f 73 74 61 74 34 28 74 62 6c 2c 69 64 78 te_stat4(tbl,idx | 3936: 2c 6e 65 71 2c 6e 6c 74 2c 6e 64 6c 74 2c 73 61 ,neq,nlt,ndlt,sa | 3952: 6d 70 6c 65 29 4b 02 06 17 25 25 01 5b 74 61 62 mple)K...%%.[tab | 3968: 6c 65 73 71 6c 69 74 65 5f 73 74 61 74 31 73 71 lesqlite_stat1sq | 3984: 6c 69 74 65 5f 73 74 61 74 31 03 43 52 45 41 54 lite_stat1.CREAT | 4000: 45 20 54 41 42 4c 45 20 73 71 6c 69 74 65 5f 73 E TABLE sqlite_s | 4016: 74 61 74 31 28 74 62 6c 2c 69 64 78 2c 73 74 61 tat1(tbl,idx,sta | 4032: 74 29 3c 01 06 17 11 11 01 65 74 61 62 6c 65 74 t)<......etablet | 4048: 31 74 31 02 43 52 45 41 54 45 20 54 41 42 4c 45 1t1.CREATE TABLE | 4064: 20 74 31 28 61 20 54 45 58 54 2c 20 62 20 49 4e t1(a TEXT, b IN | 4080: 54 2c 20 63 20 49 4e 54 2c 20 64 20 49 4e 54 29 T, c INT, d INT) | page 2 offset 4096 | 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ | 4000: 0b 07 05 13 01 01 01 62 63 64 64 06 0b 0c 06 05 .......bcdd..... | 4016: 13 02 01 01 64 65 66 01 59 09 0a 0c 05 05 13 03 ....def.Y....... | 4032: 01 01 64 65 66 02 6f 08 09 0c 04 05 13 02 01 01 ..def.o......... | 4048: 61 62 63 01 59 07 08 0c 03 05 13 02 01 01 87 62 abc.Y..........b | 4064: 63 00 ea 06 07 0c 02 05 13 02 01 01 61 62 63 00 c...........abc. | 4080: ea 06 06 0b 01 05 13 01 01 01 61 62 63 7b 04 04 ..........abc... | page 3 offset 8192 | 0: 0d 00 00 00 01 0f e0 00 0f e1 00 00 00 00 00 00 ................ | 4064: 00 1d 01 04 11 17 31 74 31 74 31 61 62 63 31 30 ......1t1t1abc10 | 4080: 30 30 30 20 35 30 30 30 20 32 30 30 30 20 31 30 000 5000 2000 10 | page 4 offset 12288 | 0: 0d 00 00 00 07 0e ac 00 0f d1 0f a0 0f 6f 0f 3e .............o.> | 16: 0f 0e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ | 3744: 00 00 00 00 00 00 00 00 00 00 00 00 2f 07 07 11 ............/... | 3760: 17 1b 1b 1b 24 74 31 74 31 61 62 63 32 20 31 20 ....$t1t1abc2 1 | 3776: 31 20 31 35 20 36 20 36 20 36 32 20 35 20 36 20 1 15 6 6 62 5 6 | 3792: 36 05 13 02 01 01 64 65 66 02 37 08 05 2f 06 07 6.....def.7../.. | 3808: 11 17 1b 1b 1b 24 74 41 74 31 61 62 63 32 20 31 .....$tAt1abc2 1 | 3824: 20 31 20 31 35 20 35 20 55 20 35 32 20 34 20 35 1 15 5 U 52 4 5 | 3840: 20 35 05 13 02 01 01 64 65 66 01 59 09 06 2e 05 5.....def.Y.... | 3856: 07 11 17 1b 1b 1b 22 74 31 74 31 61 62 63 31 20 .......t1t1abc1 | 3872: 31 20 31 20 31 34 20 34 20 34 20 34 31 20 33 20 1 1 14 4 4 41 3 | 3888: 34 20 34 08 b3 cd f0 f1 62 63 64 64 06 07 2f 05 4 4.....bcdd../. | 3904: 07 11 17 1b 1b 1b 24 74 37 74 31 61 62 63 34 20 ......$t7t1abc4 | 3920: 31 20 31 20 31 30 20 33 20 33 20 33 30 20 32 20 1 1 10 3 3 30 2 | 3936: 33 20 33 05 13 02 01 01 61 62 63 01 59 07 04 2f 3 3.....abc.Y../ | 3952: 03 07 11 17 1b 1b 1b 24 74 31 74 31 61 62 63 34 .......$t1t1abc4 | 3968: 20 32 20 31 20 31 30 20 31 20 32 20 32 30 20 31 2 1 10 1 2 20 1 | 3984: 20 32 20 32 05 13 02 01 01 61 62 63 00 ea 06 03 2 2.....abc.... | 4000: 2f 02 07 11 17 1b 1b 1b 24 74 31 74 31 61 62 63 /.......$t1t1abc | 4016: 34 20 32 20 31 20 31 30 20 31 20 31 20 31 30 20 4 2 1 10 1 1 10 | 4032: 31 20 31 20 31 05 13 02 01 01 61 62 63 00 ea 05 1 1 1.....abc... | 4048: 02 2d 01 07 11 17 1b 1b 1b 20 74 31 74 31 61 62 .-....... t1t1ab | 4064: 63 34 20 31 20 31 20 31 30 20 30 20 30 1f 30 30 c4 1 1 10 0 0.00 | 4080: 20 30 20 30 20 30 05 13 01 01 09 61 62 63 7b 04 0 0 0.....abc.. | page 5 offset 16384 | 0: 0a 00 00 00 07 0f a8 00 0f f5 00 00 00 00 00 00 ................ | 4000: 00 00 00 00 00 00 00 00 0c 05 13 02 01 01 64 65 ..............de | 4016: 66 02 37 08 05 0c 05 13 02 01 01 64 65 66 01 59 f.7........def.Y | 4032: 09 06 0b 05 12 01 01 01 62 63 64 64 06 07 0c 05 ........bcdd.... | 4048: 13 02 01 01 61 62 63 01 59 07 01 2c 05 13 02 01 ....abc.Y..,.... | 4064: 01 61 62 63 00 ea 06 03 0c 05 13 02 01 01 61 62 .abc..........ab | 4080: 63 00 ea 05 00 00 00 00 00 00 00 00 00 00 00 00 c............... | end crash-a4150b729051e4.db }]} {} do_catchsql_test 18.1 { SELECT a FROM t1 WHERE b GLOB b AND b GLOB '0^x]␅6␚xz]'; } {1 {database disk image is malformed}} finish_test |
Changes to test/cost.test.
︙ | ︙ | |||
99 100 101 102 103 104 105 | } do_eqp_test 5.2 { SELECT * FROM t2 ORDER BY x, y; } { QUERY PLAN |--SCAN t2 USING INDEX t2i1 | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | } do_eqp_test 5.2 { SELECT * FROM t2 ORDER BY x, y; } { QUERY PLAN |--SCAN t2 USING INDEX t2i1 `--USE TEMP B-TREE FOR LAST TERM OF ORDER BY } do_eqp_test 5.3 { SELECT * FROM t2 WHERE x BETWEEN ? AND ? ORDER BY rowid; } { QUERY PLAN |--SEARCH t2 USING INDEX t2i1 (x>? AND x<?) |
︙ | ︙ |
Changes to test/date.test.
︙ | ︙ | |||
205 206 207 208 209 210 211 | } return $x } datetest 3.16 "strftime('[repeat 200 %Y]','2003-10-31')" [repeat 200 2003] datetest 3.17 "strftime('[repeat 200 abc%m123]','2003-10-31')" \ [repeat 200 abc10123] | | | | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | } return $x } datetest 3.16 "strftime('[repeat 200 %Y]','2003-10-31')" [repeat 200 2003] datetest 3.17 "strftime('[repeat 200 abc%m123]','2003-10-31')" \ [repeat 200 abc10123] foreach c {a b c h i n o q r t v x y z A B C D E K L N O Q Z 0 1 2 3 4 5 6 6 7 9 _} { datetest 3.18.$c "strftime('%$c','2003-10-31')" NULL } datetest 3.20 {strftime('%e','2023-08-09')} { 9} datetest 3.21 {strftime('%F %T','2023-08-09 01:23')} {2023-08-09 01:23:00} datetest 3.22 {strftime('%k','2023-08-09 04:59:59')} { 4} datetest 3.23 {strftime('%I%P','2023-08-09 11:59:59')} {11am} |
︙ | ︙ | |||
258 259 260 261 262 263 264 | datetest 5.13 {datetime('1994-04-16 14:00:00Zulu')} NULL datetest 5.14 {datetime('1994-04-16 14:00:00Z +05:00')} NULL datetest 5.15 {datetime('1994-04-16 14:00:00 +05:00 Z')} NULL # localtime->utc and utc->localtime conversions. # # Use SQLITE_TESTCTRL_LOCALTIME_FAULT=2 to set an alternative localtime_r() | | | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | datetest 5.13 {datetime('1994-04-16 14:00:00Zulu')} NULL datetest 5.14 {datetime('1994-04-16 14:00:00Z +05:00')} NULL datetest 5.15 {datetime('1994-04-16 14:00:00 +05:00 Z')} NULL # localtime->utc and utc->localtime conversions. # # Use SQLITE_TESTCTRL_LOCALTIME_FAULT=2 to set an alternative localtime_r() # implementation that is not locale-dependent. The testing localtime_r() # operates as follows: # # (1) Localtime is 30 minutes earlier than (west of) UTC on # even days (counting from 1970-01-01) # # (2) Localtime is 30 minutes later than (east of) UTC on odd days. # |
︙ | ︙ | |||
316 317 318 319 320 321 322 323 324 325 326 327 328 329 | # Modifiers work for dates that are way out of band for localtime_r() # local_to_utc 6.21 {1800-10-29 12:00:00} {1800-10-29 12:30:00} utc_to_local 6.22 {1800-10-29 12:30:00} {1800-10-29 12:00:00} local_to_utc 6.23 {3000-10-30 12:00:00} {3000-10-30 11:30:00} utc_to_local 6.24 {3000-10-30 11:30:00} {3000-10-30 12:00:00} # Restore the use of the OS localtime_r() before going on... sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 0 # Date-time functions that contain NULL arguments return a NULL # result. # | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | # Modifiers work for dates that are way out of band for localtime_r() # local_to_utc 6.21 {1800-10-29 12:00:00} {1800-10-29 12:30:00} utc_to_local 6.22 {1800-10-29 12:30:00} {1800-10-29 12:00:00} local_to_utc 6.23 {3000-10-30 12:00:00} {3000-10-30 11:30:00} utc_to_local 6.24 {3000-10-30 11:30:00} {3000-10-30 12:00:00} # If the time is specified to be ZULU, or if it has an explicit # timezone extension, then the time will already be UTC and subsequent # 'utc' modifiers are no-ops. # do_execsql_test date-6.25 { SELECT datetime('2000-10-29 12:00Z','utc','utc'); } {{2000-10-29 12:00:00}} do_execsql_test date-6.26 { SELECT datetime('2000-10-29 12:00:00+05:00'); } {{2000-10-29 07:00:00}} do_execsql_test date-6.27 { SELECT datetime('2000-10-29 12:00:00+05:00', 'utc'); } {{2000-10-29 07:00:00}} # Multiple back-and-forth UTC to LOCAL to UTC... do_execsql_test date-6.28 { SELECT datetime('2000-10-29 12:00:00Z', 'localtime'); } {{2000-10-29 12:30:00}} do_execsql_test date-6.29 { SELECT datetime('2000-10-29 12:00:00Z', 'utc', 'localtime'); } {{2000-10-29 12:30:00}} do_execsql_test date-6.30 { SELECT datetime('2000-10-29 12:00:00Z', 'utc', 'localtime', 'utc'); } {{2000-10-29 12:00:00}} do_execsql_test date-6.31 { SELECT datetime('2000-10-29 12:00:00Z', 'utc','localtime','utc','localtime'); } {{2000-10-29 12:30:00}} do_execsql_test date-6.32 { SELECT datetime('2000-10-29 12:00:00Z', 'localtime','localtime'); } {{2000-10-29 12:30:00}} # Restore the use of the OS localtime_r() before going on... sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 0 # Date-time functions that contain NULL arguments return a NULL # result. # |
︙ | ︙ | |||
569 570 571 572 573 574 575 576 | # 2023-04 The 'subsecond' (or 'subsec') modifier alters resolutions # to at least milliseconds. Added for release 3.42.0 . datetest 18.2 {unixepoch('1970-01-01T00:00:00.1', 'subsec')} {0.1} datetest 18.3 {unixepoch('1970-01-01T00:00:00.2', 'subsecond')} {0.2} datetest 18.4 {julianday('-4713-11-24 13:40:48.864', 'subsec')} {0.07001} datetest 18.5 {typeof(unixepoch('now', 'subsecond'))} {real} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | # 2023-04 The 'subsecond' (or 'subsec') modifier alters resolutions # to at least milliseconds. Added for release 3.42.0 . datetest 18.2 {unixepoch('1970-01-01T00:00:00.1', 'subsec')} {0.1} datetest 18.3 {unixepoch('1970-01-01T00:00:00.2', 'subsecond')} {0.2} datetest 18.4 {julianday('-4713-11-24 13:40:48.864', 'subsec')} {0.07001} datetest 18.5 {typeof(unixepoch('now', 'subsecond'))} {real} # 2024-03-03 the 'ceiling' and 'floor' operators. # datetest 19.1 {date('2000-01-31','floor')} {2000-01-31} datetest 19.2a {date('2000-02-31','floor')} {2000-02-29} datetest 19.2b {date('1999-02-31','floor')} {1999-02-28} datetest 19.2c {date('1900-02-31','floor')} {1900-02-28} datetest 19.3 {date('2000-03-31','floor')} {2000-03-31} datetest 19.4 {date('2000-04-31','floor')} {2000-04-30} datetest 19.5 {date('2000-05-31','floor')} {2000-05-31} datetest 19.6 {date('2000-06-31','floor')} {2000-06-30} datetest 19.7 {date('2000-07-31','floor')} {2000-07-31} datetest 19.8 {date('2000-08-31','floor')} {2000-08-31} datetest 19.9 {date('2000-09-31','floor')} {2000-09-30} datetest 19.10 {date('2000-10-31','floor')} {2000-10-31} datetest 19.11 {date('2000-11-31','floor')} {2000-11-30} datetest 19.12 {date('2000-12-31','floor')} {2000-12-31} datetest 19.21 {date('2000-01-31','ceiling')} {2000-01-31} datetest 19.22a {date('2000-02-31','ceiling')} {2000-03-02} datetest 19.22b {date('1999-02-31','ceiling')} {1999-03-03} datetest 19.22c {date('1900-02-31','ceiling')} {1900-03-03} datetest 19.23 {date('2000-03-31','ceiling')} {2000-03-31} datetest 19.24 {date('2000-04-31','ceiling')} {2000-05-01} datetest 19.25 {date('2000-05-31','ceiling')} {2000-05-31} datetest 19.26 {date('2000-06-31','ceiling')} {2000-07-01} datetest 19.27 {date('2000-07-31','ceiling')} {2000-07-31} datetest 19.28 {date('2000-08-31','ceiling')} {2000-08-31} datetest 19.29 {date('2000-09-31','ceiling')} {2000-10-01} datetest 19.30 {date('2000-10-31','ceiling')} {2000-10-31} datetest 19.31 {date('2000-11-31','ceiling')} {2000-12-01} datetest 19.32 {date('2000-12-31','ceiling')} {2000-12-31} datetest 19.40 {date('2024-01-31','+1 month','ceiling')} {2024-03-02} datetest 19.41 {date('2024-01-31','+1 month','floor')} {2024-02-29} datetest 19.42 {date('2023-01-31','+1 month','ceiling')} {2023-03-03} datetest 19.43 {date('2023-01-31','+1 month','floor')} {2023-02-28} datetest 19.44 {date('2024-02-29','+1 year','ceiling')} {2025-03-01} datetest 19.45 {date('2024-02-29','+1 year','floor')} {2025-02-28} datetest 19.46 {date('2024-02-29','-110 years','ceiling')} {1914-03-01} datetest 19.47 {date('2024-02-29','-110 years','floor')} {1914-02-28} datetest 19.48 {date('2024-02-29','-0110-00-00','floor')} {1914-02-28} datetest 19.49 {date('2024-02-29','-0110-00-00','ceiling')} {1914-03-01} datetest 19.50 {date('2000-08-31','+0023-06-00','floor')} {2024-02-29} datetest 19.51 {date('2000-08-31','+0022-06-00','floor')} {2023-02-28} datetest 19.52 {date('2000-08-31','+0023-06-00','ceiling')} {2024-03-02} datetest 19.53 {date('2000-08-31','+0022-06-00','ceiling')} {2023-03-03} finish_test |
Changes to test/date4.test.
︙ | ︙ | |||
20 21 22 23 24 25 26 | # ifcapable {!datetime} { finish_test return } if {$tcl_platform(os)=="Linux"} { | | | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | # ifcapable {!datetime} { finish_test return } if {$tcl_platform(os)=="Linux"} { set FMT {%d,%e,%F,%H,%k,%I,%l,%j,%m,%M,%u,%w,%W,%Y,%%,%P,%p,%U,%V,%G,%g} } else { set FMT {%d,%e,%F,%H,%I,%j,%p,%R,%u,%w,%W,%%} } for {set i 0} {$i<=24858} {incr i} { set TS [expr {$i*86390}] do_execsql_test date4-$i { SELECT strftime($::FMT,$::TS,'unixepoch'); } [list [strftime $FMT $TS]] } finish_test |
Changes to test/default.test.
︙ | ︙ | |||
131 132 133 134 135 136 137 | # 2020-03-09 out-of-bounds memory access discovered by "Eternal Sakura" # and reported to chromium. # reset_db do_catchsql_test default-5.1 { CREATE TABLE t1 (a,b DEFAULT(random() NOTNULL IN (RAISE(IGNORE),2,3))); INSERT INTO t1(a) VALUES(1); | | > > > > | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | # 2020-03-09 out-of-bounds memory access discovered by "Eternal Sakura" # and reported to chromium. # reset_db do_catchsql_test default-5.1 { CREATE TABLE t1 (a,b DEFAULT(random() NOTNULL IN (RAISE(IGNORE),2,3))); INSERT INTO t1(a) VALUES(1); } {1 {default value of column [b] is not constant}} do_catchsql_test default-5.2 { CREATE TABLE Table0 (Col0 DEFAULT (RAISE(IGNORE) ) ) ; INSERT INTO Table0 DEFAULT VALUES ; } {1 {default value of column [Col0] is not constant}} finish_test |
Changes to test/distinctagg.test.
︙ | ︙ | |||
91 92 93 94 95 96 97 | 3 1 "SELECT count(DISTINCT c) FROM t1" 4 4 0 "SELECT count(DISTINCT c) FROM t1 WHERE b=3" 3 5 0 "SELECT count(DISTINCT rowid) FROM t1" 10 6 0 "SELECT count(DISTINCT a) FROM t1, t2" 5 7 0 "SELECT count(DISTINCT a) FROM t2, t1" 5 8 1 "SELECT count(DISTINCT a+b) FROM t1, t2, t2, t2" 6 9 0 "SELECT count(DISTINCT c) FROM t1 WHERE c=2" 1 | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 3 1 "SELECT count(DISTINCT c) FROM t1" 4 4 0 "SELECT count(DISTINCT c) FROM t1 WHERE b=3" 3 5 0 "SELECT count(DISTINCT rowid) FROM t1" 10 6 0 "SELECT count(DISTINCT a) FROM t1, t2" 5 7 0 "SELECT count(DISTINCT a) FROM t2, t1" 5 8 1 "SELECT count(DISTINCT a+b) FROM t1, t2, t2, t2" 6 9 0 "SELECT count(DISTINCT c) FROM t1 WHERE c=2" 1 10 0 "SELECT count(DISTINCT t1.rowid) FROM t1, t2" 10 } { do_test 3.$tn.1 { set prg [db eval "EXPLAIN $sql"] set idx [lsearch $prg OpenEphemeral] expr {$idx>=0} } $use_eph |
︙ | ︙ | |||
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | INSERT INTO t2 VALUES(2, 3, 'x'); INSERT INTO t2 VALUES(2, 3, 'y'); INSERT INTO t2 VALUES(2, 3, 'z'); CREATE TABLE t3(x, y, z); INSERT INTO t3 VALUES(1,1,1); INSERT INTO t3 VALUES(2,2,2); } foreach {tn use_eph sql res} { 1 0 "SELECT count(DISTINCT c) FROM t1 GROUP BY b" {2 3 0 1} 2 1 "SELECT count(DISTINCT a) FROM t1 GROUP BY b" {2 3 0 1} 3 1 "SELECT count(DISTINCT a) FROM t1 GROUP BY b+c" {0 1 1 1 1} 4 0 "SELECT count(DISTINCT f) FROM t2 GROUP BY d, e" {1 2 2 3} 5 1 "SELECT count(DISTINCT f) FROM t2 GROUP BY d" {2 3} 6 0 "SELECT count(DISTINCT f) FROM t2 WHERE d IS 1 GROUP BY e" {1 2 2} } { do_test 4.$tn.1 { set prg [db eval "EXPLAIN $sql"] set idx [lsearch $prg OpenEphemeral] expr {$idx>=0} } $use_eph | > > > > > > > | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | INSERT INTO t2 VALUES(2, 3, 'x'); INSERT INTO t2 VALUES(2, 3, 'y'); INSERT INTO t2 VALUES(2, 3, 'z'); CREATE TABLE t3(x, y, z); INSERT INTO t3 VALUES(1,1,1); INSERT INTO t3 VALUES(2,2,2); CREATE TABLE t4(a); CREATE INDEX t4a ON t4(a); INSERT INTO t4 VALUES(1), (2), (2), (3), (1); } foreach {tn use_eph sql res} { 1 0 "SELECT count(DISTINCT c) FROM t1 GROUP BY b" {2 3 0 1} 2 1 "SELECT count(DISTINCT a) FROM t1 GROUP BY b" {2 3 0 1} 3 1 "SELECT count(DISTINCT a) FROM t1 GROUP BY b+c" {0 1 1 1 1} 4 0 "SELECT count(DISTINCT f) FROM t2 GROUP BY d, e" {1 2 2 3} 5 1 "SELECT count(DISTINCT f) FROM t2 GROUP BY d" {2 3} 6 0 "SELECT count(DISTINCT f) FROM t2 WHERE d IS 1 GROUP BY e" {1 2 2} 7 0 "SELECT count(DISTINCT a) FROM t1" {4} 8 0 "SELECT count(DISTINCT a) FROM t4" {3} } { do_test 4.$tn.1 { set prg [db eval "EXPLAIN $sql"] set idx [lsearch $prg OpenEphemeral] expr {$idx>=0} } $use_eph |
︙ | ︙ |
Changes to test/e_reindex.test.
︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 78 | } {} db close sqlite3 db test.db do_execsql_test e_reindex-1.3 { PRAGMA integrity_check; } [list \ {row 3 missing from index i2} \ {row 3 missing from index i1} \ {row 4 missing from index i2} \ | > > | < < | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | } {} db close sqlite3 db test.db do_execsql_test e_reindex-1.3 { PRAGMA integrity_check; } [list \ {wrong # of entries in index i2} \ {wrong # of entries in index i1} \ {row 3 missing from index i2} \ {row 3 missing from index i1} \ {row 4 missing from index i2} \ {row 4 missing from index i1} ] do_execsql_test e_reindex-1.4 { REINDEX; PRAGMA integrity_check; } {ok} |
︙ | ︙ |
Changes to test/eqp.test.
︙ | ︙ | |||
409 410 411 412 413 414 415 | QUERY PLAN `--MERGE (UNION) |--LEFT | |--SCAN t1 | `--USE TEMP B-TREE FOR ORDER BY `--RIGHT |--SCAN t2 USING INDEX t2i1 | | | | | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | QUERY PLAN `--MERGE (UNION) |--LEFT | |--SCAN t1 | `--USE TEMP B-TREE FOR ORDER BY `--RIGHT |--SCAN t2 USING INDEX t2i1 `--USE TEMP B-TREE FOR LAST 2 TERMS OF ORDER BY } do_eqp_test 4.2.4 { SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 1 } { QUERY PLAN `--MERGE (INTERSECT) |--LEFT | |--SCAN t1 | `--USE TEMP B-TREE FOR ORDER BY `--RIGHT |--SCAN t2 USING INDEX t2i1 `--USE TEMP B-TREE FOR LAST 2 TERMS OF ORDER BY } do_eqp_test 4.2.5 { SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 1 } { QUERY PLAN `--MERGE (EXCEPT) |--LEFT | |--SCAN t1 | `--USE TEMP B-TREE FOR ORDER BY `--RIGHT |--SCAN t2 USING INDEX t2i1 `--USE TEMP B-TREE FOR LAST 2 TERMS OF ORDER BY } do_eqp_test 4.3.1 { SELECT x FROM t1 UNION SELECT x FROM t2 } { QUERY PLAN `--COMPOUND QUERY |
︙ | ︙ |
Added test/eqp2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # 2024 March 20 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix eqp2 do_execsql_test 1.0 { CREATE TABLE t1(a, b, c, d); CREATE INDEX i1 ON t1(a, b, c); } do_eqp_test 1.1 { SELECT * FROM t1 ORDER BY a, b, c } { QUERY PLAN `--SCAN t1 USING INDEX i1 } do_eqp_test 1.2 { SELECT * FROM t1 ORDER BY a, b, +c } { QUERY PLAN |--SCAN t1 USING INDEX i1 `--USE TEMP B-TREE FOR LAST TERM OF ORDER BY } do_eqp_test 1.3 { SELECT * FROM t1 ORDER BY a, +b, +c } { QUERY PLAN |--SCAN t1 USING INDEX i1 `--USE TEMP B-TREE FOR LAST 2 TERMS OF ORDER BY } finish_test |
Added test/exprfault2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | # 2024-05-11 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix exprfault2 do_execsql_test 1.0 { CREATE TABLE t1(a,b,c,d,f,PRIMARY KEY(b,b)); CREATE TABLE t2(x INT PRIMARY KEY, y, z); CREATE TABLE t3(a,b,c,d,e,PRIMARY KEY(a,b))WITHOUT ROWID; } faultsim_save_and_close do_faultsim_test 1 -faults oom-t* -prep { faultsim_restore_and_reopen } -body { execsql { UPDATE t3 SET (d,d,d,d, a )=(SELECT EXISTS(SELECT 1 NOT IN(SELECT EXISTS(SELECT 1 IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT y FROM t2 UNION SELECT (SELECT max( 1 IN(SELECT x NOT IN(SELECT 1 NOT IN(SELECT EXISTS(SELECT 1 IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT y FROM t2 UNION SELECT (SELECT max( (SELECT x NOT IN(SELECT 1 NOT IN(SELECT EXISTS(SELECT 1 IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT y FROM t2 UNION SELECT (SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT y FROM t2 UNION SELECT x ORDER BY 1)))INTERSECT SELECT (SELECT 1 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) ORDERa)| (SELECT 1 x ORDER BY 1)))INTERSECT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) a)| (SELECT 1 IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(ORDER BY sum((SELECT DISTINCT y FROM t2 UNION SELECT x ORDER BY 1)))INTERSECT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT x ORDER BY 1) ORDER BY 1) z)|9 AS blob) IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT DISTINCT y FROM t2 UNION SELECT x ORDER BY 1)))EXCEPT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) z) ORDER BY 1) IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT DISTINCT y FROM t2 UNION SELECT x ORDER BY 1)))INTERSECT SELECT (SELECT 1 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) ORDERa)| (SELECT 1 x ORDER BY 1)))EXCEPT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) a)| (SELECT 1 IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT DISTINCT y FROM t2 UNION SELECT x ORDER BY 1)))INTERSECT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT x ORDER BY 1) ORDER BY 1) z)|9 AS blob) IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT DISTINCT y FROM t2 UNION SELECT x ORDER BY 1)))EXCEPT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) z) ORDER BY 1)) OVER(PARTITION BY sum((SELECT y FROM t2 UNION SELECT x ORDER BY 1)))INTERSECT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) ORDERa)| (SELECT 1 x ORDER BY 1)))INTERSECT SELECT EXISTS(SELECT 5 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) ORDERa)| (SELECT 1 IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(ORDER BY sum((SELECT DISTINCT y FROM t2 UNION SELECT x ORDER BY 1)))INTERSECT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT x ORDER BY 1) ORDER BY 1) z)| 1 AS blob) IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT DISTINCT y FROM t2 UNION SELECT x ORDER BY 1)))INTERSECT SELECT EXISTS(SELECT 1 FROM t2 UNION SELECT d ORDER BY 1) ORDER BY 1) z)| (SELECT 1 IN(SELECT max( 1 IN(SELECT c ORDER BY 1)) OVER(PARTITION BY sum((SELECT y FROM t2 UNION SELECT x ORDER BY 1)))INTERSECT SELECT (SELECT 1 FROM t2 UNION SELECT x ORDER BY 1) ORDER BY 1) e)|9 AS blob) FROM t2 WHERE a<x), e= BY 1) FROM t2 UNION SELECT 1 ORDER BY 1) ORDER BY 1)) a) FILTER (GROUP BY 1 HAVING b<= OVER(ORDER BY (SELECT max(x INPARTITION BY sum((SELECT y FROM t2 UNION SELECT x IN(SELECT 1 ORDER BY 1) ORDER 1)))INTERSECT SELECT EXISTS(SELECT 1 FROM t2 WHERE xBY 1) ORDER BY 1)) FROM77; } } -test { faultsim_test_result {1 {near ")": syntax error}} } finish_test |
Changes to test/fts3fault3.test.
︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 | DELETE FROM t1; } } -test { catchsql { COMMIT } faultsim_integrity_check faultsim_test_result {0 {}} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | DELETE FROM t1; } } -test { catchsql { COMMIT } faultsim_integrity_check faultsim_test_result {0 {}} } #------------------------------------------------------------------- reset_db do_execsql_test 2.0 { BEGIN; CREATE VIRTUAL TABLE t1 USING fts3(a); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50 ) INSERT INTO t1 SELECT 'abc def ghi jkl mno pqr' FROM s; COMMIT; } faultsim_save_and_close do_faultsim_test 2 -faults oom-t* -prep { faultsim_restore_and_reopen execsql { BEGIN; CREATE TABLE x1(a PRIMARY KEY); } } -body { execsql { PRAGMA integrity_check; } } -test { faultsim_test_result {0 ok} $::TMPDBERROR } finish_test |
Changes to test/fts3snippet2.test.
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 | ); INSERT INTO t0 VALUES ('one', '1234','aaaa','bbbb'); } do_execsql_test 2.2 { SELECT snippet(t0) FROM t0 WHERE t0 MATCH '(def AND (one NEAR abc)) OR one' } {<b>one</b>} set sqlite_fts3_enable_parentheses 0 finish_test | > > > > > > > > > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | ); INSERT INTO t0 VALUES ('one', '1234','aaaa','bbbb'); } do_execsql_test 2.2 { SELECT snippet(t0) FROM t0 WHERE t0 MATCH '(def AND (one NEAR abc)) OR one' } {<b>one</b>} #------------------------------------------------------------------------- do_execsql_test 3.0 { CREATE VIRTUAL TABLE f USING fts3(a,b); INSERT INTO f VALUES (101,x'056522650565056505650d051e056505650565286505650565056505056505650565056505650565056505650565056505650565056505656505650565056505650d05650505656505650565ef65056505844c746e65650565056505650565056505650565056505650565058405800565056505650565056505651e650565056505650565056505650d056505056565056505650565056505840580056505650565056f05650565056505650565056505650565050565056505640565056505650565056505651e05650565056505650565056505650505656565056505650565056505651e0565056505650565056505650565052265056505650569056505650565056505650565056505650565056505650500406505650565056505650565056505000101e5c501014b010101c501c5c501010101f5010201010101014101017373737373737373737373737373737373737373737373737373737330737373737373737373737373737365056505650d051e05650565056528056505650d05650505656505650565650565056505650565056505e505650565056505656505650565056505650d05650505656505650565ef65056505844c746e65650565056505650565056505650565056505650565058405800565056505650565056505651e650565056505650565056505650d056505056565056505650565056505840580056505650565286505c705650565050565059494949494949494949494949494949494949494949494949494949494949494949494650565056505650565056505650565056505650565056505656505650565056505650d05650505656505650565ef650565058405056505650565056505650565056505650565056505650565058405800565056505650565056505651e650565056505650565056505650d056505056565056505650565056505840580056505650565056505650565056505650505650565056505650565056505650565056500000000000000000000000000000000000000000000000000000000000000000565056505656505650565056505650d056500000000000000000000000000000000000000000000000000000000000000000100000000000000ed0000000000ffffffffffffffffffffff0007ffffff0001c5c50001c5c50001c5c50001c5c50001c5c50001c5c50001e5c50001c5c50001c5c50001c5c50001c5c50001c5c5000100000014720000000000000016dac5c50001c5c50001c5c50001c5c50001c5c50001c5c50d0505656505650565ef650565058405056505650565056505650565056505650565056505650565058405800465056505650565056505651e650565056505650565056505650d05650505656505650565050565650584058005650565056505650565056505650565056522650565056505650d051e056505650561286505c70565056505056505650565056505650565056505650565056505650565056505656505650565056505650d05650505656505650565ef650565058405056505650565056505650565056505650565056505650565058405800565056505650565056505651e650565056505650565056505650d056505056565056505650565056505840580056505650565056505650565056505650565226505737373737373737373737373737373737373737373737373737373737373737373737373737373737373737373737373733a73737373737373737373737373737373737373737373737373737373737373737373737373737373737c7373737365056505650d051e05650565056528650565056505650505650565056505650565056505650565056505e505650565056505656505650565056505650d05650505656505650565ef65056505'); } do_execsql_test 3.1 { SELECT length(snippet(f)) FROM f WHERE b MATCH x'0565056505650565056505650565056505650565058405800565056505650565056505651e650565056505650565056505650d056505056565056505650565056505840580056505650565056505650565056505650565056505650565050565056505640565056505650565056505651e05650565056522650565056505650d051e056505650565286505650565056505056505650565056505650565056505650565056505650565056505656505650565056505650d05650505656505650565ef65056505844c746e65650565056505650565056505650565056505650565058405800565056505650565056505651e650565056505650565056505650d056505056565056505650565056505840580056505650565056f05650565056505650565056505650565050565056505640565056505650565056505651e05650565056505650565056505650505656565056505650565056505651e0565056505650565056505650565052265056505650569056505650565056505650565056505650565056505650500406505650565056505650565056505000101e5c501014b010101c501c5c501010101f50102010101010141010141010001017bf15905000000000017'; } {192} set sqlite_fts3_enable_parentheses 0 finish_test |
Changes to test/fts4intck1.test.
︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 | PRAGMA integrity_check(t2); } {ok} proc slang {in} {return $in} do_execsql_test 2.3 { PRAGMA integrity_check(t2); } {{malformed inverted index for FTS4 table main.t2}} finish_test | > > > > > > > > > > > > > > > > > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | PRAGMA integrity_check(t2); } {ok} proc slang {in} {return $in} do_execsql_test 2.3 { PRAGMA integrity_check(t2); } {{malformed inverted index for FTS4 table main.t2}} #------------------------------------------------------------------------- # Test that integrity-check works on a read-only database. # reset_db do_execsql_test 3.0 { CREATE VIRTUAL TABLE x1 USING fts4(a, b); INSERT INTO x1 VALUES('one', 'two'); INSERT INTO x1 VALUES('three', 'four'); } db close sqlite3 db test.db -readonly 1 do_execsql_test 3.1 { PRAGMA integrity_check; } {ok} finish_test |
Changes to test/func.test.
︙ | ︙ | |||
781 782 783 784 785 786 787 788 789 790 791 792 793 794 | sqlite3_bind_blob $::STMT 1 abc 3 sqlite3_step $::STMT sqlite3_finalize $::STMT execsql { SELECT quote(a), quote(b) FROM tbl2; } } {X'616263' NULL} # Correctly handle function error messages that include %. Ticket #1354 # do_test func-17.1 { proc testfunc1 args {error "Error %d with %s percents %p"} db function testfunc1 ::testfunc1 catchsql { | > > > > > | 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 | sqlite3_bind_blob $::STMT 1 abc 3 sqlite3_step $::STMT sqlite3_finalize $::STMT execsql { SELECT quote(a), quote(b) FROM tbl2; } } {X'616263' NULL} # Test the quote function for +Inf and -Inf do_execsql_test func-16.2 { SELECT quote(4.2e+859), quote(-7.8e+904); } {9.0e+999 -9.0e+999} # Correctly handle function error messages that include %. Ticket #1354 # do_test func-17.1 { proc testfunc1 args {error "Error %d with %s percents %p"} db function testfunc1 ::testfunc1 catchsql { |
︙ | ︙ | |||
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 | } } {{This is the larger-main test string}} do_test func-21.8 { execsql { SELECT replace('aaaaaaa', 'a', '0123456789'); } } {0123456789012345678901234567890123456789012345678901234567890123456789} ifcapable tclvar { do_test func-21.9 { # Attempt to exploit a buffer-overflow that at one time existed # in the REPLACE function. set ::str "[string repeat A 29998]CC[string repeat A 35537]" set ::rep [string repeat B 65536] | > > > | 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 | } } {{This is the larger-main test string}} do_test func-21.8 { execsql { SELECT replace('aaaaaaa', 'a', '0123456789'); } } {0123456789012345678901234567890123456789012345678901234567890123456789} do_execsql_test func-21.9 { SELECT typeof(replace(1,'',0)); } {text} ifcapable tclvar { do_test func-21.9 { # Attempt to exploit a buffer-overflow that at one time existed # in the REPLACE function. set ::str "[string repeat A 29998]CC[string repeat A 35537]" set ::rep [string repeat B 65536] |
︙ | ︙ | |||
1548 1549 1550 1551 1552 1553 1554 1555 1556 | # 2023-08-28 forum post https://sqlite.org/forum/forumpost/1c06ddcacc86032a # Incorrect handling of infinity by SUM(). # do_execsql_test func-38.100 { WITH t1(x) AS (VALUES(9e+999)) SELECT sum(x), avg(x), total(x) FROM t1; WITH t1(x) AS (VALUES(-9e+999)) SELECT sum(x), avg(x), total(x) FROM t1; } {Inf Inf Inf -Inf -Inf -Inf} finish_test | > > > > > > > > > > > > > > > > > > > > > | 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 | # 2023-08-28 forum post https://sqlite.org/forum/forumpost/1c06ddcacc86032a # Incorrect handling of infinity by SUM(). # do_execsql_test func-38.100 { WITH t1(x) AS (VALUES(9e+999)) SELECT sum(x), avg(x), total(x) FROM t1; WITH t1(x) AS (VALUES(-9e+999)) SELECT sum(x), avg(x), total(x) FROM t1; } {Inf Inf Inf -Inf -Inf -Inf} # 2024-03-21 https://sqlite.org/forum/forumpost/23b8688ef4 # Another problem with Kahan-Babushka-Neumaier summation and # infinities. # do_execsql_test func-39.101 { WITH RECURSIVE c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<1) SELECT sum(1.7976931348623157e308), avg(1.7976931348623157e308), total(1.7976931348623157e308) FROM c; } {1.79769313486232e+308 1.79769313486232e+308 1.79769313486232e+308} for {set i 2} {$i<10} {incr i} { do_execsql_test func-39.[expr {10*$i+100}] { WITH RECURSIVE c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<$i) SELECT sum(1.7976931348623157e308), avg(1.7976931348623157e308), total(1.7976931348623157e308) FROM c; } {Inf Inf Inf} } finish_test |
Changes to test/func2.test.
︙ | ︙ | |||
503 504 505 506 507 508 509 510 511 | set blob [execsql "SELECT SUBSTR(x'1234', 2, -1)"] bin_to_hex [lindex $blob 0] } "12" do_test func2-3.9.2 { set blob [execsql "SELECT SUBSTR(x'1234', 2, -2)"] bin_to_hex [lindex $blob 0] } "12" finish_test | > > > > > > > > > > > > > > > > > > > > > > > | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | set blob [execsql "SELECT SUBSTR(x'1234', 2, -1)"] bin_to_hex [lindex $blob 0] } "12" do_test func2-3.9.2 { set blob [execsql "SELECT SUBSTR(x'1234', 2, -2)"] bin_to_hex [lindex $blob 0] } "12" #------------------------------------------------------------------------- # At one point this was extremely slow to compile. # do_test func2-3.10 { set tm [time { execsql { SELECT '' IN (zerobloB(zerobloB(zerobloB(zerobloB(zerobloB( zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB( zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB( zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB( zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB( zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB( zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB( zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB( zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(zerobloB(1) ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) } }] set tm [lindex $tm 0] expr $tm<2000000 } {1} finish_test |
Changes to test/func4.test.
|
| | | > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # 2023-03-10 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The focus of # this file is testing the tointeger() and toreal() functions that are # part of the "totype.c" extension. This file does not test the core # SQLite library. Failures of tests in this file are related to the # ext/misc/totype.c extension. # # Several of the toreal() tests are disabled on platforms where floating # point precision is not high enough to represent their constant integer # expression arguments as double precision floating point values. # set testdir [file dirname $argv0] source $testdir/tester.tcl set saved_tcl_precision $tcl_precision set tcl_precision 0 load_static_extension db totype set highPrecision(1) [expr \ {[db eval {SELECT tointeger(9223372036854775807 + 1);}] eq {{}}}] set highPrecision(2) [expr \ {[db eval {SELECT toreal(-9223372036854775808 + 1);}] eq {{}}}] # highPrecision(3) is only known to be false on i586 with gcc-13 and -O2. # It is true on the exact same platform with -O0. Both results seem # reasonable, so we'll just very the expectation accordingly. # set highPrecision(3) [expr \ {[db eval {SELECT toreal(9007199254740992 + 1);}] eq {{}}}] if {!$highPrecision(1) || !$highPrecision(2) || !$highPrecision(3)} { puts "NOTICE: use_long_double: [use_long_double] \ highPrecision: $highPrecision(1) $highPrecision(2) $highPrecision(3)" } do_execsql_test func4-1.1 { SELECT tointeger(NULL); } {{}} do_execsql_test func4-1.2 { SELECT tointeger(''); } {{}} |
︙ | ︙ | |||
88 89 90 91 92 93 94 | SELECT tointeger(-1.79769313486232e308); } {{}} do_execsql_test func4-1.22 { SELECT tointeger(-1.79769313486232e308 + 1); } {{}} do_execsql_test func4-1.23 { SELECT tointeger(-9223372036854775808 - 1); | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | SELECT tointeger(-1.79769313486232e308); } {{}} do_execsql_test func4-1.22 { SELECT tointeger(-1.79769313486232e308 + 1); } {{}} do_execsql_test func4-1.23 { SELECT tointeger(-9223372036854775808 - 1); } {{}} do_execsql_test func4-1.24 { SELECT tointeger(-9223372036854775808); } {-9223372036854775808} do_execsql_test func4-1.25 { SELECT tointeger(-9223372036854775808 + 1); } {-9223372036854775807} do_execsql_test func4-1.26 { |
︙ | ︙ | |||
191 192 193 194 195 196 197 | SELECT tointeger(18446744073709551616); } {{}} do_execsql_test func4-1.55 { SELECT tointeger(18446744073709551616 + 1); } {{}} ifcapable floatingpoint { | < < | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | SELECT tointeger(18446744073709551616); } {{}} do_execsql_test func4-1.55 { SELECT tointeger(18446744073709551616 + 1); } {{}} ifcapable floatingpoint { do_execsql_test func4-2.1 { SELECT toreal(NULL); } {{}} do_execsql_test func4-2.2 { SELECT toreal(''); } {{}} |
︙ | ︙ | |||
265 266 267 268 269 270 271 | SELECT toreal(-1.79769313486232e308 + 1); } {-Inf} do_execsql_test func4-2.23 { SELECT toreal(-9223372036854775808 - 1); } {-9.223372036854776e+18} do_execsql_test func4-2.24 { SELECT toreal(-9223372036854775808); | | | | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | SELECT toreal(-1.79769313486232e308 + 1); } {-Inf} do_execsql_test func4-2.23 { SELECT toreal(-9223372036854775808 - 1); } {-9.223372036854776e+18} do_execsql_test func4-2.24 { SELECT toreal(-9223372036854775808); } {{}} if {$highPrecision(2)} { do_execsql_test func4-2.25 { SELECT toreal(-9223372036854775808 + 1); } {{}} } do_execsql_test func4-2.26 { SELECT toreal(-9223372036854775807 - 1); } {{}} if {$highPrecision(2)} { do_execsql_test func4-2.27 { SELECT toreal(-9223372036854775807); } {{}} do_execsql_test func4-2.28 { SELECT toreal(-9223372036854775807 + 1); } {{}} |
︙ | ︙ | |||
337 338 339 340 341 342 343 | } {4503599627370497.0} do_execsql_test func4-2.44 { SELECT toreal(9007199254740992 - 1); } {9007199254740991.0} do_execsql_test func4-2.45 { SELECT toreal(9007199254740992); } {9007199254740992.0} | | > > > > | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | } {4503599627370497.0} do_execsql_test func4-2.44 { SELECT toreal(9007199254740992 - 1); } {9007199254740991.0} do_execsql_test func4-2.45 { SELECT toreal(9007199254740992); } {9007199254740992.0} if {$highPrecision(3)} { do_execsql_test func4-2.46 { SELECT toreal(9007199254740992 + 1); } {{}} } else { do_execsql_test func4-2.46 { SELECT toreal(9007199254740992 + 1); } {9007199254740992.0} } do_execsql_test func4-2.47 { SELECT toreal(9007199254740992 + 2); } {9007199254740994.0} do_execsql_test func4-2.48 { SELECT toreal(tointeger(9223372036854775808) - 1); } {{}} |
︙ | ︙ | |||
457 458 459 460 461 462 463 | INSERT INTO t1 (x) VALUES (1234.00); } } {0 {}} do_test func4-3.18 { catchsql { INSERT INTO t1 (x) VALUES ('-9223372036854775809'); } | | | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | INSERT INTO t1 (x) VALUES (1234.00); } } {0 {}} do_test func4-3.18 { catchsql { INSERT INTO t1 (x) VALUES ('-9223372036854775809'); } } {1 {CHECK constraint failed: tointeger(x) IS NOT NULL}} if {$highPrecision(1)} { do_test func4-3.19 { catchsql { INSERT INTO t1 (x) VALUES (9223372036854775808); } } {1 {CHECK constraint failed: tointeger(x) IS NOT NULL}} } |
︙ | ︙ | |||
569 570 571 572 573 574 575 | SELECT tointeger(toreal(0)); } {0} do_execsql_test func4-5.5 { SELECT tointeger(toreal(1)); } {1} do_execsql_test func4-5.6 { SELECT tointeger(toreal(-9223372036854775808 - 1)); | | | | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 | SELECT tointeger(toreal(0)); } {0} do_execsql_test func4-5.5 { SELECT tointeger(toreal(1)); } {1} do_execsql_test func4-5.6 { SELECT tointeger(toreal(-9223372036854775808 - 1)); } {{}} do_execsql_test func4-5.7 { SELECT tointeger(toreal(-9223372036854775808)); } {{}} if {$highPrecision(2)} { do_execsql_test func4-5.8 { SELECT tointeger(toreal(-9223372036854775808 + 1)); } {{}} } do_execsql_test func4-5.9 { SELECT tointeger(toreal(-2147483648 - 1)); |
︙ | ︙ | |||
622 623 624 625 626 627 628 | } {4503599627370497} do_execsql_test func4-5.21 { SELECT tointeger(toreal(9007199254740992 - 1)); } {9007199254740991} do_execsql_test func4-5.22 { SELECT tointeger(toreal(9007199254740992)); } {9007199254740992} | | > > > > | 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | } {4503599627370497} do_execsql_test func4-5.21 { SELECT tointeger(toreal(9007199254740992 - 1)); } {9007199254740991} do_execsql_test func4-5.22 { SELECT tointeger(toreal(9007199254740992)); } {9007199254740992} if {$highPrecision(3)} { do_execsql_test func4-5.23 { SELECT tointeger(toreal(9007199254740992 + 1)); } {{}} } else { do_execsql_test func4-5.23 { SELECT tointeger(toreal(9007199254740992 + 1)); } {9007199254740992} } do_execsql_test func4-5.24 { SELECT tointeger(toreal(9007199254740992 + 2)); } {9007199254740994} if {$highPrecision(1)} { do_execsql_test func4-5.25 { SELECT tointeger(toreal(9223372036854775808 - 1)); |
︙ | ︙ |
Changes to test/fuzzcheck.c.
︙ | ︙ | |||
157 158 159 160 161 162 163 | unsigned int nInvariant; /* Number of invariant checks run */ char zTestName[100]; /* Name of current test */ } g; /* ** Include the external vt02.c and randomjson.c modules. */ | | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | unsigned int nInvariant; /* Number of invariant checks run */ char zTestName[100]; /* Name of current test */ } g; /* ** Include the external vt02.c and randomjson.c modules. */ extern int sqlite3_vt02_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_randomjson_init(sqlite3*,char**,const sqlite3_api_routines*); /* ** Print an error message and quit. */ static void fatalError(const char *zFormat, ...){ va_list ap; |
︙ | ︙ | |||
975 976 977 978 979 980 981 | extern int fuzz_invariant( sqlite3 *db, /* The database connection */ sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */ int iCnt, /* Invariant sequence number, starting at 0 */ int iRow, /* The row number for pStmt */ int nRow, /* Total number of output rows */ int *pbCorrupt, /* IN/OUT: Flag indicating a corrupt database file */ | | > | 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 | extern int fuzz_invariant( sqlite3 *db, /* The database connection */ sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */ int iCnt, /* Invariant sequence number, starting at 0 */ int iRow, /* The row number for pStmt */ int nRow, /* Total number of output rows */ int *pbCorrupt, /* IN/OUT: Flag indicating a corrupt database file */ int eVerbosity, /* How much debugging output */ unsigned int dbOpt /* Default optimization flags */ ); /* Implementation of sqlite_dbdata and sqlite_dbptr */ extern int sqlite3_dbdata_init(sqlite3*,const char**,void*); /* |
︙ | ︙ | |||
1027 1028 1029 1030 1031 1032 1033 | } return rc; } /* ** Run the SQL text */ | | > > > > > | 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 | } return rc; } /* ** Run the SQL text */ static int runDbSql( sqlite3 *db, /* Run SQL on this database connection */ const char *zSql, /* The SQL to be run */ unsigned int *pBtsFlags, unsigned int dbOpt /* Default optimization flags */ ){ int rc; sqlite3_stmt *pStmt; int bCorrupt = 0; while( isspace(zSql[0]&0x7f) ) zSql++; if( zSql[0]==0 ) return SQLITE_OK; if( eVerbosity>=4 ){ printf("RUNNING-SQL: [%s]\n", zSql); |
︙ | ︙ | |||
1103 1104 1105 1106 1107 1108 1109 | int iRow = 0; sqlite3_reset(pStmt); while( sqlite3_step(pStmt)==SQLITE_ROW ){ int iCnt = 0; iRow++; for(iCnt=0; iCnt<99999; iCnt++){ rc = fuzz_invariant(db, pStmt, iCnt, iRow, nRow, | | | 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 | int iRow = 0; sqlite3_reset(pStmt); while( sqlite3_step(pStmt)==SQLITE_ROW ){ int iCnt = 0; iRow++; for(iCnt=0; iCnt<99999; iCnt++){ rc = fuzz_invariant(db, pStmt, iCnt, iRow, nRow, &bCorrupt, eVerbosity, dbOpt); if( rc==SQLITE_DONE ) break; if( rc!=SQLITE_ERROR ) g.nInvariant++; if( eVerbosity>0 ){ if( rc==SQLITE_OK ){ printf("invariant-check: ok\n"); }else if( rc==SQLITE_CORRUPT ){ printf("invariant-check: failed due to database corruption\n"); |
︙ | ︙ | |||
1326 1327 1328 1329 1330 1331 1332 | memcpy(zSql, aData+iSql, nSql); zSql[nSql] = 0; for(i=j=0; zSql[i]; i++){ if( zSql[i]==';' ){ char cSaved = zSql[i+1]; zSql[i+1] = 0; if( sqlite3_complete(zSql+j) ){ | | | | 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 | memcpy(zSql, aData+iSql, nSql); zSql[nSql] = 0; for(i=j=0; zSql[i]; i++){ if( zSql[i]==';' ){ char cSaved = zSql[i+1]; zSql[i+1] = 0; if( sqlite3_complete(zSql+j) ){ rc = runDbSql(cx.db, zSql+j, &btsFlags, dbOpt); j = i+1; } zSql[i+1] = cSaved; if( rc==SQLITE_INTERRUPT || progress_handler(&cx) ){ goto testrun_finished; } } } if( j<i ){ runDbSql(cx.db, zSql+j, &btsFlags, dbOpt); } } testrun_finished: sqlite3_free(zSql); rc = sqlite3_close(cx.db); if( rc!=SQLITE_OK ){ fprintf(stdout, "sqlite3_close() returns %d\n", rc); |
︙ | ︙ |
Changes to test/fuzzinvariants.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | #include <stdlib.h> #include <string.h> #include <ctype.h> /* Forward references */ static char *fuzz_invariant_sql(sqlite3_stmt*, int); static int sameValue(sqlite3_stmt*,int,sqlite3_stmt*,int,sqlite3_stmt*); | | > > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #include <stdlib.h> #include <string.h> #include <ctype.h> /* Forward references */ static char *fuzz_invariant_sql(sqlite3_stmt*, int); static int sameValue(sqlite3_stmt*,int,sqlite3_stmt*,int,sqlite3_stmt*); static void reportInvariantFailed( sqlite3_stmt *pOrig, /* The original query */ sqlite3_stmt *pTest, /* The alternative test query with a missing row */ int iRow, /* Row number in pOrig */ unsigned int dbOpt, /* Optimization flags on pOrig */ int noOpt /* True if opt flags inverted for pTest */ ); /* ** Do an invariant check on pStmt. iCnt determines which invariant check to ** perform. The first check is iCnt==0. ** ** *pbCorrupt is a flag that, if true, indicates that the database file ** is known to be corrupt. A value of non-zero means "yes, the database |
︙ | ︙ | |||
64 65 66 67 68 69 70 | int fuzz_invariant( sqlite3 *db, /* The database connection */ sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */ int iCnt, /* Invariant sequence number, starting at 0 */ int iRow, /* Current row number */ int nRow, /* Number of output rows from pStmt */ int *pbCorrupt, /* IN/OUT: Flag indicating a corrupt database file */ | | > > > > > > > > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | int fuzz_invariant( sqlite3 *db, /* The database connection */ sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */ int iCnt, /* Invariant sequence number, starting at 0 */ int iRow, /* Current row number */ int nRow, /* Number of output rows from pStmt */ int *pbCorrupt, /* IN/OUT: Flag indicating a corrupt database file */ int eVerbosity, /* How much debugging output */ unsigned int dbOpt /* Default optimization flags */ ){ char *zTest; sqlite3_stmt *pTestStmt = 0; int rc; int i; int nCol; int nParam; int noOpt = (iCnt%3)==0; if( *pbCorrupt ) return SQLITE_DONE; nParam = sqlite3_bind_parameter_count(pStmt); if( nParam>100 ) return SQLITE_DONE; zTest = fuzz_invariant_sql(pStmt, iCnt); if( zTest==0 ) return SQLITE_DONE; if( noOpt ){ sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, ~dbOpt); } rc = sqlite3_prepare_v2(db, zTest, -1, &pTestStmt, 0); if( noOpt ){ sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, dbOpt); } if( rc ){ if( eVerbosity ){ printf("invariant compile failed: %s\n%s\n", sqlite3_errmsg(db), zTest); } sqlite3_free(zTest); sqlite3_finalize(pTestStmt); |
︙ | ︙ | |||
208 209 210 211 212 213 214 | sqlite3_free(zSql); } sqlite3_bind_pointer(pCk, 1, pStmt, "stmt-pointer", 0); rc = sqlite3_step(pCk); } sqlite3_finalize(pCk); if( rc==SQLITE_DONE ){ | | < | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | sqlite3_free(zSql); } sqlite3_bind_pointer(pCk, 1, pStmt, "stmt-pointer", 0); rc = sqlite3_step(pCk); } sqlite3_finalize(pCk); if( rc==SQLITE_DONE ){ reportInvariantFailed(pStmt, pTestStmt, iRow, dbOpt, noOpt); return SQLITE_INTERNAL; }else if( eVerbosity>0 ){ printf("invariant-error ignored due to the use of virtual tables\n"); } } not_a_fault: sqlite3_finalize(pTestStmt); return SQLITE_OK; } /* ** Generate SQL used to test a statement invariant. ** ** Return 0 if the iCnt is out of range. ** ** iCnt meanings: |
︙ | ︙ | |||
485 486 487 488 489 490 491 | /* ** Report a failure of the invariant: The current output row of pOrig ** does not appear in any row of the output from pTest. */ static void reportInvariantFailed( sqlite3_stmt *pOrig, /* The original query */ sqlite3_stmt *pTest, /* The alternative test query with a missing row */ | | > > | > | > | 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 | /* ** Report a failure of the invariant: The current output row of pOrig ** does not appear in any row of the output from pTest. */ static void reportInvariantFailed( sqlite3_stmt *pOrig, /* The original query */ sqlite3_stmt *pTest, /* The alternative test query with a missing row */ int iRow, /* Row number in pOrig */ unsigned int dbOpt, /* Optimization flags on pOrig */ int noOpt /* True if opt flags inverted for pTest */ ){ int iTestRow = 0; printf("Invariant check failed on row %d.\n", iRow); printf("Original query (opt-flags: 0x%08x) --------------------------\n", dbOpt); printf("%s\n", sqlite3_expanded_sql(pOrig)); printf("Alternative query (opt-flags: 0x%08x) -----------------------\n", noOpt ? ~dbOpt : dbOpt); printf("%s\n", sqlite3_expanded_sql(pTest)); printf("Result row that is missing from the alternative -----------------\n"); printRow(pOrig, iRow); printf("Complete results from the alternative query ---------------------\n"); sqlite3_reset(pTest); while( sqlite3_step(pTest)==SQLITE_ROW ){ iTestRow++; printRow(pTest, iTestRow); } sqlite3_finalize(pTest); abort(); } |
Changes to test/icu.test.
︙ | ︙ | |||
145 146 147 148 149 150 151 | SELECT upper(char(0xfb04,0xdf,0xfb04,0xe8,0xfb04)); } } # 2020-03-19 # The ESCAPE clause on LIKE takes precedence over wildcards # | | > > > > > > > > > > > > > > > > | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | SELECT upper(char(0xfb04,0xdf,0xfb04,0xe8,0xfb04)); } } # 2020-03-19 # The ESCAPE clause on LIKE takes precedence over wildcards # do_execsql_test icu-6.0 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(id INTEGER PRIMARY KEY, x TEXT); INSERT INTO t1 VALUES (1,'abcde'), (2,'abc_'), (3,'abc__'), (4,'abc%'), (5,'abc%%'); SELECT id FROM t1 WHERE x LIKE 'abc%%' ESCAPE '%'; } {4} do_execsql_test icu-6.1 { SELECT id FROM t1 WHERE x LIKE 'abc__' ESCAPE '_'; } {2} # 2024-04-02 # Optional 3rd argument to icu_load_collation() that specifies # the "strength" of comparison. # reset_db do_catchsql_test icu-7.1 { SELECT icu_load_collation('en_US','error','xyzzy'); } {1 {unknown collation strength "xyzzy" - should be one of: PRIMARY SECONDARY TERTIARY DEFAULT QUARTERNARY IDENTICAL}} do_execsql_test icu-7.2 { SELECT icu_load_collation('en_US','prim','PRIMARY'), icu_load_collation('en_US','dflt','DEFAULT'); } {{} {}} do_execsql_test icu-7.3 { SELECT char(0x100)=='a', char(0x100)=='a' COLLATE dflt, char(0x100)=='a' COLLATE prim; } {0 0 1} finish_test |
Changes to test/in4.test.
︙ | ︙ | |||
454 455 456 457 458 459 460 | ANALYZE sqlite_schema; INSERT INTO sqlite_stat1 VALUES('t1','t1abc','10000 5 00 2003 10'); ANALYZE sqlite_schema; } {} do_execsql_test 11.1 { SELECT * FROM t1 WHERE b IN (345, (SELECT 1 FROM t1 | | | | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | ANALYZE sqlite_schema; INSERT INTO sqlite_stat1 VALUES('t1','t1abc','10000 5 00 2003 10'); ANALYZE sqlite_schema; } {} do_execsql_test 11.1 { SELECT * FROM t1 WHERE b IN (345, (SELECT 1 FROM t1 WHERE b IN (coalesce(1,random())) AND c GLOB 'abc*xyz')) AND c BETWEEN 'abc' AND 'xyz'; } {xyz 1 abcdefxyz 99} do_execsql_test 11.2 { EXPLAIN SELECT * FROM t1 WHERE b IN (345, (SELECT 1 FROM t1 WHERE b IN (coalesce(1,random())) AND c GLOB 'abc*xyz')) AND c BETWEEN 'abc' AND 'xyz'; } {/ SeekScan /} # 2021-06-25 ticket 6dcbfd11cf666e21 # Another problem with OP_SeekScan # |
︙ | ︙ |
Changes to test/in5.test.
︙ | ︙ | |||
261 262 263 264 265 266 267 268 269 | do_execsql_test 9.1 { SELECT lower('1e500') FROM t0 WHERE rowid NOT IN (0, 0, lower('1e500')); } {1e500} do_execsql_test 9.2 { SELECT lower('1e500') FROM t0 WHERE rowid != lower('1e500'); } {1e500} finish_test | > > > > > > > > > > > > > > > > > > > > > > | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | do_execsql_test 9.1 { SELECT lower('1e500') FROM t0 WHERE rowid NOT IN (0, 0, lower('1e500')); } {1e500} do_execsql_test 9.2 { SELECT lower('1e500') FROM t0 WHERE rowid != lower('1e500'); } {1e500} #------------------------------------------------------------------------- # reset_db do_execsql_test 10.0 { CREATE TABLE t1(a, b TEXT COLLATE NOCASE); INSERT INTO t1 VALUES('abc', 'def'); INSERT INTO t1 VALUES('ghi', 'jkl'); } do_execsql_test 10.1 { SELECT rowid FROM t1 WHERE (a, b) IN ( VALUES('abc', 'def'), ('ghi', 'JKL') ); } {1 2} do_execsql_test 10.2 { CREATE INDEX i1 ON t1(a, b COLLATE BINARY); } do_execsql_test 10.3 { SELECT rowid FROM t1 WHERE (a, b) IN ( VALUES('abc', 'def'), ('ghi', 'JKL') ); } {1 2} finish_test |
Added test/in7.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | # 2024-05-01 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix in7 do_execsql_test 1.0 { CREATE TABLE t1(a, b, c PRIMARY KEY); CREATE TABLE t2(x, y, z); } foreach {tn nNext idx sql} { 1 1 { CREATE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE (a, b) IN (SELECT x, y FROM t2) } 2 0 { CREATE UNIQUE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE (a, b) IN (SELECT x, y FROM t2) } 3 0 { CREATE UNIQUE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE a = ? AND b = ? } 3 1 { CREATE UNIQUE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE a = ? AND b IS ? } 4 0 { CREATE UNIQUE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE a = ? AND b IN (?, ?, ?); } 5 1 { CREATE UNIQUE INDEX i1 ON t1(a, b, c); } { SELECT * FROM t1 WHERE a = ? AND b = ? } 6 0 { } { SELECT * FROM t1 WHERE c IN (SELECT z FROM t2) } 7 0 { } { SELECT * FROM t1 WHERE (a, c) IN (SELECT z, x FROM t2) } 8 1 { } { SELECT * FROM t1 WHERE a IN (SELECT z FROM t2) } 9 1 { CREATE UNIQUE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE a IN (SELECT z FROM t2) AND b IS ? } 10 0 { CREATE UNIQUE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE a IN (SELECT z FROM t2) AND b = ? } 11 1 { CREATE UNIQUE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE a IS NULL AND b IN (SELECT z FROM t2) } 12 0 { CREATE UNIQUE INDEX i1 ON t1(a, b); } { SELECT * FROM t1 WHERE a = ? AND b IN (SELECT z FROM t2) } } { do_test 1.1.$tn { execsql BEGIN execsql $idx catch { array unset root_to_tbl } catch { array unset csr_to_root } db eval {SELECT rootpage, tbl_name FROM sqlite_schema} { set root_to_tbl($rootpage) $tbl_name } set nSeen 0 db eval "explain $sql" { if {$opcode=="OpenRead"} { set csr_to_root($p1) $p2 } if {$opcode=="Next"} { catch { set root $csr_to_root($p1) set tbl $root_to_tbl($root) if {$tbl=="t1"} {incr nSeen} } } } execsql ROLLBACK set nSeen } $nNext } #------------------------------------------------------------------------- reset_db do_execsql_test 2.0 { CREATE TABLE t1(a TEXT PRIMARY KEY, b TEXT) WITHOUT ROWID; INSERT INTO t1 VALUES('1', 'one'); INSERT INTO t1 VALUES('2', NULL); INSERT INTO t1 VALUES('3', 'three'); } do_execsql_test 2.1 { SELECT b FROM t1 WHERE a IN (1,2,3) ORDER BY b ASC NULLS LAST; } {one three {}} finish_test |
Changes to test/indexexpr1.test.
︙ | ︙ | |||
611 612 613 614 615 616 617 618 619 620 621 622 623 624 | JOIN (SELECT t1.tag AS "tag", t2.type AS "type", MAX(t2.value) AS "max_value" FROM t1 JOIN t2 ON t2.t1_id = t1.id GROUP BY t2.type, t1.tag ) v ON v.type = 0 AND v.tag = u.tag; } {7 100 8 101} # 2023-11-08 Forum post https://sqlite.org/forum/forumpost/68d284c86b082c3e # # Functions that return subtypes and that are indexed cannot be used to # cover function calls from the main table, since the indexed value does # not know the subtype. # | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 | JOIN (SELECT t1.tag AS "tag", t2.type AS "type", MAX(t2.value) AS "max_value" FROM t1 JOIN t2 ON t2.t1_id = t1.id GROUP BY t2.type, t1.tag ) v ON v.type = 0 AND v.tag = u.tag; } {7 100 8 101} do_execsql_test indexexpr1-2210 { DROP TABLE t1; CREATE TABLE t1(x INT, y TEXT); INSERT INTO t1(x,y) VALUES(1,'{b:5}'); SELECT json_insert('{}', '$.a', coalesce(null,json(y)))->>'$.a.b' FROM t1; } {5} db null NULL do_execsql_test indexexpr1-2211 { CREATE INDEX t1j ON t1(coalesce(null,json(y))); SELECT json_insert('{}', '$.a', coalesce(null,json(y)))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2220 { DROP INDEX t1j; SELECT json_insert('{}', '$.a', iif(1,json(y),123))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2221 { CREATE INDEX t1j ON t1(iif(1,json(y),123)); SELECT json_insert('{}', '$.a', iif(1,json(y),123))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2230 { DROP INDEX t1j; SELECT json_insert('{}', '$.a', ifnull(NULL,json(y)))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2231 { CREATE INDEX t1j ON t1(ifnull(NULL,json(y))); SELECT json_insert('{}', '$.a', ifnull(NULL,json(y)))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2240 { DROP INDEX t1j; SELECT json_insert('{}', '$.a', nullif(json(y),8))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2241 { CREATE INDEX t1j ON t1(nullif(json(y),8)); SELECT json_insert('{}', '$.a', nullif(json(y),8))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2250 { DROP INDEX t1j; SELECT json_insert('{}', '$.a', min('~',json(y)))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2251 { CREATE INDEX t1j ON t1(min('~',json(y))); SELECT json_insert('{}', '$.a', min('~',json(y)))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2260 { DROP INDEX t1j; SELECT json_insert('{}', '$.a', max('...',json(y)))->>'$.a.b' FROM t1; } {5} do_execsql_test indexexpr1-2261 { CREATE INDEX t1j ON t1(max('...',json(y))); SELECT json_insert('{}', '$.a', max('...',json(y)))->>'$.a.b' FROM t1; } {5} # 2023-11-08 Forum post https://sqlite.org/forum/forumpost/68d284c86b082c3e # # Functions that return subtypes and that are indexed cannot be used to # cover function calls from the main table, since the indexed value does # not know the subtype. # |
︙ | ︙ |
Changes to test/join5.test.
︙ | ︙ | |||
366 367 368 369 370 371 372 | do_execsql_test 9.1 { CREATE TABLE t1(a ,b FLOAT); INSERT INTO t1 VALUES(1,1); CREATE INDEX t1x1 ON t1(a,b,a,a,a,a,a,a,a,a,a,b); ANALYZE sqlite_schema; INSERT INTO sqlite_stat1 VALUES('t1','t1x1','648 324 81 81 81 81 81 81 81081 81 81 81'); ANALYZE sqlite_schema; | > > > | > | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | do_execsql_test 9.1 { CREATE TABLE t1(a ,b FLOAT); INSERT INTO t1 VALUES(1,1); CREATE INDEX t1x1 ON t1(a,b,a,a,a,a,a,a,a,a,a,b); ANALYZE sqlite_schema; INSERT INTO sqlite_stat1 VALUES('t1','t1x1','648 324 81 81 81 81 81 81 81081 81 81 81'); ANALYZE sqlite_schema; } do_catchsql_test 9.2 { SELECT a FROM (SELECT a FROM t1 NATURAL LEFT JOIN t1) NATURAL LEFT JOIN t1 WHERE (rowid,1)<=(5,0); } {0 1} # 2022-03-02 https://sqlite.org/forum/info/50a1bbe08ce4c29c # Bloom-filter pulldown is incompatible with skip-scan. # reset_db do_execsql_test 10.1 { CREATE TABLE t1(x INT); |
︙ | ︙ |
Changes to test/joinH.test.
︙ | ︙ | |||
197 198 199 200 201 202 203 | CREATE TABLE wo1(a PRIMARY KEY, b) WITHOUT ROWID; CREATE TABLE wo2(a PRIMARY KEY, rowid) WITHOUT ROWID; CREATE TABLE wo3(a PRIMARY KEY, b) WITHOUT ROWID; } do_catchsql_test 9.1 { SELECT rowid FROM wo1, x1, x2; | | | | | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | CREATE TABLE wo1(a PRIMARY KEY, b) WITHOUT ROWID; CREATE TABLE wo2(a PRIMARY KEY, rowid) WITHOUT ROWID; CREATE TABLE wo3(a PRIMARY KEY, b) WITHOUT ROWID; } do_catchsql_test 9.1 { SELECT rowid FROM wo1, x1, x2; } {1 {ambiguous column name: rowid}} do_catchsql_test 9.2 { SELECT rowid FROM wo1, (x1, x2); } {1 {ambiguous column name: rowid}} do_catchsql_test 9.3 { SELECT rowid FROM wo1 JOIN (x1 JOIN x2); } {1 {ambiguous column name: rowid}} do_catchsql_test 9.4 { SELECT a FROM wo1, x1, x2; } {1 {ambiguous column name: a}} # It is not possible to use "rowid" in a USING clause. # |
︙ | ︙ | |||
304 305 306 307 308 309 310 311 312 | do_execsql_test 12.2 { SELECT * FROM t1 LEFT JOIN t2 ON true RIGHT JOIN t3 ON d2=e3 WHERE c2 BETWEEN NULL AND a1; } do_execsql_test 12.3 { SELECT * FROM t1 LEFT JOIN t2 ON true RIGHT JOIN t3 ON d2=e3 WHERE c2 BETWEEN NULL AND a1; } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | do_execsql_test 12.2 { SELECT * FROM t1 LEFT JOIN t2 ON true RIGHT JOIN t3 ON d2=e3 WHERE c2 BETWEEN NULL AND a1; } do_execsql_test 12.3 { SELECT * FROM t1 LEFT JOIN t2 ON true RIGHT JOIN t3 ON d2=e3 WHERE c2 BETWEEN NULL AND a1; } #------------------------------------------------------------------------- # 2024-04-05 dbsqlfuzz b9e65e2f110df998f1306571fae7af6c01e4d92b reset_db do_execsql_test 13.1 { CREATE TABLE t1(a INT AS (b), b INT); INSERT INTO t1(b) VALUES(123); CREATE TABLE t2(a INT, c INT); SELECT a FROM t2 NATURAL RIGHT JOIN t1; } {123} do_execsql_test 13.2 { CREATE INDEX t1a ON t1(a); SELECT a FROM t2 NATURAL RIGHT JOIN t1; } {123} # Further tests of the same logic (indexes on expressions # used by RIGHT JOIN) from check-in ffe23af73fcb324d and # forum post https://sqlite.org/forum/forumpost/9b491e1debf0b67a. db null NULL do_execsql_test 13.3 { CREATE TABLE t3(a INT, b INT); CREATE UNIQUE INDEX t3x ON t3(a, a+b); INSERT INTO t3(a,b) VALUES(1,2),(4,8),(16,32),(4,80),(1,-300); CREATE TABLE t4(x INT, y INT); INSERT INTO t4(x,y) SELECT a, b FROM t3; INSERT INTO t4(x,y) VALUES(99,99); SELECT a1.a, sum( a1.a+a1.b ) FROM t3 AS a1 RIGHT JOIN t4 ON a=x GROUP BY a1.a ORDER BY 1; } {NULL NULL 1 -592 4 192 16 48} do_execsql_test 13.4 { SELECT sum( a1.a+a1.b ) FROM t3 AS a1 RIGHT JOIN t3 ON true GROUP BY a1.a ORDER BY 1; } {-1480 240 480} finish_test |
Changes to test/json/README.md.
1 2 3 4 5 6 | The files in this subdirectory are used to help measure the performance of the SQLite JSON functions, especially in relation to handling large JSON inputs. # 1.0 Prerequisites | > > | | | | > > > > > > > > > | > | | | > > > > > > > > > > | | > > > | < | | | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | The files in this subdirectory are used to help measure the performance of the SQLite JSON functions, especially in relation to handling large JSON inputs. # 1.0 Prerequisites * Standard SQLite build environment (SQLite source tree, compiler, make, etc.) * Valgrind * Fossil (only the "fossil xdiff" command is used by this procedure) * tclsh # 2.0 Setup * Run: "`tclsh json-generator.tcl | sqlite3 json100mb.db`" to create the 100 megabyte test database. Do this so that the "json100mb.db" file lands in the directory from which you will run tests, not in the test/json subdirectory of the source tree. * Make a copy of "json100mb.db" into "jsonb100mb.db" - change the prefix from "json" to "jsonb". * Bring up jsonb100mb.db in the sqlite3 command-line shell. Convert all of the content into JSONB using a commands like this: > UPDATE data1 SET x=jsonb(x); > VACUUM; * Build the baseline sqlite3.c file with sqlite3.h and shell.c. > make clean sqlite3.c * Run "`sh json-speed-check.sh trunk`". This creates the baseline profile in "jout-trunk.txt" for the preformance test using text JSON. * Run "`sh json-speed-check.sh trunk --jsonb`". This creates the baseline profile in "joutb-trunk.txt" for the performance test for processing JSONB * (Optional) Verify that the json100mb.db database really does contain approximately 100MB of JSON content by running: > SELECT sum(length(x)) FROM data1; > SELECT * FROM data1 WHERE NOT json_valid(x); # 3.0 Testing * Build the sqlite3.c (with sqlite3.h and shell.c) to be tested. * Run "`sh json-speed-check.sh x1`". The profile output will appear in jout-x1.txt. Substitute any label you want in place of "x1". * Run "`sh json-speed-check.sh x1 --jsonb`". The profile output will appear in joutb-x1.txt. Substitute any label you want in place of "x1". * Run the script shown below in the CLI. Divide 2500 by the real elapse time from this test to get an estimate for number of MB/s that the JSON parser is able to process. > .open json100mb.db > .timer on > WITH RECURSIVE c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<25) > SELECT sum(json_valid(x)) FROM c, data1; |
Deleted test/json/json-q1-b.txt.
|
| < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to test/json/json-speed-check.sh.
︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 | LEAN_OPTS="$LEAN_OPTS -DSQLITE_MAX_EXPR_DEPTH=0" LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DECLTYPE" LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DEPRECATED" LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_PROGRESS_CALLBACK" LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_SHARED_CACHE" LEAN_OPTS="$LEAN_OPTS -DSQLITE_USE_ALLOCA" BASELINE="trunk" doExplain=0 doCachegrind=1 doVdbeProfile=0 doWal=1 doDiff=1 doJsonB=0 while test "$1" != ""; do | > | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | LEAN_OPTS="$LEAN_OPTS -DSQLITE_MAX_EXPR_DEPTH=0" LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DECLTYPE" LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DEPRECATED" LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_PROGRESS_CALLBACK" LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_SHARED_CACHE" LEAN_OPTS="$LEAN_OPTS -DSQLITE_USE_ALLOCA" BASELINE="trunk" TYPE="json" doExplain=0 doCachegrind=1 doVdbeProfile=0 doWal=1 doDiff=1 doJsonB=0 while test "$1" != ""; do |
︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | CC=clang ;; --gcc7) CC=gcc-7 ;; --jsonb) doJsonB=1 ;; -*) CC_OPTS="$CC_OPTS $1" ;; *) BASELINE=$1 ;; esac shift done echo "NAME = $NAME" | tee summary-$NAME.txt echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt rm -f cachegrind.out.* jsonshell $CC -g -Os -Wall -I. $CC_OPTS ./shell.c ./sqlite3.c -o jsonshell -ldl -lpthread ls -l jsonshell | tee -a summary-$NAME.txt home=`echo $0 | sed -e 's,/[^/]*$,,'` | > < | < < < | | < | | | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | CC=clang ;; --gcc7) CC=gcc-7 ;; --jsonb) doJsonB=1 TYPE="jsonb" ;; -*) CC_OPTS="$CC_OPTS $1" ;; *) BASELINE=$1 ;; esac shift done echo "NAME = $NAME" | tee summary-$NAME.txt echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt rm -f cachegrind.out.* jsonshell $CC -g -Os -Wall -I. $CC_OPTS ./shell.c ./sqlite3.c -o jsonshell -ldl -lpthread ls -l jsonshell | tee -a summary-$NAME.txt home=`echo $0 | sed -e 's,/[^/]*$,,'` DB=$TYPE''100mb.db echo ./jsonshell $DB "<$home/$TYPE-q1.txt" valgrind --tool=cachegrind ./jsonshell json100mb_b.db <$home/$TYPE-q1.txt \ 2>&1 | tee -a summary-$NAME.txt cg_anno.tcl cachegrind.out.* >$TYPE-$NAME.txt echo '*****************************************************' >>$TYPE-$NAME.txt sed 's/^[0-9=-]\{9\}/==00000==/' summary-$NAME.txt >>$TYPE-$NAME.txt if test "$NAME" != "$BASELINE" -a $doDiff -ne 0; then fossil xdiff --tk -c 20 $TYPE-$BASELINE.txt $TYPE-$NAME.txt fi |
Added test/json/jsonb-q1.txt.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | .mode qbox .timer on .param set $label 'q87' SELECT rowid, x->>$label FROM data1 WHERE x->>$label IS NOT NULL; CREATE TEMP TABLE t2(x JSON TEXT); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<25000), array1(y) AS ( SELECT json_group_array( json_object('x',x,'y',random(),'z',hex(randomblob(50))) ) FROM c ), c2(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c2 WHERE n<5) INSERT INTO t2(x) SELECT jsonb_object('a',n,'b',n*2,'c',y,'d',3,'e',5,'f',6) FROM array1, c2; CREATE INDEX t2x1 ON t2(x->>'a'); CREATE INDEX t2x2 ON t2(x->>'b'); CREATE INDEX t2x3 ON t2(x->>'e'); CREATE INDEX t2x4 ON t2(x->>'f'); UPDATE t2 SET x=jsonb_replace(x,'$.f',(x->>'f')+1); UPDATE t2 SET x=jsonb_set(x,'$.e',(x->>'f')-1); UPDATE t2 SET x=jsonb_remove(x,'$.d'); |
Changes to test/json101.test.
︙ | ︙ | |||
373 374 375 376 377 378 379 380 381 382 383 384 385 386 | WHERE jx.value<>jx.atom AND type NOT IN ('array','object'); } {} do_execsql_test json101-5.8 { SELECT j2.rowid, jx.rowid, fullkey, path, key FROM j2, json_tree(j2.json) AS jx WHERE jx.value<>jx.atom AND type NOT IN ('array','object'); } {} do_execsql_test json101-6.1 { SELECT json_valid('{"a":55,"b":72,}'); } {0} do_execsql_test json101-6.2 { SELECT json_error_position('{"a":55,"b":72,}'); } {0} | > > > > > > > > > > > > > > | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 | WHERE jx.value<>jx.atom AND type NOT IN ('array','object'); } {} do_execsql_test json101-5.8 { SELECT j2.rowid, jx.rowid, fullkey, path, key FROM j2, json_tree(j2.json) AS jx WHERE jx.value<>jx.atom AND type NOT IN ('array','object'); } {} # 2024-02-16 https://sqlite.org/forum/forumpost/ecb94cd210 # Regression in json_tree()/json_each(). The value column # should have the "J" subtype if the value is an array or # object. # do_execsql_test json101-5.10 { SELECT json_insert('{}','$.a',value) FROM json_tree('[1,2,3]') WHERE atom IS NULL; } {{{"a":[1,2,3]}}} # ^^^^^^^--- In double-quotes, a string literal, prior to bug fix do_execsql_test json101-5.11 { SELECT json_insert('{}','$.a',value) FROM json_tree('"[1,2,3]"'); } {{{"a":"[1,2,3]"}}} do_execsql_test json101-6.1 { SELECT json_valid('{"a":55,"b":72,}'); } {0} do_execsql_test json101-6.2 { SELECT json_error_position('{"a":55,"b":72,}'); } {0} |
︙ | ︙ |
Changes to test/json102.test.
︙ | ︙ | |||
759 760 761 762 763 764 765 766 767 | } {ok 2023-08-03 876 5 {{"x":77}}} do_execsql_test json102-1720 { UPDATE t1 SET memo = JSON_SET(memo, '$.y', 6) WHERE a2 IN (876) AND JSON_TYPE(memo, '$.y') IS NULL; PRAGMA integrity_check; SELECT * FROM t1; } {ok 2023-08-03 876 5 {{"x":77,"y":6}}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 | } {ok 2023-08-03 876 5 {{"x":77}}} do_execsql_test json102-1720 { UPDATE t1 SET memo = JSON_SET(memo, '$.y', 6) WHERE a2 IN (876) AND JSON_TYPE(memo, '$.y') IS NULL; PRAGMA integrity_check; SELECT * FROM t1; } {ok 2023-08-03 876 5 {{"x":77,"y":6}}} # 2024-05-21 https://sqlite.org/forum/forumpost/9e52cdfe15c3926e # What if the RHS of the -> or ->> operator is a string that looks # like a number? PostgreSQL treats it as a string. # do_execsql_test json102-1800 { SELECT '{"1":"one","2":"two","3":"three"}'->>'2'; } two db null NULL do_execsql_test json102-1801 { SELECT '{"1":"one","2":"two","3":"three"}'->>2; } NULL do_execsql_test json102-1810 { SELECT '["zero","one","two"]'->>'1'; } NULL do_execsql_test json102-1811 { SELECT '["zero","one","two"]'->>1; } one do_execsql_test json102-1820 { SELECT '{"1":"one","2":"two","3":"three"}'->'2'; } {{"two"}} do_execsql_test json102-1821 { SELECT '{"1":"one","2":"two","3":"three"}'->2; } {NULL} do_execsql_test json102-1830 { SELECT '["zero","one","two"]'->'1'; } {NULL} do_execsql_test json102-1831 { SELECT '["zero","one","two"]'->1; } {{"one"}} finish_test |
Changes to test/json106.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # Invariant tests for JSON built around the randomjson extension # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix json106 load_static_extension db randomjson db eval { CREATE TEMP TABLE t1(j0,j5,p); CREATE TEMP TABLE kv(n,key,val); } unset -nocomplain ii for {set ii 1} {$ii<=5000} {incr ii} { | > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # Invariant tests for JSON built around the randomjson extension # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix json106 # These tests require virtual table "json_tree" to run. ifcapable !vtab { finish_test ; return } load_static_extension db randomjson db eval { CREATE TEMP TABLE t1(j0,j5,p); CREATE TEMP TABLE kv(n,key,val); } unset -nocomplain ii for {set ii 1} {$ii<=5000} {incr ii} { |
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 | } 0 do_execsql_test $ii.7 { UPDATE t1 SET p=json_patch(j0,j5); SELECT count(*) FROM t1, kv WHERE p->>key IS NOT val } 0 } finish_test | > > > > > > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | } 0 do_execsql_test $ii.7 { UPDATE t1 SET p=json_patch(j0,j5); SELECT count(*) FROM t1, kv WHERE p->>key IS NOT val } 0 do_execsql_test $ii.8 { SELECT j0 FROM t1 WHERE json(j0)!=json(json_pretty(j0)); } {} do_execsql_test $ii.9 { SELECT j5 FROM t1 WHERE json(j5)!=json(json_pretty(j5)); } {} } finish_test |
Added test/json107.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | # 2024-01-23 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Legacy JSON bug: If the input is a BLOB that when cast into TEXT looks # like valid JSON, then treat it as valid JSON. # # The original intent of the JSON functions was to raise an error on any # BLOB input. That intent was clearly documented, but the code failed to # to implement it. Subsequently, many applications began to depend on the # incorrect behavior, especially apps that used readfile() to read JSON # content, since readfile() returns a BLOB. So we need to support the # bug moving forward. # # The tests in this fail verify that the original buggy behavior is # preserved. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix json107 if {[db one {PRAGMA encoding}]!="UTF-8"} { # These tests only work for a UTF-8 encoding. finish_test return } do_execsql_test 1.1 { SELECT json_valid( CAST('{"a":1}' AS BLOB) ); } 1 do_execsql_test 1.1.1 { SELECT json_valid( CAST('{"a":1}' AS BLOB), 1); } 1 do_execsql_test 1.1.2 { SELECT json_valid( CAST('{"a":1}' AS BLOB), 2); } 1 do_execsql_test 1.1.4 { SELECT json_valid( CAST('{"a":1}' AS BLOB), 4); } 0 do_execsql_test 1.1.8 { SELECT json_valid( CAST('{"a":1}' AS BLOB), 8); } 0 do_execsql_test 1.2.1 { SELECT CAST('{"a":123}' AS blob) -> 'a'; } 123 do_execsql_test 1.2.2 { SELECT CAST('{"a":123}' AS blob) ->> 'a'; } 123 do_execsql_test 1.2.3 { SELECT json_extract(CAST('{"a":123}' AS blob), '$.a'); } 123 do_execsql_test 1.3 { SELECT json_insert(CAST('{"a":123}' AS blob),'$.b',456); } {{{"a":123,"b":456}}} do_execsql_test 1.4 { SELECT json_remove(CAST('{"a":123,"b":456}' AS blob),'$.a'); } {{{"b":456}}} do_execsql_test 1.5 { SELECT json_set(CAST('{"a":123,"b":456}' AS blob),'$.a',789); } {{{"a":789,"b":456}}} do_execsql_test 1.6 { SELECT json_replace(CAST('{"a":123,"b":456}' AS blob),'$.a',789); } {{{"a":789,"b":456}}} do_execsql_test 1.7 { SELECT json_type(CAST('{"a":123,"b":456}' AS blob)); } object do_execsql_test 1.8 { SELECT json(CAST('{"a":123,"b":456}' AS blob)); } {{{"a":123,"b":456}}} ifcapable vtab { do_execsql_test 2.1 { SELECT key, value FROM json_tree( CAST('{"a":123,"b":456}' AS blob) ) WHERE atom; } {a 123 b 456} } finish_test |
Added test/json108.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | # 2024-03-06 # # 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. # #*********************************************************************** # Invariant tests for JSON built around the randomjson extension # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix json108 # These tests require virtual table "json_tree" to run. ifcapable !vtab { finish_test ; return } load_static_extension db randomjson db eval { CREATE TEMP TABLE t1(j0,j5); WITH RECURSIVE c(n) AS (VALUES(0) UNION ALL SELECT n+1 FROM c WHERE n<9) INSERT INTO t1 SELECT random_json(n), random_json5(n) FROM c; } do_execsql_test 1.1 { SELECT count(*) FROM t1 WHERE json(j0)==json(json_pretty(j0,NULL)); } 10 do_execsql_test 1.2 { SELECT count(*) FROM t1 WHERE json(j0)==json(json_pretty(j0,NULL)); } 10 do_execsql_test 1.3 { SELECT count(*) FROM t1 WHERE json(j0)==json(json_pretty(j0,'')); } 10 do_execsql_test 1.4 { SELECT count(*) FROM t1 WHERE json(j0)==json(json_pretty(j0,char(9))); } 10 do_execsql_test 1.5 { SELECT count(*) FROM t1 WHERE json(j0)==json(json_pretty(j0,'/*hello*/')); } 10 finish_test |
Changes to test/json501.test.
︙ | ︙ | |||
301 302 303 304 305 306 307 308 309 | } xyz # 2023-11-08 forum/forumpost/ddcad3e884 # do_execsql_test 13.1 { SELECT json('{x:''a "b" c''}'); } {{{"x":"a \"b\" c"}}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | } xyz # 2023-11-08 forum/forumpost/ddcad3e884 # do_execsql_test 13.1 { SELECT json('{x:''a "b" c''}'); } {{{"x":"a \"b\" c"}}} # 2024-01-31 # Allow control characters within JSON5 string literals. # for {set c 1} {$c<=0x1f} {incr c} { do_execsql_test 14.$c.1 { SELECT json_valid('"abc' || char($c) || 'xyz"'); } {0} do_execsql_test 14.$c.2 { SELECT json_valid('"abc' || char($c) || 'xyz"', 2); } {1} switch $c { 8 {set e "\\b"} 9 {set e "\\t"} 10 {set e "\\n"} 12 {set e "\\f"} 13 {set e "\\r"} default {set e [format "\\u00%02x" $c]} } do_execsql_test 14.$c.3 { SELECT json('{label:"abc' || char($c) || 'xyz"}'); } "{{\"label\":\"abc${e}xyz\"}}" do_execsql_test 14.$c.4 { SELECT jsonb('{label:"abc' || char($c) || 'xyz"}') -> '$'; } "{{\"label\":\"abc${e}xyz\"}}" } finish_test |
Changes to test/jsonb01.test.
︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 | do_execsql_test jsonb01-1.2.$id.1 { SELECT json(jsonb_remove(x,$path)) FROM t1; } $res do_execsql_test jsonb01-1.2.$id.2 { SELECT json_remove(x,$path) FROM t1; } $res } finish_test | > > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 | do_execsql_test jsonb01-1.2.$id.1 { SELECT json(jsonb_remove(x,$path)) FROM t1; } $res do_execsql_test jsonb01-1.2.$id.2 { SELECT json_remove(x,$path) FROM t1; } $res } do_catchsql_test jsonb01-2.0 { SELECT x'8ce6ffffffff171333' -> '$'; } {1 {malformed JSON}} finish_test |
Changes to test/lemon-test01.y.
1 2 3 4 5 6 7 8 9 10 11 | // A test case for the LEMON parser generator. Run as follows: // // lemon lemon-test01.y && gcc -g lemon-test01.c && ./a.out // %token_prefix TK_ %token_type int %default_type int %include { static int nSyntaxError = 0; static int nAccept = 0; static int nFailure = 0; | > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // A test case for the LEMON parser generator. Run as follows: // // lemon lemon-test01.y && gcc -g lemon-test01.c && ./a.out // // This testcase was made obsolete by check-in 7cca80808cef192f on // 2021-08-17 (associated with Forum Thread // https://sqlite.org/forum/forumpost/bd91fd965c9803c4) and no longer // works. It is retained for historical reference only. // %token_prefix TK_ %token_type int %default_type int %include { static int nSyntaxError = 0; static int nAccept = 0; static int nFailure = 0; |
︙ | ︙ | |||
24 25 26 27 28 29 30 | nFailure++; } %code { #include <assert.h> #include "lemon-test01.h" static int nTest = 0; static int nErr = 0; | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | nFailure++; } %code { #include <assert.h> #include "lemon-test01.h" static int nTest = 0; static int nErr = 0; static void testCase(int testId, int shouldBe, int actual){ nTest++; if( shouldBe==actual ){ printf("test %d: ok\n", testId); }else{ printf("test %d: got %d, expected %d\n", testId, actual, shouldBe); nErr++; } |
︙ | ︙ |
Added test/literal.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | # 2024-01-19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests for SQL literals set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix literal proc test_literal {tn lit type val} { do_execsql_test $tn.1 "SELECT typeof( $lit ), $lit" [list $type $val] ifcapable altertable { do_execsql_test $tn.2 " DROP TABLE IF EXISTS x1; CREATE TABLE x1(a); INSERT INTO x1 VALUES(123); ALTER TABLE x1 ADD COLUMN b DEFAULT $lit ; SELECT typeof(b), b FROM x1; " [list $type $val] } do_execsql_test $tn.3 " DROP TABLE IF EXISTS x1; CREATE TABLE x1(a DEFAULT $lit); INSERT INTO x1 DEFAULT VALUES; SELECT typeof(a), a FROM x1; " [list $type $val] } proc test_literal_error {tn lit unrec} { do_catchsql_test $tn "SELECT $lit" "1 {unrecognized token: \"$unrec\"}" } test_literal 1.0 45 integer 45 test_literal 1.1 0xFF integer 255 test_literal 1.2 0xFFFFFFFF integer [expr 0xFFFFFFFF] test_literal 1.3 0x123FFFFFFFF integer [expr 0x123FFFFFFFF] test_literal 1.4 -0x123FFFFFFFF integer [expr -1 * 0x123FFFFFFFF] test_literal 1.5 0xFFFFFFFFFFFFFFFF integer -1 test_literal 1.7 0x7FFFFFFFFFFFFFFF integer [expr 0x7FFFFFFFFFFFFFFF] test_literal 1.8 -0x7FFFFFFFFFFFFFFF integer [expr -0x7FFFFFFFFFFFFFFF] test_literal 1.9 +0x7FFFFFFFFFFFFFFF integer [expr +0x7FFFFFFFFFFFFFFF] test_literal 1.10 -45 integer -45 test_literal 1.11 '0xFF' text 0xFF test_literal 1.12 '-0xFF' text -0xFF test_literal 1.13 -'0xFF' integer 0 test_literal 1.14 -9223372036854775808 integer -9223372036854775808 test_literal 2.1 1e12 real 1000000000000.0 test_literal 2.2 1.0 real 1.0 test_literal 2.3 1e1000 real Inf test_literal 2.4 -1e1000 real -Inf test_literal 3.1 1_000 integer 1000 test_literal 3.2 1.1_1 real 1.11 test_literal 3.3 1_0.1_1 real 10.11 test_literal 3.4 1e1_000 real Inf test_literal 3.5 12_3_456.7_8_9 real 123456.789 test_literal 3.6 9_223_372_036_854_775_807 integer 9223372036854775807 test_literal 3.7 9_223_372_036_854_775_808 real 9.22337203685478e+18 test_literal 3.8 -9_223_372_036_854_775_808 integer -9223372036854775808 foreach {tn lit unrec} { 0 123a456 123a456 1 1_ 1_ 2 1_.4 1_.4 3 1e_4 1e_4 4 1_e4 1_e4 5 1.4_e4 1.4_e4 6 1.4e+_4 1.4e 7 1.4e-_4 1.4e 8 1.4e4_ 1.4e4_ 9 1.4_e4 1.4_e4 10 1.4e_4 1.4e_4 11 12__34 12__34 12 1234_ 1234_ 13 12._34 12._34 14 12_.34 12_.34 15 12.34_ 12.34_ 16 1.0e1_______2 1.0e1_______2 } { test_literal_error 4.$tn $lit $unrec } # dbsqlfuzz e3186a9e7826e9cd7f4085aa4452f8696485f9e1 # See tag-20240224-a and -b # do_catchsql_test 5.1 { SELECT 1 ORDER BY 2_3; } {1 {1st ORDER BY term out of range - should be between 1 and 1}} finish_test |
Added test/literal2.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | # 2018 May 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # source [file join [file dirname $argv0] pg_common.tcl] #========================================================================= start_test literal2 "2024 Jan 23" execsql_test 1.0 { SELECT 123_456 } errorsql_test 1.1 { SELECT 123__456 } execsql_float_test 2.1 { SELECT 1.0e1_2 } execsql_test 3.0.0 { SELECT 0xFF_FF } execsql_test 3.0.1 { SELECT 0xFF_EF } errorsql_test 3.0.2 { SELECT 0xFF__EF } # errorsql_test 3.0.3 { SELECT 0x_FFEF } errorsql_test 3.0.4 { SELECT 0xFFEF_ } execsql_test 3.1.0 { SELECT 0XFF_FF } execsql_test 3.1.1 { SELECT 0XFF_EF } errorsql_test 3.1.2 { SELECT 0XFF__EF } # errorsql_test 3.1.3 { SELECT 0X_FFEF } errorsql_test 3.1.4 { SELECT 0XFFEF_ } finish_test |
Added test/literal2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | # 2024 Jan 23 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # #################################################### # DO NOT EDIT! THIS FILE IS AUTOMATICALLY GENERATED! #################################################### set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix literal2 do_execsql_test 1.0 { SELECT 123_456 } {123456} # PG says ERROR: trailing junk after numeric literal at or near "123_" do_test 1.1 { catch { execsql { SELECT 123__456 } } } 1 do_test 2.1 { set myres {} foreach r [db eval {SELECT 1.0e1_2}] { lappend myres [format %.4f [set r]] } set res2 {1000000000000.0000} set i 0 foreach r [set myres] r2 [set res2] { if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} { error "list element [set i] does not match: got=[set r] expected=[set r2]" } incr i } set {} {} } {} do_execsql_test 3.0.0 { SELECT 0xFF_FF } {65535} do_execsql_test 3.0.1 { SELECT 0xFF_EF } {65519} # PG says ERROR: trailing junk after numeric literal at or near "0xFF_" do_test 3.0.2 { catch { execsql { SELECT 0xFF__EF } } } 1 # PG says ERROR: trailing junk after numeric literal at or near "0xFFEF_" do_test 3.0.4 { catch { execsql { SELECT 0xFFEF_ } } } 1 do_execsql_test 3.1.0 { SELECT 0XFF_FF } {65535} do_execsql_test 3.1.1 { SELECT 0XFF_EF } {65519} # PG says ERROR: trailing junk after numeric literal at or near "0XFF_" do_test 3.1.2 { catch { execsql { SELECT 0XFF__EF } } } 1 # PG says ERROR: trailing junk after numeric literal at or near "0XFFEF_" do_test 3.1.4 { catch { execsql { SELECT 0XFFEF_ } } } 1 finish_test |
Changes to test/memdb1.test.
︙ | ︙ | |||
80 81 82 83 84 85 86 | db deserialize -readonly 1 $db1 db eval {SELECT * FROM t1} } {1 2} do_test 152 { catchsql {INSERT INTO t1 VALUES(3,4);} } {1 {attempt to write a readonly database}} | < | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | db deserialize -readonly 1 $db1 db eval {SELECT * FROM t1} } {1 2} do_test 152 { catchsql {INSERT INTO t1 VALUES(3,4);} } {1 {attempt to write a readonly database}} do_test 160 { db deserialize -maxsize 32768 $db1 db eval {SELECT * FROM t1} } {1 2} do_test 161 { db eval {INSERT INTO t1 VALUES(3,4); SELECT * FROM t1} } {1 2 3 4} |
︙ | ︙ | |||
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | CREATE TABLE t2(x, y); } {wal} db close set fd [open test.db] fconfigure $fd -translation binary -encoding binary set data [read $fd [expr 20*1024]] sqlite3 db "" db deserialize $data do_execsql_test 810 { PRAGMA locking_mode = exclusive; SELECT * FROM t1 } {exclusive 1 2} do_execsql_test 820 { INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; } {1 2 3 4} do_catchsql_test 830 { PRAGMA wal_checkpoint; } {1 {database disk image is malformed}} } finish_test | > > > > > > > > > > > > > > | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | CREATE TABLE t2(x, y); } {wal} db close set fd [open test.db] fconfigure $fd -translation binary -encoding binary set data [read $fd [expr 20*1024]] close $fd sqlite3 db "" db deserialize $data do_execsql_test 810 { PRAGMA locking_mode = exclusive; SELECT * FROM t1 } {exclusive 1 2} do_execsql_test 820 { INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; } {1 2 3 4} do_catchsql_test 830 { PRAGMA wal_checkpoint; } {1 {database disk image is malformed}} } # 2024-01-20 # https://sqlite.org/forum/forumpost/498777780e16880a # # Make sure a database is initialized before serializing it. # reset_db sqlite3 dbempty :memory: do_test 900 { set len [string length [dbempty serialize]] expr {$len>0} } 1 dbempty close finish_test |
Changes to test/misc1.test.
︙ | ︙ | |||
650 651 652 653 654 655 656 | # 2015-03-22: NULL pointer dereference after a syntax error # do_catchsql_test misc1-21.1 { select''like''like''like#0; } {1 {near "#0": syntax error}} do_catchsql_test misc1-21.2 { VALUES(0,0x0MATCH#0; | | | 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | # 2015-03-22: NULL pointer dereference after a syntax error # do_catchsql_test misc1-21.1 { select''like''like''like#0; } {1 {near "#0": syntax error}} do_catchsql_test misc1-21.2 { VALUES(0,0x0MATCH#0; } {1 {unrecognized token: "0x0MATCH"}} # 2015-04-15 do_execsql_test misc1-22.1 { SELECT ''+3 FROM (SELECT ''+5); } {3} # 2015-04-19: NULL pointer dereference on a corrupt schema |
︙ | ︙ |
Changes to test/misc2.test.
︙ | ︙ | |||
50 51 52 53 54 55 56 | CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(1,2,3); CREATE TABLE t2(a,b,c); INSERT INTO t2 VALUES(7,8,9); } } {} ifcapable subquery { | > | | > > > > | > > | | | > > > > > | > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(1,2,3); CREATE TABLE t2(a,b,c); INSERT INTO t2 VALUES(7,8,9); } } {} ifcapable subquery { ifcapable allow_rowid_in_view { do_catchsql_test misc2-2.2 { SELECT rowid, * FROM (SELECT * FROM t1, t2); } {0 {{} 1 2 3 7 8 9}} } else { do_catchsql_test misc2-2.2 { SELECT rowid, * FROM (SELECT * FROM t1, t2); } {1 {no such column: rowid}} } do_catchsql_test misc2-2.2b { SELECT 'rowid', * FROM (SELECT * FROM t1, t2); } {0 {rowid 1 2 3 7 8 9}} } ifcapable view { ifcapable allow_rowid_in_view { do_catchsql_test misc2-2.3 { CREATE VIEW v1 AS SELECT * FROM t1, t2; SELECT rowid, * FROM v1; } {0 {{} 1 2 3 7 8 9}} } else { do_catchsql_test misc2-2.3 { CREATE VIEW v1 AS SELECT * FROM t1, t2; SELECT rowid, * FROM v1; } {1 {no such column: rowid}} } do_catchsql_test misc2-2.3b { SELECT 'rowid', * FROM v1; } {0 {rowid 1 2 3 7 8 9}} } ;# ifcapable view # Ticket #2002 and #1952. ifcapable subquery { |
︙ | ︙ |
Changes to test/misc5.test.
︙ | ︙ | |||
565 566 567 568 569 570 571 | SELECT * FROM logs LIMIT (SELECT lmt FROM logs_base) ; } } {1 {no such table: logs_base}} } # Overflow the lemon parser stack by providing an overly complex | | | | | > > > > > > > > | > > > > | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 | SELECT * FROM logs LIMIT (SELECT lmt FROM logs_base) ; } } {1 {no such table: logs_base}} } # Overflow the lemon parser stack by providing an overly complex # expression. Make sure that the overflow is detected and the # stack is grown automatically such that the application calling # SQLite never notices. # do_test misc5-7.1.1 { execsql {CREATE TABLE t1(x)} set sql "INSERT INTO t1 VALUES(" set tail "" for {set i 0} {$i<200} {incr i} { append sql "(1+" append tail ")" } append sql "0$tail); SELECT * FROM t1;" catchsql $sql } {0 200} do_test misc5-7.1.2 { execsql {DELETE FROM t1} set sql "INSERT INTO t1 VALUES(" set tail "" for {set i 0} {$i<900} {incr i} { append sql "(1+" append tail ")" } append sql "0$tail); SELECT * FROM t1;" catchsql $sql } {0 900} # Parser stack overflow is silently ignored when it occurs while parsing the # schema and PRAGMA writable_schema is turned on. # do_test misc5-7.2 { sqlite3 db2 :memory: sqlite3_db_config db2 DEFENSIVE 0 |
︙ | ︙ |
Changes to test/misc8.test.
︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | 0 8 {} 10 {} {} 0 9 {} 10 {} {} 0 10 {} 10 {} {} } # 2016-02-26: An assertion fault found by the libFuzzer project # do_catchsql_test misc8-3.0 { SELECT * FROM ( (SELECT 0 AS i) AS x1, (SELECT 1) AS x2 ) AS x3, (SELECT 6 AS j UNION ALL SELECT 7) AS x4 WHERE i<rowid ORDER BY 1; | > > > > > | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | 0 8 {} 10 {} {} 0 9 {} 10 {} {} 0 10 {} 10 {} {} } # 2016-02-26: An assertion fault found by the libFuzzer project # ifcapable allow_rowid_in_view { set nosuch "1 {ambiguous column name: rowid}" } else { set nosuch "1 {no such column: rowid}" } do_catchsql_test misc8-3.0 { SELECT * FROM ( (SELECT 0 AS i) AS x1, (SELECT 1) AS x2 ) AS x3, (SELECT 6 AS j UNION ALL SELECT 7) AS x4 WHERE i<rowid ORDER BY 1; } $nosuch # The SQLITE_DBCONFIG_MAINDBNAME interface # db close forcedelete test.db test2.db sqlite3 db test.db do_execsql_test misc8-4.0 { |
︙ | ︙ |
Changes to test/mmap1.test.
︙ | ︙ | |||
41 42 43 44 45 46 47 | string range [string repeat [set str] [expr [set n]/4]] 1 [set n] } $dbname func rblob rblob }] } | | | | | | | | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | string range [string repeat [set str] [expr [set n]/4]] 1 [set n] } $dbname func rblob rblob }] } # For cases 1.1 and 1.4, the number of pages read using xRead() is 8 on # unix and 12 on windows. The difference is that windows only ever maps # an integer number of OS pages (i.e. creates mappings that are a multiple # of 4KB in size). Whereas on unix any sized mapping may be created. # foreach {t mmap_size nRead c2init} { 1.1 { PRAGMA mmap_size = 67108864 } /8|12/ {PRAGMA mmap_size = 0} 1.2 { PRAGMA mmap_size = 53248 } /15[34]/ {PRAGMA mmap_size = 0} 1.3 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 0} 1.4 { PRAGMA mmap_size = 67108864 } /12|8/ {PRAGMA mmap_size = 67108864 } 1.5 { PRAGMA mmap_size = 53248 } /15[34]/ {PRAGMA mmap_size = 67108864 } 1.6 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 67108864 } } { do_multiclient_test tn { sql1 {PRAGMA cache_size=2000} sql2 {PRAGMA cache_size=2000} sql1 {PRAGMA page_size=1024} |
︙ | ︙ |
Added test/mmapcorrupt.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # 2024 January 23 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Test special cases of corrupt database handling in mmap-mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix mmapcorrupt ifcapable !mmap { finish_test return } database_may_be_corrupt db close sqlite3_shutdown sqlite3_config_lookaside 0 0 sqlite3_initialize reset_db do_execsql_test 1.0 { PRAGMA page_size = 16384; CREATE TABLE tn1(a PRIMARY KEY) WITHOUT ROWID; CREATE TABLE t0(a PRIMARY KEY) WITHOUT ROWID; CREATE TABLE t1(a PRIMARY KEY) WITHOUT ROWID; INSERT INTO t1 VALUES('B'); } db close set sz [file size test.db] hexio_write test.db [expr $sz-3] 800380 sqlite3 db test.db do_execsql_test 2.1 { PRAGMA mmap_size = 1000000; SELECT sql FROM sqlite_schema LIMIT 1; SELECT * FROM t0; } {1000000 {CREATE TABLE tn1(a PRIMARY KEY) WITHOUT ROWID}} do_execsql_test 2.2 { INSERT INTO t0 SELECT * FROM t1; } finish_test |
Changes to test/notnull2.test.
︙ | ︙ | |||
55 56 57 58 59 60 61 | } 100 {} do_vmstep_test 1.4.2 { SELECT * FROM t2 WHERE 0==( c IS NOT NULL ) } +1000 {} do_vmstep_test 1.5.1 { SELECT count(*) FROM t2 WHERE EXISTS( | | | | | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | } 100 {} do_vmstep_test 1.4.2 { SELECT * FROM t2 WHERE 0==( c IS NOT NULL ) } +1000 {} do_vmstep_test 1.5.1 { SELECT count(*) FROM t2 WHERE EXISTS( SELECT 1 FROM t1 WHERE t1.a=450 AND t2.d IS NULL ) } 7000 {0} do_vmstep_test 1.5.2 { SELECT count(*) FROM t2 WHERE EXISTS( SELECT 1 FROM t1 WHERE t1.a=450 AND t2.c IS NULL ) } +8000 {0} #------------------------------------------------------------------------- reset_db do_execsql_test 2.0 { CREATE TABLE T1(a INTEGER PRIMARY KEY, b); CREATE TABLE T3(k, v); } |
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 | } {{} 1 / missing {} /} do_execsql_test 4.1 { CREATE TABLE t1(a INT); INSERT INTO t1(a) VALUES(1); CREATE TABLE t2(b INT); SELECT * FROM (SELECT 3 AS c FROM t1) AS t3 LEFT JOIN t2 ON c IS NULL; } {3 {}} finish_test | > > > > > > > > | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | } {{} 1 / missing {} /} do_execsql_test 4.1 { CREATE TABLE t1(a INT); INSERT INTO t1(a) VALUES(1); CREATE TABLE t2(b INT); SELECT * FROM (SELECT 3 AS c FROM t1) AS t3 LEFT JOIN t2 ON c IS NULL; } {3 {}} # 2024-03-08 https://sqlite.org/forum/forumpost/440f2a2f17 # reset_db do_execsql_test 5.0 { CREATE TABLE t1(a INT NOT NULL); SELECT a IS NULL, a IS NOT NULL, count(*) FROM t1; } {1 0 0} finish_test |
Changes to test/orderby1.test.
︙ | ︙ | |||
516 517 518 519 520 521 522 | } do_eqp_test 8.1 { SELECT * FROM t1 ORDER BY a, b; } { QUERY PLAN |--SCAN t1 USING INDEX i1 | | | 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | } do_eqp_test 8.1 { SELECT * FROM t1 ORDER BY a, b; } { QUERY PLAN |--SCAN t1 USING INDEX i1 `--USE TEMP B-TREE FOR LAST TERM OF ORDER BY } do_execsql_test 8.2 { WITH cnt(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<10000 ) INSERT INTO t1 SELECT i%2, randomblob(500) FROM cnt; |
︙ | ︙ |
Changes to test/permutations.test.
︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 100 101 102 103 104 | foreach f [glob -nocomplain \ $testdir/../ext/rtree/*.test \ $testdir/../ext/fts5/test/*.test \ $testdir/../ext/expert/*.test \ $testdir/../ext/lsm1/test/*.test \ $testdir/../ext/recover/*.test \ $testdir/../ext/rbu/*.test \ ] { lappend alltests $f } foreach f [glob -nocomplain $testdir/../ext/session/*.test] { lappend alltests $f } unset f | > | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | foreach f [glob -nocomplain \ $testdir/../ext/rtree/*.test \ $testdir/../ext/fts5/test/*.test \ $testdir/../ext/expert/*.test \ $testdir/../ext/lsm1/test/*.test \ $testdir/../ext/recover/*.test \ $testdir/../ext/rbu/*.test \ $testdir/../ext/intck/*.test \ ] { lappend alltests $f } foreach f [glob -nocomplain $testdir/../ext/session/*.test] { lappend alltests $f } unset f |
︙ | ︙ |
Changes to test/pragma.test.
︙ | ︙ | |||
368 369 370 371 372 373 374 | # make the index appear to be empty. # set offset [expr {$pgsz*($rootpage-1)}] hexio_write test.db $offset 0a00000000040000000000 db close sqlite3 db test.db execsql {PRAGMA integrity_check} | | | | | > > > | | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | # make the index appear to be empty. # set offset [expr {$pgsz*($rootpage-1)}] hexio_write test.db $offset 0a00000000040000000000 db close sqlite3 db test.db execsql {PRAGMA integrity_check} } {{wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2}} do_test pragma-3.3 { execsql {PRAGMA integrity_check=1} } {{wrong # of entries in index i2}} do_test pragma-3.4 { execsql { ATTACH DATABASE 'test.db' AS t2; PRAGMA integrity_check } } {{wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2}} do_test pragma-3.5 { execsql { PRAGMA integrity_check=4 } } {{wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}} do_catchsql_test pragma-3.5.2 { PRAGMA integrity_check='4' } {1 {no such table: 4}} do_catchsql_test pragma-3.6 { PRAGMA integrity_check=xyz } {1 {no such table: xyz}} do_catchsql_test pragma-3.6b { PRAGMA integrity_check=t2 } {0 {{wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2}}} do_catchsql_test pragma-3.6c { PRAGMA integrity_check=sqlite_schema } {0 ok} do_test pragma-3.7 { execsql { PRAGMA integrity_check=0 } } {{wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2}} # Add additional corruption by appending unused pages to the end of # the database file testerr.db # do_test pragma-3.8 { execsql {DETACH t2} forcedelete testerr.db testerr.db-journal |
︙ | ︙ | |||
431 432 433 434 435 436 437 | execsql { ATTACH 'testerr.db' AS t2; PRAGMA integrity_check } } {{*** in database t2 *** Page 4: never used Page 5: never used | | | | | | 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | execsql { ATTACH 'testerr.db' AS t2; PRAGMA integrity_check } } {{*** in database t2 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2}} do_execsql_test pragma-3.9b { PRAGMA t2.integrity_check=t2; } {{wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2}} do_execsql_test pragma-3.9c { PRAGMA t2.integrity_check=sqlite_schema; } {ok} do_test pragma-3.10 { execsql { PRAGMA integrity_check=1 } } {{*** in database t2 *** Page 4: never used}} do_test pragma-3.11 { execsql { PRAGMA integrity_check=5 } } {{*** in database t2 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2} {row 1 missing from index i2}} do_test pragma-3.12 { execsql { PRAGMA integrity_check=4 } } {{*** in database t2 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2}} do_test pragma-3.13 { execsql { PRAGMA integrity_check=3 } } {{*** in database t2 *** Page 4: never used Page 5: never used |
︙ | ︙ | |||
483 484 485 486 487 488 489 | execsql { ATTACH 'testerr.db' AS t3; PRAGMA integrity_check } } {{*** in database t2 *** Page 4: never used Page 5: never used | | | | | | | | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | execsql { ATTACH 'testerr.db' AS t3; PRAGMA integrity_check } } {{*** in database t2 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {*** in database t3 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2}} do_test pragma-3.16 { execsql { PRAGMA integrity_check(10) } } {{*** in database t2 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {*** in database t3 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2}} do_test pragma-3.17 { execsql { PRAGMA integrity_check=8 } } {{*** in database t2 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {*** in database t3 *** Page 4: never used Page 5: never used}} do_test pragma-3.18 { execsql { PRAGMA integrity_check=4 } } {{*** in database t2 *** Page 4: never used Page 5: never used Page 6: never used} {wrong # of entries in index i2}} } do_test pragma-3.19 { catch {db close} forcedelete test.db test.db-journal sqlite3 db test.db db eval {PRAGMA integrity_check} } {ok} |
︙ | ︙ | |||
552 553 554 555 556 557 558 559 560 561 562 563 564 565 | } {{non-unique entry in index t1a} {NULL value in t1x.a} {non-unique entry in index t1a}} do_execsql_test pragma-3.22 { PRAGMA integrity_check(2); } {{non-unique entry in index t1a} {NULL value in t1x.a}} do_execsql_test pragma-3.23 { PRAGMA integrity_check(1); } {{non-unique entry in index t1a}} } # PRAGMA integrity check (or more specifically the sqlite3BtreeCount() # interface) used to leave index cursors in an inconsistent state # which could result in an assertion fault in sqlite3BtreeKey() # called from saveCursorPosition() if content is removed from the # index while the integrity_check is still running. This test verifies | > > > > > > > > > > > > > > > | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | } {{non-unique entry in index t1a} {NULL value in t1x.a} {non-unique entry in index t1a}} do_execsql_test pragma-3.22 { PRAGMA integrity_check(2); } {{non-unique entry in index t1a} {NULL value in t1x.a}} do_execsql_test pragma-3.23 { PRAGMA integrity_check(1); } {{non-unique entry in index t1a}} # forum post https://sqlite.org/forum/forumpost/ee4f6fa5ab do_execsql_test pragma-3.24 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(a); INSERT INTO t1 VALUES (1); ALTER TABLE t1 ADD COLUMN b NOT NULL DEFAULT 0.25; SELECT * FROM t1; PRAGMA integrity_check(t1); } {1 0.25 ok} do_execsql_test pragma-3.25 { ALTER TABLE t1 ADD COLUMN c CHECK (1); SELECT * FROM t1; PRAGMA integrity_check(t1); } {1 0.25 {} ok} } # PRAGMA integrity check (or more specifically the sqlite3BtreeCount() # interface) used to leave index cursors in an inconsistent state # which could result in an assertion fault in sqlite3BtreeKey() # called from saveCursorPosition() if content is removed from the # index while the integrity_check is still running. This test verifies |
︙ | ︙ |
Changes to test/pragma4.test.
︙ | ︙ | |||
79 80 81 82 83 84 85 | do_pragma_ncol_test 1.$tn.1 $sql 0 } # EXPLAIN on a PRAGMA integrity_check. # Verify that that P4_INTARRAY argument to OP_IntegrityCk is rendered # correctly. # | | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | do_pragma_ncol_test 1.$tn.1 $sql 0 } # EXPLAIN on a PRAGMA integrity_check. # Verify that that P4_INTARRAY argument to OP_IntegrityCk is rendered # correctly. # catch {db close} forcedelete test.db sqlite3 db test.db do_test pragma4-2.100 { db eval { PRAGMA page_size=512; CREATE TABLE t1(x); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<10000) INSERT INTO t1(x) SELECT zeroblob(300) FROM c; CREATE TABLE t2(y); DROP TABLE t1; } string map {\[ x \] x \173 {} \175 {}} \ [db eval {EXPLAIN PRAGMA integrity_check}] } {/ IntegrityCk 1 2 8 x[0-9]+,1x /} #-------------------------------------------------------------------------- # reset_db forcedelete test.db2 do_execsql_test 4.1.1 { |
︙ | ︙ | |||
259 260 261 262 263 264 265 266 | CREATE TABLE t4(a DEFAULT 'abc' /* comment */, b DEFAULT -1 -- comment , c DEFAULT +4.0 /* another comment */ ); PRAGMA table_info = t4; } { 0 a {} 0 'abc' 0 1 b {} 0 -1 0 2 c {} 0 +4.0 0 } | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | CREATE TABLE t4(a DEFAULT 'abc' /* comment */, b DEFAULT -1 -- comment , c DEFAULT +4.0 /* another comment */ ); PRAGMA table_info = t4; } { 0 a {} 0 'abc' 0 1 b {} 0 -1 0 2 c {} 0 +4.0 0 } # 2024-03-24 https://sqlite.org/forum/forumpost/85b6a8b6705fb77a # catch {db2 close} catch {db3 close} ifcapable vtab { reset_db do_execsql_test 6.0 { DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; CREATE TABLE t1(a INT PRIMARY KEY, b INT); CREATE TABLE t2(c INT PRIMARY KEY, d INT REFERENCES t1); SELECT t.name, f."table", f."from", i.name, i.pk FROM pragma_table_list() AS t JOIN pragma_foreign_key_list(t.name, t.schema) AS f JOIN pragma_table_info(f."table", t.schema) AS i WHERE i.pk; } {t2 t1 d a 1} } # 2024-05-08 https://sqlite.org/forum/forumpost/cf29a33e94 # ifcapable vtab { do_execsql_test 7.0 { CREATE TABLE t3 ("a" TEXT, "b" TEXT); CREATE TABLE t4 ("a" TEXT, "b" TEXT, "c" TEXT); } do_execsql_test 7.1 { CREATE TABLE pragma_t3 AS SELECT * FROM pragma_table_info('t3'); CREATE TABLE pragma_t4 AS SELECT * FROM pragma_table_info('t4'); } do_execsql_test 7.2 { SELECT pragma_t4.name, pragma_t3.name FROM pragma_t4 RIGHT JOIN pragma_t3 ON (pragma_t4.name=pragma_t3.name); } {a a b b} do_execsql_test 7.3 { SELECT t4.name, t3.name FROM pragma_table_info('t4') t4 RIGHT JOIN pragma_table_info('t3') t3 ON (t4.name=t3.name); } {a a b b} } finish_test |
Added test/pragma6.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | # 2024 February 27 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements tests for PRAGMAs quick_check and integrity_check. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix pragma6 database_may_be_corrupt #------------------------------------------------------------------------- # do_test 1.0 { sqlite3 db {} db deserialize [decode_hexdb { .open --hexdb | size 12288 pagesize 4096 filename crash-540f4c1eb1e7ac.db | page 1 offset 0 | 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. | 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 03 .....@ ........ | 32: 00 bb 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ | 96: 00 00 00 00 0d 00 00 00 02 0f 7f 00 0f c3 0f 7f ................ | 3952: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 ...............B | 3968: 02 06 17 11 11 01 71 74 61 62 6c 65 74 32 74 32 ......qtablet2t2 | 3984: 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 32 .CREATE TABLE t2 | 4000: 28 61 20 49 4e 54 2c 20 62 20 41 53 20 28 61 2a (a INT, b AS (a* | 4016: 32 29 20 53 54 4f 52 45 44 20 4e 4f 54 20 4e 55 2) STORED NOT NU | 4032: 4c 4c 29 3b 01 06 17 11 11 01 63 74 61 62 6c 65 LL);......ctable | 4048: 74 31 74 31 02 43 52 45 41 54 45 20 54 41 42 4c t1t1.CREATE TABL | 4064: 45 20 74 31 28 61 20 49 4e 54 2c 20 62 20 41 53 E t1(a INT, b AS | 4080: 20 28 61 2a 32 29 20 4e 4f 54 20 4e 55 4c 4c 29 (a*2) NOT NULL) | page 2 offset 4096 | 0: 0d 00 00 00 05 0f e7 00 00 00 00 00 00 00 00 00 ................ | 4064: 00 00 00 00 00 00 00 00 03 05 02 01 05 03 04 02 ................ | 4080: 01 04 03 03 02 01 03 03 02 02 01 02 02 01 02 09 ................ | page 3 offset 8192 | 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ | 4048: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 05 ................ | 4064: 03 01 01 05 0a 05 04 03 01 01 04 08 05 03 03 01 ................ | 4080: 01 03 06 05 02 03 00 00 00 00 00 00 00 00 00 00 ................ | end crash-540f4c1eb1e7ac.db }] } {} do_test 1.1 { execsql { CREATE TEMP TABLE t2( a t1 PRIMARY KEY default 27, b default(current_timestamp), d TEXT UNIQUE DEFAULT 'ch`arlie', c TEXT UNIQUE DEFAULT 084, UNIQUE(c,b,b,a,b) ) WITHOUT ROWID; } catchsql { INSERT INTO t1(a) VALUES(zeroblob(40000)) } set {} {} } {} do_test 1.2 { execsql { PRAGMA integrity_check; } execsql { PRAGMA quick_check; } set {} {} } {} finish_test |
Changes to test/printf.test.
︙ | ︙ | |||
3828 3829 3830 3831 3832 3833 3834 3835 3836 | # db close sqlite3 db test.db sqlite3_db_config_lookaside db 0 0 0 do_execsql_test printf-18.1 { SELECT length( format('%,.249f', -5.0e-300) ); } {252} finish_test | > > > > > > > > > > > > > > > > > | 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 | # db close sqlite3 db test.db sqlite3_db_config_lookaside db 0 0 0 do_execsql_test printf-18.1 { SELECT length( format('%,.249f', -5.0e-300) ); } {252} # 2024-02-16 # https://sqlite.org/forum/info/393708f4a8 # # The problem introduced by on 2023-07-03 by # https://sqlite.org/src/info/32befb224b254639 # do_execsql_test printf-19.1 { SELECT format('%0.0f %0.0g %0.0g', 0.9, 0.09, 1.9); } {{1 0.09 2}} do_execsql_test printf-19.2 { SELECT format('%0.0f %#0.0f',0.0, 0.0); } {{0 0.}} do_execsql_test printf-19.3 { SELECT format('%,.0f %,.0f',12345e+10, 12345e+11); } {{123,450,000,000,000 1,234,500,000,000,000}} finish_test |
Changes to test/pushdown.test.
|
| | > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # 2017-04-29 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Test cases for the push-down optimizations. # # # There are two different meanings for "push-down optimization". # # (1) "MySQL push-down" means that WHERE clause terms that can be # evaluated using only the index and without reference to the # table are run first, so that if they are false, unnecessary table # seeks are avoided. See https://sqlite.org/src/info/d7bb79ed3a40419d # from 2017-04-29. # # (2) "WHERE-clause pushdown" means to push WHERE clause terms in # outer queries down into subqueries. See # https://sqlite.org/src/info/6df18e949d367629 from 2015-06-02. # # This module started out as tests for MySQL push-down only. But because # of naming ambiguity, it has picked up test cases for WHERE-clause push-down # over the years. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix pushdown do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); |
︙ | ︙ | |||
83 84 85 86 87 88 89 | SELECT x FROM u2 WHERE x=a AND f('two') ) AND f('three')=123 } set L } {three} # 2022-11-25 dbsqlfuzz crash-3a548de406a50e896c1bf7142692d35d339d697f | | | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | SELECT x FROM u2 WHERE x=a AND f('two') ) AND f('three')=123 } set L } {three} # 2022-11-25 dbsqlfuzz crash-3a548de406a50e896c1bf7142692d35d339d697f # Disable the WHERE-clause push-down optimization for compound subqueries # if any arm of the compound has an incompatible affinity. # reset_db do_execsql_test 3.1 { CREATE TABLE t0(c0 INT); INSERT INTO t0 VALUES(0); CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT); INSERT INTO t1_a VALUES(1,'one'); |
︙ | ︙ | |||
181 182 183 184 185 186 187 | `--SCAN t2 } # ^^^^^^^^^^^^^^^^^^^^ # The query should be converted into: # SELECT (SELECT count(*) FROM t1)+(SELECT count(*) FROM t2) # 2023-05-09 https://sqlite.org/forum/forumpost/a7d4be7fb6 | | | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | `--SCAN t2 } # ^^^^^^^^^^^^^^^^^^^^ # The query should be converted into: # SELECT (SELECT count(*) FROM t1)+(SELECT count(*) FROM t2) # 2023-05-09 https://sqlite.org/forum/forumpost/a7d4be7fb6 # Restriction (9) on the WHERE-clause push-down optimization. # reset_db db null - do_execsql_test 4.1 { CREATE TABLE t1(a INT); CREATE TABLE t2(b INT); CREATE TABLE t3(c INT); |
︙ | ︙ | |||
223 224 225 226 227 228 229 230 | CREATE TABLE t5(e INT); INSERT INTO t5 VALUES(5); SELECT * FROM t1 JOIN t2 ON null RIGHT JOIN t3 ON true LEFT JOIN (t4 JOIN t5 ON d+1=e) ON d=4 WHERE e>0; } {- - 3 4 5} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | CREATE TABLE t5(e INT); INSERT INTO t5 VALUES(5); SELECT * FROM t1 JOIN t2 ON null RIGHT JOIN t3 ON true LEFT JOIN (t4 JOIN t5 ON d+1=e) ON d=4 WHERE e>0; } {- - 3 4 5} # 2024-04-05 # Allow push-down of operators of the form "expr IN table". # reset_db do_execsql_test 6.0 { CREATE TABLE t01(w,x,y,z); CREATE TABLE t02(w,x,y,z); CREATE VIEW t0(w,x,y,z) AS SELECT w,x,y,z FROM t01 UNION ALL SELECT w,x,y,z FROM t02; CREATE INDEX t01x ON t01(w,x,y); CREATE INDEX t02x ON t02(w,x,y); CREATE VIEW v1(k) AS VALUES(77),(88),(99); CREATE TABLE k1(k); INSERT INTO k1 SELECT * FROM v1; } do_eqp_test 6.1 { WITH k(n) AS (VALUES(77),(88),(99)) SELECT max(z) FROM t0 WHERE w=123 AND x IN k AND y BETWEEN 44 AND 55; } { QUERY PLAN |--CO-ROUTINE t0 | `--COMPOUND QUERY | |--LEFT-MOST SUBQUERY | | |--SEARCH t01 USING INDEX t01x (w=? AND x=? AND y>? AND y<?) | | `--LIST SUBQUERY xxxxxx | | |--MATERIALIZE k | | | `--SCAN 3 CONSTANT ROWS | | `--SCAN k | `--UNION ALL | |--SEARCH t02 USING INDEX t02x (w=? AND x=? AND y>? AND y<?) | `--LIST SUBQUERY xxxxxx | `--SCAN k |--SEARCH t0 `--LIST SUBQUERY xxxxxx `--SCAN k } # ^^^^--- The key feature above is that the SEARCH for each subquery # uses all three fields of the index w, x, and y. Prior to the push-down # of "expr IN table", only the w term of the index would be used. Similar # for the following tests: # do_eqp_test 6.2 { SELECT max(z) FROM t0 WHERE w=123 AND x IN v1 AND y BETWEEN 44 AND 55; } { QUERY PLAN |--CO-ROUTINE t0 | `--COMPOUND QUERY | |--LEFT-MOST SUBQUERY | | |--SEARCH t01 USING INDEX t01x (w=? AND x=? AND y>? AND y<?) | | `--LIST SUBQUERY xxxxxx | | |--CO-ROUTINE v1 | | | `--SCAN 3 CONSTANT ROWS | | `--SCAN v1 | `--UNION ALL | |--SEARCH t02 USING INDEX t02x (w=? AND x=? AND y>? AND y<?) | `--LIST SUBQUERY xxxxxx | |--CO-ROUTINE v1 | | `--SCAN 3 CONSTANT ROWS | `--SCAN v1 |--SEARCH t0 `--LIST SUBQUERY xxxxxx |--CO-ROUTINE v1 | `--SCAN 3 CONSTANT ROWS `--SCAN v1 } do_eqp_test 6.3 { SELECT max(z) FROM t0 WHERE w=123 AND x IN k1 AND y BETWEEN 44 AND 55; } { QUERY PLAN |--CO-ROUTINE t0 | `--COMPOUND QUERY | |--LEFT-MOST SUBQUERY | | |--SEARCH t01 USING INDEX t01x (w=? AND x=? AND y>? AND y<?) | | `--LIST SUBQUERY xxxxxx | | `--SCAN k1 | `--UNION ALL | |--SEARCH t02 USING INDEX t02x (w=? AND x=? AND y>? AND y<?) | `--LIST SUBQUERY xxxxxx | `--SCAN k1 |--SEARCH t0 `--LIST SUBQUERY xxxxxx `--SCAN k1 } finish_test |
Changes to test/quote.test.
︙ | ︙ | |||
99 100 101 102 103 104 105 | } foreach {tn sql errname} { 1 { CREATE TABLE xyz(a, b, c CHECK (c!="null") ) } null 2 { CREATE INDEX i2 ON t1(x, y, z||"abc") } abc 3 { CREATE INDEX i3 ON t1("w") } w 4 { CREATE INDEX i4 ON t1(x) WHERE z="w" } w } { | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | } foreach {tn sql errname} { 1 { CREATE TABLE xyz(a, b, c CHECK (c!="null") ) } null 2 { CREATE INDEX i2 ON t1(x, y, z||"abc") } abc 3 { CREATE INDEX i3 ON t1("w") } w 4 { CREATE INDEX i4 ON t1(x) WHERE z="w" } w } { do_catchsql_test 2.1.$tn $sql [list 1 "no such column: \"$errname\" - should this be a string literal in single-quotes?"] } do_execsql_test 2.2 { PRAGMA writable_schema = 1; CREATE TABLE xyz(a, b, c CHECK (c!="null") ); CREATE INDEX i2 ON t1(x, y, z||"abc"); CREATE INDEX i3 ON t1("w"||""); |
︙ | ︙ | |||
143 144 145 146 147 148 149 | # ticket 1c24a659e6d7f3a1 ifcapable altertable { reset_db do_catchsql_test 3.0 { CREATE TABLE t1(a,b); CREATE INDEX x1 on t1("b"); ALTER TABLE t1 DROP COLUMN b; | | | | | | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | # ticket 1c24a659e6d7f3a1 ifcapable altertable { reset_db do_catchsql_test 3.0 { CREATE TABLE t1(a,b); CREATE INDEX x1 on t1("b"); ALTER TABLE t1 DROP COLUMN b; } {1 {error in index x1 after drop column: no such column: "b" - should this be a string literal in single-quotes?}} do_catchsql_test 3.1 { DROP TABLE t1; CREATE TABLE t1(a,"b"); CREATE INDEX x1 on t1("b"); ALTER TABLE t1 DROP COLUMN b; } {1 {error in index x1 after drop column: no such column: "b" - should this be a string literal in single-quotes?}} do_catchsql_test 3.2 { DROP TABLE t1; CREATE TABLE t1(a,'b'); CREATE INDEX x1 on t1("b"); ALTER TABLE t1 DROP COLUMN b; } {1 {error in index x1 after drop column: no such column: "b" - should this be a string literal in single-quotes?}} do_catchsql_test 3.3 { DROP TABLE t1; CREATE TABLE t1(a,"b"); CREATE INDEX x1 on t1('b'); ALTER TABLE t1 DROP COLUMN b; } {1 {error in index x1 after drop column: no such column: b}} do_catchsql_test 3.4 { DROP TABLE t1; CREATE TABLE t1(a, b, c); CREATE INDEX x1 ON t1("a"||"b"); INSERT INTO t1 VALUES(1,2,3),(1,4,5); ALTER TABLE t1 DROP COLUMN b; } {1 {error in index x1 after drop column: no such column: "b" - should this be a string literal in single-quotes?}} sqlite3_db_config db SQLITE_DBCONFIG_DQS_DDL 1 do_catchsql_test 3.5 { DROP TABLE t1; CREATE TABLE t1(a, b, c); CREATE INDEX x1 ON t1("a"||"x"); INSERT INTO t1 VALUES(1,2,3),(1,4,5); ALTER TABLE t1 DROP COLUMN b; } {0 {}} } finish_test |
Added test/readonly.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # 2024 March 21 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains tests for using databases in read-only mode on # unix. # set testdir [file dirname $argv0] source $testdir/tester.tcl if {$tcl_platform(platform)=="windows"} finish_test source $testdir/lock_common.tcl source $testdir/wal_common.tcl set ::testprefix readonly do_execsql_test 1.0 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2), (3, 4), (5, 6); } db close file attributes test.db -permissions r--r--r-- sqlite3 db test.db do_catchsql_test 1.1 { INSERT INTO t1 VALUES(7, 8); } {1 {attempt to write a readonly database}} do_execsql_test 1.2 { BEGIN; SELECT * FROM t1; } {1 2 3 4 5 6} # The following attempts to open a read/write fd on the database 20,000 # times. And each time instead opens a read-only fd. At one point this # was failing to reuse cached fds, causing a "too many open file-descriptors" # error. do_test 1.3 { for {set ii 0} {$ii < 20000} {incr ii} { sqlite3 db2 test.db db2 close } set {} {} } {} finish_test |
Changes to test/recover.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2019 April 23 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Test the shell tool ".ar" command. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix recover | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2019 April 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. # #*********************************************************************** # TESTRUNNER: shell # # Test the shell tool ".ar" command. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix recover |
︙ | ︙ |
Changes to test/returning1.test.
︙ | ︙ | |||
208 209 210 211 212 213 214 | INSERT INTO log VALUES('insert', new.rowid, new.a, new.b); END; CREATE TRIGGER tr2 INSTEAD OF UPDATE ON t1 BEGIN INSERT INTO log VALUES('update', new.rowid, new.a, new.b); END; } | > | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | INSERT INTO log VALUES('insert', new.rowid, new.a, new.b); END; CREATE TRIGGER tr2 INSTEAD OF UPDATE ON t1 BEGIN INSERT INTO log VALUES('update', new.rowid, new.a, new.b); END; } ifcapable !allow_rowid_in_view { do_catchsql_test 10.3a { INSERT INTO t1(a, b) VALUES(1234, 5678) RETURNING rowid; } {1 {no such column: new.rowid}} do_catchsql_test 10.3b { UPDATE t1 SET a='z' WHERE b='y' RETURNING rowid; } {1 {no such column: new.rowid}} do_execsql_test 10.4 { SELECT * FROM log; } {} } else { # Note: The values returned by the RETURNING clauses of the following # two statements are the rowid columns of views. These values are not # well defined, so the INSERT returns -1, and the UPDATE returns NULL. # These match the values used for new.rowid expressions, but not much # else. do_catchsql_test 10.3a { INSERT INTO t1(a, b) VALUES(1234, 5678) RETURNING rowid; } {0 -1} do_catchsql_test 10.3b { UPDATE t1 SET a='z' WHERE b='y' RETURNING rowid; } {0 {{} {} {}}} do_execsql_test 10.4 { SELECT * FROM log; } { insert -1 1234 5678 update {} z y update {} z y update {} z y } } # 2021-04-27 dbsqlfuzz 78b9400770ef8cc7d9427dfba26f4fcf46ea7dc2 # Returning clauses on TEMP tables with triggers. # reset_db do_execsql_test 11.1 { CREATE TEMP TABLE t1(a,b); |
︙ | ︙ | |||
434 435 436 437 438 439 440 441 442 | } {} do_catchsql_test 19.1 { CREATE TRIGGER IF NOT EXISTS r1 AFTER DELETE ON t1 BEGIN INSERT INTO t1(a) VALUES (1) RETURNING FALSE; INSERT INTO t1(a) VALUES (2) RETURNING TRUE; END; } {0 {}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | } {} do_catchsql_test 19.1 { CREATE TRIGGER IF NOT EXISTS r1 AFTER DELETE ON t1 BEGIN INSERT INTO t1(a) VALUES (1) RETURNING FALSE; INSERT INTO t1(a) VALUES (2) RETURNING TRUE; END; } {0 {}} # 2024-04-24 # https://sqlite.org/forum/forumpost/2c83569ce8945d39 # # If the RETURNING clause includes subqueries that reference the # table being modified, make sure that the subqueries are identified # as correlated so that the results are recomputed after each step # instead of being computed once and reused. # reset_db db null N do_execsql_test 20.1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT); INSERT INTO t1 VALUES(1,10),(2,20),(3,30),(4,40),(6,60),(8,80); BEGIN; DELETE FROM t1 WHERE a<>3 RETURNING a, (SELECT min(a) FROM t1), (SELECT max(a) FROM t1), (SELECT round(avg(a),2) FROM t1); ROLLBACK; } { 1 2 8 4.6 2 3 8 5.25 4 3 8 5.67 6 3 8 5.5 8 3 3 3.0 } do_execsql_test 20.2 { BEGIN; DELETE FROM t1 RETURNING a, (SELECT min(a) FROM t1), (SELECT max(a) FROM t1), (SELECT round(avg(a),2) FROM t1); ROLLBACK; } { 1 2 8 4.6 2 3 8 5.25 3 4 8 6.0 4 6 8 7.0 6 8 8 8.0 8 N N N } do_execsql_test 20.3 { BEGIN; DELETE FROM t1 RETURNING a, (SELECT min(t2.a)+t1.a*100 FROM t1 AS t2), (SELECT max(t2.a)+t1.a*100 FROM t1 AS t2), (SELECT round(avg(t2.a),2)+t1.a*100 FROM t1 AS t2); ROLLBACK; } { 1 102 108 104.6 2 203 208 205.25 3 304 308 306.0 4 406 408 407.0 6 608 608 608.0 8 N N N } #------------------------------------------------------------------------- reset_db do_execsql_test 21.0 { PRAGMA writable_schema=ON; INSERT INTO sqlite_schema DEFAULT VALUES RETURNING sqlite_schema.name; } {{}} do_execsql_test 21.1 { INSERT INTO sqlite_temp_schema DEFAULT VALUES RETURNING sqlite_temp_schema.name; } {{}} finish_test |
Changes to test/rowid.test.
︙ | ︙ | |||
799 800 801 802 803 804 805 | CREATE TABLE t3(z); INSERT INTO t1(rowid, x) VALUES(1, 1); INSERT INTO t2(y) VALUES(2); INSERT INTO t3(rowid, z) VALUES(3, 3); } | > > | > > > > > > > > > > | | | | | | | | < | > > | 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | CREATE TABLE t3(z); INSERT INTO t1(rowid, x) VALUES(1, 1); INSERT INTO t2(y) VALUES(2); INSERT INTO t3(rowid, z) VALUES(3, 3); } ifcapable allow_rowid_in_view { set nosuch "1 {ambiguous column name: rowid}" do_execsql_test 16.1 { SELECT rowid FROM t1, t2; } {1} do_catchsql_test 16.2 { SELECT rowid FROM t1, v1; } $nosuch do_catchsql_test 16.3 { SELECT rowid FROM t3, v1; } $nosuch do_catchsql_test 16.4 { SELECT rowid FROM t3, (SELECT 123); } $nosuch do_execsql_test 16.5 { SELECT rowid FROM t2, t1; } {1} do_catchsql_test 16.6 { SELECT rowid FROM v1, t1; } $nosuch do_catchsql_test 16.7 { SELECT rowid FROM v1, t3; } $nosuch do_execsql_test 16.8 { SELECT rowid FROM (SELECT 123), t3; } {3} } else { do_execsql_test 16.1 { SELECT rowid FROM t1, t2; } {1} do_execsql_test 16.2 { SELECT rowid FROM t1, v1; } {1} do_execsql_test 16.3 { SELECT rowid FROM t3, v1; } {3} do_execsql_test 16.4 { SELECT rowid FROM t3, (SELECT 123); } {3} do_execsql_test 16.5 { SELECT rowid FROM t2, t1; } {1} do_execsql_test 16.6 { SELECT rowid FROM v1, t1; } {1} do_execsql_test 16.7 { SELECT rowid FROM v1, t3; } {3} do_execsql_test 16.8 { SELECT rowid FROM (SELECT 123), t3; } {3} } do_catchsql_test 16.9 { SELECT rowid FROM t1, t3; } {1 {ambiguous column name: rowid}} finish_test |
Changes to test/scanstatus2.test.
︙ | ︙ | |||
323 324 325 326 327 328 329 330 331 332 333 334 | QUERY (nCycle=nnn) --MATERIALIZE xy (nCycle=nnn) ----SCAN t1 (nCycle=nnn) ----USE TEMP B-TREE FOR GROUP BY (nCycle=nnn) --SCAN xy (nCycle=nnn) --SCAN xy2 (nCycle=nnn) } #explain_i { SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1 } #puts_debug_info { SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1 } finish_test | > > > > > > > > > > > | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | QUERY (nCycle=nnn) --MATERIALIZE xy (nCycle=nnn) ----SCAN t1 (nCycle=nnn) ----USE TEMP B-TREE FOR GROUP BY (nCycle=nnn) --SCAN xy (nCycle=nnn) --SCAN xy2 (nCycle=nnn) } #------------------------------------------------------------------------- reset_db # Check that an OOB parameter (45) does not cause asan or valgrind errors. # do_test 7.0 { db eval {SELECT * FROM sqlite_schema} set stmt [db version -last-stmt-ptr] sqlite3_stmt_scanstatus -flags complex $stmt 1000000 } {} #explain_i { SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1 } #puts_debug_info { SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1 } finish_test |
Changes to test/selectA.test.
︙ | ︙ | |||
1336 1337 1338 1339 1340 1341 1342 | SELECT a, b FROM t4 WHERE f()==f() ORDER BY 1,2 } { QUERY PLAN `--MERGE (UNION ALL) |--LEFT | |--SCAN t5 USING INDEX i2 | | | | 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 | SELECT a, b FROM t4 WHERE f()==f() ORDER BY 1,2 } { QUERY PLAN `--MERGE (UNION ALL) |--LEFT | |--SCAN t5 USING INDEX i2 | `--USE TEMP B-TREE FOR LAST TERM OF ORDER BY `--RIGHT |--SCAN t4 USING INDEX i1 `--USE TEMP B-TREE FOR LAST TERM OF ORDER BY } do_execsql_test 4.1.3 { SELECT c, d FROM t5 UNION ALL SELECT a, b FROM t4 WHERE f()==f() ORDER BY 1,2 |
︙ | ︙ |
Changes to test/sessionfuzz.c.
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 | #ifdef SQLITE_THREADSAFE #undef SQLITE_THREADSAFE #endif #define SQLITE_DEBUG 1 #define SQLITE_THREADSAFE 0 #define SQLITE_OMIT_LOAD_EXTENSION 0 #define SQLITE_ENABLE_SESSION 1 #define SQLITE_ENABLE_PREUPDATE_HOOK 1 #define SQLITE_ENABLE_DESERIALIZE 1 #include "sqlite3.c" /* Code to populate the database */ | > | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #ifdef SQLITE_THREADSAFE #undef SQLITE_THREADSAFE #endif #define SQLITE_DEBUG 1 #define SQLITE_THREADSAFE 0 #undef SQLITE_OMIT_LOAD_EXTENSION #define SQLITE_OMIT_LOAD_EXTENSION 0 #define SQLITE_ENABLE_SESSION 1 #define SQLITE_ENABLE_PREUPDATE_HOOK 1 #define SQLITE_ENABLE_DESERIALIZE 1 #include "sqlite3.c" /* Code to populate the database */ |
︙ | ︙ |
Changes to test/shell1.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 2009 Nov 11 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the CLI shell tool. # # # Test plan: # # shell1-1.*: Basic command line option handling. # shell1-2.*: Basic "dot" command token parsing. # shell1-3.*: Basic test that "dot" command can be called. | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 2009 Nov 11 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the CLI shell tool. # # TESTRUNNER: shell # # Test plan: # # shell1-1.*: Basic command line option handling. # shell1-2.*: Basic "dot" command token parsing. # shell1-3.*: Basic test that "dot" command can be called. |
︙ | ︙ |
Changes to test/shell2.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2009 Nov 11 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the CLI shell tool. # # $Id: shell2.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $ # # Test plan: | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2009 Nov 11 # # 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. # #*********************************************************************** # TESTRUNNER: shell # # The focus of this file is testing the CLI shell tool. # # $Id: shell2.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $ # # Test plan: |
︙ | ︙ |
Changes to test/shell3.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2009 Dec 16 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the CLI shell tool. # # $Id: shell2.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $ # # Test plan: | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2009 Dec 16 # # 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. # #*********************************************************************** # TESTRUNNER: shell # # The focus of this file is testing the CLI shell tool. # # $Id: shell2.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $ # # Test plan: |
︙ | ︙ |
Changes to test/shell4.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2010 July 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. # #*********************************************************************** # # The focus of this file is testing the CLI shell tool. # These tests are specific to the .stats command. # # 2015-03-19: Added tests for .trace # Test plan: | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2010 July 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. # #*********************************************************************** # TESTRUNNER: shell # # The focus of this file is testing the CLI shell tool. # These tests are specific to the .stats command. # # 2015-03-19: Added tests for .trace # Test plan: |
︙ | ︙ | |||
113 114 115 116 117 118 119 | catchcmd ":memory:" "CREATE TABLE t1(x);\n.trace --unknown" } {1 {Unknown option "--unknown" on ".trace"}} do_test shell4-2.2 { catchcmd ":memory:" "CREATE TABLE t1(x);\n.trace off\n.trace off\n" } {0 {}} do_test shell4-2.3 { catchcmd ":memory:" ".trace stdout\n.dump\n.trace off\n" | | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | catchcmd ":memory:" "CREATE TABLE t1(x);\n.trace --unknown" } {1 {Unknown option "--unknown" on ".trace"}} do_test shell4-2.2 { catchcmd ":memory:" "CREATE TABLE t1(x);\n.trace off\n.trace off\n" } {0 {}} do_test shell4-2.3 { catchcmd ":memory:" ".trace stdout\n.dump\n.trace off\n" } {/^0 {SELECT.*}$/} do_test shell4-2.4 { catchcmd ":memory:" ".trace stdout\nCREATE TABLE t1(x);SELECT * FROM t1;" } {0 {CREATE TABLE t1(x); SELECT * FROM t1;}} do_test shell4-2.5 { catchcmd ":memory:" "CREATE TABLE t1(x);\n.trace stdout\nSELECT * FROM t1;" } {0 {SELECT * FROM t1;}} |
︙ | ︙ |
Changes to test/shell5.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2010 August 4 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the CLI shell tool. # These tests are specific to the .import command. # # $Id: shell5.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $ # | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2010 August 4 # # 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. # #*********************************************************************** # TESTRUNNER: shell # # The focus of this file is testing the CLI shell tool. # These tests are specific to the .import command. # # $Id: shell5.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $ # |
︙ | ︙ | |||
565 566 567 568 569 570 571 572 573 | close $out forcedelete test.db catchcmd test.db {.import -csv shell5.csv t1 .mode line SELECT * FROM t1;} } {0 { 1 = あい 2 = うえお}} finish_test | > > > > > > > > > > > > > > | 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 | close $out forcedelete test.db catchcmd test.db {.import -csv shell5.csv t1 .mode line SELECT * FROM t1;} } {0 { 1 = あい 2 = うえお}} # 2024-03-11 https://sqlite.org/forum/forumpost/ca014d7358 # Import into a table that contains computed columns. # do_test shell5-7.1 { set out [open shell5.csv w] fconfigure $out -translation lf puts $out {aaa|bbb} close $out forcedelete test.db catchcmd :memory: {CREATE TABLE t1(a TEXT, b TEXT, c AS (a||b)); .import shell5.csv t1 SELECT * FROM t1;} } {0 aaa|bbb|aaabbb} finish_test |
Changes to test/shell6.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2016 December 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. # #*********************************************************************** # # Test the shell tool ".lint fkey-indexes" command. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !vtab {finish_test; return} | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2016 December 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. # #*********************************************************************** # TESTRUNNER: shell # # Test the shell tool ".lint fkey-indexes" command. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !vtab {finish_test; return} |
︙ | ︙ |
Changes to test/shell7.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2016 December 17 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Test the readfile() function built into the shell tool. Specifically, # that it does not truncate the blob read at the first embedded 0x00 # byte. # set testdir [file dirname $argv0] | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2016 December 17 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # TESTRUNNER: shell # # Test the readfile() function built into the shell tool. Specifically, # that it does not truncate the blob read at the first embedded 0x00 # byte. # set testdir [file dirname $argv0] |
︙ | ︙ |
Changes to test/shell8.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 2017 December 9 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Test the shell tool ".ar" command. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix shell8 | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2017 December 9 # # 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. # #*********************************************************************** # TESTRUNNER: shell # # Test the shell tool ".ar" command. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix shell8 |
︙ | ︙ | |||
160 161 162 163 164 165 166 | # This is a repeat of test 1.$tn.1, except that there is a 2 second # pause between creating the archive and extracting its contents. # This is to test that timestamps are set correctly. # # Because it is slow, only do this for $tn==1. if {$tn==1} { | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | # This is a repeat of test 1.$tn.1, except that there is a 2 second # pause between creating the archive and extracting its contents. # This is to test that timestamps are set correctly. # # Because it is slow, only do this for $tn==1. if {$tn==1} { do_test 1.$tn.4 { catchcmd test_ar.db $c1 file delete -force ar1 after 2000 catchcmd test_ar.db $x1 dir_to_list ar1 } $expected } |
︙ | ︙ | |||
188 189 190 191 192 193 194 195 196 | catchcmd shell8.db {.ar -c} catchcmd shell8.db {.ar -C ar2 -i .} catchcmd shell8.db {.ar -r ./file2 ./dir1} catchcmd shell8.db {.ar -g -r ./ju*2} catchcmd shell8.db {.ar -C ar4 -x .} regsub -all {ar4} [dir_content ar4] ar2 } {ar2/file1 ar2/file2 ar2/junk1} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | catchcmd shell8.db {.ar -c} catchcmd shell8.db {.ar -C ar2 -i .} catchcmd shell8.db {.ar -r ./file2 ./dir1} catchcmd shell8.db {.ar -g -r ./ju*2} catchcmd shell8.db {.ar -C ar4 -x .} regsub -all {ar4} [dir_content ar4] ar2 } {ar2/file1 ar2/file2 ar2/junk1} # Test symbolic links. # if {$tcl_platform(platform)=="unix"} { populate_dir ar2 { file1 "1234" file2 "3456" } file link ar2/link1 file1 forcedelete shell8.db forcedelete link1 do_test 3.1 { catchcmd shell8.db {.ar -C ar2 -c file2 link1 } } {0 {}} do_test 3.2 { catchcmd shell8.db {.ar -x} } {0 {}} do_test 3.3 { catchcmd shell8.db {.ar -x} } {0 {}} } finish_test |
Added test/shell9.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | # 2024 Jan 8 # # 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. # #*********************************************************************** # TESTRUNNER: shell # # The focus of this file is testing the CLI shell tool. Specifically, # testing that it is possible to run a ".dump" script that creates # virtual tables without explicitly disabling defensive mode. # # And, that it can process a ".dump" script that contains strings # delimited using double-quotes in the schema (DQS_DDL setting). # # Test plan: # # shell1-1.*: Basic command line option handling. # shell1-2.*: Basic "dot" command token parsing. # shell1-3.*: Basic test that "dot" command can be called. # shell1-{4-8}.*: Test various "dot" commands's functionality. # shell1-9.*: Basic test that "dot" commands and SQL intermix ok. # set testdir [file dirname $argv0] source $testdir/tester.tcl set CLI [test_cli_invocation] set ::testprefix shell9 ifcapable !fts5 { finish_test return } #---------------------------------------------------------------------------- # Test cases shell9-1.* verify that scripts output by .dump may be parsed # by the shell tool without explicitly disabling DEFENSIVE mode, unless # the shell is in safe mode. # do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(a, b, c); INSERT INTO t1 VALUES('one', 'two', 'three'); } db close # Create .dump file in "testdump.txt". # set out [open testdump.txt w] puts $out [lindex [catchcmd test.db .dump] 1] close $out # Check testdump.txt can be processed if the initial db is empty. # do_test 1.1.1 { forcedelete test.db catchcmd test.db ".read testdump.txt" } {0 {}} sqlite3 db test.db do_execsql_test 1.1.2 { SELECT * FROM t1; } {one two three} # Check testdump.txt cannot be processed if the initial db is not empty. # reset_db do_execsql_test 1.2.1 { CREATE TABLE t4(hello); } db close do_test 1.2.2 { catchcmd test.db ".read testdump.txt" } {1 {Parse error near line 5: table sqlite_master may not be modified}} # Check testdump.txt cannot be processed if the db is in safe mode # do_test 1.3.1 { forcedelete test.db catchsafecmd test.db ".read testdump.txt" } {1 {line 1: cannot run .read in safe mode}} do_test 1.3.2 { set fd [open testdump.txt] set script [read $fd] close $fd forcedelete test.db catchsafecmd test.db $script } {1 {Parse error near line 5: table sqlite_master may not be modified}} do_test 1.3.3 { # Quick check that the above would have worked but for safe mode. forcedelete test.db catchcmd test.db $script } {0 {}} #---------------------------------------------------------------------------- # Test cases shell9-2.* verify that a warning is printed at the top of # .dump scripts that contain virtual tables. # proc contains_warning {text} { return [string match "*WARNING: Script requires that*" $text] } reset_db do_execsql_test 2.0.1 { CREATE TABLE t1(x); CREATE TABLE t2(y); INSERT INTO t1 VALUES('one'); INSERT INTO t2 VALUES('two'); } do_test 2.0.2 { contains_warning [catchcmd test.db .dump] } 0 do_execsql_test 2.1.1 { CREATE virtual TABLE r1 USING fts5(x); } do_test 2.1.2 { contains_warning [catchcmd test.db .dump] } 1 do_test 2.2.1 { contains_warning [catchcmd test.db ".dump t1"] } 0 do_test 2.2.2 { contains_warning [catchcmd test.db ".dump r1"] } 1 #------------------------------------------------------------------------- reset_db sqlite3_db_config db DQS_DDL 1 do_execsql_test 3.1.0 { CREATE TABLE t4(hello, check( hello IS NOT "xyz") ); } db close # Create .dump file in "testdump.txt". # set out [open testdump.txt w] puts $out [lindex [catchcmd test.db .dump] 1] close $out do_test 3.1.1 { forcedelete test.db catchcmd test.db ".read testdump.txt" } {0 {}} finish_test |
Changes to test/speedtest1.c.
︙ | ︙ | |||
2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 | for(i=1; i<=n; i++){ x1 = swizzle(i, n); x2 = swizzle(x1, n); speedtest1_numbername(x1, zNum, sizeof(zNum)); printf("%5d %5d %5d %s\n", i, x1, x2, zNum); } } #ifdef __linux__ #include <sys/types.h> #include <unistd.h> /* ** Attempt to display I/O stats on Linux using /proc/PID/io | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 | for(i=1; i<=n; i++){ x1 = swizzle(i, n); x2 = swizzle(x1, n); speedtest1_numbername(x1, zNum, sizeof(zNum)); printf("%5d %5d %5d %s\n", i, x1, x2, zNum); } } /* ** This testset focuses on the speed of parsing numeric literals (integers ** and real numbers). This was added to test the impact of allowing "_" ** characters to appear in numeric SQL literals to make them easier to read. ** For example, "SELECT 1_000_000;" instead of "SELECT 1000000;". */ void testset_parsenumber(void){ const char *zSql1 = "SELECT 1, 12, 123, 1234, 12345, 123456"; const char *zSql2 = "SELECT 8227256643844975616, 7932208612563860480, " "2010730661871032832, 9138463067404021760, " "2557616153664746496, 2557616153664746496"; const char *zSql3 = "SELECT 1.0, 1.2, 1.23, 123.4, 1.2345, 1.23456"; const char *zSql4 = "SELECT 8.227256643844975616, 7.932208612563860480, " "2.010730661871032832, 9.138463067404021760, " "2.557616153664746496, 2.557616153664746496"; const int NROW = 100*g.szTest; int ii; speedtest1_begin_test(100, "parsing small integers"); for(ii=0; ii<NROW; ii++){ sqlite3_exec(g.db, zSql1, 0, 0, 0); } speedtest1_end_test(); speedtest1_begin_test(110, "parsing large integers"); for(ii=0; ii<NROW; ii++){ sqlite3_exec(g.db, zSql2, 0, 0, 0); } speedtest1_end_test(); speedtest1_begin_test(200, "parsing small reals"); for(ii=0; ii<NROW; ii++){ sqlite3_exec(g.db, zSql3, 0, 0, 0); } speedtest1_end_test(); speedtest1_begin_test(210, "parsing large reals"); for(ii=0; ii<NROW; ii++){ sqlite3_exec(g.db, zSql4, 0, 0, 0); } speedtest1_end_test(); } #ifdef __linux__ #include <sys/types.h> #include <unistd.h> /* ** Attempt to display I/O stats on Linux using /proc/PID/io |
︙ | ︙ | |||
2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 | testset_orm(); }else if( strcmp(zThisTest,"cte")==0 ){ testset_cte(); }else if( strcmp(zThisTest,"fp")==0 ){ testset_fp(); }else if( strcmp(zThisTest,"trigger")==0 ){ testset_trigger(); }else if( strcmp(zThisTest,"rtree")==0 ){ #ifdef SQLITE_ENABLE_RTREE testset_rtree(6, 147); #else fatal_error("compile with -DSQLITE_ENABLE_RTREE to enable " "the R-Tree tests\n"); #endif | > > | 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 | testset_orm(); }else if( strcmp(zThisTest,"cte")==0 ){ testset_cte(); }else if( strcmp(zThisTest,"fp")==0 ){ testset_fp(); }else if( strcmp(zThisTest,"trigger")==0 ){ testset_trigger(); }else if( strcmp(zThisTest,"parsenumber")==0 ){ testset_parsenumber(); }else if( strcmp(zThisTest,"rtree")==0 ){ #ifdef SQLITE_ENABLE_RTREE testset_rtree(6, 147); #else fatal_error("compile with -DSQLITE_ENABLE_RTREE to enable " "the R-Tree tests\n"); #endif |
︙ | ︙ |
Changes to test/sqllimits1.test.
︙ | ︙ | |||
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 | set max $::SQLITE_MAX_EXPR_DEPTH set expr "(1 [string repeat {AND 1 } $max])" catchsql [subst { SELECT $expr }] } "1 {Expression tree is too large (maximum depth $::SQLITE_MAX_EXPR_DEPTH)}" # Attempting to beat the expression depth limit using nested SELECT # queries causes a parser stack overflow. do_test sqllimits1-9.2 { set max $::SQLITE_MAX_EXPR_DEPTH set expr "SELECT 1" for {set i 0} {$i <= $max} {incr i} { set expr "SELECT ($expr)" } catchsql [subst { $expr }] } "1 {parser stack overflow}" | > < | 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 | set max $::SQLITE_MAX_EXPR_DEPTH set expr "(1 [string repeat {AND 1 } $max])" catchsql [subst { SELECT $expr }] } "1 {Expression tree is too large (maximum depth $::SQLITE_MAX_EXPR_DEPTH)}" if 0 { # Attempting to beat the expression depth limit using nested SELECT # queries causes a parser stack overflow. do_test sqllimits1-9.2 { set max $::SQLITE_MAX_EXPR_DEPTH set expr "SELECT 1" for {set i 0} {$i <= $max} {incr i} { set expr "SELECT ($expr)" } catchsql [subst { $expr }] } "1 {parser stack overflow}" do_test sqllimits1-9.3 { execsql { PRAGMA max_page_count = 1000000; -- 1 GB CREATE TABLE v0(a); INSERT INTO v0 VALUES(1); } db transaction { |
︙ | ︙ | |||
918 919 920 921 922 923 924 | CREATE TABLE b1(x); INSERT INTO b1 VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); } {0 {}} do_catchsql_test sqllimits1-18.2 { INSERT INTO b1 VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10) UNION VALUES(11); | | | 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | CREATE TABLE b1(x); INSERT INTO b1 VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); } {0 {}} do_catchsql_test sqllimits1-18.2 { INSERT INTO b1 VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10) UNION VALUES(11); } {0 {}} #------------------------------------------------------------------------- # reset_db ifcapable utf16 { do_execsql_test 19.0 { PRAGMA encoding = 'utf16'; |
︙ | ︙ |
Changes to test/subquery.test.
︙ | ︙ | |||
647 648 649 650 651 652 653 654 655 | # |--CO-ROUTINE v2 # | |--SCAN t2 # | `--USE TEMP B-TREE FOR GROUP BY # |--SCAN v2 # `--SEARCH t1 USING INDEX x12 (aa=?) # finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | # |--CO-ROUTINE v2 # | |--SCAN t2 # | `--USE TEMP B-TREE FOR GROUP BY # |--SCAN v2 # `--SEARCH t1 USING INDEX x12 (aa=?) # #----------------------------------------------------------------------------- # 2024-04-25 Column affinities for columns of compound subqueries # reset_db sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db do_execsql_test subquery-11.1 { CREATE TABLE t1(ix INT, rx REAL, bx BLOB, tx TEXT, ax); INSERT INTO t1 VALUES(1,1.0,x'31','x',NULL); WITH c(a) AS (SELECT 'y' UNION SELECT tx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT tx FROM t1 UNION SELECT 'y') SELECT affinity(a) FROM c; } {text text text text} do_execsql_test subquery-11.2 { WITH c(a) AS (SELECT 2 UNION SELECT tx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT tx FROM t1 UNION SELECT 2) SELECT affinity(a) FROM c; } {blob blob blob blob} do_execsql_test subquery-11.3 { WITH c(a) AS (SELECT 2.0 UNION SELECT tx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT tx FROM t1 UNION SELECT 2.0) SELECT affinity(a) FROM c; } {blob blob blob blob} do_execsql_test subquery-11.4 { WITH c(a) AS (SELECT null UNION SELECT tx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT tx FROM t1 UNION SELECT null) SELECT affinity(a) FROM c; } {text text text text} do_execsql_test subquery-11.5 { WITH c(a) AS (SELECT x'32' UNION SELECT tx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT tx FROM t1 UNION SELECT x'32') SELECT affinity(a) FROM c; } {text text text text} do_execsql_test subquery-11.6 { WITH c(a) AS (SELECT 3 UNION SELECT ix FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT ix FROM t1 UNION SELECT 3) SELECT affinity(a) FROM c; } {integer integer integer integer} do_execsql_test subquery-11.7 { WITH c(a) AS (SELECT 3.0 UNION SELECT ix FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT ix FROM t1 UNION SELECT 3.0) SELECT affinity(a) FROM c; } {integer integer integer integer} do_execsql_test subquery-11.8 { WITH c(a) AS (SELECT '3' UNION SELECT ix FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT ix FROM t1 UNION SELECT '3') SELECT affinity(a) FROM c; } {blob blob blob blob} do_execsql_test subquery-11.10 { WITH c(a) AS (SELECT x'32' UNION SELECT ix FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT ix FROM t1 UNION SELECT x'32') SELECT affinity(a) FROM c; } {integer integer integer integer} do_execsql_test subquery-11.11 { WITH c(a) AS (SELECT 4 UNION SELECT rx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT rx FROM t1 UNION SELECT 4) SELECT affinity(a) FROM c; } {real real real real} do_execsql_test subquery-11.12 { WITH c(a) AS (SELECT '4' UNION SELECT rx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT rx FROM t1 UNION SELECT '4') SELECT affinity(a) FROM c; } {blob blob blob blob} do_execsql_test subquery-11.13 { WITH c(a) AS (SELECT null UNION SELECT rx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT rx FROM t1 UNION SELECT null) SELECT affinity(a) FROM c; } {real real real real} do_execsql_test subquery-11.14 { WITH c(a) AS (SELECT x'b4' UNION SELECT rx FROM t1) SELECT affinity(a) FROM c; WITH c(a) AS (SELECT rx FROM t1 UNION SELECT x'b4') SELECT affinity(a) FROM c; } {real real real real} finish_test |
Changes to test/tabfunc01.test.
︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 327 328 329 330 | } {1 {table pragma_compile_options may not be altered}} do_test tabfunc01-930 { catchsql { ALTER TABLE pragma_compile_options DROP COLUMN start; } } {1 {table pragma_compile_options may not be altered}} } # Free up memory allocations intarray_addr int64array_addr doublearray_addr textarray_addr | > > > > > > > > > > > > > > > > > > > > > > > > > > | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | } {1 {table pragma_compile_options may not be altered}} do_test tabfunc01-930 { catchsql { ALTER TABLE pragma_compile_options DROP COLUMN start; } } {1 {table pragma_compile_options may not be altered}} } #----------------------------------------------------------------------------- # 2024-04-26 LIMIT and OFFSET passed into virtual tables # https://sqlite.org/forum/forumpost/c243b8f856 # do_execsql_test tabfunc01-900 { SELECT * FROM ( SELECT * FROM generate_series(1,10) UNION ALL SELECT * FROM generate_series(101,104) ) LIMIT 10 OFFSET 5; } {6 7 8 9 10 101 102 103 104} do_execsql_test tabfunc01-910 { SELECT * FROM ( SELECT * FROM generate_series(1,10) UNION ALL SELECT * FROM generate_series(101,104) ) LIMIT -1 OFFSET 5; } {6 7 8 9 10 101 102 103 104} do_execsql_test tabfunc01-920 { SELECT * FROM ( SELECT * FROM generate_series(1,10) UNION ALL SELECT * FROM generate_series(101,104) ) LIMIT -1 OFFSET 0; } {1 2 3 4 5 6 7 8 9 10 101 102 103 104} # Free up memory allocations intarray_addr int64array_addr doublearray_addr textarray_addr |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
550 551 552 553 554 555 556 | lappend leftover [file normalize $a] } } } } unset -nocomplain a set testdir [file normalize $testdir] | | | 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | lappend leftover [file normalize $a] } } } } unset -nocomplain a set testdir [file normalize $testdir] set cmdlinearg(TESTFIXTURE_HOME) [file dirname [info nameofexec]] set cmdlinearg(INFO_SCRIPT) [file normalize [info script]] set argv0 [file normalize $argv0] if {$cmdlinearg(testdir)!=""} { file mkdir $cmdlinearg(testdir) cd $cmdlinearg(testdir) } set argv $leftover |
︙ | ︙ | |||
879 880 881 882 883 884 885 886 887 888 889 890 891 892 | global CLI set out [open cmds.txt w] puts $out $cmd close $out set line "exec $CLI $db < cmds.txt" set rc [catch { eval $line } msg] list $rc $msg } proc catchcmdex {db {cmd ""}} { global CLI set out [open cmds.txt w] fconfigure $out -encoding binary -translation binary puts -nonewline $out $cmd | > > > > > > > > > | 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 | global CLI set out [open cmds.txt w] puts $out $cmd close $out set line "exec $CLI $db < cmds.txt" set rc [catch { eval $line } msg] list $rc $msg } proc catchsafecmd {db {cmd ""}} { global CLI set out [open cmds.txt w] puts $out $cmd close $out set line "exec $CLI -safe $db < cmds.txt" set rc [catch { eval $line } msg] list $rc $msg } proc catchcmdex {db {cmd ""}} { global CLI set out [open cmds.txt w] fconfigure $out -encoding binary -translation binary puts -nonewline $out $cmd |
︙ | ︙ |
Changes to test/testrunner.tcl.
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 | proc usage {} { set a0 [file tail $::argv0] puts stderr [string trim [subst -nocommands { Usage: $a0 ?SWITCHES? ?PERMUTATION? ?PATTERNS? $a0 PERMUTATION FILE $a0 njob ?NJOB? $a0 status where SWITCHES are: | > > | > | > | > > > | | > > > > | > | | > > > > > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | proc usage {} { set a0 [file tail $::argv0] puts stderr [string trim [subst -nocommands { Usage: $a0 ?SWITCHES? ?PERMUTATION? ?PATTERNS? $a0 PERMUTATION FILE $a0 help $a0 njob ?NJOB? $a0 script ?-msvc? CONFIG $a0 status where SWITCHES are: --buildonly Build test exes but do not run tests --config CONFIGS Only use configs on comma-separate list CONFIGS --dryrun Write what would have happened to testrunner.log --explain Write summary to stdout --jobs NUM Run tests using NUM separate processes --omit CONFIGS Omit configs on comma-separated list CONFIGS --stop-on-coredump Stop running if any test segfaults --stop-on-error Stop running after any reported error --zipvfs ZIPVFSDIR ZIPVFS source directory Special values for PERMUTATION that work with plain tclsh: list - show all allowed PERMUTATION arguments. mdevtest - tests recommended prior to normal development check-ins. release - full release test with various builds. sdevtest - like mdevtest but using ASAN and UBSAN. Other PERMUTATION arguments must be run using testfixture, not tclsh: all - all tcl test scripts, plus a subset of test scripts rerun with various permutations. full - all tcl test scripts. veryquick - a fast subset of the tcl test scripts. This is the default. If no PATTERN arguments are present, all tests specified by the PERMUTATION are run. Otherwise, each pattern is interpreted as a glob pattern. Only those tcl tests for which the final component of the filename matches at least one specified pattern are run. If no PATTERN arguments are present, then various fuzztest, threadtest and other tests are run as part of the "release" permutation. These are omitted if any PATTERN arguments are specified on the command line. If a PERMUTATION is specified and is followed by the path to a Tcl script instead of a list of patterns, then that single Tcl test script is run with the specified permutation. The "status" and "njob" commands are designed to be run from the same directory as a running testrunner.tcl script that is running tests. The "status" command prints a report describing the current state and progress of the tests. The "njob" command may be used to query or modify the number of sub-processes the test script uses to run tests. The "script" command outputs the script used to build a configuration. Add the "-msvc" option for a Windows-compatible script. For a list of available configurations enter "$a0 script help". Full documentation here: https://sqlite.org/src/doc/trunk/doc/testrunner.md }]] exit 1 } #------------------------------------------------------------------------- #------------------------------------------------------------------------- |
︙ | ︙ | |||
122 123 124 125 126 127 128 129 130 131 132 133 134 135 | } } } return $ret } proc default_njob {} { set nCore [guess_number_of_cores] if {$nCore<=2} { set nHelper 1 } else { set nHelper [expr int($nCore*0.5)] } return $nHelper | > > > > | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | } } } return $ret } proc default_njob {} { global env if {[info exists env(NJOB)] && $env(NJOB)>=1} { return $env(NJOB) } set nCore [guess_number_of_cores] if {$nCore<=2} { set nHelper 1 } else { set nHelper [expr int($nCore*0.5)] } return $nHelper |
︙ | ︙ | |||
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | set TRG(nJob) [default_njob] ;# Default number of helper processes set TRG(patternlist) [list] set TRG(cmdline) $argv set TRG(reporttime) 2000 set TRG(fuzztest) 0 ;# is the fuzztest option present. set TRG(zipvfs) "" ;# -zipvfs option, if any set TRG(buildonly) 0 ;# True if --buildonly option set TRG(dryrun) 0 ;# True if --dryrun option switch -nocase -glob -- $tcl_platform(os) { *darwin* { set TRG(platform) osx set TRG(make) make.sh set TRG(makecmd) "bash make.sh" set TRG(testfixture) testfixture set TRG(run) run.sh set TRG(runcmd) "bash run.sh" } *linux* { set TRG(platform) linux set TRG(make) make.sh set TRG(makecmd) "bash make.sh" set TRG(testfixture) testfixture set TRG(run) run.sh set TRG(runcmd) "bash run.sh" } *win* { set TRG(platform) win set TRG(make) make.bat | > > > > > > > | > | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | set TRG(nJob) [default_njob] ;# Default number of helper processes set TRG(patternlist) [list] set TRG(cmdline) $argv set TRG(reporttime) 2000 set TRG(fuzztest) 0 ;# is the fuzztest option present. set TRG(zipvfs) "" ;# -zipvfs option, if any set TRG(buildonly) 0 ;# True if --buildonly option set TRG(config) {} ;# Only build the named configurations set TRG(omitconfig) {} ;# Do not build these configurations set TRG(dryrun) 0 ;# True if --dryrun option set TRG(explain) 0 ;# True for the --explain option set TRG(stopOnError) 0 ;# Stop running at first failure set TRG(stopOnCore) 0 ;# Stop on a core-dump switch -nocase -glob -- $tcl_platform(os) { *darwin* { set TRG(platform) osx set TRG(make) make.sh set TRG(makecmd) "bash make.sh" set TRG(testfixture) testfixture set TRG(shell) sqlite3 set TRG(run) run.sh set TRG(runcmd) "bash run.sh" } *linux* { set TRG(platform) linux set TRG(make) make.sh set TRG(makecmd) "bash make.sh" set TRG(testfixture) testfixture set TRG(shell) sqlite3 set TRG(run) run.sh set TRG(runcmd) "bash run.sh" } *win* { set TRG(platform) win set TRG(make) make.bat set TRG(makecmd) "call make.bat" set TRG(testfixture) testfixture.exe set TRG(shell) sqlite3.exe set TRG(run) run.bat set TRG(runcmd) "run.bat" } default { error "cannot determine platform!" } } |
︙ | ︙ | |||
235 236 237 238 239 240 241 | cmd TEXT NOT NULL, -- shell command to run depid INTEGER, -- identifier of dependency (or '') priority INTEGER NOT NULL, -- higher priority jobs may run earlier /* Fields updated as jobs run */ starttime INTEGER, endtime INTEGER, | | | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | cmd TEXT NOT NULL, -- shell command to run depid INTEGER, -- identifier of dependency (or '') priority INTEGER NOT NULL, -- higher priority jobs may run earlier /* Fields updated as jobs run */ starttime INTEGER, endtime INTEGER, state TEXT CHECK( state IN ('','ready','running','done','failed','omit') ), output TEXT ); CREATE TABLE config( name TEXT COLLATE nocase PRIMARY KEY, value ) WITHOUT ROWID; |
︙ | ︙ | |||
322 323 324 325 326 327 328 329 330 331 332 333 334 335 | set res [mydb one { SELECT value FROM config WHERE name='njob' }] mydb close puts "$res" exit } #-------------------------------------------------------------------------- #-------------------------------------------------------------------------- # Check if this is the "script" command: # if {[string compare -nocase script [lindex $argv 0]]==0} { if {[llength $argv]!=2 && !([llength $argv]==3&&[lindex $argv 1]=="-msvc")} { usage } | > > > > > > > > | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | set res [mydb one { SELECT value FROM config WHERE name='njob' }] mydb close puts "$res" exit } #-------------------------------------------------------------------------- #-------------------------------------------------------------------------- # Check if this is the "help" command: # if {[string compare -nocase help [lindex $argv 0]]==0} { usage } #-------------------------------------------------------------------------- #-------------------------------------------------------------------------- # Check if this is the "script" command: # if {[string compare -nocase script [lindex $argv 0]]==0} { if {[llength $argv]!=2 && !([llength $argv]==3&&[lindex $argv 1]=="-msvc")} { usage } |
︙ | ︙ | |||
404 405 406 407 408 409 410 411 412 413 414 415 416 417 | if {$S(failed)>0} { puts "Failures: " mydb eval { SELECT * FROM jobs WHERE state='failed' ORDER BY starttime } job { display_job [array get job] } } mydb close exit } #------------------------------------------------------------------------- | > > > > | 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 | if {$S(failed)>0} { puts "Failures: " mydb eval { SELECT * FROM jobs WHERE state='failed' ORDER BY starttime } job { display_job [array get job] } set nOmit [db one {SELECT count(*) FROM jobs WHERE state='omit'}] if {$nOmit} { puts "$nOmit jobs omitted due to failures" } } mydb close exit } #------------------------------------------------------------------------- |
︙ | ︙ | |||
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | if {$isLast} { usage } } elseif {($n>2 && [string match "$a*" --zipvfs]) || $a=="-z"} { incr ii set TRG(zipvfs) [file normalize [lindex $argv $ii]] if {$isLast} { usage } } elseif {($n>2 && [string match "$a*" --buildonly]) || $a=="-b"} { set TRG(buildonly) 1 } elseif {($n>2 && [string match "$a*" --dryrun]) || $a=="-d"} { set TRG(dryrun) 1 } else { usage } } else { lappend TRG(patternlist) [string map {% *} $a] } } set argv [list] | > > > > > > > > > > > > < < | 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | if {$isLast} { usage } } elseif {($n>2 && [string match "$a*" --zipvfs]) || $a=="-z"} { incr ii set TRG(zipvfs) [file normalize [lindex $argv $ii]] if {$isLast} { usage } } elseif {($n>2 && [string match "$a*" --buildonly]) || $a=="-b"} { set TRG(buildonly) 1 } elseif {($n>2 && [string match "$a*" --config]) || $a=="-c"} { incr ii set TRG(config) [lindex $argv $ii] } elseif {($n>2 && [string match "$a*" --dryrun]) || $a=="-d"} { set TRG(dryrun) 1 } elseif {($n>2 && [string match "$a*" --explain]) || $a=="-e"} { set TRG(explain) 1 } elseif {($n>2 && [string match "$a*" --omit]) || $a=="-c"} { incr ii set TRG(omitconfig) [lindex $argv $ii] } elseif {[string match "$a*" --stop-on-error]} { set TRG(stopOnError) 1 } elseif {[string match "$a*" --stop-on-coredump]} { set TRG(stopOnCore) 1 } else { usage } } else { lappend TRG(patternlist) [string map {% *} $a] } } set argv [list] # This script runs individual tests - tcl scripts or [make xyz] commands - # in directories named "testdir$N", where $N is an integer. This variable # contains a list of integers indicating the directories in use. # # This variable is accessed only via the following commands: # |
︙ | ︙ | |||
613 614 615 616 617 618 619 | $state ) } trdb last_insert_rowid } | > > > > > > > > > | | 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 | $state ) } trdb last_insert_rowid } # Argument $build is either an empty string, or else a list of length 3 # describing the job to build testfixture. In the usual form: # # {ID DIRNAME DISPLAYNAME} # # e.g # # {1 /home/user/sqlite/test/testrunner_bld_xyz All-Debug} # proc add_tcl_jobs {build config patternlist {shelldepid ""}} { global TRG set topdir [file dirname $::testdir] set testrunner_tcl [file normalize [info script]] if {$build==""} { set testfixture [info nameofexec] |
︙ | ︙ | |||
662 663 664 665 666 667 668 669 670 671 672 | } set lProp [trd_test_script_properties $f] set priority 0 if {[lsearch $lProp slow]>=0} { set priority 2 } if {[lsearch $lProp superslow]>=0} { set priority 4 } add_job \ -displaytype tcl \ -displayname $displayname \ -cmd $cmd \ | > > > | < | > > > > > > | > > > > > > > > > > > > > > > > > | 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 | } set lProp [trd_test_script_properties $f] set priority 0 if {[lsearch $lProp slow]>=0} { set priority 2 } if {[lsearch $lProp superslow]>=0} { set priority 4 } set depid [lindex $build 0] if {$shelldepid!="" && [lsearch $lProp shell]>=0} { set depid $shelldepid } add_job \ -displaytype tcl \ -displayname $displayname \ -cmd $cmd \ -depid $depid \ -priority $priority } } proc add_build_job {buildname target {postcmd ""} {depid ""}} { global TRG set dirname "[string tolower [string map {- _} $buildname]]_$target" set dirname "testrunner_bld_$dirname" set cmd "$TRG(makecmd) $target" if {$postcmd!=""} { append cmd "\n" append cmd $postcmd } set id [add_job \ -displaytype bld \ -displayname "Build $buildname ($target)" \ -dirname $dirname \ -build $buildname \ -cmd $cmd \ -depid $depid \ -priority 3 ] list $id [file normalize $dirname] $buildname } proc add_shell_build_job {buildname dirname depid} { global TRG if {$TRG(platform)=="win"} { set path [string map {/ \\} "$dirname/"] set copycmd "xcopy $TRG(shell) $path" } else { set copycmd "cp $TRG(shell) $dirname/" } return [ add_build_job $buildname $TRG(shell) $copycmd $depid ] } proc add_make_job {bld target} { global TRG if {$TRG(platform)=="win"} { set path [string map {/ \\} [lindex $bld 1]] set cmd "xcopy /S $path\\* ." |
︙ | ︙ | |||
763 764 765 766 767 768 769 | # Used to add jobs for "mdevtest" and "sdevtest". # proc add_devtest_jobs {lBld patternlist} { global TRG foreach b $lBld { set bld [add_build_job $b $TRG(testfixture)] | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > > > > > > | 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 | # Used to add jobs for "mdevtest" and "sdevtest". # proc add_devtest_jobs {lBld patternlist} { global TRG foreach b $lBld { set bld [add_build_job $b $TRG(testfixture)] add_tcl_jobs $bld veryquick $patternlist SHELL if {$patternlist==""} { add_fuzztest_jobs $b } if {[trdb one "SELECT EXISTS (SELECT 1 FROM jobs WHERE depid='SHELL')"]} { set sbld [add_shell_build_job $b [lindex $bld 1] [lindex $bld 0]] set sbldid [lindex $sbld 0] trdb eval { UPDATE jobs SET depid=$sbldid WHERE depid='SHELL' } } } } # Check to ensure that the interpreter is a full-blown "testfixture" # build and not just a "tclsh". If this is not the case, issue an # error message and exit. # proc must_be_testfixture {} { if {[lsearch [info commands] sqlite3_soft_heap_limit]<0} { puts "Use testfixture, not tclsh, for these arguments." exit 1 } } proc add_jobs_from_cmdline {patternlist} { global TRG if {$TRG(zipvfs)!=""} { add_zipvfs_jobs if {[llength $patternlist]==0} return } if {[llength $patternlist]==0} { set patternlist [list veryquick] } set first [lindex $patternlist 0] switch -- $first { all { must_be_testfixture set patternlist [lrange $patternlist 1 end] set clist [trd_all_configs] foreach c $clist { add_tcl_jobs "" $c $patternlist } } mdevtest { set config_set { All-O0 All-Debug } add_devtest_jobs $config_set [lrange $patternlist 1 end] } sdevtest { set config_set { All-Sanitize All-Debug } add_devtest_jobs $config_set [lrange $patternlist 1 end] } release { set patternlist [lrange $patternlist 1 end] foreach b [trd_builds $TRG(platform)] { if {$TRG(config)!="" && ![regexp "\\y$b\\y" $TRG(config)]} continue if {[regexp "\\y$b\\y" $TRG(omitconfig)]} continue set bld [add_build_job $b $TRG(testfixture)] foreach c [trd_configs $TRG(platform) $b] { add_tcl_jobs $bld $c $patternlist } if {$patternlist==""} { foreach e [trd_extras $TRG(platform) $b] { if {$e=="fuzztest"} { add_fuzztest_jobs $b } else { add_make_job $bld $e } } } } } list { set allperm [array names ::testspec] lappend allperm all mdevtest sdevtest release list puts "Allowed values for the PERMUTATION argument: [lsort $allperm]" exit 0 } default { must_be_testfixture if {[info exists ::testspec($first)]} { add_tcl_jobs "" $first [lrange $patternlist 1 end] } else { add_tcl_jobs "" full $patternlist } } } |
︙ | ︙ | |||
849 850 851 852 853 854 855 856 857 858 859 | add_jobs_from_cmdline $TRG(patternlist) } } proc mark_job_as_finished {jobid output state endtm} { r_write_db { trdb eval { UPDATE jobs SET output=$output, state=$state, endtime=$endtm WHERE jobid=$jobid; | > > > > > | | 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 | add_jobs_from_cmdline $TRG(patternlist) } } proc mark_job_as_finished {jobid output state endtm} { r_write_db { if {$state=="failed"} { set childstate omit } else { set childstate ready } trdb eval { UPDATE jobs SET output=$output, state=$state, endtime=$endtm WHERE jobid=$jobid; UPDATE jobs SET state=$childstate WHERE depid=$jobid; } } } proc script_input_ready {fd iJob jobid} { global TRG global O |
︙ | ︙ | |||
884 885 886 887 888 889 890 891 892 893 894 895 896 897 | set rc [catch { close $fd } msg] if {$rc} { if {[info exists TRG(reportlength)]} { puts -nonewline "[string repeat " " $TRG(reportlength)]\r" } puts "FAILED: $job(displayname) ($iJob)" set state "failed" } set tm [clock_milliseconds] set jobtm [expr {$tm - $job(starttime)}] puts $TRG(log) "### $job(displayname) ${jobtm}ms ($state)" puts $TRG(log) [string trim $O($iJob)] | > > > > > > > > | 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 | set rc [catch { close $fd } msg] if {$rc} { if {[info exists TRG(reportlength)]} { puts -nonewline "[string repeat " " $TRG(reportlength)]\r" } puts "FAILED: $job(displayname) ($iJob)" set state "failed" if {$TRG(stopOnError)} { puts "OUTPUT: $O($iJob)" exit 1 } if {$TRG(stopOnCore) && [string first {core dumped} $O($iJob)]>0} { puts "OUTPUT: $O($iJob)" exit 1 } } set tm [clock_milliseconds] set jobtm [expr {$tm - $job(starttime)}] puts $TRG(log) "### $job(displayname) ${jobtm}ms ($state)" puts $TRG(log) [string trim $O($iJob)] |
︙ | ︙ | |||
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 | set bWin [expr {$TRG(platform)=="win"}] set script [trd_buildscript $job(build) $srcdir $bWin] } set fd [open [file join $dir $TRG(make)] w] puts $fd $script close $fd } if { $TRG(dryrun) } { mark_job_as_finished $job(jobid) "" done 0 dirs_freeDir $iJob if {$job(build)!=""} { puts $TRG(log) "(cd $dir ; $job(cmd) )" } else { puts $TRG(log) "$job(cmd)" } } else { set pwd [pwd] cd $dir set fd [open $TRG(run) w] | > > > > > > > > > > > | | 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 | set bWin [expr {$TRG(platform)=="win"}] set script [trd_buildscript $job(build) $srcdir $bWin] } set fd [open [file join $dir $TRG(make)] w] puts $fd $script close $fd } # Add a batch/shell file command to set the directory used for temp # files to the test's working directory. Otherwise, tests that use # large numbers of temp files (e.g. zipvfs), might generate temp # filename collisions. if {$TRG(platform)=="win"} { set set_tmp_dir "SET SQLITE_TMPDIR=[file normalize $dir]" } else { set set_tmp_dir "export SQLITE_TMPDIR=\"[file normalize $dir]\"" } if { $TRG(dryrun) } { mark_job_as_finished $job(jobid) "" done 0 dirs_freeDir $iJob if {$job(build)!=""} { puts $TRG(log) "(cd $dir ; $job(cmd) )" } else { puts $TRG(log) "$job(cmd)" } } else { set pwd [pwd] cd $dir set fd [open $TRG(run) w] puts $fd $set_tmp_dir puts $fd $job(cmd) close $fd set fd [open "|$TRG(runcmd) 2>@1" r] cd $pwd fconfigure $fd -blocking false fileevent $fd readable [list script_input_ready $fd $iJob $job(jobid)] } |
︙ | ︙ | |||
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 | puts "$nErr failures:" trdb eval { SELECT displayname FROM jobs WHERE state='failed' } { puts "FAILED: $displayname" } } } puts "\nTest database is $TRG(dbname)" puts "Test log is $TRG(logname)" } # Handle the --buildonly option, if it was specified. # proc handle_buildonly {} { global TRG if {$TRG(buildonly)} { r_write_db { trdb eval { DELETE FROM jobs WHERE displaytype!='bld' } } } } sqlite3 trdb $TRG(dbname) trdb timeout $TRG(timeout) set tm [lindex [time { make_new_testset }] 0] | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < | | > < | 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 | puts "$nErr failures:" trdb eval { SELECT displayname FROM jobs WHERE state='failed' } { puts "FAILED: $displayname" } } set nOmit [trdb one {SELECT count(*) FROM jobs WHERE state='omit'}] if {$nOmit>0} { puts "$nOmit jobs skipped due to prior failures" } } puts "\nTest database is $TRG(dbname)" puts "Test log is $TRG(logname)" } # Handle the --buildonly option, if it was specified. # proc handle_buildonly {} { global TRG if {$TRG(buildonly)} { r_write_db { trdb eval { DELETE FROM jobs WHERE displaytype!='bld' } } } } # Handle the --explain option. Provide a human-readable # explanation of all the tests that are in the trdb database jobs # table. # proc explain_layer {indent depid} { global TRG if {$TRG(buildonly)} { set showtests 0 } else { set showtests 1 } trdb eval {SELECT jobid, displayname, displaytype, dirname FROM jobs WHERE depid=$depid ORDER BY displayname} { if {$displaytype=="bld"} { puts "${indent}$displayname in $dirname" explain_layer "${indent} " $jobid } elseif {$showtests} { puts "${indent}[lindex $displayname end]" } } } proc explain_tests {} { explain_layer "" "" } sqlite3 trdb $TRG(dbname) trdb timeout $TRG(timeout) set tm [lindex [time { make_new_testset }] 0] if {$TRG(explain)} { explain_tests } else { if {$TRG(nJob)>1} { puts "splitting work across $TRG(nJob) jobs" } puts "built testset in [expr $tm/1000]ms.." handle_buildonly run_testset } trdb close |
Changes to test/testrunner_data.tcl.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 | set tcltest(linux.User-Auth) veryquick set tcltest(linux.Update-Delete-Limit) veryquick set tcltest(linux.Extra-Robustness) veryquick set tcltest(linux.Device-Two) veryquick set tcltest(linux.No-lookaside) veryquick set tcltest(linux.Devkit) veryquick set tcltest(linux.Apple) veryquick set tcltest(linux.Sanitize) veryquick set tcltest(linux.Device-One) all set tcltest(linux.Default) all_plus_autovacuum_crash set tcltest(linux.Valgrind) valgrind set tcltest(osx.Locking-Style) veryquick set tcltest(osx.Have-Not) veryquick | > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | set tcltest(linux.User-Auth) veryquick set tcltest(linux.Update-Delete-Limit) veryquick set tcltest(linux.Extra-Robustness) veryquick set tcltest(linux.Device-Two) veryquick set tcltest(linux.No-lookaside) veryquick set tcltest(linux.Devkit) veryquick set tcltest(linux.Apple) veryquick set tcltest(linux.Android) veryquick set tcltest(linux.Sanitize) veryquick set tcltest(linux.Device-One) all set tcltest(linux.Default) all_plus_autovacuum_crash set tcltest(linux.Valgrind) valgrind set tcltest(osx.Locking-Style) veryquick set tcltest(osx.Have-Not) veryquick |
︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 62 | set extra(linux.Secure-Delete) {fuzztest sourcetest} set extra(linux.Unlock-Notify) {fuzztest sourcetest} set extra(linux.Update-Delete-Limit) {fuzztest sourcetest} set extra(linux.Extra-Robustness) {fuzztest sourcetest} set extra(linux.Device-Two) {fuzztest sourcetest threadtest} set extra(linux.No-lookaside) {fuzztest sourcetest} set extra(linux.Devkit) {fuzztest sourcetest} set extra(linux.Apple) {fuzztest sourcetest} set extra(linux.Sanitize) {fuzztest sourcetest} set extra(linux.Default) {fuzztest sourcetest threadtest} set extra(osx.Apple) {fuzztest threadtest} set extra(osx.Have-Not) {fuzztest sourcetest} set extra(osx.Locking-Style) {mptest fuzztest sourcetest} | > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | set extra(linux.Secure-Delete) {fuzztest sourcetest} set extra(linux.Unlock-Notify) {fuzztest sourcetest} set extra(linux.Update-Delete-Limit) {fuzztest sourcetest} set extra(linux.Extra-Robustness) {fuzztest sourcetest} set extra(linux.Device-Two) {fuzztest sourcetest threadtest} set extra(linux.No-lookaside) {fuzztest sourcetest} set extra(linux.Devkit) {fuzztest sourcetest} set extra(linux.Android) {fuzztest sourcetest} set extra(linux.Apple) {fuzztest sourcetest} set extra(linux.Sanitize) {fuzztest sourcetest} set extra(linux.Default) {fuzztest sourcetest threadtest} set extra(osx.Apple) {fuzztest threadtest} set extra(osx.Have-Not) {fuzztest sourcetest} set extra(osx.Locking-Style) {mptest fuzztest sourcetest} |
︙ | ︙ | |||
104 105 106 107 108 109 110 111 112 113 114 115 116 117 | } set build(Sanitize) { CC=clang -fsanitize=address,undefined -fno-sanitize-recover=undefined -DSQLITE_ENABLE_STAT4 -DSQLITE_OMIT_LOOKASIDE=1 -DCONFIG_SLOWDOWN_FACTOR=5.0 --enable-debug --enable-all } set build(Stdcall) { -DUSE_STDCALL=1 -O2 } | > | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | } set build(Sanitize) { CC=clang -fsanitize=address,undefined -fno-sanitize-recover=undefined -DSQLITE_ENABLE_STAT4 -DSQLITE_OMIT_LOOKASIDE=1 -DCONFIG_SLOWDOWN_FACTOR=5.0 -DSQLITE_ENABLE_RBU --enable-debug --enable-all } set build(Stdcall) { -DUSE_STDCALL=1 -O2 } |
︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 | -DSQLITE_THREADSAFE=2 --enable-fts5 --enable-session } set build(Locking-Style) { -O2 -DSQLITE_ENABLE_LOCKING_STYLE=1 } set build(Apple) { -Os -DHAVE_GMTIME_R=1 -DHAVE_ISNAN=1 -DHAVE_LOCALTIME_R=1 -DHAVE_PREAD=1 -DHAVE_PWRITE=1 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | -DSQLITE_THREADSAFE=2 --enable-fts5 --enable-session } set build(Locking-Style) { -O2 -DSQLITE_ENABLE_LOCKING_STYLE=1 } set build(Android) { -Os -DHAVE_USLEEP=1 -DSQLITE_HAVE_ISNAN -DSQLITE_POWERSAFE_OVERWRITE=1 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_DEFAULT_AUTOVACUUM=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_BACKWARDS -DSQLITE_ENABLE_FTS4 -DSQLITE_SECURE_DELETE -DSQLITE_ENABLE_BATCH_ATOMIC_WRITE -DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD -DSQLITE_ALLOW_ROWID_IN_VIEW -DSQLITE_ENABLE_BYTECODE_VTAB -Wno-unused-parameter -Werror -DUSE_PREAD64 -Dfdatasync=fdatasync -DHAVE_MALLOC_H=1 -DHAVE_MALLOC_USABLE_SIZE -DSQLITE_ENABLE_DBSTAT_VTAB } # Compile-options used by Android but omitted from these # tests: # -DNDEBUG=1 # -DSQLITE_DEFAULT_LEGACY_ALTER_TABLE # -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 # -DSQLITE_DEFAULT_FILE_PERMISSIONS=0600 # -DSQLITE_OMIT_BUILTIN_TEST # -DSQLITE_OMIT_LOAD_EXTENSION # -DSQLITE_OMIT_COMPILEOPTION_DIAGS # set build(Apple) { -Os -DHAVE_GMTIME_R=1 -DHAVE_ISNAN=1 -DHAVE_LOCALTIME_R=1 -DHAVE_PREAD=1 -DHAVE_PWRITE=1 |
︙ | ︙ | |||
594 595 596 597 598 599 600 | # use the MSVC compiler, false otherwise. # proc trd_buildscript {config srcdir bMsvc} { trd_import # Ensure that the named configuration exists. if {![info exists build($config)]} { | > | > > > > | 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 | # use the MSVC compiler, false otherwise. # proc trd_buildscript {config srcdir bMsvc} { trd_import # Ensure that the named configuration exists. if {![info exists build($config)]} { if {$config!="help"} { puts "No such build config: $config" } puts "Available configurations: [lsort [array names build]]" flush stdout exit 1 } # Generate and return the script. return [make_script $build($config) $srcdir $bMsvc] } # Usage: |
︙ | ︙ | |||
633 634 635 636 637 638 639 | } set trd_test_script_properties_cache($path) $ret close $fd } set trd_test_script_properties_cache($path) } | < | 675 676 677 678 679 680 681 | } set trd_test_script_properties_cache($path) $ret close $fd } set trd_test_script_properties_cache($path) } |
Changes to test/tkt-8454a207b9.test.
︙ | ︙ | |||
45 46 47 48 49 50 51 | } } {0 text} do_test tkt-8454a207b9.4 { db eval { ALTER TABLE t1 ADD COLUMN e DEFAULT -123.0; SELECT e, typeof(e) FROM t1; } | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | } } {0 text} do_test tkt-8454a207b9.4 { db eval { ALTER TABLE t1 ADD COLUMN e DEFAULT -123.0; SELECT e, typeof(e) FROM t1; } } {-123.0 real} do_test tkt-8454a207b9.5 { db eval { ALTER TABLE t1 ADD COLUMN f DEFAULT -123.5; SELECT f, typeof(f) FROM t1; } } {-123.5 real} do_test tkt-8454a207b9.6 { |
︙ | ︙ |
Changes to test/tkt-bd484a090c.test.
︙ | ︙ | |||
26 27 28 29 30 31 32 | sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 1 do_test 2.1 { catchsql { SELECT datetime('now', 'localtime') } } {1 {local time unavailable}} do_test 2.2 { | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 | sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 1 do_test 2.1 { catchsql { SELECT datetime('now', 'localtime') } } {1 {local time unavailable}} do_test 2.2 { catchsql { SELECT datetime('2000-01-01', 'utc') } } {1 {local time unavailable}} sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 0 finish_test |
Changes to test/trigger9.test.
︙ | ︙ | |||
238 239 240 241 242 243 244 | END; CREATE TRIGGER tr3 INSTEAD OF INSERT ON v1 BEGIN INSERT INTO log VALUES('insert'); END; } | > | | | | | | > > > > > > > > > > > > > > | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | END; CREATE TRIGGER tr3 INSTEAD OF INSERT ON v1 BEGIN INSERT INTO log VALUES('insert'); END; } ifcapable !allow_rowid_in_view { do_catchsql_test 4.2 { DELETE FROM v1 WHERE rowid=1; } {1 {no such column: rowid}} do_catchsql_test 4.3 { UPDATE v1 SET a=b WHERE rowid=2; } {1 {no such column: rowid}} } else { do_execsql_test 4.2a { DELETE FROM log; } do_catchsql_test 4.2 { DELETE FROM v1 WHERE rowid=1; } {0 {}} do_catchsql_test 4.3 { UPDATE v1 SET a=b WHERE rowid=2; } {0 {}} do_execsql_test 4.3b { SELECT * FROM log; } } finish_test |
Changes to test/types3.test.
︙ | ︙ | |||
8 9 10 11 12 13 14 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The focus # of this file is testing the interaction of SQLite manifest types # with Tcl dual-representations. # | < < | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The focus # of this file is testing the interaction of SQLite manifest types # with Tcl dual-representations. # set testdir [file dirname $argv0] source $testdir/tester.tcl # A variable with only a string representation comes in as TEXT do_test types3-1.1 { set V {} |
︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 | set V [db one {SELECT '1234567890123456.0'}] tcl_variable_type V } {} do_test types3-2.6 { set V [db one {SELECT NULL}] tcl_variable_type V } {} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | set V [db one {SELECT '1234567890123456.0'}] tcl_variable_type V } {} do_test types3-2.6 { set V [db one {SELECT NULL}] tcl_variable_type V } {} # See https://sqlite.org/forum/forumpost/3776b48e71 # # On a text-affinity comparison of two values where one of # the values has both MEM_Str and a numeric type like MEM_Int, # make sure that only the MEM_Str representation is used. # sqlite3_create_function db do_execsql_test types3-3.1 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x TEXT PRIMARY KEY); INSERT INTO t1 VALUES('1'); SELECT * FROM t1 WHERE NOT x=upper(1); } {} do_execsql_test types3-3.2 { SELECT * FROM t1 WHERE NOT x=add_text_type(1); } {} do_execsql_test types3-3.3 { SELECT * FROM t1 WHERE NOT x=add_int_type('1'); } {} do_execsql_test types3-3.4 { DELETE FROM t1; INSERT INTO t1 VALUES(1.25); SELECT * FROM t1 WHERE NOT x=add_real_type('1.25'); } {} do_execsql_test types3-3.5 { SELECT * FROM t1 WHERE NOT x=add_text_type(1.25); } {} finish_test |
Changes to test/unionall.test.
︙ | ︙ | |||
342 343 344 345 346 347 348 349 350 351 352 | } {} do_execsql_test 5.10 { SELECT *, '+' FROM t1 LEFT JOIN t2 ON (a NOT IN(SELECT v FROM t1, t3 WHERE a=k)=NOT EXISTS(SELECT 1 FROM t1 LEFT JOIN t3 ON (a=k))); } {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +} do_execsql_test 5.20 { SELECT *, '+' FROM t1 LEFT JOIN t3 ON (a NOT IN(SELECT v FROM t1 LEFT JOIN t2 ON (a=k))=k); } {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +} do_catchsql_test 5.30 { SELECT * FROM (t1 NATURAL JOIN pragma_table_xinfo('t1_a') NATURAL JOIN t3) t1 NATURAL JOIN t2 NATURAL JOIN t3 WHERE rowid ISNULL>0 AND 0%y; | > | > | 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | } {} do_execsql_test 5.10 { SELECT *, '+' FROM t1 LEFT JOIN t2 ON (a NOT IN(SELECT v FROM t1, t3 WHERE a=k)=NOT EXISTS(SELECT 1 FROM t1 LEFT JOIN t3 ON (a=k))); } {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +} do_execsql_test 5.20 { SELECT *, '+' FROM t1 LEFT JOIN t3 ON (a NOT IN(SELECT v FROM t1 LEFT JOIN t2 ON (a=k))=k); } {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +} ifcapable vtab { do_catchsql_test 5.30 { SELECT * FROM (t1 NATURAL JOIN pragma_table_xinfo('t1_a') NATURAL JOIN t3) t1 NATURAL JOIN t2 NATURAL JOIN t3 WHERE rowid ISNULL>0 AND 0%y; } {1 {ambiguous column name: rowid}} } reset_db do_execsql_test 6.0 { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(3,4); |
︙ | ︙ |
Changes to test/upsert1.test.
︙ | ︙ | |||
263 264 265 266 267 268 269 270 271 | CREATE TABLE t1(a INT, b INT); CREATE UNIQUE INDEX t1x ON t1(b+3); } sqlite3_db_config db ENABLE_QPSG 1 do_catchsql_test upsert1-1210 { INSERT INTO t1(a,b) VALUES(1,2) ON CONFLICT(b+?1) DO NOTHING; } {1 {ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | CREATE TABLE t1(a INT, b INT); CREATE UNIQUE INDEX t1x ON t1(b+3); } sqlite3_db_config db ENABLE_QPSG 1 do_catchsql_test upsert1-1210 { INSERT INTO t1(a,b) VALUES(1,2) ON CONFLICT(b+?1) DO NOTHING; } {1 {ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint}} # 2024-04-11 https://sqlite.org/forum/forumpost/284955a3cd454a15 # Incorrect value passed into a trigger that fires as the result of # an upsert. # reset_db do_execsql_test upsert1-1300 { CREATE TABLE t1(x INT, y TEXT); INSERT INTO t1 VALUES (11, printf('%.9000c','a')), (11, printf('%.9000c','a')), (33, printf('%.9000c','b')), (33, printf('%.9000c','b')); CREATE TABLE t2(x INT UNIQUE, y TEXT); CREATE TRIGGER r1 BEFORE UPDATE ON t2 BEGIN SELECT raise(ABORT,'Incorrect old.y value passed to trigger!') WHERE old.y != new.y; /* ^^^ This trigger will fire and cause the ABORT if the problem has ** not been fixed, or if there is a regression. */ END; INSERT INTO t2(x, y) SELECT x, y FROM t1 WHERE true ON CONFLICT (x) DO UPDATE SET y = excluded.y; } {} finish_test |
Changes to test/upsert5.test.
︙ | ︙ | |||
403 404 405 406 407 408 409 410 411 | do_catchsql_test 2.1 { INSERT INTO t2(a,b,c,e,d) VALUES(1,2,3,4,5) ON CONFLICT(c) DO UPDATE SET b='' ON CONFLICT((SELECT t2 FROM nosuchtable)) DO NOTHING; } {1 {no such table: nosuchtable}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | do_catchsql_test 2.1 { INSERT INTO t2(a,b,c,e,d) VALUES(1,2,3,4,5) ON CONFLICT(c) DO UPDATE SET b='' ON CONFLICT((SELECT t2 FROM nosuchtable)) DO NOTHING; } {1 {no such table: nosuchtable}} # 2024-03-08 https://sqlite.org/forum/forumpost/919c6579c8 # A redundant ON CONFLICT clause in an upsert can lead to # index corruption. # reset_db do_execsql_test 3.0 { CREATE TABLE t1(aa INTEGER PRIMARY KEY, bb INT); INSERT INTO t1 VALUES(11,22); CREATE UNIQUE INDEX t1bb ON t1(bb); REPLACE INTO t1 VALUES(11,33) ON CONFLICT(bb) DO UPDATE SET aa = 44 ON CONFLICT(bb) DO UPDATE SET aa = 44; PRAGMA integrity_check; } {ok} do_execsql_test 3.1 { SELECT * FROM t1 NOT INDEXED; } {11 33} do_execsql_test 3.2 { SELECT * FROM t1 INDEXED BY t1bb; } {11 33} do_execsql_test 3.3 { DROP TABLE t1; CREATE TABLE t1(aa INTEGER PRIMARY KEY, bb INT, cc INT); INSERT INTO t1 VALUES(10,21,32),(11,22,33),(12,23,34); CREATE UNIQUE INDEX t1bb ON t1(bb); CREATE UNIQUE INDEX t1cc ON t1(cc); REPLACE INTO t1 VALUES(11,44,55) ON CONFLICT(bb) DO UPDATE SET aa = 99 ON CONFLICT(cc) DO UPDATE SET aa = 99 ON CONFLICT(bb) DO UPDATE SET aa = 99; PRAGMA integrity_check; } {ok} do_execsql_test 3.4 { SELECT * FROM t1 NOT INDEXED ORDER BY +aa; } {10 21 32 11 44 55 12 23 34} do_execsql_test 3.5 { SELECT * FROM t1 INDEXED BY t1bb ORDER BY +aa; } {10 21 32 11 44 55 12 23 34} do_execsql_test 3.6 { SELECT * FROM t1 INDEXED BY t1cc ORDER BY +aa; } {10 21 32 11 44 55 12 23 34} finish_test |
Changes to test/vacuum-into.test.
︙ | ︙ | |||
22 23 24 25 26 27 28 | omit_test vacuum.test {Compiled with SQLITE_OMIT_VACUUM} finish_test return } forcedelete out.db do_execsql_test vacuum-into-100 { | | > > > > > > > > > > > > > > > > > > > > > > > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | omit_test vacuum.test {Compiled with SQLITE_OMIT_VACUUM} finish_test return } forcedelete out.db do_execsql_test vacuum-into-100 { CREATE TABLE t1( a INTEGER PRIMARY KEY, b ANY, c INT AS (b+1), --- See "2024-04-09" block CHECK( typeof(b)!='integer' OR b>a-5 ) --- comment below ); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) INSERT INTO t1(a,b) SELECT x, randomblob(600) FROM c; CREATE INDEX t1b ON t1(b); DELETE FROM t1 WHERE a%2; SELECT count(*), sum(a), sum(length(b)) FROM t1; } {50 2550 30000} # Update 2024-04-09 for forum post eec177d68fe7fa2c. # # VACUUM INTO is sensitive to tables holding both generated columns # and CHECK constraints. # # CHECK constraints are ignored for read-only databases in order to save # memory (see check-in 34ddf02d3d21151b on 2014-05-21). But the xfer # optimization normally only works if CHECK constraints match between the # source and destination tables. So the xfer optimization was not # working for VACUUM INTO when the source was a read-only database and the # table held CHECK constraints. But if the table has generated columns, # then the xfer optimization is required or else VACUUM will raise an # error. # # Fix this by ignoring CHECK constraints when determining whether or not # the xfer optimization can run while doing VACUUM. do_execsql_test vacuum-into-110 { VACUUM main INTO 'out.db'; } {} sqlite3 db2 out.db do_test vacuum-into-120 { db2 eval {SELECT count(*), sum(a), sum(length(b)) FROM t1} } {50 2550 30000} |
︙ | ︙ | |||
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | } 1 do_catchsql_test vacuum-into-420 { VACUUM INTO target2() } {1 {no such function: target2}} # The ability to VACUUM INTO a read-only database db close sqlite3 db test.db -readonly 1 forcedelete test.db2 do_execsql_test vacuum-into-500 { VACUUM INTO 'test.db2'; } sqlite3 db2 test.db2 do_test vacuum-into-510 { db2 eval {SELECT name FROM sqlite_master ORDER BY 1} } {t1 t1b t2} db2 close db close | > > > > > > > > > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | } 1 do_catchsql_test vacuum-into-420 { VACUUM INTO target2() } {1 {no such function: target2}} # The ability to VACUUM INTO a read-only database db close if {$tcl_platform(platform)=="windows"} { file attributes test.db -readonly 1 } else { file attributes test.db -permissions 292 ;# 292 == 0444 } sqlite3 db test.db -readonly 1 forcedelete test.db2 do_execsql_test vacuum-into-500 { VACUUM INTO 'test.db2'; } if {$tcl_platform(platform)=="windows"} { file attributes test.db -readonly 0 } else { file attributes test.db -permissions 420 ;# 420 = 0644 } sqlite3 db2 test.db2 do_test vacuum-into-510 { db2 eval {SELECT name FROM sqlite_master ORDER BY 1} } {t1 t1b t2} db2 close db close |
︙ | ︙ |
Added test/values.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 | # 2024 March 3 # # 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. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix values do_execsql_test 1.0 { CREATE TABLE x1(a, b, c); } explain_i { INSERT INTO x1(a, b, c) VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4); } do_execsql_test 1.1.1 { INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4); } do_execsql_test 1.1.2 { SELECT * FROM x1; } { 1 1 1 2 2 2 3 3 3 4 4 4 } do_execsql_test 1.2.0 { DELETE FROM x1 } do_execsql_test 1.2.1 { INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3) UNION ALL SELECT 4, 4, 4; SELECT * FROM x1; } {1 1 1 2 2 2 3 3 3 4 4 4} sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 4 do_execsql_test 1.2.2 { DELETE FROM x1; INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5) UNION ALL SELECT 6, 6, 6; SELECT * FROM x1; } {1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6} do_execsql_test 1.2.3 { DELETE FROM x1; INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4) UNION ALL SELECT 6, 6, 6; SELECT * FROM x1; } {1 1 1 2 2 2 3 3 3 4 4 4 6 6 6} do_execsql_test 1.2.4 { DELETE FROM x1; INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3) UNION ALL SELECT 6, 6, 6; SELECT * FROM x1; } { 1 1 1 2 2 2 3 3 3 6 6 6 } set a 4 set b 5 set c 6 do_execsql_test 1.2.5 { DELETE FROM x1; INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, $a), (5, 5, $b), (6, 6, $c) } do_execsql_test 1.2.6 { SELECT * FROM x1; } { 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 } #------------------------------------------------------------------------- # SQLITE_LIMIT_COMPOUND_SELECT set to 0. # reset_db do_execsql_test 2.0 { CREATE TABLE x1(a, b, c); } sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 3 do_catchsql_test 2.1.1 { INSERT INTO x1 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9), (10, 10, 10, 10) } {1 {all VALUES must have the same number of terms}} do_catchsql_test 2.1.2 { INSERT INTO x1 VALUES (1, 1, 1), (2, 2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9), (10, 10, 10) } {1 {all VALUES must have the same number of terms}} sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 0 do_execsql_test 2.2 { INSERT INTO x1 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9), (10, 10, 10) } {} do_execsql_test 2.3 { INSERT INTO x1 VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9), (10, 10, 10) UNION ALL SELECT 5, 12, 12 ORDER BY 1 } {} #------------------------------------------------------------------------- reset_db do_execsql_test 3.0 { CREATE TABLE y1(x, y); } do_execsql_test 3.1.1 { DELETE FROM y1; INSERT INTO y1 VALUES(1, 2), (3, 4), (row_number() OVER (), 5); } do_execsql_test 3.1.2 { SELECT * FROM y1; } {1 2 3 4 1 5} do_execsql_test 3.2.1 { DELETE FROM y1; INSERT INTO y1 VALUES(1, 2), (3, 4), (row_number() OVER (), 6) , (row_number() OVER (), 7) } do_execsql_test 3.1.2 { SELECT * FROM y1; } {1 2 3 4 1 6 1 7} #------------------------------------------------------------------------- reset_db do_execsql_test 4.0 { CREATE TABLE x1(a PRIMARY KEY, b) WITHOUT ROWID; } foreach {tn iLimit} {1 0 2 3} { sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT $iLimit do_execsql_test 4.1.1 { DELETE FROM x1; INSERT INTO x1 VALUES (1, 1), (2, (SELECT * FROM (VALUES('a'), ('b'), ('c'), ('d')) )) } do_execsql_test 4.1.2 { SELECT * FROM x1 } {1 1 2 a} do_execsql_test 4.2.1 { DELETE FROM x1; INSERT INTO x1 VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, (SELECT * FROM (VALUES('a'), ('b'), ('c'), ('d')) )) } do_execsql_test 4.2.2 { SELECT * FROM x1 } {1 1 2 2 3 3 4 4 5 a} do_execsql_test 4.3.1 { DELETE FROM x1; INSERT INTO x1 VALUES (1, (SELECT * FROM (VALUES('a'), ('b'), ('c'), ('d'), ('e')) )) } do_execsql_test 4.3.2 { SELECT * FROM x1 } {1 a} } #------------------------------------------------------------------------ reset_db do_execsql_test 5.0 { CREATE VIEW v1 AS VALUES(1, 2, 3), (4, 5, 6), (7, 8, 9); } do_execsql_test 5.1 { SELECT * FROM v1 } {1 2 3 4 5 6 7 8 9} #------------------------------------------------------------------------- reset_db do_execsql_test 6.0 { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1), (2); } do_execsql_test 6.1 { SELECT ( VALUES( x ), ( x ) ) FROM t1; } {1 2} #------------------------------------------------------------------------- reset_db do_execsql_test 6.0 { CREATE TABLE t1(x); INSERT INTO t1 VALUES('x'), ('y'); } do_execsql_test 6.1 { SELECT * FROM t1, (VALUES(1), (2)) } {x 1 x 2 y 1 y 2} do_execsql_test 6.2 { VALUES(CAST(44 AS REAL)),(55); } {44.0 55} #------------------------------------------------------------------------ do_execsql_test 7.1 { WITH x1(a, b) AS ( VALUES(1, 2), ('a', 'b') ) SELECT * FROM x1 one, x1 two } { 1 2 1 2 1 2 a b a b 1 2 a b a b } #------------------------------------------------------------------------- reset_db set VVV { ( VALUES('a', 'b'), ('c', 'd'), (123, NULL) ) } set VVV2 { ( SELECT 'a' AS column1, 'b' AS column2 UNION ALL SELECT 'c', 'd' UNION ALL SELECT 123, NULL ) } do_execsql_test 8.0 { CREATE TABLE t1(x); INSERT INTO t1 VALUES('d'), (NULL), (123) } foreach {tn q res} { 1 "SELECT * FROM t1 LEFT JOIN VVV" { d a b d c d d 123 {} {} a b {} c d {} 123 {} 123 a b 123 c d 123 123 {} } 2 "SELECT * FROM t1 LEFT JOIN VVV ON (column1=x)" { d {} {} {} {} {} 123 123 {} } 3 "SELECT * FROM t1 RIGHT JOIN VVV" { d a b d c d d 123 {} {} a b {} c d {} 123 {} 123 a b 123 c d 123 123 {} } 4 "SELECT * FROM t1 RIGHT JOIN VVV ON (column1=x)" { 123 123 {} {} a b {} c d } 5 "SELECT * FROM t1 FULL OUTER JOIN VVV ON (column1=x)" { d {} {} {} {} {} 123 123 {} {} a b {} c d } 6 "SELECT count(*) FROM VVV" { 3 } 7 "SELECT (SELECT column1 FROM VVV)" { a } 8 "SELECT * FROM VVV UNION ALL SELECT * FROM VVV" { a b c d 123 {} a b c d 123 {} } 9 "SELECT * FROM VVV INTERSECT SELECT * FROM VVV" { 123 {} a b c d } 10 "SELECT * FROM VVV eXCEPT SELECT * FROM VVV" { } 11 "SELECT * FROM VVV eXCEPT SELECT 'a', 'b'" { 123 {} c d } } { set q1 [string map [list VVV $VVV] $q] set q2 [string map [list VVV $VVV2] $q] set q3 "WITH VVV AS $VVV $q" do_execsql_test 8.1.$tn.1 $q1 $res do_execsql_test 8.1.$tn.2 $q2 $res do_execsql_test 8.1.$tn.3 $q3 $res } #------------------------------------------------------------------------- reset_db do_execsql_test 9.1 { VALUES(456), (123), (NULL) UNION ALL SELECT 122 ORDER BY 1 } { {} 122 123 456 } do_execsql_test 9.2 { VALUES (1, 2), (3, 4), ( ( SELECT column1 FROM ( VALUES (5, 6), (7, 8) ) ), ( SELECT max(column2) FROM ( VALUES (5, 1), (7, 6) ) ) ) } { 1 2 3 4 5 6 } do_execsql_test 10.1 { CREATE TABLE a2(a, b, c DEFAULT 'xyz'); } do_execsql_test 10.2 { INSERT INTO a2(a) VALUES(3),(4); } #------------------------------------------------------------------------- reset_db ifcapable fts5 { do_execsql_test 11.0 { CREATE VIRTUAL TABLE ft USING fts3(x); } do_execsql_test 11.1 { INSERT INTO ft VALUES('one'), ('two'); } } #------------------------------------------------------------------------- reset_db do_execsql_test 12.0 { CREATE TABLE t1(a, b); } do_execsql_test 12.1 { INSERT INTO t1 SELECT 1, 2 UNION ALL VALUES(3, 4), (5, 6); } do_execsql_test 12.2 { SELECT * FROM t1 } {1 2 3 4 5 6} #------------------------------------------------------------------------- reset_db do_execsql_test 13.0 { CREATE TABLE t1(x); INSERT INTO t1 VALUES('xyz'); SELECT ( VALUES( (max(substr('abc', 1, 1), x)) ), (123), (456) ) FROM t1; } {xyz} do_catchsql_test 13.1 { VALUES(300), (zeroblob(300) OVER win); } {1 {zeroblob() may not be used as a window function}} #-------------------------------------------------------------------------- reset_db do_execsql_test 14.1 { PRAGMA encoding = utf16; CREATE TABLE t1(a, b); } {} db close sqlite3 db test.db do_execsql_test 14.2 { INSERT INTO t1 VALUES (17, 'craft'), (16, 'urtlek' IN(1,2,3)); } #-------------------------------------------------------------------------- # reset_db do_eqp_test 15.1 { VALUES(1),(2),(3),(4),(5); } { QUERY PLAN `--SCAN 5-ROW VALUES CLAUSE } do_execsql_test 15.2 { CREATE TABLE t1(a,b); } do_eqp_test 15.3 { INSERT INTO t1 VALUES (1,2),(3,4),(7,8); } { QUERY PLAN `--SCAN 3-ROW VALUES CLAUSE } do_eqp_test 15.4 { INSERT INTO t1 VALUES (1,2),(3,4),(7,8), (5,row_number()OVER()); } { QUERY PLAN `--COMPOUND QUERY |--LEFT-MOST SUBQUERY | `--SCAN 3-ROW VALUES CLAUSE `--UNION ALL |--CO-ROUTINE (subquery-xxxxxx) | `--SCAN CONSTANT ROW `--SCAN (subquery-xxxxxx) } do_eqp_test 15.5 { SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6)), (VALUES('a'),('b'),('c')); } { QUERY PLAN |--SCAN 6-ROW VALUES CLAUSE `--SCAN 3-ROW VALUES CLAUSE } do_execsql_test 15.6 { CREATE TABLE t2(x,y); } do_eqp_test 15.7 { SELECT * FROM t2 UNION ALL VALUES(1,2),(3,4),(5,6),(7,8); } { QUERY PLAN `--COMPOUND QUERY |--LEFT-MOST SUBQUERY | `--SCAN t2 `--UNION ALL `--SCAN 4-ROW VALUES CLAUSE } #-------------------------------------------------------------------------- # The VALUES-as-coroutine optimization can be applied to later rows of # a VALUES clause even if earlier rows do not qualify. # reset_db do_execsql_test 16.1 { CREATE TABLE t1(a,b); } do_execsql_test 16.2 { BEGIN; INSERT INTO t1 VALUES(1,2),(3,4),(5,6), (7,row_number()OVER()), (9,10), (11,12), (13,14), (15,16); SELECT * FROM t1 ORDER BY a, b; ROLLBACK; } {1 2 3 4 5 6 7 1 9 10 11 12 13 14 15 16} do_eqp_test 16.3 { INSERT INTO t1 VALUES(1,2),(3,4),(5,6), (7,row_number()OVER()), (9,10), (11,12), (13,14), (15,16); } { QUERY PLAN `--COMPOUND QUERY |--LEFT-MOST SUBQUERY | `--SCAN 3-ROW VALUES CLAUSE |--UNION ALL | |--CO-ROUTINE (subquery-xxxxxx) | | `--SCAN CONSTANT ROW | `--SCAN (subquery-xxxxxx) `--UNION ALL `--SCAN 4-ROW VALUES CLAUSE } do_execsql_test 16.4 { BEGIN; INSERT INTO t1 VALUES (1,row_number()OVER()), (2,3), (4,5), (6,7); SELECT * FROM t1 ORDER BY a, b; ROLLBACK; } {1 1 2 3 4 5 6 7} do_eqp_test 16.5 { INSERT INTO t1 VALUES (1,row_number()OVER()), (2,3), (4,5), (6,7); } { QUERY PLAN `--COMPOUND QUERY |--LEFT-MOST SUBQUERY | |--CO-ROUTINE (subquery-xxxxxx) | | `--SCAN CONSTANT ROW | `--SCAN (subquery-xxxxxx) `--UNION ALL `--SCAN 3-ROW VALUES CLAUSE } do_execsql_test 16.6 { BEGIN; INSERT INTO t1 VALUES (1,2),(3,4), (5,row_number()OVER()), (7,8),(9,10),(11,12), (13,row_number()OVER()), (15,16),(17,18),(19,20),(21,22); SELECT * FROM t1 ORDER BY a, b; ROLLBACK; } { 1 2 3 4 5 1 7 8 9 10 11 12 13 1 15 16 17 18 19 20 21 22} do_eqp_test 16.7 { INSERT INTO t1 VALUES (1,2),(3,4), (5,row_number()OVER()), (7,8),(9,10),(11,12), (13,row_number()OVER()), (15,16),(17,18),(19,20),(21,22); } { QUERY PLAN `--COMPOUND QUERY |--LEFT-MOST SUBQUERY | `--SCAN 2-ROW VALUES CLAUSE |--UNION ALL | |--CO-ROUTINE (subquery-xxxxxx) | | `--SCAN CONSTANT ROW | `--SCAN (subquery-xxxxxx) |--UNION ALL | `--SCAN 3-ROW VALUES CLAUSE |--UNION ALL | |--CO-ROUTINE (subquery-xxxxxx) | | `--SCAN CONSTANT ROW | `--SCAN (subquery-xxxxxx) `--UNION ALL `--SCAN 4-ROW VALUES CLAUSE } #-------------------------------------------------------------------------- # 2024-03-23 dbsqlfuzz crash-c2c5e7e08b7e489d270a26d895077a03f678c33b # do_execsql_test 17.1 { DROP TABLE IF EXISTS t1; CREATE TABLE t1 AS SELECT * FROM (VALUES(1,2), (3,4 IN (1,2,3))); } do_execsql_test 17.2 { SELECT * FROM t1 } {1 2 3 0} # 2024-03-25 dbsqlfuzz crash-74cf7c9904360322a6c917e4934b127543d1cd51 # do_catchsql_test 18.1 { DROP TABLE t1; CREATE TABLE t1(x INTEGER PRIMARY KEY); INSERT INTO t1 VALUES(RAISE(IGNORE)),(0); } {1 {RAISE() may only be used within a trigger-program}} do_catchsql_test 18.2 { DROP TABLE t1; CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z); CREATE TRIGGER r2 AFTER INSERT ON t1 BEGIN INSERT INTO t1(y) VALUES(RAISE(IGNORE)),(0); END; INSERT INTO t1 VALUES(1,2,3); SELECT * FROM t1; } {0 {1 2 3}} do_catchsql_test 18.3.1 { DROP TABLE t1; CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z); CREATE TRIGGER r2 AFTER INSERT ON t1 BEGIN INSERT INTO t1(y) VALUES(RAISE(ABORT,'error 18.3')),(0); END; INSERT INTO t1 VALUES(1,2,3); } {1 {error 18.3}} do_execsql_test 18.3.2 { SELECT * FROM t1; } {} do_catchsql_test 18.4.1 { DROP TABLE t1; CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z); CREATE TRIGGER r2 AFTER INSERT ON t1 BEGIN INSERT INTO t1(y) VALUES(1),(RAISE(ABORT,'error 18.4')),(0); END; INSERT INTO t1 VALUES(1,2,3); } {1 {error 18.4}} do_execsql_test 18.4.2 { SELECT * FROM t1; } {} do_catchsql_test 18.5.1 { DROP TABLE t1; CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z); CREATE TRIGGER r2 AFTER INSERT ON t1 BEGIN INSERT INTO t1(y) VALUES(1), (CASE WHEN new.z>7 THEN RAISE(ABORT,'error 18.5') ELSE 2 END); END; INSERT INTO t1 VALUES(1,2,3); SELECT * FROM t1; } {0 {1 2 3 2 1 {} 3 2 {}}} do_catchsql_test 18.5.2 { DELETE FROM t1; INSERT INTO t1 VALUES(1,2,13); } {1 {error 18.5}} do_catchsql_test 18.5.3 { SELECT * FROM t1; } {0 {}} # 2024-04-18 dbsqlfuzz crash-bde3bf80aedf25afa56e2997a0545a314765d3f8 # Verify that the VALUES expressions used as an argument to an outer # join work correctly. # reset_db db null NULL do_execsql_test 19.1 { CREATE TABLE t1(a INT, b INT); INSERT INTO t1 VALUES(11,22); SELECT * FROM t1 LEFT JOIN (VALUES(33,44),(55,66)) AS t2 ON a=b; } {11 22 NULL NULL} do_execsql_test 19.2 { SELECT * FROM (VALUES(33,44),(55,66)) AS t2 RIGHT JOIN t1 ON a=b; } {NULL NULL 11 22} do_execsql_test 19.3 { SELECT *, '|' FROM t1 FULL JOIN (VALUES(33,44),(55,66)) AS t2 ON a=b ORDER BY +column1 } {11 22 NULL NULL | NULL NULL 33 44 | NULL NULL 55 66 |} do_execsql_test 19.4 { SELECT *, '|' FROM (VALUES(33,44),(55,66)) AS t2 FULL JOIN t1 ON a=b ORDER BY +column1 } {NULL NULL 11 22 | 33 44 NULL NULL | 55 66 NULL NULL |} # 2024-04-21 dbsqlfuzz 6fd1ff3a64bef4a6c092e8d757548e95698b0df5 # A continuation of the 2024-04-18 problem above. We have to create # Pseudo-cursor that is always NULL on the viaCoroutine loop in case # there are OP_Columns generated against it by the sub-WHERE clause. # db null N do_execsql_test 19.5 { DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; DROP TABLE IF EXISTS t3; CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); CREATE TABLE t2(column1,column2); INSERT INTO t2 VALUES(11,22),(33,44); CREATE TABLE t3(d,e); INSERT INTO t3 VALUES(3,4); } do_execsql_test 19.6 { -- output verify using PG 14.2 SELECT * FROM t1 CROSS JOIN t2 FULL JOIN t3 ON a=d ORDER BY +d, +column1; } {1 2 11 22 N N 1 2 33 44 N N N N N N 3 4} do_execsql_test 19.7 { SELECT * FROM t1 CROSS JOIN (VALUES(11,22),(33,44)) FULL JOIN t3 ON a=d ORDER BY +d, +column1; } {1 2 11 22 N N 1 2 33 44 N N N N N N 3 4} do_execsql_test 19.8 { -- output verified using PG 14.2 SELECT * FROM t1 CROSS JOIN t2 FULL JOIN t3 ON a=d WHERE column1 IS NULL; } {N N N N 3 4} do_execsql_test 19.9 { SELECT * FROM t1 CROSS JOIN (VALUES(11,22),(33,44)) FULL JOIN t3 ON a=d WHERE column1 IS NULL; } {N N N N 3 4} finish_test |
Added test/valuesfault.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # 2024 March 3 # # 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. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix valuesfault source $testdir/malloc_common.tcl do_execsql_test 1.0 { CREATE TABLE x1(a, b, c); } faultsim_save_and_close do_faultsim_test 1 -prep { faultsim_restore_and_reopen sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 2 } -body { execsql { INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4); } } -test { faultsim_test_result {0 {}} } finish_test |
Changes to test/view.test.
︙ | ︙ | |||
796 797 798 799 800 801 802 803 804 | do_execsql_test view-30.1 { PRAGMA table_info = t1; } { 0 a INT 0 {} 0 1 b BLOB 0 {} 0 } do_execsql_test view-30.2 { PRAGMA table_info = t2; } { 0 a INT 0 {} 0 1 b BLOB 0 {} 0 } } finish_test | > > > > > > > > > > > > > > > > > > > > > > > | 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 | do_execsql_test view-30.1 { PRAGMA table_info = t1; } { 0 a INT 0 {} 0 1 b BLOB 0 {} 0 } do_execsql_test view-30.2 { PRAGMA table_info = t2; } { 0 a INT 0 {} 0 1 b BLOB 0 {} 0 } } #----------------------------------------------------------------------- # 2024-04-25 Trying to make type information on compound subqueries # more predictable and rational. # reset_db do_execsql_test view-31.1 { CREATE TABLE x2(b TEXT); CREATE TABLE x1(a TEXT); INSERT INTO x1 VALUES('123'); -- Two queries get the same result even though the order of terms -- in the CTE is reversed WITH c(x) AS ( SELECT b FROM x2 UNION SELECT 123 ) SELECT count(*) FROM x1 WHERE a IN c; WITH c(x) AS ( SELECT 123 UNION SELECT b FROM x2 ) SELECT count(*) FROM x1 WHERE a IN c; } {0 0} do_execsql_test view-31.2 { CREATE TABLE t3(a INTEGER, b TEXT); INSERT INTO t3 VALUES(123, 123); WITH s AS ( VALUES(123), (456) ) SELECT * FROM t3 WHERE b IN s; } {123 123} finish_test |
Added test/vtabL.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | # 2024-03-26 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix vtabL ifcapable !vtab { finish_test return } register_tcl_module db proc vtab_command {method args} { switch -- $method { xConnect { return $::create_table_sql } } return {} } foreach {tn cts} { 1 {SELECT 123} 2 {SELECT 123, 456} 3 {INSERT INTO t1 VALUES(5, 6)} 4 {CREATE INDEX i1 ON t1(a)} 5 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END;} 6 {DROP TABLE nosuchtable} 7 {DROP TABLE x1} 8 {DROP TABLE t1} } { set ::create_table_sql $cts do_catchsql_test 1.$tn { CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); } {1 {declare_vtab: syntax error}} } foreach {tn cts} { 9 {CREATE TABLE xyz AS SELECT * FROM sqlite_schema} 10 {CREATE TABLE xyz AS SELECT 1 AS 'col'} } { set ::create_table_sql $cts do_catchsql_test 1.$tn { CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); } {1 {declare_vtab: SQL logic error}} } foreach {tn cts} { 1 {CREATE TABLE IF NOT EXISTS t1(a, b)} 2 {CREATE TABLE ""(a, b PRIMARY KEY) WITHOUT ROWID} } { set ::create_table_sql $cts execsql { DROP TABLE IF EXISTS x1 } do_execsql_test 2.$tn.1 { CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); } do_execsql_test 2.$tn.2 { SELECT a, b FROM x1 } } finish_test |
Changes to test/whereL.test.
︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | ORDER BY t1.a; } { QUERY PLAN |--SEARCH t1 USING INDEX sqlite_autoindex_t1_1 (a=?) |--SEARCH t2 USING INDEX sqlite_autoindex_t2_1 (a=?) `--SCAN t3 } # Constant propagation in the face of collating sequences: # do_execsql_test 200 { CREATE TABLE c3(x COLLATE binary, y COLLATE nocase, z COLLATE binary); CREATE INDEX c3x ON c3(x); INSERT INTO c3 VALUES('ABC', 'ABC', 'abc'); | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | ORDER BY t1.a; } { QUERY PLAN |--SEARCH t1 USING INDEX sqlite_autoindex_t1_1 (a=?) |--SEARCH t2 USING INDEX sqlite_autoindex_t2_1 (a=?) `--SCAN t3 } do_eqp_test 121 { SELECT * FROM t1, t2, t3 WHERE t1.a=t2.a AND t2.a=t3.j AND t3.j=abs(5) ORDER BY t1.a; } { QUERY PLAN |--SEARCH t1 USING INDEX sqlite_autoindex_t1_1 (a=?) |--SEARCH t2 USING INDEX sqlite_autoindex_t2_1 (a=?) `--SCAN t3 } # The sqlite3ExprIsConstant() routine does not believe that # the expression "coalesce(5,random())" is constant. So the # optimization does not apply in this case. # sqlite3_create_function db do_eqp_test 122 { SELECT * FROM t1, t2, t3 WHERE t1.a=t2.a AND t2.a=t3.j AND t3.j=coalesce(5,random()) ORDER BY t1.a; } { QUERY PLAN |--SCAN t3 |--SEARCH t1 USING INDEX sqlite_autoindex_t1_1 (a=?) |--SEARCH t2 USING INDEX sqlite_autoindex_t2_1 (a=?) `--USE TEMP B-TREE FOR ORDER BY } # Constant propagation in the face of collating sequences: # do_execsql_test 200 { CREATE TABLE c3(x COLLATE binary, y COLLATE nocase, z COLLATE binary); CREATE INDEX c3x ON c3(x); INSERT INTO c3 VALUES('ABC', 'ABC', 'abc'); |
︙ | ︙ | |||
204 205 206 207 208 209 210 211 212 | } 1 do_eqp_test 710 { SELECT v FROM t1 WHERE abs(v)=1 and v=1; } { QUERY PLAN `--SEARCH t1 USING INDEX idx (<expr>=?) } finish_test | > > > > > > > > > > > > > > > > > > | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | } 1 do_eqp_test 710 { SELECT v FROM t1 WHERE abs(v)=1 and v=1; } { QUERY PLAN `--SEARCH t1 USING INDEX idx (<expr>=?) } # 2024-03-07 https://sqlite.org/forum/forumpost/ecdfc02339 # A refinement is needed to the enhancements tested by the prior test case # to avoid another problem with indexes on constant expressions. # reset_db db null NULL do_execsql_test 800 { CREATE TABLE t0(c0, c1); CREATE TABLE t1(c2); CREATE INDEX i0 ON t1(NULL); INSERT INTO t1(c2) VALUES (0.2); CREATE VIEW v0(c3) AS SELECT DISTINCT c2 FROM t1; SELECT * FROM v0 LEFT JOIN t0 ON c3<NULL LEFT JOIN t1 ON 1; } {0.2 NULL NULL 0.2} do_execsql_test 810 { SELECT * FROM v0 LEFT JOIN t0 ON c3<NULL LEFT JOIN t1 ON 1 WHERE c2/0.1; } {0.2 NULL NULL 0.2} finish_test |
Added test/whereN.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | # 2024-04-02 # # 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. # #*********************************************************************** # Tests for the whereInterstageHeuristic() routine in the query planner. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix whereN # The following is a simplified and "sanitized" version of the original # real-world query that brought the problem to light. # # The issue is a slow query. The answer is correct, but it was taking too # much time, because it was doing a full table scan rather than an indexed # lookup. # # The problem was that the query planner was overestimating the number of # output rows. The estimated number of output rows is accurate if the # DSNAME parameter is "ds-one". In that case, a large fraction of the rows # in "violation" end up being output. The query planner correctly deduces # that it is faster to do a full table scan of the large "violation" table # to avoid the after-query sort that implements the ORDER BY clause. However, # if the DSNAME is "ds-two", then only a few rows (about 6) are generated, # and it is much much faster to do an indexed lookup of "violation" followed # by a sort operation to implement ORDER BY # # The problem, of course, is that the query planner has no way of knowing # in advance how many rows will be generated. The query planner tries to # estimate a worst case, which is a large number of output rows, and it picks # the best plan for that case. However, the plan choosen is very inefficient # when the number of output rows is small. # # The whereInterstageHeuristic() routine in the query planner attempts to # correct this by adjusting the query plan such that it avoids the very bad # query plan for a small number of rows, at the expense of a slightly less # efficient plan for a large number of rows. The large number of rows case # is perhaps 5% slower with the revised plan, but the small number of # rows case is around 100 times faster. That seems like a good tradeoff. # do_execsql_test 1.0 { CREATE TABLE datasource(dsid INT, name TEXT); INSERT INTO datasource VALUES(1,'ds-one'),(2,'ds-two'),(3,'ds-three'); CREATE INDEX ds1 ON datasource(name, dsid); CREATE TABLE rule(rid INT, team_id INT, dsid INT); WITH RECURSIVE c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<9) INSERT INTO rule(rid,team_id,dsid) SELECT n, 1, 1 FROM c; WITH RECURSIVE c(n) AS (VALUES(10) UNION ALL SELECT n+1 FROM c WHERE n<24) INSERT INTO rule(rid,team_id,dsid) SELECT n, 2, 2 FROM c; CREATE INDEX rule2 ON rule(dsid, rid); CREATE TABLE violation(vid INT, rid INT, vx BLOB); /*** Uncomment to insert actual data WITH src(rid, cnt) AS (VALUES(1,3586),(2,1343),(3,6505),(5,76230), (6,740),(7,287794),(8,457),(12,1), (14,1),(16,1),(17,1),(18,1),(19,1)) INSERT INTO violation(vid, rid, vx) SELECT rid*1000000+value, rid, randomblob(15) FROM src, generate_series(1,cnt); ***/ CREATE INDEX v1 ON violation(rid, vid); CREATE INDEX v2 ON violation(vid); ANALYZE; DELETE FROM sqlite_stat1; DROP TABLE IF EXISTS sqlite_stat4; INSERT INTO sqlite_stat1 VALUES ('violation','v2','376661 1'), ('violation','v1','376661 28974 1'), ('rule','rule2','24 12 1'), ('datasource','ds1','3 1 1'); ANALYZE sqlite_schema; } set DSNAME ds-two ;# Only a few rows. Change to "ds-one" for many rows. do_eqp_test 1.1 { SELECT count(*), length(group_concat(vx)) FROM ( SELECT V.* FROM datasource DS, rule R, violation V WHERE V.rid=R.rid AND R.dsid=DS.dsid AND DS.name=$DSNAME ORDER BY V.vid desc ); } { QUERY PLAN |--CO-ROUTINE (subquery-xxxxxx) | |--SEARCH DS USING COVERING INDEX ds1 (name=?) | |--SEARCH R USING COVERING INDEX rule2 (dsid=?) | |--SEARCH V USING INDEX v1 (rid=?) | `--USE TEMP B-TREE FOR ORDER BY `--SCAN (subquery-xxxxxx) } # ^^^^---- We want to see three SEARCH terms. No SCAN terms. # The ORDER BY is implemented by a separate sorter pass. finish_test |
Changes to test/window1.test.
︙ | ︙ | |||
2371 2372 2373 2374 2375 2376 2377 2378 2379 | CREATE INDEX t1x ON t1(likely(x)); INSERT INTO t1 VALUES(1),(2),(4),(8); } do_execsql_test 77.2 { SELECT max(~likely(x)) FILTER (WHERE true) FROM t1 INDEXED BY t1x GROUP BY x; } {-2 -3 -5 -9} finish_test | > > > > > > > > > > > > > > > > > > | 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 | CREATE INDEX t1x ON t1(likely(x)); INSERT INTO t1 VALUES(1),(2),(4),(8); } do_execsql_test 77.2 { SELECT max(~likely(x)) FILTER (WHERE true) FROM t1 INDEXED BY t1x GROUP BY x; } {-2 -3 -5 -9} # 2024-05-23 https://sqlite.org/forum/forumpost/bf8f43aa522c2299 # # A bug in group_concat() when used as a window function, reported # just hours after the 3.46.0 release, though first appearing # in 3.28.0. # # When used as a window function, a group_concat() was not # correctly distinguishing between NULL and empty-string for # its return value. # do_execsql_test 78.1 { SELECT quote(group_concat(x) OVER ()) FROM (SELECT '' AS x); } '' do_execsql_test 78.2 { SELECT quote(group_concat(x) OVER ( ORDER BY y RANGE BETWEEN 1 FOLLOWING AND 2 FOLLOWING )) FROM (SELECT 'abc' AS x, 1 AS y); } NULL finish_test |
Changes to tool/lemon.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /* ** Compilers are getting increasingly pedantic about type conversions ** as C evolves ever closer to Ada.... To work around the latest problems ** we have to define the following variant of strlen(). */ #define lemonStrlen(X) ((int)strlen(X)) /* ** Compilers are starting to complain about the use of sprintf() and strcpy(), ** saying they are unsafe. So we define our own versions of those routines too. ** ** There are three routines here: lemon_sprintf(), lemon_vsprintf(), and ** lemon_addtext(). The first two are replacements for sprintf() and vsprintf(). | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | /* ** Compilers are getting increasingly pedantic about type conversions ** as C evolves ever closer to Ada.... To work around the latest problems ** we have to define the following variant of strlen(). */ #define lemonStrlen(X) ((int)strlen(X)) /* ** Header on the linked list of memory allocations. */ typedef struct MemChunk MemChunk; struct MemChunk { MemChunk *pNext; size_t sz; /* Actually memory follows */ }; /* ** Global linked list of all memory allocations. */ static MemChunk *memChunkList = 0; /* ** Wrappers around malloc(), calloc(), realloc() and free(). ** ** All memory allocations are kept on a doubly-linked list. The ** lemon_free_all() function can be called prior to exit to clean ** up any memory leaks. ** ** This is not necessary. But compilers and getting increasingly ** fussy about memory leaks, even in command-line programs like Lemon ** where they do not matter. So this code is provided to hush the ** warnings. */ static void *lemon_malloc(size_t nByte){ MemChunk *p; if( nByte<0 ) return 0; p = malloc( nByte + sizeof(MemChunk) ); if( p==0 ){ fprintf(stderr, "Out of memory. Failed to allocate %lld bytes.\n", (long long int)nByte); exit(1); } p->pNext = memChunkList; p->sz = nByte; memChunkList = p; return (void*)&p[1]; } static void *lemon_calloc(size_t nElem, size_t sz){ void *p = lemon_malloc(nElem*sz); memset(p, 0, nElem*sz); return p; } static void lemon_free(void *pOld){ if( pOld ){ MemChunk *p = (MemChunk*)pOld; p--; memset(pOld, 0, p->sz); } } static void *lemon_realloc(void *pOld, size_t nNew){ void *pNew; MemChunk *p; if( pOld==0 ) return lemon_malloc(nNew); p = (MemChunk*)pOld; p--; if( p->sz>=nNew ) return pOld; pNew = lemon_malloc( nNew ); memcpy(pNew, pOld, p->sz); return pNew; } /* Free all outstanding memory allocations. ** Do this right before exiting. */ static void lemon_free_all(void){ while( memChunkList ){ MemChunk *pNext = memChunkList->pNext; free( memChunkList ); memChunkList = pNext; } } /* ** Compilers are starting to complain about the use of sprintf() and strcpy(), ** saying they are unsafe. So we define our own versions of those routines too. ** ** There are three routines here: lemon_sprintf(), lemon_vsprintf(), and ** lemon_addtext(). The first two are replacements for sprintf() and vsprintf(). |
︙ | ︙ | |||
414 415 416 417 418 419 420 421 422 423 424 425 426 427 | char *accept; /* Code to execute when the parser excepts */ char *extracode; /* Code appended to the generated file */ char *tokendest; /* Code to execute to destroy token data */ char *vardest; /* Code for the default non-terminal destructor */ char *filename; /* Name of the input file */ char *outname; /* Name of the current output file */ char *tokenprefix; /* A prefix added to token names in the .h file */ int nconflict; /* Number of parsing conflicts */ int nactiontab; /* Number of entries in the yy_action[] table */ int nlookaheadtab; /* Number of entries in yy_lookahead[] */ int tablesize; /* Total table size of all tables in bytes */ int basisflag; /* Print only basis configurations */ int printPreprocessed; /* Show preprocessor output on stdout */ int has_fallback; /* True if any %fallback is seen in the grammar */ | > > | 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | char *accept; /* Code to execute when the parser excepts */ char *extracode; /* Code appended to the generated file */ char *tokendest; /* Code to execute to destroy token data */ char *vardest; /* Code for the default non-terminal destructor */ char *filename; /* Name of the input file */ char *outname; /* Name of the current output file */ char *tokenprefix; /* A prefix added to token names in the .h file */ char *reallocFunc; /* Function to use to allocate stack space */ char *freeFunc; /* Function to use to free stack space */ int nconflict; /* Number of parsing conflicts */ int nactiontab; /* Number of entries in the yy_action[] table */ int nlookaheadtab; /* Number of entries in yy_lookahead[] */ int tablesize; /* Total table size of all tables in bytes */ int basisflag; /* Print only basis configurations */ int printPreprocessed; /* Show preprocessor output on stdout */ int has_fallback; /* True if any %fallback is seen in the grammar */ |
︙ | ︙ | |||
491 492 493 494 495 496 497 | static struct action *Action_new(void){ static struct action *actionfreelist = 0; struct action *newaction; if( actionfreelist==0 ){ int i; int amt = 100; | | | 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | static struct action *Action_new(void){ static struct action *actionfreelist = 0; struct action *newaction; if( actionfreelist==0 ){ int i; int amt = 100; actionfreelist = (struct action *)lemon_calloc(amt, sizeof(struct action)); if( actionfreelist==0 ){ fprintf(stderr,"Unable to allocate memory for a new parser action."); exit(1); } for(i=0; i<amt-1; i++) actionfreelist[i].next = &actionfreelist[i+1]; actionfreelist[amt-1].next = 0; } |
︙ | ︙ | |||
610 611 612 613 614 615 616 | #define acttab_yyaction(X,N) ((X)->aAction[N].action) /* The value for the N-th entry in yy_lookahead */ #define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) /* Free all memory associated with the given acttab */ void acttab_free(acttab *p){ | | | | | | | 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 | #define acttab_yyaction(X,N) ((X)->aAction[N].action) /* The value for the N-th entry in yy_lookahead */ #define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) /* Free all memory associated with the given acttab */ void acttab_free(acttab *p){ lemon_free( p->aAction ); lemon_free( p->aLookahead ); lemon_free( p ); } /* Allocate a new acttab structure */ acttab *acttab_alloc(int nsymbol, int nterminal){ acttab *p = (acttab *) lemon_calloc( 1, sizeof(*p) ); if( p==0 ){ fprintf(stderr,"Unable to allocate memory for a new acttab."); exit(1); } memset(p, 0, sizeof(*p)); p->nsymbol = nsymbol; p->nterminal = nterminal; return p; } /* Add a new action to the current transaction set. ** ** This routine is called once for each lookahead for a particular ** state. */ void acttab_action(acttab *p, int lookahead, int action){ if( p->nLookahead>=p->nLookaheadAlloc ){ p->nLookaheadAlloc += 25; p->aLookahead = (struct lookahead_action *) lemon_realloc( p->aLookahead, sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); if( p->aLookahead==0 ){ fprintf(stderr,"malloc failed\n"); exit(1); } } if( p->nLookahead==0 ){ |
︙ | ︙ | |||
686 687 688 689 690 691 692 | ** in the worst case. The worst case occurs if the transaction set ** must be appended to the current action table */ n = p->nsymbol + 1; if( p->nAction + n >= p->nActionAlloc ){ int oldAlloc = p->nActionAlloc; p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; | | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | ** in the worst case. The worst case occurs if the transaction set ** must be appended to the current action table */ n = p->nsymbol + 1; if( p->nAction + n >= p->nActionAlloc ){ int oldAlloc = p->nActionAlloc; p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; p->aAction = (struct lookahead_action *) lemon_realloc( p->aAction, sizeof(p->aAction[0])*p->nActionAlloc); if( p->aAction==0 ){ fprintf(stderr,"malloc failed\n"); exit(1); } for(i=oldAlloc; i<p->nActionAlloc; i++){ p->aAction[i].lookahead = -1; |
︙ | ︙ | |||
1308 1309 1310 1311 1312 1313 1314 | static struct config *current = 0; /* Top of list of configurations */ static struct config **currentend = 0; /* Last on list of configs */ static struct config *basis = 0; /* Top of list of basis configs */ static struct config **basisend = 0; /* End of list of basis configs */ /* Return a pointer to a new configuration */ PRIVATE struct config *newconfig(void){ | | | 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 | static struct config *current = 0; /* Top of list of configurations */ static struct config **currentend = 0; /* Last on list of configs */ static struct config *basis = 0; /* Top of list of basis configs */ static struct config **basisend = 0; /* End of list of basis configs */ /* Return a pointer to a new configuration */ PRIVATE struct config *newconfig(void){ return (struct config*)lemon_calloc(1, sizeof(struct config)); } /* The configuration "old" is no longer used */ PRIVATE void deleteconfig(struct config *old) { old->next = freelist; freelist = old; |
︙ | ︙ | |||
1524 1525 1526 1527 1528 1529 1530 | /* This routine is called with the argument to each -D command-line option. ** Add the macro defined to the azDefine array. */ static void handle_D_option(char *z){ char **paz; nDefine++; | | | | | | | 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 | /* This routine is called with the argument to each -D command-line option. ** Add the macro defined to the azDefine array. */ static void handle_D_option(char *z){ char **paz; nDefine++; azDefine = (char **) lemon_realloc(azDefine, sizeof(azDefine[0])*nDefine); if( azDefine==0 ){ fprintf(stderr,"out of memory\n"); exit(1); } bDefineUsed = (char*)lemon_realloc(bDefineUsed, nDefine); if( bDefineUsed==0 ){ fprintf(stderr,"out of memory\n"); exit(1); } bDefineUsed[nDefine-1] = 0; paz = &azDefine[nDefine-1]; *paz = (char *) lemon_malloc( lemonStrlen(z)+1 ); if( *paz==0 ){ fprintf(stderr,"out of memory\n"); exit(1); } lemon_strcpy(*paz, z); for(z=*paz; *z && *z!='='; z++){} *z = 0; } /* Rember the name of the output directory */ static char *outputDir = NULL; static void handle_d_option(char *z){ outputDir = (char *) lemon_malloc( lemonStrlen(z)+1 ); if( outputDir==0 ){ fprintf(stderr,"out of memory\n"); exit(1); } lemon_strcpy(outputDir, z); } static char *user_templatename = NULL; static void handle_T_option(char *z){ user_templatename = (char *) lemon_malloc( lemonStrlen(z)+1 ); if( user_templatename==0 ){ memory_error(); } lemon_strcpy(user_templatename, z); } /* Merge together to lists of rules ordered by rule.iRule */ |
︙ | ︙ | |||
1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 | } if( lem.nconflict > 0 ){ fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); } /* return 0 on success, 1 on failure. */ exitcode = ((lem.errorcnt > 0) || (lem.nconflict > 0)) ? 1 : 0; exit(exitcode); return (exitcode); } /******************** From the file "msort.c" *******************************/ /* ** A generic merge-sort program. ** | > | 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 | } if( lem.nconflict > 0 ){ fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); } /* return 0 on success, 1 on failure. */ exitcode = ((lem.errorcnt > 0) || (lem.nconflict > 0)) ? 1 : 0; lemon_free_all(); exit(exitcode); return (exitcode); } /******************** From the file "msort.c" *******************************/ /* ** A generic merge-sort program. ** |
︙ | ︙ | |||
2385 2386 2387 2388 2389 2390 2391 | psp->errorcnt++; psp->state = RESYNC_AFTER_RULE_ERROR; } break; case IN_RHS: if( x[0]=='.' ){ struct rule *rp; | | | 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 | psp->errorcnt++; psp->state = RESYNC_AFTER_RULE_ERROR; } break; case IN_RHS: if( x[0]=='.' ){ struct rule *rp; rp = (struct rule *)lemon_calloc( sizeof(struct rule) + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1); if( rp==0 ){ ErrorMsg(psp->filename,psp->tokenlineno, "Can't allocate enough memory for this rule."); psp->errorcnt++; psp->prevrule = 0; }else{ |
︙ | ︙ | |||
2437 2438 2439 2440 2441 2442 2443 | psp->alias[psp->nrhs] = 0; psp->nrhs++; } }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 && ISUPPER(x[1]) ){ struct symbol *msp = psp->rhs[psp->nrhs-1]; if( msp->type!=MULTITERMINAL ){ struct symbol *origsp = msp; | | | | | 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 | psp->alias[psp->nrhs] = 0; psp->nrhs++; } }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 && ISUPPER(x[1]) ){ struct symbol *msp = psp->rhs[psp->nrhs-1]; if( msp->type!=MULTITERMINAL ){ struct symbol *origsp = msp; msp = (struct symbol *) lemon_calloc(1,sizeof(*msp)); memset(msp, 0, sizeof(*msp)); msp->type = MULTITERMINAL; msp->nsubsym = 1; msp->subsym = (struct symbol**)lemon_calloc(1,sizeof(struct symbol*)); msp->subsym[0] = origsp; msp->name = origsp->name; psp->rhs[psp->nrhs-1] = msp; } msp->nsubsym++; msp->subsym = (struct symbol **) lemon_realloc(msp->subsym, sizeof(struct symbol*)*msp->nsubsym); msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]); if( ISLOWER(x[1]) || ISLOWER(msp->subsym[0]->name[0]) ){ ErrorMsg(psp->filename,psp->tokenlineno, "Cannot form a compound containing a non-terminal"); psp->errorcnt++; } |
︙ | ︙ | |||
2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 | psp->insertLineMacro = 0; }else if( strcmp(x,"token_type")==0 ){ psp->declargslot = &(psp->gp->tokentype); psp->insertLineMacro = 0; }else if( strcmp(x,"default_type")==0 ){ psp->declargslot = &(psp->gp->vartype); psp->insertLineMacro = 0; }else if( strcmp(x,"stack_size")==0 ){ psp->declargslot = &(psp->gp->stacksize); psp->insertLineMacro = 0; }else if( strcmp(x,"start_symbol")==0 ){ psp->declargslot = &(psp->gp->start); psp->insertLineMacro = 0; }else if( strcmp(x,"left")==0 ){ | > > > > > > | 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 | psp->insertLineMacro = 0; }else if( strcmp(x,"token_type")==0 ){ psp->declargslot = &(psp->gp->tokentype); psp->insertLineMacro = 0; }else if( strcmp(x,"default_type")==0 ){ psp->declargslot = &(psp->gp->vartype); psp->insertLineMacro = 0; }else if( strcmp(x,"realloc")==0 ){ psp->declargslot = &(psp->gp->reallocFunc); psp->insertLineMacro = 0; }else if( strcmp(x,"free")==0 ){ psp->declargslot = &(psp->gp->freeFunc); psp->insertLineMacro = 0; }else if( strcmp(x,"stack_size")==0 ){ psp->declargslot = &(psp->gp->stacksize); psp->insertLineMacro = 0; }else if( strcmp(x,"start_symbol")==0 ){ psp->declargslot = &(psp->gp->start); psp->insertLineMacro = 0; }else if( strcmp(x,"left")==0 ){ |
︙ | ︙ | |||
2657 2658 2659 2660 2661 2662 2663 | for(z=psp->filename, nBack=0; *z; z++){ if( *z=='\\' ) nBack++; } lemon_sprintf(zLine, "#line %d ", psp->tokenlineno); nLine = lemonStrlen(zLine); n += nLine + lemonStrlen(psp->filename) + nBack; } | | | 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 | for(z=psp->filename, nBack=0; *z; z++){ if( *z=='\\' ) nBack++; } lemon_sprintf(zLine, "#line %d ", psp->tokenlineno); nLine = lemonStrlen(zLine); n += nLine + lemonStrlen(psp->filename) + nBack; } *psp->declargslot = (char *) lemon_realloc(*psp->declargslot, n); zBuf = *psp->declargslot + nOld; if( addLineMacro ){ if( nOld && zBuf[-1]!='\n' ){ *(zBuf++) = '\n'; } memcpy(zBuf, zLine, nLine); zBuf += nLine; |
︙ | ︙ | |||
2771 2772 2773 2774 2775 2776 2777 | break; case WAITING_FOR_CLASS_TOKEN: if( x[0]=='.' ){ psp->state = WAITING_FOR_DECL_OR_RULE; }else if( ISUPPER(x[0]) || ((x[0]=='|' || x[0]=='/') && ISUPPER(x[1])) ){ struct symbol *msp = psp->tkclass; msp->nsubsym++; | | | 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 | break; case WAITING_FOR_CLASS_TOKEN: if( x[0]=='.' ){ psp->state = WAITING_FOR_DECL_OR_RULE; }else if( ISUPPER(x[0]) || ((x[0]=='|' || x[0]=='/') && ISUPPER(x[1])) ){ struct symbol *msp = psp->tkclass; msp->nsubsym++; msp->subsym = (struct symbol **) lemon_realloc(msp->subsym, sizeof(struct symbol*)*msp->nsubsym); if( !ISUPPER(x[0]) ) x++; msp->subsym[msp->nsubsym-1] = Symbol_new(x); }else{ ErrorMsg(psp->filename, psp->tokenlineno, "%%token_class argument \"%s\" should be a token", x); psp->errorcnt++; |
︙ | ︙ | |||
2986 2987 2988 2989 2990 2991 2992 | ErrorMsg(ps.filename,0,"Can't open this file for reading."); gp->errorcnt++; return; } fseek(fp,0,2); filesize = ftell(fp); rewind(fp); | | | | | 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 | ErrorMsg(ps.filename,0,"Can't open this file for reading."); gp->errorcnt++; return; } fseek(fp,0,2); filesize = ftell(fp); rewind(fp); filebuf = (char *)lemon_malloc( filesize+1 ); if( filesize>100000000 || filebuf==0 ){ ErrorMsg(ps.filename,0,"Input file too large."); lemon_free(filebuf); gp->errorcnt++; fclose(fp); return; } if( fread(filebuf,1,filesize,fp)!=filesize ){ ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", filesize); lemon_free(filebuf); gp->errorcnt++; fclose(fp); return; } fclose(fp); filebuf[filesize] = 0; |
︙ | ︙ | |||
3109 3110 3111 3112 3113 3114 3115 | } c = *cp; *cp = 0; /* Null terminate the token */ parseonetoken(&ps); /* Parse the token */ *cp = (char)c; /* Restore the buffer */ cp = nextcp; } | | | | 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 | } c = *cp; *cp = 0; /* Null terminate the token */ parseonetoken(&ps); /* Parse the token */ *cp = (char)c; /* Restore the buffer */ cp = nextcp; } lemon_free(filebuf); /* Release the buffer after parsing */ gp->rule = ps.firstrule; gp->errorcnt = ps.errorcnt; } /*************************** From the file "plink.c" *********************/ /* ** Routines processing configuration follow-set propagation links ** in the LEMON parser generator. */ static struct plink *plink_freelist = 0; /* Allocate a new plink */ struct plink *Plink_new(void){ struct plink *newlink; if( plink_freelist==0 ){ int i; int amt = 100; plink_freelist = (struct plink *)lemon_calloc( amt, sizeof(struct plink) ); if( plink_freelist==0 ){ fprintf(stderr, "Unable to allocate memory for a new follow-set propagation link.\n"); exit(1); } for(i=0; i<amt-1; i++) plink_freelist[i].next = &plink_freelist[i+1]; plink_freelist[amt-1].next = 0; |
︙ | ︙ | |||
3180 3181 3182 3183 3184 3185 3186 | } } /*********************** From the file "report.c" **************************/ /* ** Procedures for generating reports and tables in the LEMON parser generator. */ | | < < | | 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 | } } /*********************** From the file "report.c" **************************/ /* ** Procedures for generating reports and tables in the LEMON parser generator. */ /* Generate a filename with the given suffix. */ PRIVATE char *file_makename(struct lemon *lemp, const char *suffix) { char *name; char *cp; char *filename = lemp->filename; int sz; if( outputDir ){ cp = strrchr(filename, '/'); if( cp ) filename = cp + 1; } sz = lemonStrlen(filename); sz += lemonStrlen(suffix); if( outputDir ) sz += lemonStrlen(outputDir) + 1; sz += 5; name = (char*)lemon_malloc( sz ); if( name==0 ){ fprintf(stderr,"Can't allocate space for a filename.\n"); exit(1); } name[0] = 0; if( outputDir ){ lemon_strcpy(name, outputDir); |
︙ | ︙ | |||
3226 3227 3228 3229 3230 3231 3232 | PRIVATE FILE *file_open( struct lemon *lemp, const char *suffix, const char *mode ){ FILE *fp; | | | 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 | PRIVATE FILE *file_open( struct lemon *lemp, const char *suffix, const char *mode ){ FILE *fp; if( lemp->outname ) lemon_free(lemp->outname); lemp->outname = file_makename(lemp, suffix); fp = fopen(lemp->outname,mode); if( fp==0 && *mode=='w' ){ fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); lemp->errorcnt++; return 0; } |
︙ | ︙ | |||
3542 3543 3544 3545 3546 3547 3548 | cp = strrchr(argv0,'\\'); #else cp = strrchr(argv0,'/'); #endif if( cp ){ c = *cp; *cp = 0; | | | | | | 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 | cp = strrchr(argv0,'\\'); #else cp = strrchr(argv0,'/'); #endif if( cp ){ c = *cp; *cp = 0; path = (char *)lemon_malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 ); if( path ) lemon_sprintf(path,"%s/%s",argv0,name); *cp = c; }else{ pathlist = getenv("PATH"); if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; pathbuf = (char *) lemon_malloc( lemonStrlen(pathlist) + 1 ); path = (char *)lemon_malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 ); if( (pathbuf != 0) && (path!=0) ){ pathbufptr = pathbuf; lemon_strcpy(pathbuf, pathlist); while( *pathbuf ){ cp = strchr(pathbuf,':'); if( cp==0 ) cp = &pathbuf[lemonStrlen(pathbuf)]; c = *cp; *cp = 0; lemon_sprintf(path,"%s/%s",pathbuf,name); *cp = c; if( c==0 ) pathbuf[0] = 0; else pathbuf = &cp[1]; if( access(path,modemask)==0 ) break; } } lemon_free(pathbufptr); } return path; } /* Given an action, compute the integer value for that action ** which is to be put in the action table of the generated machine. ** Return negative if no action should be generated. |
︙ | ︙ | |||
3696 3697 3698 3699 3700 3701 3702 | return 0; } in = fopen(tpltname,"rb"); if( in==0 ){ fprintf(stderr,"Can't open the template file \"%s\".\n",tpltname); lemp->errorcnt++; } | | | 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 | return 0; } in = fopen(tpltname,"rb"); if( in==0 ){ fprintf(stderr,"Can't open the template file \"%s\".\n",tpltname); lemp->errorcnt++; } lemon_free(toFree); return in; } /* Print a #line directive line to the output file. */ PRIVATE void tplt_linedir(FILE *out, int lineno, char *filename) { fprintf(out,"#line %d \"",lineno); |
︙ | ︙ | |||
3825 3826 3827 3828 3829 3830 3831 | used += n; assert( used>=0 ); } n = lemonStrlen(zText); } if( (int) (n+sizeof(zInt)*2+used) >= alloced ){ alloced = n + sizeof(zInt)*2 + used + 200; | | | 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 | used += n; assert( used>=0 ); } n = lemonStrlen(zText); } if( (int) (n+sizeof(zInt)*2+used) >= alloced ){ alloced = n + sizeof(zInt)*2 + used + 200; z = (char *) lemon_realloc(z, alloced); } if( z==0 ) return empty; while( n-- > 0 ){ c = *(zText++); if( c=='%' && n>0 && zText[0]=='d' ){ lemon_sprintf(zInt, "%d", p1); p1 = p2; |
︙ | ︙ | |||
4115 4116 4117 4118 4119 4120 4121 | char *stddt; /* Standardized name for a datatype */ int i,j; /* Loop counters */ unsigned hash; /* For hashing the name of a type */ const char *name; /* Name of the parser */ /* Allocate and initialize types[] and allocate stddt[] */ arraysize = lemp->nsymbol * 2; | | | | 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 | char *stddt; /* Standardized name for a datatype */ int i,j; /* Loop counters */ unsigned hash; /* For hashing the name of a type */ const char *name; /* Name of the parser */ /* Allocate and initialize types[] and allocate stddt[] */ arraysize = lemp->nsymbol * 2; types = (char**)lemon_calloc( arraysize, sizeof(char*) ); if( types==0 ){ fprintf(stderr,"Out of memory.\n"); exit(1); } for(i=0; i<arraysize; i++) types[i] = 0; maxdtlength = 0; if( lemp->vartype ){ maxdtlength = lemonStrlen(lemp->vartype); } for(i=0; i<lemp->nsymbol; i++){ int len; struct symbol *sp = lemp->symbols[i]; if( sp->datatype==0 ) continue; len = lemonStrlen(sp->datatype); if( len>maxdtlength ) maxdtlength = len; } stddt = (char*)lemon_malloc( maxdtlength*2 + 1 ); if( stddt==0 ){ fprintf(stderr,"Out of memory.\n"); exit(1); } /* Build a hash table of datatypes. The ".dtnum" field of each symbol ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is |
︙ | ︙ | |||
4181 4182 4183 4184 4185 4186 4187 | break; } hash++; if( hash>=(unsigned)arraysize ) hash = 0; } if( types[hash]==0 ){ sp->dtnum = hash + 1; | | | 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 | break; } hash++; if( hash>=(unsigned)arraysize ) hash = 0; } if( types[hash]==0 ){ sp->dtnum = hash + 1; types[hash] = (char*)lemon_malloc( lemonStrlen(stddt)+1 ); if( types[hash]==0 ){ fprintf(stderr,"Out of memory.\n"); exit(1); } lemon_strcpy(types[hash],stddt); } } |
︙ | ︙ | |||
4203 4204 4205 4206 4207 4208 4209 | if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } fprintf(out,"typedef union {\n"); lineno++; fprintf(out," int yyinit;\n"); lineno++; fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; for(i=0; i<arraysize; i++){ if( types[i]==0 ) continue; fprintf(out," %s yy%d;\n",types[i],i+1); lineno++; | | | | | 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 | if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } fprintf(out,"typedef union {\n"); lineno++; fprintf(out," int yyinit;\n"); lineno++; fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; for(i=0; i<arraysize; i++){ if( types[i]==0 ) continue; fprintf(out," %s yy%d;\n",types[i],i+1); lineno++; lemon_free(types[i]); } if( lemp->errsym && lemp->errsym->useCnt ){ fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++; } lemon_free(stddt); lemon_free(types); fprintf(out,"} YYMINORTYPE;\n"); lineno++; *plineno = lineno; } /* ** Return the name of a C datatype able to represent values between ** lwr and upr, inclusive. If pnByte!=NULL then also write the sizeof |
︙ | ︙ | |||
4305 4306 4307 4308 4309 4310 4311 | ){ FILE *out, *in, *sql; int lineno; struct state *stp; struct action *ap; struct rule *rp; struct acttab *pActtab; | | | 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 | ){ FILE *out, *in, *sql; int lineno; struct state *stp; struct action *ap; struct rule *rp; struct acttab *pActtab; int i, j, n, sz, mn, mx; int nLookAhead; int szActionType; /* sizeof(YYACTIONTYPE) */ int szCodeType; /* sizeof(YYCODETYPE) */ const char *name; int mnTknOfst, mxTknOfst; int mnNtOfst, mxNtOfst; struct axset *ax; |
︙ | ︙ | |||
4438 4439 4440 4441 4442 4443 4444 | } /* Generate the include code, if any */ tplt_print(out,lemp,lemp->include,&lineno); if( mhflag ){ char *incName = file_makename(lemp, ".h"); fprintf(out,"#include \"%s\"\n", incName); lineno++; | | | 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 | } /* Generate the include code, if any */ tplt_print(out,lemp,lemp->include,&lineno); if( mhflag ){ char *incName = file_makename(lemp, ".h"); fprintf(out,"#include \"%s\"\n", incName); lineno++; lemon_free(incName); } tplt_xfer(lemp->name,in,out,&lineno); /* Generate #defines for all tokens */ if( lemp->tokenprefix ) prefix = lemp->tokenprefix; else prefix = ""; if( mhflag ){ |
︙ | ︙ | |||
4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 | }else{ fprintf(out,"#define %sARG_SDECL\n",name); lineno++; fprintf(out,"#define %sARG_PDECL\n",name); lineno++; fprintf(out,"#define %sARG_PARAM\n",name); lineno++; fprintf(out,"#define %sARG_FETCH\n",name); lineno++; fprintf(out,"#define %sARG_STORE\n",name); lineno++; } if( lemp->ctx && lemp->ctx[0] ){ i = lemonStrlen(lemp->ctx); while( i>=1 && ISSPACE(lemp->ctx[i-1]) ) i--; while( i>=1 && (ISALNUM(lemp->ctx[i-1]) || lemp->ctx[i-1]=='_') ) i--; fprintf(out,"#define %sCTX_SDECL %s;\n",name,lemp->ctx); lineno++; fprintf(out,"#define %sCTX_PDECL ,%s\n",name,lemp->ctx); lineno++; fprintf(out,"#define %sCTX_PARAM ,%s\n",name,&lemp->ctx[i]); lineno++; | > > > > > > > > > > > > > > > | 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 | }else{ fprintf(out,"#define %sARG_SDECL\n",name); lineno++; fprintf(out,"#define %sARG_PDECL\n",name); lineno++; fprintf(out,"#define %sARG_PARAM\n",name); lineno++; fprintf(out,"#define %sARG_FETCH\n",name); lineno++; fprintf(out,"#define %sARG_STORE\n",name); lineno++; } if( lemp->reallocFunc ){ fprintf(out,"#define YYREALLOC %s\n", lemp->reallocFunc); lineno++; }else{ fprintf(out,"#define YYREALLOC realloc\n"); lineno++; } if( lemp->freeFunc ){ fprintf(out,"#define YYFREE %s\n", lemp->freeFunc); lineno++; }else{ fprintf(out,"#define YYFREE free\n"); lineno++; } if( lemp->reallocFunc && lemp->freeFunc ){ fprintf(out,"#define YYDYNSTACK 1\n"); lineno++; }else{ fprintf(out,"#define YYDYNSTACK 0\n"); lineno++; } if( lemp->ctx && lemp->ctx[0] ){ i = lemonStrlen(lemp->ctx); while( i>=1 && ISSPACE(lemp->ctx[i-1]) ) i--; while( i>=1 && (ISALNUM(lemp->ctx[i-1]) || lemp->ctx[i-1]=='_') ) i--; fprintf(out,"#define %sCTX_SDECL %s;\n",name,lemp->ctx); lineno++; fprintf(out,"#define %sCTX_PDECL ,%s\n",name,lemp->ctx); lineno++; fprintf(out,"#define %sCTX_PARAM ,%s\n",name,&lemp->ctx[i]); lineno++; |
︙ | ︙ | |||
4530 4531 4532 4533 4534 4535 4536 | fprintf(out,"#define YYFALLBACK 1\n"); lineno++; } /* Compute the action table, but do not output it yet. The action ** table must be computed before generating the YYNSTATE macro because ** we need to know how many states can be eliminated. */ | | | 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 | fprintf(out,"#define YYFALLBACK 1\n"); lineno++; } /* Compute the action table, but do not output it yet. The action ** table must be computed before generating the YYNSTATE macro because ** we need to know how many states can be eliminated. */ ax = (struct axset *) lemon_calloc(lemp->nxstate*2, sizeof(ax[0])); if( ax==0 ){ fprintf(stderr,"malloc failed\n"); exit(1); } for(i=0; i<lemp->nxstate; i++){ stp = lemp->sorted[i]; ax[i*2].stp = stp; |
︙ | ︙ | |||
4588 4589 4590 4591 4592 4593 4594 | } printf("%4d: State %3d %s n: %2d size: %5d freespace: %d\n", i, stp->statenum, ax[i].isTkn ? "Token" : "Var ", ax[i].nAction, pActtab->nAction, nn); } #endif } | | | 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 | } printf("%4d: State %3d %s n: %2d size: %5d freespace: %d\n", i, stp->statenum, ax[i].isTkn ? "Token" : "Var ", ax[i].nAction, pActtab->nAction, nn); } #endif } lemon_free(ax); /* Mark rules that are actually used for reduce actions after all ** optimizations have been applied */ for(rp=lemp->rule; rp; rp=rp->next) rp->doesReduce = LEMON_FALSE; for(i=0; i<lemp->nxstate; i++){ for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ |
︙ | ︙ | |||
4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 | fprintf(out,"#define YY_MAX_SHIFTREDUCE %d\n", i-1); lineno++; fprintf(out,"#define YY_ERROR_ACTION %d\n", lemp->errAction); lineno++; fprintf(out,"#define YY_ACCEPT_ACTION %d\n", lemp->accAction); lineno++; fprintf(out,"#define YY_NO_ACTION %d\n", lemp->noAction); lineno++; fprintf(out,"#define YY_MIN_REDUCE %d\n", lemp->minReduce); lineno++; i = lemp->minReduce + lemp->nrule; fprintf(out,"#define YY_MAX_REDUCE %d\n", i-1); lineno++; tplt_xfer(lemp->name,in,out,&lineno); /* Now output the action table and its associates: ** ** yy_action[] A single table containing all actions. ** yy_lookahead[] A table containing the lookahead for each entry in ** yy_action. Used to detect hash collisions. | > > > > > > > > > > > > > > > > | 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 | fprintf(out,"#define YY_MAX_SHIFTREDUCE %d\n", i-1); lineno++; fprintf(out,"#define YY_ERROR_ACTION %d\n", lemp->errAction); lineno++; fprintf(out,"#define YY_ACCEPT_ACTION %d\n", lemp->accAction); lineno++; fprintf(out,"#define YY_NO_ACTION %d\n", lemp->noAction); lineno++; fprintf(out,"#define YY_MIN_REDUCE %d\n", lemp->minReduce); lineno++; i = lemp->minReduce + lemp->nrule; fprintf(out,"#define YY_MAX_REDUCE %d\n", i-1); lineno++; /* Minimum and maximum token values that have a destructor */ mn = mx = 0; for(i=0; i<lemp->nsymbol; i++){ struct symbol *sp = lemp->symbols[i]; if( sp && sp->type!=TERMINAL && sp->destructor ){ if( mn==0 || sp->index<mn ) mn = sp->index; if( sp->index>mx ) mx = sp->index; } } if( lemp->tokendest ) mn = 0; if( lemp->vardest ) mx = lemp->nsymbol-1; fprintf(out,"#define YY_MIN_DSTRCTR %d\n", mn); lineno++; fprintf(out,"#define YY_MAX_DSTRCTR %d\n", mx); lineno++; tplt_xfer(lemp->name,in,out,&lineno); /* Now output the action table and its associates: ** ** yy_action[] A single table containing all actions. ** yy_lookahead[] A table containing the lookahead for each entry in ** yy_action. Used to detect hash collisions. |
︙ | ︙ | |||
4763 4764 4765 4766 4767 4768 4769 | } fprintf(out, "};\n"); lineno++; tplt_xfer(lemp->name,in,out,&lineno); /* Generate the table of fallback tokens. */ if( lemp->has_fallback ){ | | | 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 | } fprintf(out, "};\n"); lineno++; tplt_xfer(lemp->name,in,out,&lineno); /* Generate the table of fallback tokens. */ if( lemp->has_fallback ){ mx = lemp->nterminal - 1; /* 2019-08-28: Generate fallback entries for every token to avoid ** having to do a range check on the index */ /* while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; } */ lemp->tablesize += (mx+1)*szCodeType; for(i=0; i<=mx; i++){ struct symbol *p = lemp->symbols[i]; if( p->fallback==0 ){ |
︙ | ︙ | |||
5198 5199 5200 5201 5202 5203 5204 | { size = n+1; } /* Allocate a new set */ char *SetNew(void){ char *s; | | | | 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 | { size = n+1; } /* Allocate a new set */ char *SetNew(void){ char *s; s = (char*)lemon_calloc( size, 1); if( s==0 ){ memory_error(); } return s; } /* Deallocate a set */ void SetFree(char *s) { lemon_free(s); } /* Add a new element to the set. Return TRUE if the element was added ** and FALSE if it was already there. */ int SetAdd(char *s, int e) { int rv; |
︙ | ︙ | |||
5267 5268 5269 5270 5271 5272 5273 | const char *Strsafe(const char *y) { const char *z; char *cpy; if( y==0 ) return 0; z = Strsafe_find(y); | | | 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 | const char *Strsafe(const char *y) { const char *z; char *cpy; if( y==0 ) return 0; z = Strsafe_find(y); if( z==0 && (cpy=(char *)lemon_malloc( lemonStrlen(y)+1 ))!=0 ){ lemon_strcpy(cpy,y); z = cpy; Strsafe_insert(z); } MemoryCheck(z); return z; } |
︙ | ︙ | |||
5303 5304 5305 5306 5307 5308 5309 | /* There is only one instance of the array, which is the following */ static struct s_x1 *x1a; /* Allocate a new associative array */ void Strsafe_init(void){ if( x1a ) return; | | | | | 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 | /* There is only one instance of the array, which is the following */ static struct s_x1 *x1a; /* Allocate a new associative array */ void Strsafe_init(void){ if( x1a ) return; x1a = (struct s_x1*)lemon_malloc( sizeof(struct s_x1) ); if( x1a ){ x1a->size = 1024; x1a->count = 0; x1a->tbl = (x1node*)lemon_calloc(1024, sizeof(x1node) + sizeof(x1node*)); if( x1a->tbl==0 ){ lemon_free(x1a); x1a = 0; }else{ int i; x1a->ht = (x1node**)&(x1a->tbl[1024]); for(i=0; i<1024; i++) x1a->ht[i] = 0; } } |
︙ | ︙ | |||
5344 5345 5346 5347 5348 5349 5350 | } if( x1a->count>=x1a->size ){ /* Need to make the hash table bigger */ int i,arrSize; struct s_x1 array; array.size = arrSize = x1a->size*2; array.count = x1a->count; | | | | 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 | } if( x1a->count>=x1a->size ){ /* Need to make the hash table bigger */ int i,arrSize; struct s_x1 array; array.size = arrSize = x1a->size*2; array.count = x1a->count; array.tbl = (x1node*)lemon_calloc(arrSize, sizeof(x1node)+sizeof(x1node*)); if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ array.ht = (x1node**)&(array.tbl[arrSize]); for(i=0; i<arrSize; i++) array.ht[i] = 0; for(i=0; i<x1a->count; i++){ x1node *oldnp, *newnp; oldnp = &(x1a->tbl[i]); h = strhash(oldnp->data) & (arrSize-1); newnp = &(array.tbl[i]); if( array.ht[h] ) array.ht[h]->from = &(newnp->next); newnp->next = array.ht[h]; newnp->data = oldnp->data; newnp->from = &(array.ht[h]); array.ht[h] = newnp; } /* lemon_free(x1a->tbl); // This program was originally for 16-bit machines. ** Don't worry about freeing memory on modern platforms. */ *x1a = array; } /* Insert the new data */ h = ph & (x1a->size-1); np = &(x1a->tbl[x1a->count++]); np->data = data; |
︙ | ︙ | |||
5400 5401 5402 5403 5404 5405 5406 | */ struct symbol *Symbol_new(const char *x) { struct symbol *sp; sp = Symbol_find(x); if( sp==0 ){ | | | 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 | */ struct symbol *Symbol_new(const char *x) { struct symbol *sp; sp = Symbol_find(x); if( sp==0 ){ sp = (struct symbol *)lemon_calloc(1, sizeof(struct symbol) ); MemoryCheck(sp); sp->name = Strsafe(x); sp->type = ISUPPER(*x) ? TERMINAL : NONTERMINAL; sp->rule = 0; sp->fallback = 0; sp->prec = -1; sp->assoc = UNK; |
︙ | ︙ | |||
5471 5472 5473 5474 5475 5476 5477 | /* There is only one instance of the array, which is the following */ static struct s_x2 *x2a; /* Allocate a new associative array */ void Symbol_init(void){ if( x2a ) return; | | | | | 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 | /* There is only one instance of the array, which is the following */ static struct s_x2 *x2a; /* Allocate a new associative array */ void Symbol_init(void){ if( x2a ) return; x2a = (struct s_x2*)lemon_malloc( sizeof(struct s_x2) ); if( x2a ){ x2a->size = 128; x2a->count = 0; x2a->tbl = (x2node*)lemon_calloc(128, sizeof(x2node) + sizeof(x2node*)); if( x2a->tbl==0 ){ lemon_free(x2a); x2a = 0; }else{ int i; x2a->ht = (x2node**)&(x2a->tbl[128]); for(i=0; i<128; i++) x2a->ht[i] = 0; } } |
︙ | ︙ | |||
5512 5513 5514 5515 5516 5517 5518 | } if( x2a->count>=x2a->size ){ /* Need to make the hash table bigger */ int i,arrSize; struct s_x2 array; array.size = arrSize = x2a->size*2; array.count = x2a->count; | | | | 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 | } if( x2a->count>=x2a->size ){ /* Need to make the hash table bigger */ int i,arrSize; struct s_x2 array; array.size = arrSize = x2a->size*2; array.count = x2a->count; array.tbl = (x2node*)lemon_calloc(arrSize, sizeof(x2node)+sizeof(x2node*)); if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ array.ht = (x2node**)&(array.tbl[arrSize]); for(i=0; i<arrSize; i++) array.ht[i] = 0; for(i=0; i<x2a->count; i++){ x2node *oldnp, *newnp; oldnp = &(x2a->tbl[i]); h = strhash(oldnp->key) & (arrSize-1); newnp = &(array.tbl[i]); if( array.ht[h] ) array.ht[h]->from = &(newnp->next); newnp->next = array.ht[h]; newnp->key = oldnp->key; newnp->data = oldnp->data; newnp->from = &(array.ht[h]); array.ht[h] = newnp; } /* lemon_free(x2a->tbl); // This program was originally written for 16-bit ** machines. Don't worry about freeing this trivial amount of memory ** on modern platforms. Just leak it. */ *x2a = array; } /* Insert the new data */ h = ph & (x2a->size-1); np = &(x2a->tbl[x2a->count++]); |
︙ | ︙ | |||
5589 5590 5591 5592 5593 5594 5595 | ** problems, or if the array is empty. */ struct symbol **Symbol_arrayof() { struct symbol **array; int i,arrSize; if( x2a==0 ) return 0; arrSize = x2a->count; | | | 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 | ** problems, or if the array is empty. */ struct symbol **Symbol_arrayof() { struct symbol **array; int i,arrSize; if( x2a==0 ) return 0; arrSize = x2a->count; array = (struct symbol **)lemon_calloc(arrSize, sizeof(struct symbol *)); if( array ){ for(i=0; i<arrSize; i++) array[i] = x2a->tbl[i].data; } return array; } /* Compare two configurations */ |
︙ | ︙ | |||
5637 5638 5639 5640 5641 5642 5643 | return h; } /* Allocate a new state structure */ struct state *State_new() { struct state *newstate; | | | 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 | return h; } /* Allocate a new state structure */ struct state *State_new() { struct state *newstate; newstate = (struct state *)lemon_calloc(1, sizeof(struct state) ); MemoryCheck(newstate); return newstate; } /* There is one instance of the following structure for each ** associative array of type "x3". */ |
︙ | ︙ | |||
5670 5671 5672 5673 5674 5675 5676 | /* There is only one instance of the array, which is the following */ static struct s_x3 *x3a; /* Allocate a new associative array */ void State_init(void){ if( x3a ) return; | | | | | 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 | /* There is only one instance of the array, which is the following */ static struct s_x3 *x3a; /* Allocate a new associative array */ void State_init(void){ if( x3a ) return; x3a = (struct s_x3*)lemon_malloc( sizeof(struct s_x3) ); if( x3a ){ x3a->size = 128; x3a->count = 0; x3a->tbl = (x3node*)lemon_calloc(128, sizeof(x3node) + sizeof(x3node*)); if( x3a->tbl==0 ){ lemon_free(x3a); x3a = 0; }else{ int i; x3a->ht = (x3node**)&(x3a->tbl[128]); for(i=0; i<128; i++) x3a->ht[i] = 0; } } |
︙ | ︙ | |||
5711 5712 5713 5714 5715 5716 5717 | } if( x3a->count>=x3a->size ){ /* Need to make the hash table bigger */ int i,arrSize; struct s_x3 array; array.size = arrSize = x3a->size*2; array.count = x3a->count; | | | | 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 | } if( x3a->count>=x3a->size ){ /* Need to make the hash table bigger */ int i,arrSize; struct s_x3 array; array.size = arrSize = x3a->size*2; array.count = x3a->count; array.tbl = (x3node*)lemon_calloc(arrSize, sizeof(x3node)+sizeof(x3node*)); if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ array.ht = (x3node**)&(array.tbl[arrSize]); for(i=0; i<arrSize; i++) array.ht[i] = 0; for(i=0; i<x3a->count; i++){ x3node *oldnp, *newnp; oldnp = &(x3a->tbl[i]); h = statehash(oldnp->key) & (arrSize-1); newnp = &(array.tbl[i]); if( array.ht[h] ) array.ht[h]->from = &(newnp->next); newnp->next = array.ht[h]; newnp->key = oldnp->key; newnp->data = oldnp->data; newnp->from = &(array.ht[h]); array.ht[h] = newnp; } lemon_free(x3a->tbl); *x3a = array; } /* Insert the new data */ h = ph & (x3a->size-1); np = &(x3a->tbl[x3a->count++]); np->key = key; np->data = data; |
︙ | ︙ | |||
5768 5769 5770 5771 5772 5773 5774 | ** problems, or if the array is empty. */ struct state **State_arrayof(void) { struct state **array; int i,arrSize; if( x3a==0 ) return 0; arrSize = x3a->count; | | | 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 | ** problems, or if the array is empty. */ struct state **State_arrayof(void) { struct state **array; int i,arrSize; if( x3a==0 ) return 0; arrSize = x3a->count; array = (struct state **)lemon_calloc(arrSize, sizeof(struct state *)); if( array ){ for(i=0; i<arrSize; i++) array[i] = x3a->tbl[i].data; } return array; } /* Hash a configuration */ |
︙ | ︙ | |||
5810 5811 5812 5813 5814 5815 5816 | /* There is only one instance of the array, which is the following */ static struct s_x4 *x4a; /* Allocate a new associative array */ void Configtable_init(void){ if( x4a ) return; | | | | | 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 | /* There is only one instance of the array, which is the following */ static struct s_x4 *x4a; /* Allocate a new associative array */ void Configtable_init(void){ if( x4a ) return; x4a = (struct s_x4*)lemon_malloc( sizeof(struct s_x4) ); if( x4a ){ x4a->size = 64; x4a->count = 0; x4a->tbl = (x4node*)lemon_calloc(64, sizeof(x4node) + sizeof(x4node*)); if( x4a->tbl==0 ){ lemon_free(x4a); x4a = 0; }else{ int i; x4a->ht = (x4node**)&(x4a->tbl[64]); for(i=0; i<64; i++) x4a->ht[i] = 0; } } |
︙ | ︙ | |||
5851 5852 5853 5854 5855 5856 5857 | } if( x4a->count>=x4a->size ){ /* Need to make the hash table bigger */ int i,arrSize; struct s_x4 array; array.size = arrSize = x4a->size*2; array.count = x4a->count; | > | < < < | 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 | } if( x4a->count>=x4a->size ){ /* Need to make the hash table bigger */ int i,arrSize; struct s_x4 array; array.size = arrSize = x4a->size*2; array.count = x4a->count; array.tbl = (x4node*)lemon_calloc(arrSize, sizeof(x4node) + sizeof(x4node*)); if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ array.ht = (x4node**)&(array.tbl[arrSize]); for(i=0; i<arrSize; i++) array.ht[i] = 0; for(i=0; i<x4a->count; i++){ x4node *oldnp, *newnp; oldnp = &(x4a->tbl[i]); h = confighash(oldnp->data) & (arrSize-1); newnp = &(array.tbl[i]); if( array.ht[h] ) array.ht[h]->from = &(newnp->next); newnp->next = array.ht[h]; newnp->data = oldnp->data; newnp->from = &(array.ht[h]); array.ht[h] = newnp; } *x4a = array; } /* Insert the new data */ h = ph & (x4a->size-1); np = &(x4a->tbl[x4a->count++]); np->data = data; if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); |
︙ | ︙ |
Changes to tool/lempar.c.
︙ | ︙ | |||
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | ** zero the stack is dynamically sized using realloc() ** ParseARG_SDECL A static variable declaration for the %extra_argument ** ParseARG_PDECL A parameter declaration for the %extra_argument ** ParseARG_PARAM Code to pass %extra_argument as a subroutine parameter ** ParseARG_STORE Code to store %extra_argument into yypParser ** ParseARG_FETCH Code to extract %extra_argument from yypParser ** ParseCTX_* As ParseARG_ except for %extra_context ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. ** YYNRULE the number of rules in the grammar ** YYNTOKEN Number of terminal symbols ** YY_MAX_SHIFT Maximum value for shift actions ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions ** YY_ERROR_ACTION The yy_action[] code for syntax error ** YY_ACCEPT_ACTION The yy_action[] code for accept ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ %% /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) /* Define the yytestcase() macro to be a no-op if is not already defined ** otherwise. ** ** Applications can choose to define yytestcase() in the %include section ** to a macro that can assist in verifying code coverage. For production ** code the yytestcase() macro should be turned off. But it is useful ** for testing. */ #ifndef yytestcase # define yytestcase(X) #endif /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an ** action integer. ** | > > > > > > > > > > > > > > > > > > > > > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | ** zero the stack is dynamically sized using realloc() ** ParseARG_SDECL A static variable declaration for the %extra_argument ** ParseARG_PDECL A parameter declaration for the %extra_argument ** ParseARG_PARAM Code to pass %extra_argument as a subroutine parameter ** ParseARG_STORE Code to store %extra_argument into yypParser ** ParseARG_FETCH Code to extract %extra_argument from yypParser ** ParseCTX_* As ParseARG_ except for %extra_context ** YYREALLOC Name of the realloc() function to use ** YYFREE Name of the free() function to use ** YYDYNSTACK True if stack space should be extended on heap ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. ** YYNRULE the number of rules in the grammar ** YYNTOKEN Number of terminal symbols ** YY_MAX_SHIFT Maximum value for shift actions ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions ** YY_ERROR_ACTION The yy_action[] code for syntax error ** YY_ACCEPT_ACTION The yy_action[] code for accept ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions ** YY_MIN_DSTRCTR Minimum symbol value that has a destructor ** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ %% /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) /* Define the yytestcase() macro to be a no-op if is not already defined ** otherwise. ** ** Applications can choose to define yytestcase() in the %include section ** to a macro that can assist in verifying code coverage. For production ** code the yytestcase() macro should be turned off. But it is useful ** for testing. */ #ifndef yytestcase # define yytestcase(X) #endif /* Macro to determine if stack space has the ability to grow using ** heap memory. */ #if YYSTACKDEPTH<=0 || YYDYNSTACK # define YYGROWABLESTACK 1 #else # define YYGROWABLESTACK 0 #endif /* Guarantee a minimum number of initial stack slots. */ #if YYSTACKDEPTH<=0 # undef YYSTACKDEPTH # define YYSTACKDEPTH 2 /* Need a minimum stack size */ #endif /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an ** action integer. ** |
︙ | ︙ | |||
208 209 210 211 212 213 214 | int yyhwm; /* High-water mark of the stack */ #endif #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ #endif ParseARG_SDECL /* A place to hold %extra_argument */ ParseCTX_SDECL /* A place to hold %extra_context */ | < | | | < < < < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | int yyhwm; /* High-water mark of the stack */ #endif #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ #endif ParseARG_SDECL /* A place to hold %extra_argument */ ParseCTX_SDECL /* A place to hold %extra_context */ yyStackEntry *yystackEnd; /* Last entry in the stack */ yyStackEntry *yystack; /* The parser stack */ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct yyParser yyParser; #include <assert.h> #ifndef NDEBUG #include <stdio.h> static FILE *yyTraceFILE = 0; |
︙ | ︙ | |||
269 270 271 272 273 274 275 | */ static const char *const yyRuleName[] = { %% }; #endif /* NDEBUG */ | | > | | | | | > | > < | | | | | | | > | < > | > > > > > < < | | < < < < < < < < | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | */ static const char *const yyRuleName[] = { %% }; #endif /* NDEBUG */ #if YYGROWABLESTACK /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int yyGrowStack(yyParser *p){ int oldSize = 1 + (int)(p->yystackEnd - p->yystack); int newSize; int idx; yyStackEntry *pNew; newSize = oldSize*2 + 100; idx = (int)(p->yytos - p->yystack); if( p->yystack==p->yystk0 ){ pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); if( pNew==0 ) return 1; memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); }else{ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); if( pNew==0 ) return 1; } p->yystack = pNew; p->yytos = &p->yystack[idx]; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", yyTracePrompt, oldSize, newSize); } #endif p->yystackEnd = &p->yystack[newSize-1]; return 0; } #endif /* YYGROWABLESTACK */ #if !YYGROWABLESTACK /* For builds that do no have a growable stack, yyGrowStack always ** returns an error. */ # define yyGrowStack(X) 1 #endif /* Datatype of the argument to the memory allocated passed as the ** second argument to ParseAlloc() below. This can be changed by ** putting an appropriate #define in the %include section of the input ** grammar. */ #ifndef YYMALLOCARGTYPE # define YYMALLOCARGTYPE size_t #endif /* Initialize a new parser that has already been allocated. */ void ParseInit(void *yypRawParser ParseCTX_PDECL){ yyParser *yypParser = (yyParser*)yypRawParser; ParseCTX_STORE #ifdef YYTRACKMAXSTACKDEPTH yypParser->yyhwm = 0; #endif yypParser->yystack = yypParser->yystk0; yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yypParser->yytos = yypParser->yystack; yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; } #ifndef Parse_ENGINEALWAYSONSTACK /* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. |
︙ | ︙ | |||
422 423 424 425 426 427 428 | } /* ** Clear all secondary memory allocations from the parser */ void ParseFinalize(void *p){ yyParser *pParser = (yyParser*)p; | > > > > | > > > > > > > > > > > > | > | | 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | } /* ** Clear all secondary memory allocations from the parser */ void ParseFinalize(void *p){ yyParser *pParser = (yyParser*)p; /* In-lined version of calling yy_pop_parser_stack() for each ** element left in the stack */ yyStackEntry *yytos = pParser->yytos; while( yytos>pParser->yystack ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sPopping %s\n", yyTracePrompt, yyTokenName[yytos->major]); } #endif if( yytos->major>=YY_MIN_DSTRCTR ){ yy_destructor(pParser, yytos->major, &yytos->minor); } yytos--; } #if YYGROWABLESTACK if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); #endif } #ifndef Parse_ENGINEALWAYSONSTACK /* ** Deallocate and destroy a parser. Destructors are called for ** all stack elements before shutting the parser down. |
︙ | ︙ | |||
650 651 652 653 654 655 656 | yypParser->yytos++; #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif | < < | < < < < | > > < < | 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | yypParser->yytos++; #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif yytos = yypParser->yytos; if( yytos>yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } yytos = yypParser->yytos; assert( yytos <= yypParser->yystackEnd ); } if( yyNewState > YY_MAX_SHIFT ){ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; } yytos->stateno = yyNewState; yytos->major = yyMajor; yytos->minor.yy0 = yyMinor; yyTraceShift(yypParser, yyNewState, "Shift"); } /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side |
︙ | ︙ | |||
907 908 909 910 911 912 913 | #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); } #endif | < < < < < < < | 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 | #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); } #endif if( yypParser->yytos>=yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); break; } } } yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor ParseCTX_PARAM); }else if( yyact <= YY_MAX_SHIFTREDUCE ){ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt--; #endif |
︙ | ︙ |
Changes to tool/mkctimec.tcl.
︙ | ︙ | |||
101 102 103 104 105 106 107 108 109 110 111 112 113 114 | } # All Boolean compile time options which default to 0 or empty. # set boolean_defnil_options { SQLITE_32BIT_ROWID SQLITE_4_BYTE_ALIGNED_MALLOC SQLITE_ALLOW_URI_AUTHORITY SQLITE_BUG_COMPATIBLE_20160819 SQLITE_CASE_SENSITIVE_LIKE SQLITE_CHECK_PAGES SQLITE_COVERAGE_TEST SQLITE_DEBUG SQLITE_DEFAULT_AUTOMATIC_INDEX | > | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | } # All Boolean compile time options which default to 0 or empty. # set boolean_defnil_options { SQLITE_32BIT_ROWID SQLITE_4_BYTE_ALIGNED_MALLOC SQLITE_ALLOW_ROWID_IN_VIEW SQLITE_ALLOW_URI_AUTHORITY SQLITE_BUG_COMPATIBLE_20160819 SQLITE_CASE_SENSITIVE_LIKE SQLITE_CHECK_PAGES SQLITE_COVERAGE_TEST SQLITE_DEBUG SQLITE_DEFAULT_AUTOMATIC_INDEX |
︙ | ︙ |
Changes to tool/mkopcodeh.tcl.
︙ | ︙ | |||
77 78 79 80 81 82 83 84 85 86 87 88 89 90 | if {[regexp {^case OP_} $line]} { set line [split $line] set name [string trim [lindex $line 1] :] if {$name=="OP_Abortable"} continue; # put OP_Abortable last set op($name) -1 set group($name) 0 set jump($name) 0 set in1($name) 0 set in2($name) 0 set in3($name) 0 set out2($name) 0 set out3($name) 0 set ncycle($name) 0 for {set i 3} {$i<[llength $line]-1} {incr i} { | > | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | if {[regexp {^case OP_} $line]} { set line [split $line] set name [string trim [lindex $line 1] :] if {$name=="OP_Abortable"} continue; # put OP_Abortable last set op($name) -1 set group($name) 0 set jump($name) 0 set jump0($name) 0 set in1($name) 0 set in2($name) 0 set in3($name) 0 set out2($name) 0 set out3($name) 0 set ncycle($name) 0 for {set i 3} {$i<[llength $line]-1} {incr i} { |
︙ | ︙ | |||
105 106 107 108 109 110 111 112 113 114 115 116 117 118 | jump {set jump($name) 1} in1 {set in1($name) 1} in2 {set in2($name) 1} in3 {set in3($name) 1} out2 {set out2($name) 1} out3 {set out3($name) 1} ncycle {set ncycle($name) 1} } } if {$group($name)} { set newGroup 0 if {[info exists groups($nGroup)]} { if {$prevName=="" || !$group($prevName)} { set newGroup 1 | > | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | jump {set jump($name) 1} in1 {set in1($name) 1} in2 {set in2($name) 1} in3 {set in3($name) 1} out2 {set out2($name) 1} out3 {set out3($name) 1} ncycle {set ncycle($name) 1} jump0 {set jump($name) 1; set jump0($name) 1;} } } if {$group($name)} { set newGroup 0 if {[info exists groups($nGroup)]} { if {$prevName=="" || !$group($prevName)} { set newGroup 1 |
︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 142 143 144 145 146 | # Assign numbers to all opcodes and output the result. # puts "/* Automatically generated. Do not edit */" puts "/* See the tool/mkopcodeh.tcl script for details */" foreach name {OP_Noop OP_Explain OP_Abortable} { set jump($name) 0 set in1($name) 0 set in2($name) 0 set in3($name) 0 set out2($name) 0 set out3($name) 0 set ncycle($name) 0 set op($name) -1 | > | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | # Assign numbers to all opcodes and output the result. # puts "/* Automatically generated. Do not edit */" puts "/* See the tool/mkopcodeh.tcl script for details */" foreach name {OP_Noop OP_Explain OP_Abortable} { set jump($name) 0 set jump0($name) 0 set in1($name) 0 set in2($name) 0 set in3($name) 0 set out2($name) 0 set out3($name) 0 set ncycle($name) 0 set op($name) -1 |
︙ | ︙ | |||
252 253 254 255 256 257 258 | if {![info exists used($i)]} { set def($i) "OP_NotUsed_$i" } if {$i>$max} {set max $i} set name $def($i) puts -nonewline [format {#define %-16s %3d} $name $i] set com {} | > > | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | if {![info exists used($i)]} { set def($i) "OP_NotUsed_$i" } if {$i>$max} {set max $i} set name $def($i) puts -nonewline [format {#define %-16s %3d} $name $i] set com {} if {[info exists jump0($name)] && $jump0($name)} { lappend com "jump0" } elseif {[info exists jump($name)] && $jump($name)} { lappend com "jump" } if {[info exists sameas($i)]} { lappend com "same as $sameas($i)" } if {[info exists synopsis($name)]} { lappend com "synopsis: $synopsis($name)" |
︙ | ︙ | |||
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | if {$jump($name)} {incr x 1} if {$in1($name)} {incr x 2} if {$in2($name)} {incr x 4} if {$in3($name)} {incr x 8} if {$out2($name)} {incr x 16} if {$out3($name)} {incr x 32} if {$ncycle($name)} {incr x 64} } set bv($i) $x } puts "" puts "/* Properties such as \"out2\" or \"jump\" that are specified in" puts "** comments following the \"case\" for each opcode in the vdbe.c" puts "** are encoded into bitvectors as follows:" puts "*/" puts "#define OPFLG_JUMP 0x01 /* jump: P2 holds jmp target */" puts "#define OPFLG_IN1 0x02 /* in1: P1 is an input */" puts "#define OPFLG_IN2 0x04 /* in2: P2 is an input */" puts "#define OPFLG_IN3 0x08 /* in3: P3 is an input */" puts "#define OPFLG_OUT2 0x10 /* out2: P2 is an output */" puts "#define OPFLG_OUT3 0x20 /* out3: P3 is an output */" puts "#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */" puts "#define OPFLG_INITIALIZER \173\\" for {set i 0} {$i<=$max} {incr i} { if {$i%8==0} { puts -nonewline [format "/* %3d */" $i] } puts -nonewline [format " 0x%02x," $bv($i)] if {$i%8==7} { | > > | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | if {$jump($name)} {incr x 1} if {$in1($name)} {incr x 2} if {$in2($name)} {incr x 4} if {$in3($name)} {incr x 8} if {$out2($name)} {incr x 16} if {$out3($name)} {incr x 32} if {$ncycle($name)} {incr x 64} if {$jump0($name)} {incr x 128} } set bv($i) $x } puts "" puts "/* Properties such as \"out2\" or \"jump\" that are specified in" puts "** comments following the \"case\" for each opcode in the vdbe.c" puts "** are encoded into bitvectors as follows:" puts "*/" puts "#define OPFLG_JUMP 0x01 /* jump: P2 holds jmp target */" puts "#define OPFLG_IN1 0x02 /* in1: P1 is an input */" puts "#define OPFLG_IN2 0x04 /* in2: P2 is an input */" puts "#define OPFLG_IN3 0x08 /* in3: P3 is an input */" puts "#define OPFLG_OUT2 0x10 /* out2: P2 is an output */" puts "#define OPFLG_OUT3 0x20 /* out3: P3 is an output */" puts "#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */" puts "#define OPFLG_JUMP0 0x80 /* jump0: P2 might be zero */" puts "#define OPFLG_INITIALIZER \173\\" for {set i 0} {$i<=$max} {incr i} { if {$i%8==0} { puts -nonewline [format "/* %3d */" $i] } puts -nonewline [format " 0x%02x," $bv($i)] if {$i%8==7} { |
︙ | ︙ |
Changes to tool/mksqlite3c.tcl.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # For example, the "parse.c" and "parse.h" files to implement the # the parser are derived from "parse.y" using lemon. And the # "keywordhash.h" files is generated by a program named "mkkeywordhash". # # After the "tsrc" directory has been created and populated, run # this script: # | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # For example, the "parse.c" and "parse.h" files to implement the # the parser are derived from "parse.y" using lemon. And the # "keywordhash.h" files is generated by a program named "mkkeywordhash". # # After the "tsrc" directory has been created and populated, run # this script: # # tclsh mksqlite3c.tcl [flags] [extra source files] # # The amalgamated SQLite code will be written into sqlite3.c # set help {Usage: tclsh mksqlite3c.tcl <options> where <options> is zero or more of the following with these effects: --nostatic => Do not generate with compile-time modifiable linkage. |
︙ | ︙ | |||
38 39 40 41 42 43 44 45 46 47 48 49 50 51 | # set addstatic 1 set linemacros 0 set useapicall 0 set enable_recover 0 set srcdir tsrc for {set i 0} {$i<[llength $argv]} {incr i} { set x [lindex $argv $i] if {[regexp {^-?-enable-recover$} $x]} { set enable_recover 1 } elseif {[regexp {^-?-nostatic$} $x]} { set addstatic 0 | > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | # set addstatic 1 set linemacros 0 set useapicall 0 set enable_recover 0 set srcdir tsrc set extrasrc [list] for {set i 0} {$i<[llength $argv]} {incr i} { set x [lindex $argv $i] if {[regexp {^-?-enable-recover$} $x]} { set enable_recover 1 } elseif {[regexp {^-?-nostatic$} $x]} { set addstatic 0 |
︙ | ︙ | |||
59 60 61 62 63 64 65 | if {$i==[llength $argv]} { error "No argument following $x" } set srcdir [lindex $argv $i] } elseif {[regexp {^-?-((help)|\?)$} $x]} { puts $help exit 0 | | > > | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | if {$i==[llength $argv]} { error "No argument following $x" } set srcdir [lindex $argv $i] } elseif {[regexp {^-?-((help)|\?)$} $x]} { puts $help exit 0 } elseif {[regexp {^-?-} $x]} { error "unknown command-line option: $x" } else { lappend extrasrc $x } } set in [open $srcdir/sqlite3.h] set cnt 0 set VERSION ????? while {![eof $in]} { set line [gets $in] |
︙ | ︙ | |||
345 346 347 348 349 350 351 352 353 354 355 356 357 358 | puts $out $line } } close $in section_comment "End of $tail" } # Process the source files. Process files containing commonly # used subroutines first in order to help the compiler find # inlining opportunities. # set flist { sqliteInt.h | > > > > > > > > > > > > > > > | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | puts $out $line } } close $in section_comment "End of $tail" } # Read the source file named $filename and write it into the # sqlite3.c output file. The only transformation is the trimming # of EOL whitespace. # proc copy_file_verbatim {filename} { global out set in [open $filename r] set tail [file tail $filename] section_comment "Begin EXTRA_SRC file $tail" while {![eof $in]} { set line [string trimright [gets $in]] puts $out $line } section_comment "End of EXTRA_SRC $tail" } # Process the source files. Process files containing commonly # used subroutines first in order to help the compiler find # inlining opportunities. # set flist { sqliteInt.h |
︙ | ︙ | |||
466 467 468 469 470 471 472 | fts3_icu.c sqlite3rbu.c dbstat.c dbpage.c sqlite3session.c fts5.c stmt.c | | > > > | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | fts3_icu.c sqlite3rbu.c dbstat.c dbpage.c sqlite3session.c fts5.c stmt.c } if {$enable_recover} { lappend flist sqlite3recover.c dbdata.c } foreach file $flist { copy_file $srcdir/$file } foreach file $extrasrc { copy_file_verbatim $file } puts $out \ "/* Return the source-id for this library */ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }" puts $out \ "/************************** End of sqlite3.c ******************************/" close $out |
Changes to tool/speed-check.sh.
︙ | ︙ | |||
157 158 159 160 161 162 163 164 165 166 167 168 169 170 | ;; --cte) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset cte" ;; --fp) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset fp" ;; --stmtscanstatus) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --stmtscanstatus" ;; -*) CC_OPTS="$CC_OPTS $1" ;; *) | > > > | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | ;; --cte) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset cte" ;; --fp) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset fp" ;; --parsenumber) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset parsenumber" ;; --stmtscanstatus) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --stmtscanstatus" ;; -*) CC_OPTS="$CC_OPTS $1" ;; *) |
︙ | ︙ |
Changes to tool/sqldiff.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This is a utility program that computes the differences in content ** between two SQLite databases. ** | | > > > > > > > > > > > > > > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This is a utility program that computes the differences in content ** between two SQLite databases. ** ** To compile, simply link against SQLite. (Windows builds must also link ** against ext/consio/console_io.c.) ** ** See the showHelp() routine below for a brief description of how to ** run the utility. */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <ctype.h> #include <string.h> #include <assert.h> #include "sqlite3.h" /* Output function substitutions that cause UTF8 characters to be rendered ** correctly on Windows: ** ** fprintf() -> Wfprintf() ** */ #if defined(_WIN32) # include "console_io.h" # define Wfprintf fPrintfUtf8 #else # define Wfprintf fprintf #endif /* ** All global variables are gathered into the "g" singleton. */ struct GlobalVars { const char *zArgv0; /* Name of program */ int bSchemaOnly; /* Only show schema differences */ |
︙ | ︙ | |||
42 43 44 45 46 47 48 | /* ** Allowed values for g.fDebug */ #define DEBUG_COLUMN_NAMES 0x000001 #define DEBUG_DIFF_SQL 0x000002 /* | < < < < < < < < | < < | < < < > > < | > > | > < | | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | /* ** Allowed values for g.fDebug */ #define DEBUG_COLUMN_NAMES 0x000001 #define DEBUG_DIFF_SQL 0x000002 /* ** Clear and free an sqlite3_str object */ static void strFree(sqlite3_str *pStr){ sqlite3_free(sqlite3_str_finish(pStr)); } /* ** Print an error resulting from faulting command-line arguments and ** abort the program. */ static void cmdlineError(const char *zFormat, ...){ sqlite3_str *pOut = sqlite3_str_new(0); va_list ap; va_start(ap, zFormat); sqlite3_str_vappendf(pOut, zFormat, ap); va_end(ap); Wfprintf(stderr, "%s: %s\n", g.zArgv0, sqlite3_str_value(pOut)); strFree(pOut); Wfprintf(stderr, "\"%s --help\" for more help\n", g.zArgv0); exit(1); } /* ** Print an error message for an error that occurs at runtime, then ** abort the program. */ static void runtimeError(const char *zFormat, ...){ sqlite3_str *pOut = sqlite3_str_new(0); va_list ap; va_start(ap, zFormat); sqlite3_str_vappendf(pOut, zFormat, ap); va_end(ap); Wfprintf(stderr, "%s: %s\n", g.zArgv0, sqlite3_str_value(pOut)); strFree(pOut); exit(1); } /* Safely quote an SQL identifier. Use the minimum amount of transformation ** necessary to allow the string to be used with %s. ** ** Space to hold the returned string is obtained from sqlite3_malloc(). The ** caller is responsible for ensuring this space is freed when no longer |
︙ | ︙ | |||
449 450 451 452 453 454 455 | char *zId = safeId(zTab); /* Name of the table */ char **az = 0; /* List of columns */ int nPk; /* Number of true primary key columns */ int nCol; /* Number of data columns */ int i; /* Loop counter */ sqlite3_stmt *pStmt; /* SQL statement */ const char *zSep; /* Separator string */ | | | | | < | | | | | | | | | | | | | | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | char *zId = safeId(zTab); /* Name of the table */ char **az = 0; /* List of columns */ int nPk; /* Number of true primary key columns */ int nCol; /* Number of data columns */ int i; /* Loop counter */ sqlite3_stmt *pStmt; /* SQL statement */ const char *zSep; /* Separator string */ sqlite3_str *pIns; /* Beginning of the INSERT statement */ pStmt = db_prepare("SELECT sql FROM aux.sqlite_schema WHERE name=%Q", zTab); if( SQLITE_ROW==sqlite3_step(pStmt) ){ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); if( !g.bSchemaOnly ){ az = columnNames("aux", zTab, &nPk, 0); pIns = sqlite3_str_new(0); if( az==0 ){ pStmt = db_prepare("SELECT * FROM aux.%s", zId); sqlite3_str_appendf(pIns,"INSERT INTO %s VALUES", zId); }else{ sqlite3_str *pSql = sqlite3_str_new(0); zSep = "SELECT"; for(i=0; az[i]; i++){ sqlite3_str_appendf(pSql, "%s %s", zSep, az[i]); zSep = ","; } sqlite3_str_appendf(pSql," FROM aux.%s", zId); zSep = " ORDER BY"; for(i=1; i<=nPk; i++){ sqlite3_str_appendf(pSql, "%s %d", zSep, i); zSep = ","; } pStmt = db_prepare("%s", sqlite3_str_value(pSql)); strFree(pSql); sqlite3_str_appendf(pIns, "INSERT INTO %s", zId); zSep = "("; for(i=0; az[i]; i++){ sqlite3_str_appendf(pIns, "%s%s", zSep, az[i]); zSep = ","; } sqlite3_str_appendf(pIns,") VALUES"); namelistFree(az); } nCol = sqlite3_column_count(pStmt); while( SQLITE_ROW==sqlite3_step(pStmt) ){ Wfprintf(out, "%s",sqlite3_str_value(pIns)); zSep = "("; for(i=0; i<nCol; i++){ Wfprintf(out, "%s",zSep); printQuoted(out, sqlite3_column_value(pStmt,i)); zSep = ","; } Wfprintf(out, ");\n"); } sqlite3_finalize(pStmt); strFree(pIns); } /* endif !g.bSchemaOnly */ pStmt = db_prepare("SELECT sql FROM aux.sqlite_schema" " WHERE type='index' AND tbl_name=%Q AND sql IS NOT NULL", zTab); while( SQLITE_ROW==sqlite3_step(pStmt) ){ Wfprintf(out, "%s;\n", sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); sqlite3_free(zId); } /* |
︙ | ︙ | |||
527 528 529 530 531 532 533 | int nPk; /* Primary key columns in main */ int nPk2; /* Primary key columns in aux */ int n = 0; /* Number of columns in main */ int n2; /* Number of columns in aux */ int nQ; /* Number of output columns in the diff query */ int i; /* Loop counter */ const char *zSep; /* Separator string */ | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | int nPk; /* Primary key columns in main */ int nPk2; /* Primary key columns in aux */ int n = 0; /* Number of columns in main */ int n2; /* Number of columns in aux */ int nQ; /* Number of output columns in the diff query */ int i; /* Loop counter */ const char *zSep; /* Separator string */ sqlite3_str *pSql; /* Comparison query */ sqlite3_stmt *pStmt; /* Query statement to do the diff */ const char *zLead = /* Becomes line-comment for sqlite_schema */ (g.bSchemaCompare)? "-- " : ""; pSql = sqlite3_str_new(0); if( g.fDebug==DEBUG_COLUMN_NAMES ){ /* Simply run columnNames() on all tables of the origin ** database and show the results. This is used for testing ** and debugging of the columnNames() function. */ az = columnNames("aux",zTab, &nPk, 0); if( az==0 ){ Wfprintf(stdout, "Rowid not accessible for %s\n", zId); }else{ Wfprintf(stdout, "%s:", zId); for(i=0; az[i]; i++){ Wfprintf(stdout, " %s", az[i]); if( i+1==nPk ) Wfprintf(stdout, " *"); } Wfprintf(stdout, "\n"); } goto end_diff_one_table; } if( sqlite3_table_column_metadata(g.db,"aux",zTab,0,0,0,0,0,0) ){ if( !sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){ /* Table missing from second database. */ if( g.bSchemaCompare ) Wfprintf(out, "-- 2nd DB has no %s table\n", zTab); else Wfprintf(out, "DROP TABLE %s;\n", zId); } goto end_diff_one_table; } if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){ /* Table missing from source */ if( g.bSchemaCompare ){ Wfprintf(out, "-- 1st DB has no %s table\n", zTab); }else{ dump_table(zTab, out); } goto end_diff_one_table; } az = columnNames("main", zTab, &nPk, 0); az2 = columnNames("aux", zTab, &nPk2, 0); if( az && az2 ){ for(n=0; az[n] && az2[n]; n++){ if( sqlite3_stricmp(az[n],az2[n])!=0 ) break; } } if( az==0 || az2==0 || nPk!=nPk2 || az[n] ){ /* Schema mismatch */ Wfprintf(out, "%sDROP TABLE %s; -- due to schema mismatch\n", zLead, zId); dump_table(zTab, out); goto end_diff_one_table; } /* Build the comparison query */ for(n2=n; az2[n2]; n2++){ char *zNTab = safeId(az2[n2]); Wfprintf(out, "ALTER TABLE %s ADD COLUMN %s;\n", zId, zNTab); sqlite3_free(zNTab); } nQ = nPk2+1+2*(n2-nPk2); if( n2>nPk2 ){ zSep = "SELECT "; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%sB.%s", zSep, az[i]); zSep = ", "; } sqlite3_str_appendf(pSql, ", 1 /* changed row */"); while( az[i] ){ sqlite3_str_appendf(pSql, ", A.%s IS NOT B.%s, B.%s", az[i], az2[i], az2[i]); i++; } while( az2[i] ){ sqlite3_str_appendf(pSql, ", B.%s IS NOT NULL, B.%s", az2[i], az2[i]); i++; } sqlite3_str_appendf(pSql, "\n FROM main.%s A, aux.%s B\n", zId, zId); zSep = " WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]); zSep = " AND"; } zSep = "\n AND ("; while( az[i] ){ sqlite3_str_appendf(pSql, "%sA.%s IS NOT B.%s%s\n", zSep, az[i], az2[i], az2[i+1]==0 ? ")" : ""); zSep = " OR "; i++; } while( az2[i] ){ sqlite3_str_appendf(pSql, "%sB.%s IS NOT NULL%s\n", zSep, az2[i], az2[i+1]==0 ? ")" : ""); zSep = " OR "; i++; } sqlite3_str_appendf(pSql, " UNION ALL\n"); } zSep = "SELECT "; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%sA.%s", zSep, az[i]); zSep = ", "; } sqlite3_str_appendf(pSql, ", 2 /* deleted row */"); while( az2[i] ){ sqlite3_str_appendf(pSql, ", NULL, NULL"); i++; } sqlite3_str_appendf(pSql, "\n FROM main.%s A\n", zId); sqlite3_str_appendf(pSql, " WHERE NOT EXISTS(SELECT 1 FROM aux.%s B\n", zId); zSep = " WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]); zSep = " AND"; } sqlite3_str_appendf(pSql, ")\n"); zSep = " UNION ALL\nSELECT "; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%sB.%s", zSep, az[i]); zSep = ", "; } sqlite3_str_appendf(pSql, ", 3 /* inserted row */"); while( az2[i] ){ sqlite3_str_appendf(pSql, ", 1, B.%s", az2[i]); i++; } sqlite3_str_appendf(pSql, "\n FROM aux.%s B\n", zId); sqlite3_str_appendf(pSql, " WHERE NOT EXISTS(SELECT 1 FROM main.%s A\n", zId); zSep = " WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]); zSep = " AND"; } sqlite3_str_appendf(pSql, ")\n ORDER BY"); zSep = " "; for(i=1; i<=nPk; i++){ sqlite3_str_appendf(pSql, "%s%d", zSep, i); zSep = ", "; } sqlite3_str_appendf(pSql, ";\n"); if( g.fDebug & DEBUG_DIFF_SQL ){ printf("SQL for %s:\n%s\n", zId, sqlite3_str_value(pSql)); goto end_diff_one_table; } /* Drop indexes that are missing in the destination */ pStmt = db_prepare( "SELECT name FROM main.sqlite_schema" " WHERE type='index' AND tbl_name=%Q" |
︙ | ︙ | |||
701 702 703 704 705 706 707 | fprintf(out, "DROP INDEX %s;\n", z); sqlite3_free(z); } sqlite3_finalize(pStmt); /* Run the query and output differences */ if( !g.bSchemaOnly ){ | | | 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 | fprintf(out, "DROP INDEX %s;\n", z); sqlite3_free(z); } sqlite3_finalize(pStmt); /* Run the query and output differences */ if( !g.bSchemaOnly ){ pStmt = db_prepare("%s", sqlite3_str_value(pSql)); while( SQLITE_ROW==sqlite3_step(pStmt) ){ int iType = sqlite3_column_int(pStmt, nPk); if( iType==1 || iType==2 ){ if( iType==1 ){ /* Change the content of a row */ fprintf(out, "%sUPDATE %s", zLead, zId); zSep = " SET"; for(i=nPk+1; i<nQ; i+=2){ |
︙ | ︙ | |||
759 760 761 762 763 764 765 | zTab, zTab); while( SQLITE_ROW==sqlite3_step(pStmt) ){ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); end_diff_one_table: | | | 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 | zTab, zTab); while( SQLITE_ROW==sqlite3_step(pStmt) ){ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); end_diff_one_table: strFree(pSql); sqlite3_free(zId); namelistFree(az); namelistFree(az2); return; } /* |
︙ | ︙ | |||
1167 1168 1169 1170 1171 1172 1173 | } /* ** End of code copied from fossil. **************************************************************************/ static void strPrintfArray( | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | > > > | | | | | | | | | | | | | | | | 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 | } /* ** End of code copied from fossil. **************************************************************************/ static void strPrintfArray( sqlite3_str *pStr, /* String object to append to */ const char *zSep, /* Separator string */ const char *zFmt, /* Format for each entry */ char **az, int n /* Array of strings & its size (or -1) */ ){ int i; for(i=0; az[i] && (i<n || n<0); i++){ if( i!=0 ) sqlite3_str_appendf(pStr, "%s", zSep); sqlite3_str_appendf(pStr, zFmt, az[i], az[i], az[i]); } } static void getRbudiffQuery( const char *zTab, char **azCol, int nPK, int bOtaRowid, sqlite3_str *pSql ){ int i; /* First the newly inserted rows: **/ sqlite3_str_appendf(pSql, "SELECT "); strPrintfArray(pSql, ", ", "%s", azCol, -1); sqlite3_str_appendf(pSql, ", 0, "); /* Set ota_control to 0 for an insert */ strPrintfArray(pSql, ", ", "NULL", azCol, -1); sqlite3_str_appendf(pSql, " FROM aux.%Q AS n WHERE NOT EXISTS (\n", zTab); sqlite3_str_appendf(pSql, " SELECT 1 FROM ", zTab); sqlite3_str_appendf(pSql, " main.%Q AS o WHERE ", zTab); strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK); sqlite3_str_appendf(pSql, "\n) AND "); strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK); /* Deleted rows: */ sqlite3_str_appendf(pSql, "\nUNION ALL\nSELECT "); strPrintfArray(pSql, ", ", "%s", azCol, nPK); if( azCol[nPK] ){ sqlite3_str_appendf(pSql, ", "); strPrintfArray(pSql, ", ", "NULL", &azCol[nPK], -1); } sqlite3_str_appendf(pSql, ", 1, "); /* Set ota_control to 1 for a delete */ strPrintfArray(pSql, ", ", "NULL", azCol, -1); sqlite3_str_appendf(pSql, " FROM main.%Q AS n WHERE NOT EXISTS (\n", zTab); sqlite3_str_appendf(pSql, " SELECT 1 FROM ", zTab); sqlite3_str_appendf(pSql, " aux.%Q AS o WHERE ", zTab); strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK); sqlite3_str_appendf(pSql, "\n) AND "); strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK); /* Updated rows. If all table columns are part of the primary key, there ** can be no updates. In this case this part of the compound SELECT can ** be omitted altogether. */ if( azCol[nPK] ){ sqlite3_str_appendf(pSql, "\nUNION ALL\nSELECT "); strPrintfArray(pSql, ", ", "n.%s", azCol, nPK); sqlite3_str_appendf(pSql, ",\n"); strPrintfArray(pSql, " ,\n", " CASE WHEN n.%s IS o.%s THEN NULL ELSE n.%s END", &azCol[nPK], -1 ); if( bOtaRowid==0 ){ sqlite3_str_appendf(pSql, ", '"); strPrintfArray(pSql, "", ".", azCol, nPK); sqlite3_str_appendf(pSql, "' ||\n"); }else{ sqlite3_str_appendf(pSql, ",\n"); } strPrintfArray(pSql, " ||\n", " CASE WHEN n.%s IS o.%s THEN '.' ELSE 'x' END", &azCol[nPK], -1 ); sqlite3_str_appendf(pSql, "\nAS ota_control, "); strPrintfArray(pSql, ", ", "NULL", azCol, nPK); sqlite3_str_appendf(pSql, ",\n"); strPrintfArray(pSql, " ,\n", " CASE WHEN n.%s IS o.%s THEN NULL ELSE o.%s END", &azCol[nPK], -1 ); sqlite3_str_appendf(pSql, "\nFROM main.%Q AS o, aux.%Q AS n\nWHERE ", zTab, zTab); strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK); sqlite3_str_appendf(pSql, " AND ota_control LIKE '%%x%%'"); } /* Now add an ORDER BY clause to sort everything by PK. */ sqlite3_str_appendf(pSql, "\nORDER BY "); for(i=1; i<=nPK; i++) sqlite3_str_appendf(pSql, "%s%d", ((i>1)?", ":""), i); } static void rbudiff_one_table(const char *zTab, FILE *out){ int bOtaRowid; /* True to use an ota_rowid column */ int nPK; /* Number of primary key columns in table */ char **azCol; /* NULL terminated array of col names */ int i; int nCol; sqlite3_str *pCt; /* The "CREATE TABLE data_xxx" statement */ sqlite3_str *pSql; /* Query to find differences */ sqlite3_str *pInsert; /* First part of output INSERT statement */ sqlite3_stmt *pStmt = 0; int nRow = 0; /* Total rows in data_xxx table */ /* --rbu mode must use real primary keys. */ g.bSchemaPK = 1; pCt = sqlite3_str_new(0); pSql = sqlite3_str_new(0); pInsert = sqlite3_str_new(0); /* Check that the schemas of the two tables match. Exit early otherwise. */ checkSchemasMatch(zTab); /* Grab the column names and PK details for the table(s). If no usable PK ** columns are found, bail out early. */ azCol = columnNames("main", zTab, &nPK, &bOtaRowid); if( azCol==0 ){ runtimeError("table %s has no usable PK columns", zTab); } for(nCol=0; azCol[nCol]; nCol++); /* Build and output the CREATE TABLE statement for the data_xxx table */ sqlite3_str_appendf(pCt, "CREATE TABLE IF NOT EXISTS 'data_%q'(", zTab); if( bOtaRowid ) sqlite3_str_appendf(pCt, "rbu_rowid, "); strPrintfArray(pCt, ", ", "%s", &azCol[bOtaRowid], -1); sqlite3_str_appendf(pCt, ", rbu_control);"); /* Get the SQL for the query to retrieve data from the two databases */ getRbudiffQuery(zTab, azCol, nPK, bOtaRowid, pSql); /* Build the first part of the INSERT statement output for each row ** in the data_xxx table. */ sqlite3_str_appendf(pInsert, "INSERT INTO 'data_%q' (", zTab); if( bOtaRowid ) sqlite3_str_appendf(pInsert, "rbu_rowid, "); strPrintfArray(pInsert, ", ", "%s", &azCol[bOtaRowid], -1); sqlite3_str_appendf(pInsert, ", rbu_control) VALUES("); pStmt = db_prepare("%s", sqlite3_str_value(pSql)); while( sqlite3_step(pStmt)==SQLITE_ROW ){ /* If this is the first row output, print out the CREATE TABLE ** statement first. And reset pCt so that it will not be ** printed again. */ if( sqlite3_str_length(pCt) ){ fprintf(out, "%s\n", sqlite3_str_value(pCt)); sqlite3_str_reset(pCt); } /* Output the first part of the INSERT statement */ fprintf(out, "%s", sqlite3_str_value(pInsert)); nRow++; if( sqlite3_column_type(pStmt, nCol)==SQLITE_INTEGER ){ for(i=0; i<=nCol; i++){ if( i>0 ) fprintf(out, ", "); printQuoted(out, sqlite3_column_value(pStmt, i)); } |
︙ | ︙ | |||
1365 1366 1367 1368 1369 1370 1371 | /* And the closing bracket of the insert statement */ fprintf(out, ");\n"); } sqlite3_finalize(pStmt); if( nRow>0 ){ | | > | | | | | | | 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 | /* And the closing bracket of the insert statement */ fprintf(out, ");\n"); } sqlite3_finalize(pStmt); if( nRow>0 ){ sqlite3_str *pCnt = sqlite3_str_new(0); sqlite3_str_appendf(pCnt, "INSERT INTO rbu_count VALUES('data_%q', %d);", zTab, nRow); fprintf(out, "%s\n", sqlite3_str_value(pCnt)); strFree(pCnt); } strFree(pCt); strFree(pSql); strFree(pInsert); } /* ** Display a summary of differences between two versions of the same ** table table. ** ** * Number of rows changed |
︙ | ︙ | |||
1395 1396 1397 1398 1399 1400 1401 | char **az2 = 0; /* Columns in aux */ int nPk; /* Primary key columns in main */ int nPk2; /* Primary key columns in aux */ int n = 0; /* Number of columns in main */ int n2; /* Number of columns in aux */ int i; /* Loop counter */ const char *zSep; /* Separator string */ | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 | char **az2 = 0; /* Columns in aux */ int nPk; /* Primary key columns in main */ int nPk2; /* Primary key columns in aux */ int n = 0; /* Number of columns in main */ int n2; /* Number of columns in aux */ int i; /* Loop counter */ const char *zSep; /* Separator string */ sqlite3_str *pSql; /* Comparison query */ sqlite3_stmt *pStmt; /* Query statement to do the diff */ sqlite3_int64 nUpdate; /* Number of updated rows */ sqlite3_int64 nUnchanged; /* Number of unmodified rows */ sqlite3_int64 nDelete; /* Number of deleted rows */ sqlite3_int64 nInsert; /* Number of inserted rows */ pSql = sqlite3_str_new(0); if( sqlite3_table_column_metadata(g.db,"aux",zTab,0,0,0,0,0,0) ){ if( !sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){ /* Table missing from second database. */ Wfprintf(out, "%s: missing from second database\n", zTab); } goto end_summarize_one_table; } if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){ /* Table missing from source */ Wfprintf(out, "%s: missing from first database\n", zTab); goto end_summarize_one_table; } az = columnNames("main", zTab, &nPk, 0); az2 = columnNames("aux", zTab, &nPk2, 0); if( az && az2 ){ for(n=0; az[n]; n++){ if( sqlite3_stricmp(az[n],az2[n])!=0 ) break; } } if( az==0 || az2==0 || nPk!=nPk2 || az[n] ){ /* Schema mismatch */ Wfprintf(out, "%s: incompatible schema\n", zTab); goto end_summarize_one_table; } /* Build the comparison query */ for(n2=n; az[n2]; n2++){} sqlite3_str_appendf(pSql, "SELECT 1, count(*)"); if( n2==nPk2 ){ sqlite3_str_appendf(pSql, ", 0\n"); }else{ zSep = ", sum("; for(i=nPk; az[i]; i++){ sqlite3_str_appendf(pSql, "%sA.%s IS NOT B.%s", zSep, az[i], az[i]); zSep = " OR "; } sqlite3_str_appendf(pSql, ")\n"); } sqlite3_str_appendf(pSql, " FROM main.%s A, aux.%s B\n", zId, zId); zSep = " WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]); zSep = " AND"; } sqlite3_str_appendf(pSql, " UNION ALL\n"); sqlite3_str_appendf(pSql, "SELECT 2, count(*), 0\n"); sqlite3_str_appendf(pSql, " FROM main.%s A\n", zId); sqlite3_str_appendf(pSql, " WHERE NOT EXISTS(SELECT 1 FROM aux.%s B ", zId); zSep = "WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]); zSep = " AND"; } sqlite3_str_appendf(pSql, ")\n"); sqlite3_str_appendf(pSql, " UNION ALL\n"); sqlite3_str_appendf(pSql, "SELECT 3, count(*), 0\n"); sqlite3_str_appendf(pSql, " FROM aux.%s B\n", zId); sqlite3_str_appendf(pSql, " WHERE NOT EXISTS(SELECT 1 FROM main.%s A ", zId); zSep = "WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]); zSep = " AND"; } sqlite3_str_appendf(pSql, ")\n ORDER BY 1;\n"); if( (g.fDebug & DEBUG_DIFF_SQL)!=0 ){ Wfprintf(stdout, "SQL for %s:\n%s\n", zId, sqlite3_str_value(pSql)); goto end_summarize_one_table; } /* Run the query and output difference summary */ pStmt = db_prepare("%s", sqlite3_str_value(pSql)); nUpdate = 0; nInsert = 0; nDelete = 0; nUnchanged = 0; while( SQLITE_ROW==sqlite3_step(pStmt) ){ switch( sqlite3_column_int(pStmt,0) ){ case 1: nUpdate = sqlite3_column_int64(pStmt,2); nUnchanged = sqlite3_column_int64(pStmt,1) - nUpdate; break; case 2: nDelete = sqlite3_column_int64(pStmt,1); break; case 3: nInsert = sqlite3_column_int64(pStmt,1); break; } } sqlite3_finalize(pStmt); Wfprintf(out, "%s: %lld changes, %lld inserts, %lld deletes, %lld unchanged\n", zTab, nUpdate, nInsert, nDelete, nUnchanged); end_summarize_one_table: strFree(pSql); sqlite3_free(zId); namelistFree(az); namelistFree(az2); return; } /* |
︙ | ︙ | |||
1584 1585 1586 1587 1588 1589 1590 | sqlite3_stmt *pStmt; /* SQL statment */ char *zId = safeId(zTab); /* Escaped name of the table */ char **azCol = 0; /* List of escaped column names */ int nCol = 0; /* Number of columns */ int *aiFlg = 0; /* 0 if column is not part of PK */ int *aiPk = 0; /* Column numbers for each PK column */ int nPk = 0; /* Number of PRIMARY KEY columns */ | | | | 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 | sqlite3_stmt *pStmt; /* SQL statment */ char *zId = safeId(zTab); /* Escaped name of the table */ char **azCol = 0; /* List of escaped column names */ int nCol = 0; /* Number of columns */ int *aiFlg = 0; /* 0 if column is not part of PK */ int *aiPk = 0; /* Column numbers for each PK column */ int nPk = 0; /* Number of PRIMARY KEY columns */ sqlite3_str *pSql; /* SQL for the diff query */ int i, k; /* Loop counters */ const char *zSep; /* List separator */ /* Check that the schemas of the two tables match. Exit early otherwise. */ checkSchemasMatch(zTab); pSql = sqlite3_str_new(0); pStmt = db_prepare("PRAGMA main.table_info=%Q", zTab); while( SQLITE_ROW==sqlite3_step(pStmt) ){ nCol++; azCol = sqlite3_realloc(azCol, sizeof(char*)*nCol); if( azCol==0 ) runtimeError("out of memory"); aiFlg = sqlite3_realloc(aiFlg, sizeof(int)*nCol); |
︙ | ︙ | |||
1613 1614 1615 1616 1617 1618 1619 | } aiPk[i-1] = nCol-1; } } sqlite3_finalize(pStmt); if( nPk==0 ) goto end_changeset_one_table; if( nCol>nPk ){ | | | | | > | | | | | | | | > | | | | | | | > | | | | | | | | 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 | } aiPk[i-1] = nCol-1; } } sqlite3_finalize(pStmt); if( nPk==0 ) goto end_changeset_one_table; if( nCol>nPk ){ sqlite3_str_appendf(pSql, "SELECT %d", SQLITE_UPDATE); for(i=0; i<nCol; i++){ if( aiFlg[i] ){ sqlite3_str_appendf(pSql, ",\n A.%s", azCol[i]); }else{ sqlite3_str_appendf(pSql, ",\n A.%s IS NOT B.%s, A.%s, B.%s", azCol[i], azCol[i], azCol[i], azCol[i]); } } sqlite3_str_appendf(pSql,"\n FROM main.%s A, aux.%s B\n", zId, zId); zSep = " WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, azCol[aiPk[i]], azCol[aiPk[i]]); zSep = " AND"; } zSep = "\n AND ("; for(i=0; i<nCol; i++){ if( aiFlg[i] ) continue; sqlite3_str_appendf(pSql, "%sA.%s IS NOT B.%s", zSep, azCol[i], azCol[i]); zSep = " OR\n "; } sqlite3_str_appendf(pSql,")\n UNION ALL\n"); } sqlite3_str_appendf(pSql, "SELECT %d", SQLITE_DELETE); for(i=0; i<nCol; i++){ if( aiFlg[i] ){ sqlite3_str_appendf(pSql, ",\n A.%s", azCol[i]); }else{ sqlite3_str_appendf(pSql, ",\n 1, A.%s, NULL", azCol[i]); } } sqlite3_str_appendf(pSql, "\n FROM main.%s A\n", zId); sqlite3_str_appendf(pSql, " WHERE NOT EXISTS(SELECT 1 FROM aux.%s B\n", zId); zSep = " WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, azCol[aiPk[i]], azCol[aiPk[i]]); zSep = " AND"; } sqlite3_str_appendf(pSql, ")\n UNION ALL\n"); sqlite3_str_appendf(pSql, "SELECT %d", SQLITE_INSERT); for(i=0; i<nCol; i++){ if( aiFlg[i] ){ sqlite3_str_appendf(pSql, ",\n B.%s", azCol[i]); }else{ sqlite3_str_appendf(pSql, ",\n 1, NULL, B.%s", azCol[i]); } } sqlite3_str_appendf(pSql, "\n FROM aux.%s B\n", zId); sqlite3_str_appendf(pSql, " WHERE NOT EXISTS(SELECT 1 FROM main.%s A\n", zId); zSep = " WHERE"; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, azCol[aiPk[i]], azCol[aiPk[i]]); zSep = " AND"; } sqlite3_str_appendf(pSql, ")\n"); sqlite3_str_appendf(pSql, " ORDER BY"); zSep = " "; for(i=0; i<nPk; i++){ sqlite3_str_appendf(pSql, "%s %d", zSep, aiPk[i]+2); zSep = ","; } sqlite3_str_appendf(pSql, ";\n"); if( g.fDebug & DEBUG_DIFF_SQL ){ Wfprintf(stdout, "SQL for %s:\n%s\n", zId, sqlite3_str_value(pSql)); goto end_changeset_one_table; } putc('T', out); putsVarint(out, (sqlite3_uint64)nCol); for(i=0; i<nCol; i++) putc(aiFlg[i], out); fwrite(zTab, 1, strlen(zTab), out); putc(0, out); pStmt = db_prepare("%s", sqlite3_str_value(pSql)); while( SQLITE_ROW==sqlite3_step(pStmt) ){ int iType = sqlite3_column_int(pStmt,0); putc(iType, out); putc(0, out); switch( sqlite3_column_int(pStmt,0) ){ case SQLITE_UPDATE: { for(k=1, i=0; i<nCol; i++){ |
︙ | ︙ | |||
1754 1755 1756 1757 1758 1759 1760 | end_changeset_one_table: while( nCol>0 ) sqlite3_free(azCol[--nCol]); sqlite3_free(azCol); sqlite3_free(aiPk); sqlite3_free(zId); sqlite3_free(aiFlg); | | | 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 | end_changeset_one_table: while( nCol>0 ) sqlite3_free(azCol[--nCol]); sqlite3_free(azCol); sqlite3_free(aiPk); sqlite3_free(zId); sqlite3_free(aiFlg); strFree(pSql); } /* ** Return true if the ascii character passed as the only argument is a ** whitespace character. Otherwise return false. */ static int is_whitespace(char x){ |
︙ | ︙ | |||
1907 1908 1909 1910 1911 1912 1913 | } } /* ** Print sketchy documentation for this utility program */ static void showHelp(void){ | | | | 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 | } } /* ** Print sketchy documentation for this utility program */ static void showHelp(void){ Wfprintf(stdout, "Usage: %s [options] DB1 DB2\n", g.zArgv0); Wfprintf(stdout, "Output SQL text that would transform DB1 into DB2.\n" "Options:\n" " --changeset FILE Write a CHANGESET into FILE\n" " -L|--lib LIBRARY Load an SQLite extension library\n" " --primarykey Use schema-defined PRIMARY KEYs\n" " --rbu Output SQL to create/populate RBU table(s)\n" " --schema Show only differences in the schema\n" |
︙ | ︙ | |||
2045 2046 2047 2048 2049 2050 2051 | } rc = sqlite3_exec(g.db, "SELECT * FROM aux.sqlite_schema", 0, 0, &zErrMsg); if( rc || zErrMsg ){ cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb2); } if( neverUseTransaction ) useTransaction = 0; | | | | | 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 | } rc = sqlite3_exec(g.db, "SELECT * FROM aux.sqlite_schema", 0, 0, &zErrMsg); if( rc || zErrMsg ){ cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb2); } if( neverUseTransaction ) useTransaction = 0; if( useTransaction ) Wfprintf(out, "BEGIN TRANSACTION;\n"); if( xDiff==rbudiff_one_table ){ Wfprintf(out, "CREATE TABLE IF NOT EXISTS rbu_count" "(tbl TEXT PRIMARY KEY COLLATE NOCASE, cnt INTEGER) " "WITHOUT ROWID;\n" ); } if( zTab ){ xDiff(zTab, out); }else{ /* Handle tables one by one */ pStmt = db_prepare("%s", all_tables_sql() ); while( SQLITE_ROW==sqlite3_step(pStmt) ){ xDiff((const char*)sqlite3_column_text(pStmt,0), out); } sqlite3_finalize(pStmt); } if( useTransaction ) Wfprintf(stdout,"COMMIT;\n"); /* TBD: Handle trigger differences */ /* TBD: Handle view differences */ sqlite3_close(g.db); return 0; } |
Changes to tool/sqlite3_analyzer.c.in.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #define SQLITE_MAX_EXPR_DEPTH 0 #define SQLITE_OMIT_LOAD_EXTENSION 1 #if !defined(SQLITE_AMALGAMATION) && !defined(USE_EXTERNAL_SQLITE) INCLUDE sqlite3.c #endif INCLUDE $ROOT/src/tclsqlite.c const char *sqlite3_analyzer_init_proc(Tcl_Interp *interp){ (void)interp; return BEGIN_STRING INCLUDE $ROOT/tool/spaceanal.tcl END_STRING ; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #define SQLITE_MAX_EXPR_DEPTH 0 #define SQLITE_OMIT_LOAD_EXTENSION 1 #if !defined(SQLITE_AMALGAMATION) && !defined(USE_EXTERNAL_SQLITE) INCLUDE sqlite3.c #endif INCLUDE $ROOT/src/tclsqlite.c #if defined(_WIN32) INCLUDE $ROOT/ext/consio/console_io.h INCLUDE $ROOT/ext/consio/console_io.c /* Substitute "puts" command. Only these forms recognized: ** ** puts STRING ** puts stderr STRING ** puts -nonewline STRING */ static int subst_puts( void *NotUsed, Tcl_Interp *interp, int objc, Tcl_Obj *const*objv ){ FILE *pOut = stdout; const char *zOut; int addNewLine = 1; if( objc==2 ){ zOut = Tcl_GetString(objv[1]); }else if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "?stderr|-nonewline? STRING"); return TCL_ERROR; }else{ const char *zArg = Tcl_GetString(objv[1]); if( zArg==0 ) return TCL_ERROR; zOut = Tcl_GetString(objv[2]); if( strcmp(zArg, "stderr")==0 ){ pOut = stderr; }else if( strcmp(zArg, "-nonewline")==0 ){ addNewLine = 0; }else{ Tcl_AppendResult(interp, "bad argument: ", zArg, 0); return TCL_ERROR; } } fPutsUtf8(zOut, pOut); if( addNewLine ) fPutsUtf8("\n", pOut); return TCL_OK; } #endif /* defined(_WIN32) */ const char *sqlite3_analyzer_init_proc(Tcl_Interp *interp){ #if defined(_WIN32) Tcl_CreateObjCommand(interp, "puts", subst_puts, 0, 0); #else (void)interp; #endif return BEGIN_STRING INCLUDE $ROOT/tool/spaceanal.tcl END_STRING ; } |