Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 184a0cd2e855c517 To 8a6196ab29052071
2024-03-19
| ||
13:55 | On second thought, change SQLITE_TESTCTRL_ROWID_IN_VIEW into a start-time option SQLITE_CONFIG_NO_ROWID_IN_VIEW. check-in: b8e045c9e1 user: drh tags: rowid-in-view | |
13:31 | When compiled with SQLITE_ALLOW_ROWID_IN_VIEW, rowid-in-view is on by default but can now be turned off using SQLITE_TESTCTRL_ROWID_IN_VIEW. Without the compile-time option, rowid-in-view is always off. check-in: 8a6196ab29 user: drh tags: rowid-in-view | |
02:30 | First steps toward getting -DSQLITE_ALLOW_ROWID_IN_VIEW to work again. That compile-time option is untested, undocumented, and unsupported. But it was mentioned in the release notes for version 3.36.0, so I think that means we need to support it forever. check-in: 7c46ff6402 user: drh tags: rowid-in-view | |
2023-11-20
| ||
15:58 | Back out an incorrect change to the sqlite3ExprCompareSkip() function from long ago. check-in: 89658abbcd user: drh tags: branch-3.44 | |
13:59 | Arrange to not compile unused console I/O function. check-in: 184a0cd2e8 user: larrybr tags: branch-3.44 | |
13:12 | Convert an assert in OP_VCheck into a branch that aborts the opcode, as this can happen on some very obscure conditions, as discovered by dbsqlfuzz. check-in: 0d5f68717c user: drh tags: branch-3.44 | |
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 \ |
︙ | ︙ | |||
443 444 445 446 447 448 449 450 451 452 453 454 455 456 | $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/mmapwarm.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/normalize.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/prefixes.c \ $(TOP)/ext/misc/qpvtab.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/remember.c \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/unionvtab.c \ $(TOP)/ext/misc/wholenumber.c \ | > | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/mmapwarm.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/normalize.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/prefixes.c \ $(TOP)/ext/misc/qpvtab.c \ $(TOP)/ext/misc/randomjson.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/remember.c \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/unionvtab.c \ $(TOP)/ext/misc/wholenumber.c \ |
︙ | ︙ | |||
596 597 598 599 600 601 602 603 604 605 606 607 608 609 | SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB SHELL_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC FUZZERSHELL_OPT = FUZZCHECK_OPT += -I$(TOP)/test FUZZCHECK_OPT += -I$(TOP)/ext/recover FUZZCHECK_OPT += \ -DSQLITE_OSS_FUZZ \ -DSQLITE_ENABLE_BYTECODE_VTAB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ | > | 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 | SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB SHELL_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC SHELL_OPT += -DSQLITE_STRICT_SUBTYPE=1 FUZZERSHELL_OPT = FUZZCHECK_OPT += -I$(TOP)/test FUZZCHECK_OPT += -I$(TOP)/ext/recover FUZZCHECK_OPT += \ -DSQLITE_OSS_FUZZ \ -DSQLITE_ENABLE_BYTECODE_VTAB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ |
︙ | ︙ | |||
626 627 628 629 630 631 632 | -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \ -DSQLITE_ENABLE_STAT4 \ -DSQLITE_ENABLE_STMT_SCANSTATUS \ -DSQLITE_MAX_MEMORY=50000000 \ -DSQLITE_MAX_MMAP_SIZE=0 \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_PRINTF_PRECISION_LIMIT=1000 \ | | > > > | 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 | -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \ -DSQLITE_ENABLE_STAT4 \ -DSQLITE_ENABLE_STMT_SCANSTATUS \ -DSQLITE_MAX_MEMORY=50000000 \ -DSQLITE_MAX_MMAP_SIZE=0 \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_PRINTF_PRECISION_LIMIT=1000 \ -DSQLITE_PRIVATE="" \ -DSQLITE_STRICT_SUBTYPE=1 \ -DSQLITE_STATIC_RANDOMJSON FUZZCHECK_SRC += $(TOP)/test/fuzzcheck.c FUZZCHECK_SRC += $(TOP)/test/ossfuzz.c FUZZCHECK_SRC += $(TOP)/test/fuzzinvariants.c FUZZCHECK_SRC += $(TOP)/ext/recover/dbdata.c FUZZCHECK_SRC += $(TOP)/ext/recover/sqlite3recover.c FUZZCHECK_SRC += $(TOP)/test/vt02.c FUZZCHECK_SRC += $(TOP)/ext/misc/randomjson.c DBFUZZ_OPT = ST_OPT = -DSQLITE_OS_KV_OPTIONAL # In wasi-sdk builds, disable the CLI shell build in the "all" target. SQLITE3_SHELL_TARGET_ = sqlite3$(TEXE) SQLITE3_SHELL_TARGET_1 = |
︙ | ︙ | |||
705 706 707 708 709 710 711 712 713 714 715 716 717 718 | $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS) fuzzcheck-asan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP) $(LTLINK) -o $@ -fsanitize=address $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS) fuzzcheck-ubsan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP) $(LTLINK) -o $@ -fsanitize=undefined $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS) ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c \ $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) sessionfuzz$(TEXE): $(TOP)/test/sessionfuzz.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(TOP)/test/sessionfuzz.c $(TLIBS) | > > > > > > > > > > > > > > > | 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 | $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS) fuzzcheck-asan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP) $(LTLINK) -o $@ -fsanitize=address $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS) fuzzcheck-ubsan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP) $(LTLINK) -o $@ -fsanitize=undefined $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS) # Usage: FUZZDB=filename make run-fuzzcheck # # Where filename is a fuzzcheck database, this target builds and runs # fuzzcheck, fuzzcheck-asan, and fuzzcheck-ubsan on that database. # # FUZZDB can be a glob pattern of two or more databases. Example: # # FUZZDB=test/fuzzdata*.db make run-fuzzcheck # run-fuzzcheck: fuzzcheck$(TEXE) fuzzcheck-asan$(TEXE) fuzzcheck-ubsan$(TEXE) @if test "$(FUZZDB)" = ""; then echo 'ERROR: No FUZZDB specified. Rerun with FUZZDB=filename'; exit 1; fi ./fuzzcheck$(TEXE) --spinner $(FUZZDB) ./fuzzcheck-asan$(TEXE) --spinner $(FUZZDB) ./fuzzcheck-ubsan$(TEXE) --spinner $(FUZZDB) ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c \ $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) sessionfuzz$(TEXE): $(TOP)/test/sessionfuzz.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(TOP)/test/sessionfuzz.c $(TLIBS) |
︙ | ︙ | |||
791 792 793 794 795 796 797 | 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 |
︙ | ︙ | |||
1128 1129 1130 1131 1132 1133 1134 | 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. # |
︙ | ︙ | |||
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 | TESTFIXTURE_FLAGS += -DBUILD_sqlite TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB TESTFIXTURE_FLAGS += -DSQLITE_CKSUMVFS_STATIC TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC1 = sqlite3.c 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) | > > | | | 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 | TESTFIXTURE_FLAGS += -DBUILD_sqlite TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB TESTFIXTURE_FLAGS += -DSQLITE_CKSUMVFS_STATIC TESTFIXTURE_FLAGS += -DSQLITE_STATIC_RANDOMJSON TESTFIXTURE_FLAGS += -DSQLITE_STRICT_SUBTYPE=1 TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC1 = sqlite3.c 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 |
︙ | ︙ | |||
1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 | $(TOP)\ext\misc\ieee754.c \ $(TOP)\ext\misc\mmapwarm.c \ $(TOP)\ext\misc\nextchar.c \ $(TOP)\ext\misc\normalize.c \ $(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\prefixes.c \ $(TOP)\ext\misc\qpvtab.c \ $(TOP)\ext\misc\regexp.c \ $(TOP)\ext\misc\remember.c \ $(TOP)\ext\misc\series.c \ $(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 | > > > | 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 | $(TOP)\ext\misc\ieee754.c \ $(TOP)\ext\misc\mmapwarm.c \ $(TOP)\ext\misc\nextchar.c \ $(TOP)\ext\misc\normalize.c \ $(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\prefixes.c \ $(TOP)\ext\misc\qpvtab.c \ $(TOP)\ext\misc\randomjson.c \ $(TOP)\ext\misc\regexp.c \ $(TOP)\ext\misc\remember.c \ $(TOP)\ext\misc\series.c \ $(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 |
︙ | ︙ | |||
1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 | !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1 !ENDIF # <<mark>> # Extra compiler options for various test tools. # MPTESTER_COMPILE_OPTS = -DSQLITE_ENABLE_FTS5 FUZZERSHELL_COMPILE_OPTS = | > | 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 | !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_STRICT_SUBTYPE=1 !ENDIF # <<mark>> # Extra compiler options for various test tools. # MPTESTER_COMPILE_OPTS = -DSQLITE_ENABLE_FTS5 FUZZERSHELL_COMPILE_OPTS = |
︙ | ︙ | |||
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 | FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_STAT4 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MMAP_SIZE=0 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRIVATE="" FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS4 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS5 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_RTREE FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzcheck.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\ossfuzz.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzinvariants.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\vt02.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\dbdata.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\sqlite3recover.c OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION KV_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ ST_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 # Standard options to testfixture. | > > > | 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 | FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_STAT4 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MMAP_SIZE=0 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRIVATE="" FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_STRICT_SUBTYPE=1 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_STATIC_RANDOMJSON FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS4 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS5 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_RTREE FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzcheck.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\ossfuzz.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzinvariants.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\vt02.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\dbdata.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\sqlite3recover.c FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\randomjson.c OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION KV_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ ST_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 # Standard options to testfixture. |
︙ | ︙ | |||
1819 1820 1821 1822 1823 1824 1825 | # <</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) |
︙ | ︙ | |||
1904 1905 1906 1907 1908 1909 1910 | 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 # |
︙ | ︙ | |||
2254 2255 2256 2257 2258 2259 2260 | 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. # |
︙ | ︙ | |||
2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 | TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CKSUMVFS_STATIC=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS) TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C) !IF $(USE_AMALGAMATION)==0 TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0) !ELSE TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1) | > > | 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 | TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CKSUMVFS_STATIC=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS) TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_STATIC_RANDOMJSON TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_STRICT_SUBTYPE=1 TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C) !IF $(USE_AMALGAMATION)==0 TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0) !ELSE TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1) |
︙ | ︙ | |||
2471 2472 2473 2474 2475 2476 2477 | 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) |
︙ | ︙ | |||
2528 2529 2530 2531 2532 2533 2534 | 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 README.md.
︙ | ︙ | |||
155 156 157 158 159 160 161 | the "tclsqlite.c" file which implements the [Tcl bindings](https://sqlite.org/tclsqlite.html) for SQLite. (Historical note: SQLite began as a Tcl extension and only later escaped to the wild as an independent library.) Test scripts and programs are found in the **test/** subdirectory. Additional test code is found in other source repositories. | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | the "tclsqlite.c" file which implements the [Tcl bindings](https://sqlite.org/tclsqlite.html) for SQLite. (Historical note: SQLite began as a Tcl extension and only later escaped to the wild as an independent library.) Test scripts and programs are found in the **test/** subdirectory. Additional test code is found in other source repositories. See [How SQLite Is Tested](https://www.sqlite.org/testing.html) for additional information. The **ext/** subdirectory contains code for extensions. The Full-text search engine is in **ext/fts3**. The R-Tree engine is in **ext/rtree**. The **ext/misc** subdirectory contains a number of smaller, single-file extensions, such as a REGEXP operator. |
︙ | ︙ | |||
179 180 181 182 183 184 185 | of the automatically-generated files, simply run "make target_source". The "target_source" make target will create a subdirectory "tsrc/" and fill it with all the source files needed to build SQLite, both manually-edited files and automatically-generated files. The SQLite interface is defined by the **sqlite3.h** header file, which is generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | of the automatically-generated files, simply run "make target_source". The "target_source" make target will create a subdirectory "tsrc/" and fill it with all the source files needed to build SQLite, both manually-edited files and automatically-generated files. The SQLite interface is defined by the **sqlite3.h** header file, which is generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The [Tcl script](https://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion. The manifest.uuid file contains the SHA3 hash of the particular check-in and is used to generate the SQLITE\_SOURCE\_ID macro. The VERSION file contains the current SQLite version number. The sqlite3.h header is really just a copy of src/sqlite.h.in with the source-id and version number inserted at just the right spots. Note that comment text in the sqlite3.h file is used to generate much of the SQLite API documentation. The Tcl scripts used to generate that documentation are in a separate source repository. |
︙ | ︙ | |||
246 247 248 249 250 251 252 | all of the source code is contained within a single translation unit so that the compiler can do extra cross-procedure optimization, but no individual source file exceeds 32K lines in length. ## How It All Fits Together SQLite is modular in design. | | | | | | | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | all of the source code is contained within a single translation unit so that the compiler can do extra cross-procedure optimization, but no individual source file exceeds 32K lines in length. ## How It All Fits Together SQLite is modular in design. See the [architectural description](https://www.sqlite.org/arch.html) for details. Other documents that are useful in (helping to understand how SQLite works include the [file format](https://www.sqlite.org/fileformat2.html) description, the [virtual machine](https://www.sqlite.org/opcode.html) that runs prepared statements, the description of [how transactions work](https://www.sqlite.org/atomiccommit.html), and the [overview of the query planner](https://www.sqlite.org/optoverview.html). Years of effort have gone into optimizing SQLite, both for small size and high performance. And optimizations tend to result in complex code. So there is a lot of complexity in the current SQLite implementation. It will not be the easiest library in the world to hack. Key files: |
︙ | ︙ | |||
349 350 351 352 353 354 355 | Using the makefile to verify source integrity is good for detecting accidental changes to the source tree, but malicious changes could be hidden by also modifying the makefiles. ## Contacts | | | | | 349 350 351 352 353 354 355 356 357 358 359 | Using the makefile to verify source integrity is good for detecting accidental changes to the source tree, but malicious changes could be hidden by also modifying the makefiles. ## Contacts The main SQLite website is [https://sqlite.org/](https://sqlite.org/) with geographically distributed backups at [https://www2.sqlite.org/](https://www2.sqlite.org) and [https://www3.sqlite.org/](https://www3.sqlite.org). |
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 |
︙ | ︙ | |||
986 987 988 989 990 991 992 993 994 995 996 997 998 999 | !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1 !ENDIF # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # core: dll shell | > | 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 | !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_STRICT_SUBTYPE=1 !ENDIF # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # core: dll shell |
︙ | ︙ |
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 configure.ac.
︙ | ︙ | |||
70 71 72 73 74 75 76 | # target platform. "" for Unix and ".exe" for windows. # # This configure.in file is easy to reuse on other projects. Just # change the argument to AC_INIT. And disable any features that # you don't need (for example BLT) by erasing or commenting out # the corresponding code. # | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | # target platform. "" for Unix and ".exe" for windows. # # This configure.in file is easy to reuse on other projects. Just # change the argument to AC_INIT. And disable any features that # you don't need (for example BLT) by erasing or commenting out # the corresponding code. # AC_INIT([sqlite],m4_esyscmd(cat VERSION | tr -d '\n')) dnl Make sure the local VERSION file matches this configure script sqlite_version_sanity_check=`cat $srcdir/VERSION | tr -d '\n'` if test "$PACKAGE_VERSION" != "$sqlite_version_sanity_check" ; then AC_MSG_ERROR([configure script is out of date: configure \$PACKAGE_VERSION = $PACKAGE_VERSION top level VERSION file = $sqlite_version_sanity_check |
︙ | ︙ |
Changes to doc/compile-for-windows.md.
1 2 3 | # Notes On Compiling SQLite On Windows 11 Here are step-by-step instructions on how to build SQLite from | | | 1 2 3 4 5 6 7 8 9 10 11 | # Notes On Compiling SQLite On Windows 11 Here are step-by-step instructions on how to build SQLite from canonical source on a new Windows 11 PC, as of 2023-11-01: 1. Install Microsoft Visual Studio. The free "community edition" will work fine. Do a standard install for C++ development. SQLite only needs the "cl" compiler and the "nmake" build tool. 2. Under the "Start" menu, find "All Apps" then go to "Visual Studio 20XX" |
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 3. Ensure that `c:\tcl32\bin` comes before `c:\tcl\bin` on your PATH environment variable. You can achieve this using a command like: <ul> <li> `set PATH=c:\tcl32\bin;%PATH%` </ul> ## Statically Linking The TCL Library Some utility programs associated with SQLite need to be linked with TCL in order to function. The [sqlite3_analyzer.exe program](https://sqlite.org/sqlanalyze.html) is an example. You can build as described above, and then enter: | > > > > > > > > > > > > | 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 | 3. Ensure that `c:\tcl32\bin` comes before `c:\tcl\bin` on your PATH environment variable. You can achieve this using a command like: <ul> <li> `set PATH=c:\tcl32\bin;%PATH%` </ul> ## Building a DLL The command the developers use for building the deliverable DLL on the [download page](https://sqlite.org/download.html) is as follows: > ~~~~ nmake /f Makefile.msc sqlite3.dll USE_NATIVE_LIBPATHS=1 "OPTS=-DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_JSON1=1 -DSQLITE_ENABLE_GEOPOLY=1 -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 -DSQLITE_ENABLE_SERIALIZE=1 -DSQLITE_ENABLE_MATH_FUNCTIONS=1" ~~~~ That command generates both the sqlite3.dll and sqlite3.def files. The same command works for both 32-bit and 64-bit builds. ## Statically Linking The TCL Library Some utility programs associated with SQLite need to be linked with TCL in order to function. The [sqlite3_analyzer.exe program](https://sqlite.org/sqlanalyze.html) is an example. You can build as described above, and then enter: |
︙ | ︙ |
Added doc/jsonb.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # The JSONB Format This document describes SQLite's JSONB binary encoding of JSON. ## 1.0 What Is JSONB? Beginning with version 3.45.0 (circa 2024-01-01), SQLite supports an alternative binary encoding of JSON which we call "JSONB". JSONB is a binary format that stored as a BLOB. The advantage of JSONB over ordinary text RFC 8259 JSON is that JSONB is both slightly smaller (by between 5% and 10% in most cases) and can be processed in less than half the number of CPU cycles. The built-in [JSON SQL functions] of SQLite can accept either ordinary text JSON or the binary JSONB encoding for any of their JSON inputs. The "JSONB" name is inspired by [PostgreSQL](https://postgresql.org), but the on-disk format for SQLite's JSONB is not the same as PostgreSQL's. The two formats have the same name, but they have wildly different internal representations and are not in any way binary compatible. The central idea behind this JSONB specification is that each element begins with a header that includes the size and type of that element. The header takes the place of punctuation such as double-quotes, curly-brackes, square-brackets, commas, and colons. Since the size and type of each element is contained in its header, the element can be read faster since it is no longer necessary to carefully scan forward looking for the closing delimiter. The payload of JSONB is the same as for corresponding text JSON. The same payload bytes occur in the same order. The only real difference between JSONB and ordinary text JSON is that JSONB includes a binary header on each element and omits delimiter and separator punctuation. ### 1.1 Internal Use Only The details of the JSONB are not intended to be visible to application developers. Application developers should look at JSONB as an opaque BLOB used internally by SQLite. Nevertheless, we want the format to be backwards compatible across all future versions of SQLite. To that end, the format is documented by this file in the source tree. But this file should be used only by SQLite core developers, not by developers of applications that only use SQLite. ## 2.0 The Purpose Of This Document JSONB is not intended as an external format to be used by applications. JSONB is designed for internal use by SQLite only. Programmers do not need to understand the JSONB format in order to use it effectively. Applications should access JSONB only through the [JSON SQL functions], not by looking at individual bytes of the BLOB. However, JSONB is intended to be portable and backwards compatible for all future versions of SQLite. In other words, you should not have to export and reimport your SQLite database files when you upgrade to a newer SQLite version. For that reason, the JSONB format needs to be well-defined. This document is therefore similar in purpose to the [SQLite database file format] document that describes the on-disk format of an SQLite database file. Applications are not expected to directly read and write the bits and bytes of SQLite database files. The SQLite database file format is carefully documented so that it can be stable and enduring. In the same way, the JSONB representation of JSON is documented here so that it too can be stable and enduring, not so that applications can read or writes individual bytes. ## 3.0 Encoding JSONB is a direct translation of the underlying text JSON. The difference is that JSONB uses a binary encoding that is faster to parse compared to the detailed syntax of text JSON. Each JSON element is encoded as a header and a payload. The header determines type of element (string, numeric, boolean, null, object, or array) and the size of the payload. The header can be between 1 and 9 bytes in size. The payload can be any size from zero bytes up to the maximum allowed BLOB size. ### 3.1 Payload Size The upper four bits of the first byte of the header determine size of the header and possibly also the size of the payload. If the upper four bits have a value between 0 and 11, then the header is exactly one byte in size and the payload size is determined by those upper four bits. If the upper four bits have a value between 12 and 15, that means that the total header size is 2, 3, 5, or 9 bytes and the payload size is unsigned big-endian integer that is contained in the subsequent bytes. The size integer is the one byte that following the initial header byte if the upper four bits are 12, two bytes if the upper bits are 13, four bytes if the upper bits are 14, and eight bytes if the upper bits are 15. The current design of SQLite does not support BLOB values larger than 2GiB, so the eight-byte variant of the payload size integer will never be used by the current code. The eight-byte payload size integer is included in the specification to allow for future expansion. The header for an element does *not* need to be in its simplest form. For example, consider the JSON numeric value "`1`". That element can be encode in five different ways: * `0x13 0x31` * `0xc3 0x01 0x31` * `0xd3 0x00 0x01 0x31` * `0xe3 0x00 0x00 0x00 0x01 0x31` * `0xf3 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x31` The shortest encoding is preferred, of course, and usually happens with primitive elements such as numbers. However the total size of an array or object might not be known exactly when the header of the element is first generated. It is convenient to reserve space for the largest possible header and then go back and fill in the correct payload size at the end. This technique can result in array or object headers that are larger than absolutely necessary. ### 3.2 Element Type The least-significant four bits of the first byte of the header (the first byte masked against 0x0f) determine element type. The following codes are used: <ol> <li type="0"><p><b>NULL</b> → The element is a JSON "null". The payload size for a true JSON NULL must must be zero. Future versions of SQLite might extend the JSONB format with elements that have a zero element type but a non-zero size. In that way, legacy versions of SQLite will interpret the element as a NULL for backwards compatibility while newer versions will interpret the element in some other way. <li value="1"><p><b>TRUE</b> → The element is a JSON "true". The payload size must be zero for a actual "true" value. Elements with type 1 and a non-zero payload size are reserved for future expansion. Legacy implementations that see an element type of 1 with a non-zero payload size should continue to interpret that element as "true" for compatibility. <li value="2"><p><b>FALSE</b> → The element is a JSON "false". The payload size must be zero for a actual "false" value. Elements with type 2 and a non-zero payload size are reserved for future expansion. Legacy implementations that see an element type of 2 with a non-zero payload size should continue to interpret that element as "false" for compatibility. <li value="3"><p><b>INT</b> → The element is a JSON integer value in the canonical RFC 8259 format, without extensions. The payload is the ASCII text representation of that numeric value. <li value="4"><p><b>INT5</b> → The element is a JSON integer value that is not in the canonical format. The payload is the ASCII text representation of that numeric value. Because the payload is in a non-standard format, it will need to be translated when the JSONB is converted into RFC 8259 text JSON. <li value="5"><p><b>FLOAT</b> → The element is a JSON floating-point value in the canonical RFC 8259 format, without extensions. The payload is the ASCII text representation of that numeric value. <li value="6"><p><b>FLOAT5</b> → The element is a JSON floating-point value that is not in the canonical format. The payload is the ASCII text representation of that numeric value. Because the payload is in a non-standard format, it will need to be translated when the JSONB is converted into RFC 8259 text JSON. <li value="7"><p><b>TEXT</b> → The element is a JSON string value that does not contain any escapes nor any characters that need to be escaped for either SQL or JSON. The payload is the UTF8 text representation of the string value. The payload does <i>not</i> include string delimiters. <li value="8"><p><b>TEXTJ</b> → The element is a JSON string value that contains RFC 8259 character escapes (such as "<tt>\n</tt>" or "<tt>\u0020</tt>"). Those escapes will need to be translated into actual UTF8 if this element is [json_extract|extracted] into SQL. The payload is the UTF8 text representation of the escaped string value. The payload does <i>not</i> include string delimiters. <li value="9"><p><b>TEXT5</b> → The element is a JSON string value that contains character escapes, including some character escapes that part of JSON5 and which are not found in the canonical RFC 8259 spec. Those escapes will need to be translated into standard JSON prior to rendering the JSON as text, or into their actual UTF8 characters if this element is [json_extract|extracted] into SQL. The payload is the UTF8 text representation of the escaped string value. The payload does <i>not</i> include string delimiters. <li value="10"><p><b>TEXTRAW</b> → The element is a JSON string value that contains UTF8 characters that need to be escaped if this string is rendered into standard JSON text. The payload does <i>not</i> include string delimiters. <li value="11"><p><b>ARRAY</b> → The element is a JSON array. The payload contains JSONB elements that comprise values contained within the array. <li value="12"><p><b>OBJECT</b> → The element is a JSON object. The payload contains pairs of JSONB elements that comprise entries for the JSON object. The first element in each pair must be a string (types 7 through 10). The second element of each pair may be any types, including nested arrays or objects. <li value="13"><p><b>RESERVED-13</b> → Reserved for future expansion. Legacy implements that encounter this element type should raise an error. <li value="14"><p><b>RESERVED-14</b> → Reserved for future expansion. Legacy implements that encounter this element type should raise an error. <li value="15"><p><b>RESERVED-15</b> → Reserved for future expansion. Legacy implements that encounter this element type should raise an error. </ol> Element types outside the range of 0 to 12 are reserved for future expansion. The current implement raises an error if see an element type other than those listed above. However, future versions of SQLite might use of the three remaining element types to implement indexing or similar optimizations, to speed up lookup against large JSON arrays and/or objects. ### 3.3 Design Rationale For Element Types A key goal of JSONB is that it should be quick to translate to and from text JSON and/or be constructed from SQL values. When converting from text into JSONB, we do not want the converter subroutine to burn CPU cycles converting elements values into some standard format which might never be used. Format conversion is "lazy" - it is deferred until actually needed. This has implications for the JSONB format design: 1. Numeric values are stored as text, not a numbers. The values are a direct copy of the text JSON values from which they are derived. 2. There are multiple element types depending on the details of value formats. For example, INT is used for pure RFC-8259 integer literals and INT5 exists for JSON5 extensions such as hexadecimal notation. FLOAT is used for pure RFC-8259 floating point literals and FLOAT5 is used for JSON5 extensions. There are four different representations of strings, depending on where the string came from and how special characters within the string are escaped. A second goal of JSONB is that it should be capable of serving as the "parse tree" for JSON when a JSON value is being processed by the various [JSON SQL functions] built into SQLite. Before JSONB was developed, operations such [json_replace()] and [json_patch()] and similar worked in three stages: 1. Translate the text JSON into a internal format that is easier to scan and edit. 2. Perform the requested operation on the JSON. 3. Translate the internal format back into text. JSONB seeks to serve as the internal format directly - bypassing the first and third stages of that process. Since most of the CPU cycles are spent on the first and third stages, that suggests that JSONB processing will be much faster than text JSON processing. So when processing JSONB, only the second stage of the three-stage process is required. But when processing text JSON, it is still necessary to do stages one and three. If JSONB is to be used as the internal binary representation, this is yet another reason to store numeric values as text. Storing numbers as text minimizes the amount of conversion work needed for stages one and three. This is also why there are four different representations of text in JSONB. Different text representations are used for text coming from different sources (RFC-8259 JSON, JSON5, or SQL string values) and conversions only happen if and when they are actually needed. ### 3.4 Valid JSONB BLOBs A valid JSONB BLOB consists of a single JSON element. The element must exactly fill the BLOB. This one element is often a JSON object or array and those usually contain additional elements as its payload, but the element can be a primite value such a string, number, boolean, or null. When the built-in JSON functions are attempting to determine if a BLOB argument is a JSONB or just a random BLOB, they look at the header of the outer element to see that it is well-formed and that the element completely fills the BLOB. If these conditions are met, then the BLOB is accepted as a JSONB value. |
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.
1 2 3 4 5 6 7 8 9 10 11 | # The testrunner.tcl Script # 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. | > > > > > > > > > > > > > > > > > > > | | > > > | > | > > | 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 | # The testrunner.tcl Script <ul type=none> <li> 1. <a href=#overview>Overview</a> <li> 2. <a href=#binary_tests>Binary Tests</a> <ul type=none> <li> 2.1. <a href=#organization_tests>Organization of Tcl Tests</a> <li> 2.2. <a href=#run_tests>Commands to Run Tests</a> <li> 2.3. <a href=#binary_test_failures>Investigating Binary Test Failures</a> </ul> <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' |
︙ | ︙ | |||
36 37 38 39 40 41 42 | ``` 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 86 87 88 89 90 91 92 93 94 95 96 97 | ``` 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. The following sub-sections describe the various options that can be passed to testrunner.tcl to test binary testfixture builds. <a name=organization_tests></a> ## 2.1. Organization of Tcl Tests Tcl tests are stored in files that match the pattern *\*.test*. They are found in both the $TOP/test/ directory, and in the various sub-directories of the $TOP/ext/ directory of the source tree. Not all *\*.test* files contain Tcl tests - a handful are Tcl scripts designed to invoke other *\*.test* files. |
︙ | ︙ | |||
87 88 89 90 91 92 93 94 95 96 97 98 99 100 | * Runtime configuration to apply before running each test script (e.g. enabling auto-vacuum, or disable lookaside). Running **all** tests is to run all tests in the full test set, plus a dozen or so permutations. The specific permutations that are run as part of "all" are defined in file *testrunner_data.tcl*. ## 2.2. Commands to Run Tests To run the "veryquick" test set, use either of the following: ``` ./testfixture $TESTDIR/testrunner.tcl ./testfixture $TESTDIR/testrunner.tcl veryquick | > | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | * Runtime configuration to apply before running each test script (e.g. enabling auto-vacuum, or disable lookaside). Running **all** tests is to run all tests in the full test set, plus a dozen or so permutations. The specific permutations that are run as part of "all" are defined in file *testrunner_data.tcl*. <a name=run_tests></a> ## 2.2. Commands to Run Tests To run the "veryquick" test set, use either of the following: ``` ./testfixture $TESTDIR/testrunner.tcl ./testfixture $TESTDIR/testrunner.tcl veryquick |
︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 | To run the subset of the "full" test suite for which the test file name matches a specified pattern (e.g. all tests that start with "fts5"), either of: ``` ./testfixture $TESTDIR/testrunner.tcl fts5% ./testfixture $TESTDIR/testrunner.tcl 'fts5*' ``` To run "all" tests (full + permutations): ``` ./testfixture $TESTDIR/testrunner.tcl all ``` | > > > > > > | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | To run the subset of the "full" test suite for which the test file name matches a specified pattern (e.g. all tests that start with "fts5"), either of: ``` ./testfixture $TESTDIR/testrunner.tcl fts5% ./testfixture $TESTDIR/testrunner.tcl 'fts5*' ``` Strictly speaking, for a test to be run the pattern must match the script filename, not including the directory, using the rules of Tcl's \[string match\] command. Except that before the matching is done, any "%" characters specified as part of the pattern are transformed to "\*". To run "all" tests (full + permutations): ``` ./testfixture $TESTDIR/testrunner.tcl all ``` |
︙ | ︙ | |||
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 | ``` ./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT ``` TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT? # 3. Source Code Tests The commands described in this section invoke the C compiler to build binaries from the source tree, then use those binaries to run Tcl and other tests. The advantages of this are that: * it is possible to test multiple build configurations with a single command, and * it ensures that tests are always run using binaries created with the same set of compiler options. The testrunner.tcl commands described in this section may be run using either a *testfixture* (or testfixture.exe) build, or with any other Tcl shell that supports SQLite 3.31.1 or newer via "package require sqlite3". TODO: ./configure + Makefile.msc build systems. | > > | | | 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 | ``` ./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT ``` TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT? <a name=source_code_tests></a> # 3. Source Code Tests The commands described in this section invoke the C compiler to build binaries from the source tree, then use those binaries to run Tcl and other tests. The advantages of this are that: * it is possible to test multiple build configurations with a single command, and * it ensures that tests are always run using binaries created with the same set of compiler options. The testrunner.tcl commands described in this section may be run using either a *testfixture* (or testfixture.exe) build, or with any other Tcl shell that supports SQLite 3.31.1 or newer via "package require sqlite3". 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: |
︙ | ︙ | |||
197 198 199 200 201 202 203 | on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details of the specific tests run. ``` tclsh $TESTDIR/testrunner.tcl release ``` | > > > > > > > > > > > | > | | 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 | on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details of the specific tests run. ``` tclsh $TESTDIR/testrunner.tcl release ``` As with <a href=#source code tests>source code tests</a>, one or more patterns may be appended to any of the above commands (mdevtest, sdevtest or release). In that case only Tcl tests (no fuzz or other tests) that match the specified pattern are run. For example, to run the just the Tcl rtree tests in all builds and configurations supported by "release": ``` tclsh $TESTDIR/testrunner.tcl release rtree% ``` <a name=zipvfs_tests></a> ## 3.2. Running ZipVFS Tests testrunner.tcl can build a zipvfs-enabled testfixture and use it to run tests from the Zipvfs project with the following command: ``` tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS ``` This can be combined with any of "mdevtest", "sdevtest" or "release" to test both SQLite and Zipvfs with a single command: ``` tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest ``` <a name=source_code_test_failures></a> ## 3.3. Investigating Source Code Test Failures Investigating a test failure that occurs during source code testing is a two step process: 1. Recreating the build configuration in which the test failed, and 2. Re-running the actual test. |
︙ | ︙ | |||
236 237 238 239 240 241 242 | 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 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 | 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 code and binary tests. The **--buildonly** option instructs testrunner.tcl just to build the binaries required by a test, not to run any actual tests. For example: ``` # Build binaries required by release test. tclsh $TESTDIR/testrunner.tcl --buildonly release" ``` The **--dryrun** option prevents testrunner.tcl from building any binaries 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. ``` $ ./testfixture $TESTDIR/testrunner.tcl splitting work across 16 jobs |
︙ | ︙ | |||
270 271 272 273 274 275 276 | 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 \ |
︙ | ︙ | |||
339 340 341 342 343 344 345 | } # endif /* Get stream info, either for designated output or error stream when ** chix equals 1 or 2, or for an arbitrary stream when chix == 0. ** In either case, ppst references a caller-owned PerStreamTags ** struct which may be filled in if none of the known writable | | | | | 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 | } # endif /* Get stream info, either for designated output or error stream when ** chix equals 1 or 2, or for an arbitrary stream when chix == 0. ** In either case, ppst references a caller-owned PerStreamTags ** struct which may be filled in if none of the known writable ** streams is being held by consoleInfo. The ppf parameter is a ** byref output when chix!=0 and a byref input when chix==0. */ static PerStreamTags * getEmitStreamInfo(unsigned chix, PerStreamTags *ppst, /* in/out */ FILE **ppf){ PerStreamTags *ppstTry; FILE *pfEmit; if( chix > 0 ){ ppstTry = &consoleInfo.pstDesignated[chix]; if( !isValidStreamInfo(ppstTry) ){ ppstTry = &consoleInfo.pstSetup[chix]; pfEmit = ppst->pf; }else pfEmit = ppstTry->pf; if( !isValidStreamInfo(ppstTry) ){ pfEmit = (chix > 1)? stderr : stdout; ppstTry = ppst; streamOfConsole(pfEmit, ppstTry); } *ppf = pfEmit; }else{ ppstTry = isKnownWritable(*ppf); |
︙ | ︙ | |||
545 546 547 548 549 550 551 | } } return z; } #endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/ #ifndef SQLITE_CIO_NO_TRANSLATE | < | | | | | | | 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 | } } return z; } #endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/ #ifndef SQLITE_CIO_NO_TRANSLATE # ifdef CONSIO_SPUTB SQLITE_INTERNAL_LINKAGE int fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){ assert(pfO!=0); # if CIO_WIN_WC_XLATE PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); if( pstReachesConsole(ppst) ){ int rv; maybeSetupAsConsole(ppst, 1); rv = conZstrEmit(ppst, cBuf, nAccept); if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); return rv; }else { # endif return (int)fwrite(cBuf, 1, nAccept, pfO); # if CIO_WIN_WC_XLATE } # endif } # endif SQLITE_INTERNAL_LINKAGE int oPutbUtf8(const char *cBuf, int nAccept){ FILE *pfOut; PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ # if CIO_WIN_WC_XLATE PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); |
︙ | ︙ | |||
672 673 674 675 676 677 678 | return fgets(cBuf, ncMax, pfIn); # if CIO_WIN_WC_XLATE } # endif } #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ | > > > | | 680 681 682 683 684 685 686 687 688 689 690 691 | 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.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 | ** Platform dependencies are "hidden" here by various stratagems so ** that, provided certain conditions are met, the programs using this ** source or object code compiled from it need no explicit conditional ** compilation in their source for their console and stream I/O. ** ** The symbols and functionality exposed here are not a public API. ** This code may change in tandem with other project code as needed. */ | > > > > > | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ** Platform dependencies are "hidden" here by various stratagems so ** that, provided certain conditions are met, the programs using this ** source or object code compiled from it need no explicit conditional ** compilation in their source for their console and stream I/O. ** ** The symbols and functionality exposed here are not a public API. ** 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 |
︙ | ︙ | |||
157 158 159 160 161 162 163 | ** accepted char or character sequence is limited by nAccept. ** ** Returns the number of accepted char values. */ #ifdef CONSIO_SPUTB SQLITE_INTERNAL_LINKAGE int fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); | < > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | ** accepted char or character sequence is limited by nAccept. ** ** Returns the number of accepted char values. */ #ifdef CONSIO_SPUTB SQLITE_INTERNAL_LINKAGE int fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated output. */ #endif SQLITE_INTERNAL_LINKAGE int oPutbUtf8(const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated error. */ #ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept); #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_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/extract_api_docs.tcl.
︙ | ︙ | |||
219 220 221 222 223 224 225 226 227 | fts5_extension { output [get_fts5_struct $data "typedef.*Fts5ExtensionApi" "^.;"] } Fts5ExtensionApi { set struct [get_fts5_struct $data "^struct Fts5ExtensionApi" "^.;"] set map [list] foreach {k v} [get_struct_members $data] { if {[string match x* $k]==0} continue | > | > | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | fts5_extension { output [get_fts5_struct $data "typedef.*Fts5ExtensionApi" "^.;"] } Fts5ExtensionApi { set struct [get_fts5_struct $data "^struct Fts5ExtensionApi" "^.;"] set map [list] set lKey [list] foreach {k v} [get_struct_members $data] { if {[string match x* $k]==0} continue lappend lKey $k } foreach k [lsort -decr $lKey] { lappend map $k "<a href=#$k>$k</a>" } output [string map $map $struct] } api { get_api_docs $data } |
︙ | ︙ |
Changes to ext/fts5/fts5.h.
︙ | ︙ | |||
84 85 86 87 88 89 90 | ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** This function may be quite inefficient if used with an FTS5 table ** created with the "columnsize=0" option. ** ** xColumnText: | > > > | | > > | | | > | | | | 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 | ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** This function may be quite inefficient if used with an FTS5 table ** created with the "columnsize=0" option. ** ** xColumnText: ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. ** ** Otherwise, this function attempts to retrieve the text of column iCol of ** the current document. If successful, (*pz) is set to point to a buffer ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, ** if an error occurs, an SQLite error code is returned and the final values ** of (*pz) and (*pn) are undefined. ** ** xPhraseCount: ** Returns the number of phrases in the current query expression. ** ** xPhraseSize: ** If parameter iCol is less than zero, or greater than or equal to the ** number of phrases in the current query, as returned by xPhraseCount, ** 0 is returned. Otherwise, this function returns the number of tokens in ** phrase iPhrase of the query. Phrases are numbered starting from zero. ** ** xInstCount: ** Set *pnInst to the total number of occurrences of all phrases within ** the query within the current row. Return SQLITE_OK if successful, or ** an error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. If the FTS5 table is created ** with either "detail=none" or "detail=column" and "content=" option ** (i.e. if it is a contentless table), then this API always returns 0. ** ** xInst: ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value ** output by xInstCount(). If iIdx is less than zero or greater than ** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. ** ** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the ** first token of the phrase. SQLITE_OK is returned if successful, or an ** error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xRowid: ** Returns the rowid of the current row. ** |
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 | ** current query is executed. Any column filter that applies to ** phrase iPhrase of the current query is included in $p. For each ** row visited, the callback function passed as the fourth argument ** is invoked. The context and API objects passed to the callback ** function may be used to access the properties of each matched row. ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. ** Otherwise, the error code is propagated upwards. ** ** If the query runs to completion without incident, SQLITE_OK is returned. | > > > > | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | ** current query is executed. Any column filter that applies to ** phrase iPhrase of the current query is included in $p. For each ** row visited, the callback function passed as the fourth argument ** is invoked. The context and API objects passed to the callback ** function may be used to access the properties of each matched row. ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** ** If parameter iPhrase is less than zero, or greater than or equal to ** the number of phrases in the query, as returned by xPhraseCount(), ** this function returns SQLITE_RANGE. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. ** Otherwise, the error code is propagated upwards. ** ** If the query runs to completion without incident, SQLITE_OK is returned. |
︙ | ︙ | |||
257 258 259 260 261 262 263 264 265 | ** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext ** (or xInst/xInstCount). The chief advantage of this API is that it is ** significantly more efficient than those alternatives when used with ** "detail=column" tables. ** ** xPhraseNextColumn() ** See xPhraseFirstColumn above. */ struct Fts5ExtensionApi { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | ** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext ** (or xInst/xInstCount). The chief advantage of this API is that it is ** significantly more efficient than those alternatives when used with ** "detail=column" tables. ** ** xPhraseNextColumn() ** See xPhraseFirstColumn above. ** ** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase iPhrase of the current ** query. Before returning, output parameter *ppToken is set to point ** to a buffer containing the requested token, and *pnToken to the ** size of this buffer in bytes. ** ** If iPhrase or iToken are less than zero, or if iPhrase is greater than ** or equal to the number of phrases in the query as reported by ** xPhraseCount(), or if iToken is equal to or greater than the number of ** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken are both zeroed. ** ** The output text is not a copy of the query text that specified the ** token. It is the output of the tokenizer module. For tokendata=1 ** tables, this includes any embedded 0x00 and trailing data. ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in ** bytes. This API is not available if the specified token matches a ** prefix query term. In that case both output variables are always set ** to 0. ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { int iVersion; /* Currently always set to 3 */ void *(*xUserData)(Fts5Context*); int (*xColumnCount)(Fts5Context*); int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken); |
︙ | ︙ | |||
294 295 296 297 298 299 300 301 302 303 304 305 306 307 | void *(*xGetAuxdata)(Fts5Context*, int bClear); int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* | > > > > > > > | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | void *(*xGetAuxdata)(Fts5Context*, int bClear); int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); /* Below this point are iVersion>=3 only */ int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* |
︙ | ︙ |
Changes to ext/fts5/fts5Int.h.
︙ | ︙ | |||
192 193 194 195 196 197 198 199 200 201 202 203 204 205 | int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; Fts5Tokenizer *pTok; fts5_tokenizer *pTokApi; int bLock; /* True when table is preparing statement */ int ePattern; /* FTS_PATTERN_XXX constant */ | > | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int bTokendata; /* "tokendata=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; Fts5Tokenizer *pTok; fts5_tokenizer *pTokApi; int bLock; /* True when table is preparing statement */ int ePattern; /* FTS_PATTERN_XXX constant */ |
︙ | ︙ | |||
380 381 382 383 384 385 386 | }; #define sqlite3Fts5IterEof(x) ((x)->bEof) /* ** Values used as part of the flags argument passed to IndexQuery(). */ | | | | | | | | > > | 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 | }; #define sqlite3Fts5IterEof(x) ((x)->bEof) /* ** Values used as part of the flags argument passed to IndexQuery(). */ #define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ #define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ #define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ #define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ /* The following are used internally by the fts5_index.c module. They are ** defined here only to make it easier to avoid clashes with the flags ** above. */ #define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 #define FTS5INDEX_QUERY_NOOUTPUT 0x0020 #define FTS5INDEX_QUERY_SKIPHASH 0x0040 #define FTS5INDEX_QUERY_NOTOKENDATA 0x0080 #define FTS5INDEX_QUERY_SCANONETERM 0x0100 /* ** Create/destroy an Fts5Index object. */ int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); int sqlite3Fts5IndexClose(Fts5Index *p); |
︙ | ︙ | |||
459 460 461 462 463 464 465 466 467 468 469 470 471 472 | */ const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*); int sqlite3Fts5IterNextScan(Fts5IndexIter*); void *sqlite3Fts5StructureRef(Fts5Index*); void sqlite3Fts5StructureRelease(void*); int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Insert or remove data to or from the index. Each time a document is ** added to or removed from the index, this function is called one or more ** times. ** ** For an insert, it must be called once for each token in the new document. | > > > > | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | */ const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*); int sqlite3Fts5IterNextScan(Fts5IndexIter*); void *sqlite3Fts5StructureRef(Fts5Index*); void sqlite3Fts5StructureRelease(void*); int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Used by xInstToken(): */ int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*); /* ** Insert or remove data to or from the index. Each time a document is ** added to or removed from the index, this function is called one or more ** times. ** ** For an insert, it must be called once for each token in the new document. |
︙ | ︙ | |||
535 536 537 538 539 540 541 542 543 544 545 546 547 548 | int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge); int sqlite3Fts5IndexReset(Fts5Index *p); int sqlite3Fts5IndexLoadConfig(Fts5Index *p); int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); /* ** End of interface to code in fts5_index.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_varint.c. | > > > > > > > | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 | int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge); int sqlite3Fts5IndexReset(Fts5Index *p); int sqlite3Fts5IndexLoadConfig(Fts5Index *p); int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); /* Used to populate hash tables for xInstToken in detail=none/column mode. */ int sqlite3Fts5IndexIterWriteTokendata( Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff ); /* ** End of interface to code in fts5_index.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_varint.c. |
︙ | ︙ | |||
641 642 643 644 645 646 647 648 649 650 651 652 653 654 | Fts5Hash*, /* Hash table to query */ const char *pTerm, int nTerm /* Query prefix */ ); void sqlite3Fts5HashScanNext(Fts5Hash*); int sqlite3Fts5HashScanEof(Fts5Hash*); void sqlite3Fts5HashScanEntry(Fts5Hash *, const char **pzTerm, /* OUT: term (nul-terminated) */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ); /* | > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 | Fts5Hash*, /* Hash table to query */ const char *pTerm, int nTerm /* Query prefix */ ); void sqlite3Fts5HashScanNext(Fts5Hash*); int sqlite3Fts5HashScanEof(Fts5Hash*); void sqlite3Fts5HashScanEntry(Fts5Hash *, const char **pzTerm, /* OUT: term (nul-terminated) */ int *pnTerm, /* OUT: Size of term in bytes */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ); /* |
︙ | ︙ | |||
766 767 768 769 770 771 772 773 774 775 776 777 778 779 | Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int ); void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64); int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); /******************************************* ** The fts5_expr.c API above this point is used by the other hand-written ** C code in this module. The interfaces below this point are called by ** the parser code in fts5parse.y. */ void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...); | > > > > | 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 | Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int ); void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64); int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*); int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*); void sqlite3Fts5ExprClearTokens(Fts5Expr*); /******************************************* ** The fts5_expr.c API above this point is used by the other hand-written ** C code in this module. The interfaces below this point are called by ** the parser code in fts5parse.y. */ void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...); |
︙ | ︙ |
Changes to ext/fts5/fts5_aux.c.
︙ | ︙ | |||
207 208 209 210 211 212 213 214 215 216 217 218 219 220 | if( rc==SQLITE_OK ){ rc = fts5CInstIterNext(&p->iter); } } if( iPos==p->iRangeEnd ){ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); p->iOff = iEndOff; } return rc; } | > > > > > > > > | 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | if( rc==SQLITE_OK ){ rc = fts5CInstIterNext(&p->iter); } } if( iPos==p->iRangeEnd ){ if( p->bOpen ){ if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); p->iOff = iEndOff; } fts5HighlightAppend(&rc, p, p->zClose, -1); p->bOpen = 0; } fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); p->iOff = iEndOff; } return rc; } |
︙ | ︙ | |||
240 241 242 243 244 245 246 | iCol = sqlite3_value_int(apVal[0]); memset(&ctx, 0, sizeof(HighlightContext)); ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ctx.iRangeEnd = -1; rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); | | > > | | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | iCol = sqlite3_value_int(apVal[0]); memset(&ctx, 0, sizeof(HighlightContext)); ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ctx.iRangeEnd = -1; rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); if( rc==SQLITE_RANGE ){ sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); rc = SQLITE_OK; }else if( ctx.zIn ){ if( rc==SQLITE_OK ){ rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); } if( rc==SQLITE_OK ){ rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); } |
︙ | ︙ |
Changes to ext/fts5/fts5_buffer.c.
︙ | ︙ | |||
64 65 66 67 68 69 70 71 72 73 74 75 76 77 | int *pRc, Fts5Buffer *pBuf, u32 nData, const u8 *pData ){ if( nData ){ if( fts5BufferGrow(pRc, pBuf, nData) ) return; memcpy(&pBuf->p[pBuf->n], pData, nData); pBuf->n += nData; } } /* ** Append the nul-terminated string zStr to the buffer pBuf. This function | > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | int *pRc, Fts5Buffer *pBuf, u32 nData, const u8 *pData ){ if( nData ){ if( fts5BufferGrow(pRc, pBuf, nData) ) return; assert( pBuf->p!=0 ); memcpy(&pBuf->p[pBuf->n], pData, nData); pBuf->n += nData; } } /* ** Append the nul-terminated string zStr to the buffer pBuf. This function |
︙ | ︙ | |||
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | int sqlite3Fts5PoslistNext64( const u8 *a, int n, /* Buffer containing poslist */ int *pi, /* IN/OUT: Offset within a[] */ i64 *piOff /* IN/OUT: Current offset */ ){ int i = *pi; if( i>=n ){ /* EOF */ *piOff = -1; return 1; }else{ i64 iOff = *piOff; u32 iVal; fts5FastGetVarint32(a, i, iVal); if( iVal<=1 ){ if( iVal==0 ){ *pi = i; return 0; } fts5FastGetVarint32(a, i, iVal); | > > | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | int sqlite3Fts5PoslistNext64( const u8 *a, int n, /* Buffer containing poslist */ int *pi, /* IN/OUT: Offset within a[] */ i64 *piOff /* IN/OUT: Current offset */ ){ int i = *pi; assert( a!=0 || i==0 ); if( i>=n ){ /* EOF */ *piOff = -1; return 1; }else{ i64 iOff = *piOff; u32 iVal; assert( a!=0 ); fts5FastGetVarint32(a, i, iVal); if( iVal<=1 ){ if( iVal==0 ){ *pi = i; return 0; } fts5FastGetVarint32(a, i, iVal); |
︙ | ︙ |
Changes to ext/fts5/fts5_config.c.
︙ | ︙ | |||
393 394 395 396 397 398 399 400 401 402 403 404 405 406 | }; if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){ *pzErr = sqlite3_mprintf("malformed detail=... directive"); } return rc; } *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); return SQLITE_ERROR; } /* ** Allocate an instance of the default tokenizer ("simple") at | > > > > > > > > > > | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | }; if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){ *pzErr = sqlite3_mprintf("malformed detail=... directive"); } return rc; } if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ *pzErr = sqlite3_mprintf("malformed tokendata=... directive"); rc = SQLITE_ERROR; }else{ pConfig->bTokendata = (zArg[0]=='1'); } return rc; } *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); return SQLITE_ERROR; } /* ** Allocate an instance of the default tokenizer ("simple") at |
︙ | ︙ |
Changes to ext/fts5/fts5_expr.c.
︙ | ︙ | |||
96 97 98 99 100 101 102 | /* ** An instance of the following structure represents a single search term ** or term prefix. */ struct Fts5ExprTerm { u8 bPrefix; /* True for a prefix term */ u8 bFirst; /* True if token must be first in column */ | | > > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | /* ** An instance of the following structure represents a single search term ** or term prefix. */ struct Fts5ExprTerm { u8 bPrefix; /* True for a prefix term */ u8 bFirst; /* True if token must be first in column */ char *pTerm; /* Term data */ int nQueryTerm; /* Effective size of term in bytes */ int nFullTerm; /* Size of term in bytes incl. tokendata */ Fts5IndexIter *pIter; /* Iterator for this term */ Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */ }; /* ** A phrase. One or more terms that must appear in a contiguous sequence ** within a document for it to match. |
︙ | ︙ | |||
963 964 965 966 967 968 969 | for(p=pTerm; p; p=p->pSynonym){ int rc; if( p->pIter ){ sqlite3Fts5IterClose(p->pIter); p->pIter = 0; } rc = sqlite3Fts5IndexQuery( | | | 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 | for(p=pTerm; p; p=p->pSynonym){ int rc; if( p->pIter ){ sqlite3Fts5IterClose(p->pIter); p->pIter = 0; } rc = sqlite3Fts5IndexQuery( pExpr->pIndex, p->pTerm, p->nQueryTerm, (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0), pNear->pColset, &p->pIter ); assert( (rc==SQLITE_OK)==(p->pIter!=0) ); if( rc!=SQLITE_OK ) return rc; |
︙ | ︙ | |||
1600 1601 1602 1603 1604 1605 1606 | static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){ if( pPhrase ){ int i; for(i=0; i<pPhrase->nTerm; i++){ Fts5ExprTerm *pSyn; Fts5ExprTerm *pNext; Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; | | | 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 | static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){ if( pPhrase ){ int i; for(i=0; i<pPhrase->nTerm; i++){ Fts5ExprTerm *pSyn; Fts5ExprTerm *pNext; Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; sqlite3_free(pTerm->pTerm); sqlite3Fts5IterClose(pTerm->pIter); for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){ pNext = pSyn->pSynonym; sqlite3Fts5IterClose(pSyn->pIter); fts5BufferFree((Fts5Buffer*)&pSyn[1]); sqlite3_free(pSyn); } |
︙ | ︙ | |||
1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 | } return pRet; } typedef struct TokenCtx TokenCtx; struct TokenCtx { Fts5ExprPhrase *pPhrase; int rc; }; /* ** Callback for tokenizing terms used by ParseTerm(). */ static int fts5ParseTokenize( | > | 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 | } return pRet; } typedef struct TokenCtx TokenCtx; struct TokenCtx { Fts5ExprPhrase *pPhrase; Fts5Config *pConfig; int rc; }; /* ** Callback for tokenizing terms used by ParseTerm(). */ static int fts5ParseTokenize( |
︙ | ︙ | |||
1731 1732 1733 1734 1735 1736 1737 | Fts5ExprTerm *pSyn; sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1; pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte); if( pSyn==0 ){ rc = SQLITE_NOMEM; }else{ memset(pSyn, 0, (size_t)nByte); | | > > > > | | 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 | Fts5ExprTerm *pSyn; sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1; pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte); if( pSyn==0 ){ rc = SQLITE_NOMEM; }else{ memset(pSyn, 0, (size_t)nByte); pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); pSyn->nFullTerm = pSyn->nQueryTerm = nToken; if( pCtx->pConfig->bTokendata ){ pSyn->nQueryTerm = (int)strlen(pSyn->pTerm); } memcpy(pSyn->pTerm, pToken, nToken); pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn; } }else{ Fts5ExprTerm *pTerm; if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ Fts5ExprPhrase *pNew; |
︙ | ︙ | |||
1757 1758 1759 1760 1761 1762 1763 | pNew->nTerm = nNew - SZALLOC; } } if( rc==SQLITE_OK ){ pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; memset(pTerm, 0, sizeof(Fts5ExprTerm)); | | > > > > | 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 | pNew->nTerm = nNew - SZALLOC; } } if( rc==SQLITE_OK ){ pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; memset(pTerm, 0, sizeof(Fts5ExprTerm)); pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); pTerm->nFullTerm = pTerm->nQueryTerm = nToken; if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){ pTerm->nQueryTerm = (int)strlen(pTerm->pTerm); } } } pCtx->rc = rc; return rc; } |
︙ | ︙ | |||
1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 | Fts5Config *pConfig = pParse->pConfig; TokenCtx sCtx; /* Context object passed to callback */ int rc; /* Tokenize return code */ char *z = 0; memset(&sCtx, 0, sizeof(TokenCtx)); sCtx.pPhrase = pAppend; rc = fts5ParseStringFromToken(pToken, &z); if( rc==SQLITE_OK ){ int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_PREFIX : 0); int n; sqlite3Fts5Dequote(z); n = (int)strlen(z); | > | 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 | Fts5Config *pConfig = pParse->pConfig; TokenCtx sCtx; /* Context object passed to callback */ int rc; /* Tokenize return code */ char *z = 0; memset(&sCtx, 0, sizeof(TokenCtx)); sCtx.pPhrase = pAppend; sCtx.pConfig = pConfig; rc = fts5ParseStringFromToken(pToken, &z); if( rc==SQLITE_OK ){ int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_PREFIX : 0); int n; sqlite3Fts5Dequote(z); n = (int)strlen(z); |
︙ | ︙ | |||
1871 1872 1873 1874 1875 1876 1877 | */ int sqlite3Fts5ExprClonePhrase( Fts5Expr *pExpr, int iPhrase, Fts5Expr **ppNew ){ int rc = SQLITE_OK; /* Return code */ | | | | > > | | > | > | | > | | | | < | < | | | | | | | | | | | > | 1883 1884 1885 1886 1887 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 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 | */ int sqlite3Fts5ExprClonePhrase( Fts5Expr *pExpr, int iPhrase, Fts5Expr **ppNew ){ int rc = SQLITE_OK; /* Return code */ Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */ Fts5Expr *pNew = 0; /* Expression to return via *ppNew */ TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ rc = SQLITE_RANGE; }else{ pOrig = pExpr->apExprPhrase[iPhrase]; pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); } if( rc==SQLITE_OK ){ pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase*)); } if( rc==SQLITE_OK ){ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprNode)); } if( rc==SQLITE_OK ){ pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); } if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; if( pColsetOrig ){ sqlite3_int64 nByte; Fts5Colset *pColset; nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); if( pColset ){ memcpy(pColset, pColsetOrig, (size_t)nByte); } pNew->pRoot->pNear->pColset = pColset; } } if( rc==SQLITE_OK ){ if( pOrig->nTerm ){ int i; /* Used to iterate through phrase terms */ sCtx.pConfig = pExpr->pConfig; for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){ int tflags = 0; Fts5ExprTerm *p; for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0); tflags = FTS5_TOKEN_COLOCATED; } if( rc==SQLITE_OK ){ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst; } } }else{ /* This happens when parsing a token or quoted phrase that contains ** no token characters at all. (e.g ... MATCH '""'). */ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); } } if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ /* All the allocations succeeded. Put the expression object together. */ pNew->pIndex = pExpr->pIndex; pNew->pConfig = pExpr->pConfig; pNew->nPhrase = 1; |
︙ | ︙ | |||
2292 2293 2294 2295 2296 2297 2298 2299 2300 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( &pParse->rc, sizeof(Fts5ExprPhrase) ); if( pPhrase ){ if( parseGrowPhraseArray(pParse) ){ fts5ExprPhraseFree(pPhrase); }else{ pParse->apPhrase[pParse->nPhrase++] = pPhrase; pPhrase->nTerm = 1; | > > | < < > > | 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( &pParse->rc, sizeof(Fts5ExprPhrase) ); if( pPhrase ){ if( parseGrowPhraseArray(pParse) ){ fts5ExprPhraseFree(pPhrase); }else{ Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii]; Fts5ExprTerm *pTo = &pPhrase->aTerm[0]; pParse->apPhrase[pParse->nPhrase++] = pPhrase; pPhrase->nTerm = 1; pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm); pTo->nQueryTerm = p->nQueryTerm; pTo->nFullTerm = p->nFullTerm; pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase) ); } } } |
︙ | ︙ | |||
2481 2482 2483 2484 2485 2486 2487 | static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ sqlite3_int64 nByte = 0; Fts5ExprTerm *p; char *zQuoted; /* Determine the maximum amount of space required. */ for(p=pTerm; p; p=p->pSynonym){ | | | > | | 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 | static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ sqlite3_int64 nByte = 0; Fts5ExprTerm *p; char *zQuoted; /* Determine the maximum amount of space required. */ for(p=pTerm; p; p=p->pSynonym){ nByte += pTerm->nQueryTerm * 2 + 3 + 2; } zQuoted = sqlite3_malloc64(nByte); if( zQuoted ){ int i = 0; for(p=pTerm; p; p=p->pSynonym){ char *zIn = p->pTerm; char *zEnd = &zIn[p->nQueryTerm]; zQuoted[i++] = '"'; while( zIn<zEnd ){ if( *zIn=='"' ) zQuoted[i++] = '"'; zQuoted[i++] = *zIn++; } zQuoted[i++] = '"'; if( p->pSynonym ) zQuoted[i++] = '|'; } if( pTerm->bPrefix ){ |
︙ | ︙ | |||
2568 2569 2570 2571 2572 2573 2574 | if( zRet==0 ) return 0; for(i=0; i<pNear->nPhrase; i++){ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; zRet = fts5PrintfAppend(zRet, " {"); for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){ | | | > > | 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 | if( zRet==0 ) return 0; for(i=0; i<pNear->nPhrase; i++){ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; zRet = fts5PrintfAppend(zRet, " {"); for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){ Fts5ExprTerm *p = &pPhrase->aTerm[iTerm]; zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ", p->nQueryTerm, p->pTerm ); if( pPhrase->aTerm[iTerm].bPrefix ){ zRet = fts5PrintfAppend(zRet, "*"); } } if( zRet ) zRet = fts5PrintfAppend(zRet, "}"); if( zRet==0 ) return 0; |
︙ | ︙ | |||
2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 | static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ int i; for(i=0; i<pColset->nCol; i++){ if( pColset->aiCol[i]==iCol ) return 1; } return 0; } static int fts5ExprPopulatePoslistsCb( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iUnused1, /* Byte offset of token within input text */ int iUnused2 /* Byte offset of end of token within input text */ ){ Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx; Fts5Expr *pExpr = p->pExpr; int i; UNUSED_PARAM2(iUnused1, iUnused2); | > > > > > > > > > > > > > | > > > | | < | | > > > > > > > | 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 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 | static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ int i; for(i=0; i<pColset->nCol; i++){ if( pColset->aiCol[i]==iCol ) return 1; } return 0; } /* ** pToken is a buffer nToken bytes in size that may or may not contain ** an embedded 0x00 byte. If it does, return the number of bytes in ** the buffer before the 0x00. If it does not, return nToken. */ static int fts5QueryTerm(const char *pToken, int nToken){ int ii; for(ii=0; ii<nToken && pToken[ii]; ii++){} return ii; } static int fts5ExprPopulatePoslistsCb( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iUnused1, /* Byte offset of token within input text */ int iUnused2 /* Byte offset of end of token within input text */ ){ Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx; Fts5Expr *pExpr = p->pExpr; int i; int nQuery = nToken; i64 iRowid = pExpr->pRoot->iRowid; UNUSED_PARAM2(iUnused1, iUnused2); if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE; if( pExpr->pConfig->bTokendata ){ nQuery = fts5QueryTerm(pToken, nQuery); } if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; for(i=0; i<pExpr->nPhrase; i++){ Fts5ExprTerm *pT; if( p->aPopulator[i].bOk==0 ) continue; for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix)) && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 ){ int rc = sqlite3Fts5PoslistWriterAppend( &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff ); if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){ int iCol = p->iOff>>32; int iTokOff = p->iOff & 0x7FFFFFFF; rc = sqlite3Fts5IndexIterWriteTokendata( pT->pIter, pToken, nToken, iRowid, iCol, iTokOff ); } if( rc ) return rc; break; } } } return SQLITE_OK; } |
︙ | ︙ | |||
3131 3132 3133 3134 3135 3136 3137 | }else{ *ppCollist = 0; *pnCollist = 0; } return rc; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 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 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 3255 3256 3257 | }else{ *ppCollist = 0; *pnCollist = 0; } return rc; } /* ** Does the work of the fts5_api.xQueryToken() API method. */ int sqlite3Fts5ExprQueryToken( Fts5Expr *pExpr, int iPhrase, int iToken, const char **ppOut, int *pnOut ){ Fts5ExprPhrase *pPhrase = 0; if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ return SQLITE_RANGE; } pPhrase = pExpr->apExprPhrase[iPhrase]; if( iToken<0 || iToken>=pPhrase->nTerm ){ return SQLITE_RANGE; } *ppOut = pPhrase->aTerm[iToken].pTerm; *pnOut = pPhrase->aTerm[iToken].nFullTerm; return SQLITE_OK; } /* ** Does the work of the fts5_api.xInstToken() API method. */ int sqlite3Fts5ExprInstToken( Fts5Expr *pExpr, i64 iRowid, int iPhrase, int iCol, int iOff, int iToken, const char **ppOut, int *pnOut ){ Fts5ExprPhrase *pPhrase = 0; Fts5ExprTerm *pTerm = 0; int rc = SQLITE_OK; if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ return SQLITE_RANGE; } pPhrase = pExpr->apExprPhrase[iPhrase]; if( iToken<0 || iToken>=pPhrase->nTerm ){ return SQLITE_RANGE; } pTerm = &pPhrase->aTerm[iToken]; if( pTerm->bPrefix==0 ){ if( pExpr->pConfig->bTokendata ){ rc = sqlite3Fts5IterToken( pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut ); }else{ *ppOut = pTerm->pTerm; *pnOut = pTerm->nFullTerm; } } return rc; } /* ** Clear the token mappings for all Fts5IndexIter objects mannaged by ** the expression passed as the only argument. */ void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ int ii; for(ii=0; ii<pExpr->nPhrase; ii++){ Fts5ExprTerm *pT; for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){ sqlite3Fts5IndexIterClearTokendata(pT->pIter); } } } |
Changes to ext/fts5/fts5_hash.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | int nSlot; /* Size of aSlot[] array */ Fts5HashEntry *pScan; /* Current ordered scan item */ Fts5HashEntry **aSlot; /* Array of hash slots */ }; /* ** Each entry in the hash table is represented by an object of the | | | | | > > > > > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | int nSlot; /* Size of aSlot[] array */ Fts5HashEntry *pScan; /* Current ordered scan item */ Fts5HashEntry **aSlot; /* Array of hash slots */ }; /* ** Each entry in the hash table is represented by an object of the ** following type. Each object, its key, and its current data are stored ** in a single memory allocation. The key immediately follows the object ** in memory. The position list data immediately follows the key data ** in memory. ** ** The key is Fts5HashEntry.nKey bytes in size. It consists of a single ** byte identifying the index (either the main term index or a prefix-index), ** followed by the term data. For example: "0token". There is no ** nul-terminator - in this case nKey=6. ** ** The data that follows the key is in a similar, but not identical format ** to the doclist data stored in the database. It is: ** ** * Rowid, as a varint ** * Position list, without 0x00 terminator. ** * Size of previous position list and rowid, as a 4 byte |
︙ | ︙ | |||
170 171 172 173 174 175 176 | memset(apNew, 0, nNew*sizeof(Fts5HashEntry*)); for(i=0; i<pHash->nSlot; i++){ while( apOld[i] ){ unsigned int iHash; Fts5HashEntry *p = apOld[i]; apOld[i] = p->pHashNext; | | < | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | memset(apNew, 0, nNew*sizeof(Fts5HashEntry*)); for(i=0; i<pHash->nSlot; i++){ while( apOld[i] ){ unsigned int iHash; Fts5HashEntry *p = apOld[i]; apOld[i] = p->pHashNext; iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey); p->pHashNext = apNew[iHash]; apNew[iHash] = p; } } sqlite3_free(apOld); pHash->nSlot = nNew; |
︙ | ︙ | |||
255 256 257 258 259 260 261 | bNew = (pHash->eDetail==FTS5_DETAIL_FULL); /* Attempt to locate an existing hash entry */ iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ char *zKey = fts5EntryKey(p); if( zKey[0]==bByte | | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | bNew = (pHash->eDetail==FTS5_DETAIL_FULL); /* Attempt to locate an existing hash entry */ iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ char *zKey = fts5EntryKey(p); if( zKey[0]==bByte && p->nKey==nToken+1 && memcmp(&zKey[1], pToken, nToken)==0 ){ break; } } /* If an existing hash entry cannot be found, create a new one. */ |
︙ | ︙ | |||
285 286 287 288 289 290 291 | if( !p ) return SQLITE_NOMEM; memset(p, 0, sizeof(Fts5HashEntry)); p->nAlloc = (int)nByte; zKey = fts5EntryKey(p); zKey[0] = bByte; memcpy(&zKey[1], pToken, nToken); assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) ); | | | | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | if( !p ) return SQLITE_NOMEM; memset(p, 0, sizeof(Fts5HashEntry)); p->nAlloc = (int)nByte; zKey = fts5EntryKey(p); zKey[0] = bByte; memcpy(&zKey[1], pToken, nToken); assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) ); p->nKey = nToken+1; zKey[nToken+1] = '\0'; p->nData = nToken+1 + sizeof(Fts5HashEntry); p->pHashNext = pHash->aSlot[iHash]; pHash->aSlot[iHash] = p; pHash->nEntry++; /* Add the first rowid field to the hash-entry */ p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid); p->iRowid = iRowid; |
︙ | ︙ | |||
404 405 406 407 408 409 410 | if( p1==0 ){ *ppOut = p2; p2 = 0; }else if( p2==0 ){ *ppOut = p1; p1 = 0; }else{ | < > | > > > | > | > | 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 | if( p1==0 ){ *ppOut = p2; p2 = 0; }else if( p2==0 ){ *ppOut = p1; p1 = 0; }else{ char *zKey1 = fts5EntryKey(p1); char *zKey2 = fts5EntryKey(p2); int nMin = MIN(p1->nKey, p2->nKey); int cmp = memcmp(zKey1, zKey2, nMin); if( cmp==0 ){ cmp = p1->nKey - p2->nKey; } assert( cmp!=0 ); if( cmp>0 ){ /* p2 is smaller */ *ppOut = p2; ppOut = &p2->pScanNext; p2 = p2->pScanNext; }else{ /* p1 is smaller */ *ppOut = p1; |
︙ | ︙ | |||
451 452 453 454 455 456 457 | if( !ap ) return SQLITE_NOMEM; memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot); for(iSlot=0; iSlot<pHash->nSlot; iSlot++){ Fts5HashEntry *pIter; for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ if( pTerm==0 | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | if( !ap ) return SQLITE_NOMEM; memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot); for(iSlot=0; iSlot<pHash->nSlot; iSlot++){ Fts5HashEntry *pIter; for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ if( pTerm==0 || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) ){ Fts5HashEntry *pEntry = pIter; pEntry->pScanNext = 0; for(i=0; ap[i]; i++){ pEntry = fts5HashEntryMerge(pEntry, ap[i]); ap[i] = 0; } |
︙ | ︙ | |||
490 491 492 493 494 495 496 | ){ unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm); char *zKey = 0; Fts5HashEntry *p; for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ zKey = fts5EntryKey(p); | < | | | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | ){ unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm); char *zKey = 0; Fts5HashEntry *p; for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ zKey = fts5EntryKey(p); if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break; } if( p ){ int nHashPre = sizeof(Fts5HashEntry) + nTerm; int nList = p->nData - nHashPre; u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10)); if( pRet ){ Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre]; memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList); nList += fts5HashAddPoslistSize(pHash, p, pFaux); *pnDoclist = nList; |
︙ | ︙ | |||
556 557 558 559 560 561 562 563 564 565 566 567 568 | int sqlite3Fts5HashScanEof(Fts5Hash *p){ return (p->pScan==0); } void sqlite3Fts5HashScanEntry( Fts5Hash *pHash, const char **pzTerm, /* OUT: term (nul-terminated) */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ){ Fts5HashEntry *p; if( (p = pHash->pScan) ){ char *zKey = fts5EntryKey(p); | > | > | | > | 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 | int sqlite3Fts5HashScanEof(Fts5Hash *p){ return (p->pScan==0); } void sqlite3Fts5HashScanEntry( Fts5Hash *pHash, const char **pzTerm, /* OUT: term (nul-terminated) */ int *pnTerm, /* OUT: Size of term in bytes */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ){ Fts5HashEntry *p; if( (p = pHash->pScan) ){ char *zKey = fts5EntryKey(p); int nTerm = p->nKey; fts5HashAddPoslistSize(pHash, p, 0); *pzTerm = zKey; *pnTerm = nTerm; *ppDoclist = (const u8*)&zKey[nTerm]; *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm); }else{ *pzTerm = 0; *pnTerm = 0; *ppDoclist = 0; *pnDoclist = 0; } } |
Changes to ext/fts5/fts5_index.c.
︙ | ︙ | |||
319 320 321 322 323 324 325 326 327 328 329 330 331 332 | typedef struct Fts5PageWriter Fts5PageWriter; typedef struct Fts5SegIter Fts5SegIter; typedef struct Fts5DoclistIter Fts5DoclistIter; typedef struct Fts5SegWriter Fts5SegWriter; typedef struct Fts5Structure Fts5Structure; typedef struct Fts5StructureLevel Fts5StructureLevel; typedef struct Fts5StructureSegment Fts5StructureSegment; struct Fts5Data { u8 *p; /* Pointer to buffer containing record */ int nn; /* Size of record in bytes */ int szLeaf; /* Size of leaf without page-index */ }; | > > > | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | typedef struct Fts5PageWriter Fts5PageWriter; typedef struct Fts5SegIter Fts5SegIter; typedef struct Fts5DoclistIter Fts5DoclistIter; typedef struct Fts5SegWriter Fts5SegWriter; typedef struct Fts5Structure Fts5Structure; typedef struct Fts5StructureLevel Fts5StructureLevel; typedef struct Fts5StructureSegment Fts5StructureSegment; typedef struct Fts5TokenDataIter Fts5TokenDataIter; typedef struct Fts5TokenDataMap Fts5TokenDataMap; typedef struct Fts5TombstoneArray Fts5TombstoneArray; struct Fts5Data { u8 *p; /* Pointer to buffer containing record */ int nn; /* Size of record in bytes */ int szLeaf; /* Size of leaf without page-index */ }; |
︙ | ︙ | |||
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | i64 iWriteRowid; /* Rowid for current doc being written */ int bDelete; /* Current write is a delete */ int nContentlessDelete; /* Number of contentless delete ops */ int nPendingRow; /* Number of INSERT in hash table */ /* Error state. */ int rc; /* Current error code */ /* State used by the fts5DataXXX() functions. */ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlite3_stmt *pIdxSelect; int nRead; /* Total number of blocks read */ sqlite3_stmt *pDeleteFromIdx; sqlite3_stmt *pDataVersion; i64 iStructVersion; /* data_version when pStruct read */ Fts5Structure *pStruct; /* Current db structure (or NULL) */ | > > | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | i64 iWriteRowid; /* Rowid for current doc being written */ int bDelete; /* Current write is a delete */ int nContentlessDelete; /* Number of contentless delete ops */ int nPendingRow; /* Number of INSERT in hash table */ /* Error state. */ int rc; /* Current error code */ int flushRc; /* State used by the fts5DataXXX() functions. */ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlite3_stmt *pIdxSelect; sqlite3_stmt *pIdxNextSelect; int nRead; /* Total number of blocks read */ sqlite3_stmt *pDeleteFromIdx; sqlite3_stmt *pDataVersion; i64 iStructVersion; /* data_version when pStruct read */ Fts5Structure *pStruct; /* Current db structure (or NULL) */ |
︙ | ︙ | |||
514 515 516 517 518 519 520 | struct Fts5SegIter { Fts5StructureSegment *pSeg; /* Segment to iterate through */ int flags; /* Mask of configuration flags */ int iLeafPgno; /* Current leaf page number */ Fts5Data *pLeaf; /* Current leaf data */ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ i64 iLeafOffset; /* Byte offset within current leaf */ | | < | 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 | struct Fts5SegIter { Fts5StructureSegment *pSeg; /* Segment to iterate through */ int flags; /* Mask of configuration flags */ int iLeafPgno; /* Current leaf page number */ Fts5Data *pLeaf; /* Current leaf data */ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ i64 iLeafOffset; /* Byte offset within current leaf */ Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */ /* Next method */ void (*xNext)(Fts5Index*, Fts5SegIter*, int*); /* The page and offset from which the current term was read. The offset ** is the offset of the first rowid in the current doclist. */ int iTermLeafPgno; |
︙ | ︙ | |||
541 542 543 544 545 546 547 548 549 550 551 552 553 554 | /* Variables populated based on current entry. */ Fts5Buffer term; /* Current term */ i64 iRowid; /* Current rowid */ int nPos; /* Number of bytes in current position list */ u8 bDel; /* True if the delete flag is set */ }; /* ** Argument is a pointer to an Fts5Data structure that contains a ** leaf page. */ #define ASSERT_SZLEAF_OK(x) assert( \ (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \ | > > > > > > > > > | 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 | /* Variables populated based on current entry. */ Fts5Buffer term; /* Current term */ i64 iRowid; /* Current rowid */ int nPos; /* Number of bytes in current position list */ u8 bDel; /* True if the delete flag is set */ }; /* ** Array of tombstone pages. Reference counted. */ struct Fts5TombstoneArray { int nRef; /* Number of pointers to this object */ int nTombstone; Fts5Data *apTombstone[1]; /* Array of tombstone pages */ }; /* ** Argument is a pointer to an Fts5Data structure that contains a ** leaf page. */ #define ASSERT_SZLEAF_OK(x) assert( \ (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \ |
︙ | ︙ | |||
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 | ** ** aFirst[1] contains the index in aSeg[] of the iterator that points to ** the smallest key overall. aFirst[0] is unused. ** ** poslist: ** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered. ** There is no way to tell if this is populated or not. */ struct Fts5Iter { Fts5IndexIter base; /* Base class containing output vars */ Fts5Index *pIndex; /* Index that owns this iterator */ Fts5Buffer poslist; /* Buffer containing current poslist */ Fts5Colset *pColset; /* Restrict matches to these columns */ /* Invoked to set output variables. */ void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*); int nSeg; /* Size of aSeg[] array */ int bRev; /* True to iterate in reverse order */ u8 bSkipEmpty; /* True to skip deleted entries */ i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ Fts5CResult *aFirst; /* Current merge state (see above) */ Fts5SegIter aSeg[1]; /* Array of segment iterators */ }; | > > > > > > > < | 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 | ** ** aFirst[1] contains the index in aSeg[] of the iterator that points to ** the smallest key overall. aFirst[0] is unused. ** ** poslist: ** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered. ** There is no way to tell if this is populated or not. ** ** pColset: ** If not NULL, points to an object containing a set of column indices. ** Only matches that occur in one of these columns will be returned. ** The Fts5Iter does not own the Fts5Colset object, and so it is not ** freed when the iterator is closed - it is owned by the upper layer. */ struct Fts5Iter { Fts5IndexIter base; /* Base class containing output vars */ Fts5TokenDataIter *pTokenDataIter; Fts5Index *pIndex; /* Index that owns this iterator */ Fts5Buffer poslist; /* Buffer containing current poslist */ Fts5Colset *pColset; /* Restrict matches to these columns */ /* Invoked to set output variables. */ void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*); int nSeg; /* Size of aSeg[] array */ int bRev; /* True to iterate in reverse order */ u8 bSkipEmpty; /* True to skip deleted entries */ i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ Fts5CResult *aFirst; /* Current merge state (see above) */ Fts5SegIter aSeg[1]; /* Array of segment iterators */ }; /* ** An instance of the following type is used to iterate through the contents ** of a doclist-index record. ** ** pData: ** Record containing the doclist-index data. |
︙ | ︙ | |||
1524 1525 1526 1527 1528 1529 1530 | }else{ int iOff; for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){ if( pData->p[iOff] ) break; } if( iOff<pData->nn ){ | | | | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 | }else{ int iOff; for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){ if( pData->p[iOff] ) break; } if( iOff<pData->nn ){ u64 iVal; pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1; iOff += fts5GetVarint(&pData->p[iOff], &iVal); pLvl->iRowid += iVal; pLvl->iOff = iOff; }else{ pLvl->bEof = 1; } } |
︙ | ︙ | |||
1905 1906 1907 1908 1909 1910 1911 | pIter->xNext = fts5SegIterNext_None; }else{ pIter->xNext = fts5SegIterNext; } } /* | | | | | > | | < | > > | 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 | pIter->xNext = fts5SegIterNext_None; }else{ pIter->xNext = fts5SegIterNext; } } /* ** Allocate a tombstone hash page array object (pIter->pTombArray) for ** the iterator passed as the second argument. If an OOM error occurs, ** leave an error in the Fts5Index object. */ static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ const int nTomb = pIter->pSeg->nPgTombstone; if( nTomb>0 ){ int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray); Fts5TombstoneArray *pNew; pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); if( pNew ){ pNew->nTombstone = nTomb; pNew->nRef = 1; pIter->pTombArray = pNew; } } } /* ** Initialize the iterator object pIter to iterate through the entries in ** segment pSeg. The iterator is left pointing to the first entry when |
︙ | ︙ | |||
2173 2174 2175 2176 2177 2178 2179 2180 2181 | iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep); } pIter->iLeafOffset = iOff; fts5SegIterLoadTerm(p, pIter, nKeep); }else{ const u8 *pList = 0; const char *zTerm = 0; int nList; sqlite3Fts5HashScanNext(p->pHash); | > | | | 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 | iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep); } pIter->iLeafOffset = iOff; fts5SegIterLoadTerm(p, pIter, nKeep); }else{ const u8 *pList = 0; const char *zTerm = 0; int nTerm = 0; int nList; sqlite3Fts5HashScanNext(p->pHash); sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); if( pList==0 ) goto next_none_eof; pIter->pLeaf->p = (u8*)pList; pIter->pLeaf->nn = nList; pIter->pLeaf->szLeaf = nList; pIter->iEndofDoclist = nList; sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm); pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); } if( pbNewTerm ) *pbNewTerm = 1; }else{ goto next_none_eof; } |
︙ | ︙ | |||
2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 | assert_nc( iDelta>0 ); } pIter->iLeafOffset = iOff; }else if( pIter->pSeg==0 ){ const u8 *pList = 0; const char *zTerm = 0; int nList = 0; assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ sqlite3Fts5HashScanNext(p->pHash); | > | | < | 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 | assert_nc( iDelta>0 ); } pIter->iLeafOffset = iOff; }else if( pIter->pSeg==0 ){ const u8 *pList = 0; const char *zTerm = 0; int nTerm = 0; int nList = 0; assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ sqlite3Fts5HashScanNext(p->pHash); sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); } if( pList==0 ){ fts5DataRelease(pIter->pLeaf); pIter->pLeaf = 0; }else{ pIter->pLeaf->p = (u8*)pList; pIter->pLeaf->nn = nList; pIter->pLeaf->szLeaf = nList; pIter->iEndofDoclist = nList+1; sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm); pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); *pbNewTerm = 1; } }else{ iOff = 0; /* Next entry is not on the current page */ while( iOff==0 ){ |
︙ | ︙ | |||
2648 2649 2650 2651 2652 2653 2654 | pIter->iLeafPgno = iPg - 1; fts5SegIterNextPage(p, pIter); if( pIter->pLeaf ){ fts5LeafSeek(p, bGe, pIter, pTerm, nTerm); } | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 | pIter->iLeafPgno = iPg - 1; fts5SegIterNextPage(p, pIter); if( pIter->pLeaf ){ fts5LeafSeek(p, bGe, pIter, pTerm, nTerm); } if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){ pIter->flags |= FTS5_SEGITER_ONETERM; if( pIter->pLeaf ){ if( flags & FTS5INDEX_QUERY_DESC ){ pIter->flags |= FTS5_SEGITER_REVERSE; } if( bDlidx ){ fts5SegIterLoadDlidx(p, pIter); } if( flags & FTS5INDEX_QUERY_DESC ){ fts5SegIterReverse(p, pIter); } } } fts5SegIterSetNext(p, pIter); if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){ fts5SegIterAllocTombstone(p, pIter); } /* Either: ** ** 1) an error has occurred, or ** 2) the iterator points to EOF, or ** 3) the iterator points to an entry with term (pTerm/nTerm), or ** 4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points ** to an entry with a term greater than or equal to (pTerm/nTerm). */ assert_nc( p->rc!=SQLITE_OK /* 1 */ || pIter->pLeaf==0 /* 2 */ || fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0 /* 3 */ || (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0) /* 4 */ ); } /* ** SQL used by fts5SegIterNextInit() to find the page to open. */ static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){ if( p->pIdxNextSelect==0 ){ Fts5Config *pConfig = p->pConfig; fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf( "SELECT pgno FROM '%q'.'%q_idx' WHERE " "segid=? AND term>? ORDER BY term ASC LIMIT 1", pConfig->zDb, pConfig->zName )); } return p->pIdxNextSelect; } /* ** This is similar to fts5SegIterSeekInit(), except that it initializes ** the segment iterator to point to the first term following the page ** with pToken/nToken on it. */ static void fts5SegIterNextInit( Fts5Index *p, const char *pTerm, int nTerm, Fts5StructureSegment *pSeg, /* Description of segment */ Fts5SegIter *pIter /* Object to populate */ ){ int iPg = -1; /* Page of segment to open */ int bDlidx = 0; sqlite3_stmt *pSel = 0; /* SELECT to find iPg */ pSel = fts5IdxNextStmt(p); if( pSel ){ assert( p->rc==SQLITE_OK ); sqlite3_bind_int(pSel, 1, pSeg->iSegid); sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC); if( sqlite3_step(pSel)==SQLITE_ROW ){ i64 val = sqlite3_column_int64(pSel, 0); iPg = (int)(val>>1); bDlidx = (val & 0x0001); } p->rc = sqlite3_reset(pSel); sqlite3_bind_null(pSel, 2); if( p->rc ) return; } memset(pIter, 0, sizeof(*pIter)); pIter->pSeg = pSeg; pIter->flags |= FTS5_SEGITER_ONETERM; if( iPg>=0 ){ pIter->iLeafPgno = iPg - 1; fts5SegIterNextPage(p, pIter); fts5SegIterSetNext(p, pIter); } if( pIter->pLeaf ){ const u8 *a = pIter->pLeaf->p; int iTermOff = 0; pIter->iPgidxOff = pIter->pLeaf->szLeaf; pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff); pIter->iLeafOffset = iTermOff; fts5SegIterLoadTerm(p, pIter, 0); fts5SegIterLoadNPos(p, pIter); if( bDlidx ) fts5SegIterLoadDlidx(p, pIter); assert( p->rc!=SQLITE_OK || fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0 ); } } /* ** Initialize the object pIter to point to term pTerm/nTerm within the ** in-memory hash table. If there is no such term in the hash-table, the ** iterator is set to EOF. ** ** If an error occurs, Fts5Index.rc is set to an appropriate error code. If ** an error has already occurred when this function is called, it is a no-op. |
︙ | ︙ | |||
2707 2708 2709 2710 2711 2712 2713 | assert( p->pHash ); assert( p->rc==SQLITE_OK ); if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){ const u8 *pList = 0; p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); | | < | 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 | assert( p->pHash ); assert( p->rc==SQLITE_OK ); if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){ const u8 *pList = 0; p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList); if( pList ){ pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); if( pLeaf ){ pLeaf->p = (u8*)pList; } } |
︙ | ︙ | |||
2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 | int ii; for(ii=0; ii<n; ii++){ fts5DataRelease(ap[ii]); } sqlite3_free(ap); } } /* ** Zero the iterator passed as the only argument. */ static void fts5SegIterClear(Fts5SegIter *pIter){ fts5BufferFree(&pIter->term); fts5DataRelease(pIter->pLeaf); fts5DataRelease(pIter->pNextLeaf); | > > > > > > > > > > > > > > > > > | | 2862 2863 2864 2865 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 | int ii; for(ii=0; ii<n; ii++){ fts5DataRelease(ap[ii]); } sqlite3_free(ap); } } /* ** Decrement the ref-count of the object passed as the only argument. If it ** reaches 0, free it and its contents. */ static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){ if( p ){ p->nRef--; if( p->nRef<=0 ){ int ii; for(ii=0; ii<p->nTombstone; ii++){ fts5DataRelease(p->apTombstone[ii]); } sqlite3_free(p); } } } /* ** Zero the iterator passed as the only argument. */ static void fts5SegIterClear(Fts5SegIter *pIter){ fts5BufferFree(&pIter->term); fts5DataRelease(pIter->pLeaf); fts5DataRelease(pIter->pNextLeaf); fts5TombstoneArrayDelete(pIter->pTombArray); fts5DlidxIterFree(pIter->pDlidx); sqlite3_free(pIter->aRowidOffset); memset(pIter, 0, sizeof(Fts5SegIter)); } #ifdef SQLITE_DEBUG |
︙ | ︙ | |||
3019 3020 3021 3022 3023 3024 3025 | if( pIter->pLeaf==0 ) break; if( bRev==0 && pIter->iRowid>=iMatch ) break; if( bRev!=0 && pIter->iRowid<=iMatch ) break; bMove = 1; }while( p->rc==SQLITE_OK ); } | < | 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 | if( pIter->pLeaf==0 ) break; if( bRev==0 && pIter->iRowid>=iMatch ) break; if( bRev!=0 && pIter->iRowid<=iMatch ) break; bMove = 1; }while( p->rc==SQLITE_OK ); } /* ** Free the iterator object passed as the second argument. */ static void fts5MultiIterFree(Fts5Iter *pIter){ if( pIter ){ int i; for(i=0; i<pIter->nSeg; i++){ |
︙ | ︙ | |||
3164 3165 3166 3167 3168 3169 3170 3171 | ** Return true if the iterator passed as the only argument points ** to an segment entry for which there is a tombstone. Return false ** if there is no tombstone or if the iterator is already at EOF. */ static int fts5MultiIterIsDeleted(Fts5Iter *pIter){ int iFirst = pIter->aFirst[1].iFirst; Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; | > | | | | | | | | 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 | ** Return true if the iterator passed as the only argument points ** to an segment entry for which there is a tombstone. Return false ** if there is no tombstone or if the iterator is already at EOF. */ static int fts5MultiIterIsDeleted(Fts5Iter *pIter){ int iFirst = pIter->aFirst[1].iFirst; Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; Fts5TombstoneArray *pArray = pSeg->pTombArray; if( pSeg->pLeaf && pArray ){ /* Figure out which page the rowid might be present on. */ int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone; assert( iPg>=0 ); /* If tombstone hash page iPg has not yet been loaded from the ** database, load it now. */ if( pArray->apTombstone[iPg]==0 ){ pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex, FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg) ); if( pArray->apTombstone[iPg]==0 ) return 0; } return fts5IndexTombstoneQuery( pArray->apTombstone[iPg], pArray->nTombstone, pSeg->iRowid ); } return 0; } |
︙ | ︙ | |||
3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 | }else{ pIter->xSetOutputs = fts5IterSetOutputs_Col; } } } } /* ** Allocate a new Fts5Iter object. ** ** The new object will be used to iterate through data in structure pStruct. ** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel ** is zero or greater, data from the first nSegment segments on level iLevel | > > > > > > > > > > > > > > > > > > > > > > > > > > | 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 | }else{ pIter->xSetOutputs = fts5IterSetOutputs_Col; } } } } /* ** All the component segment-iterators of pIter have been set up. This ** functions finishes setup for iterator pIter itself. */ static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){ int iIter; for(iIter=pIter->nSeg-1; iIter>0; iIter--){ int iEq; if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){ Fts5SegIter *pSeg = &pIter->aSeg[iEq]; if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); fts5MultiIterAdvanced(p, pIter, iEq, iIter); } } fts5MultiIterSetEof(pIter); fts5AssertMultiIterSetup(p, pIter); if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter)) || fts5MultiIterIsDeleted(pIter) ){ fts5MultiIterNext(p, pIter, 0, 0); }else if( pIter->base.bEof==0 ){ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; pIter->xSetOutputs(pIter, pSeg); } } /* ** Allocate a new Fts5Iter object. ** ** The new object will be used to iterate through data in structure pStruct. ** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel ** is zero or greater, data from the first nSegment segments on level iLevel |
︙ | ︙ | |||
3801 3802 3803 3804 3805 3806 3807 | for(iSeg=nSeg-1; iSeg>=0; iSeg--){ fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]); } } assert( iIter==nSeg ); } | | < < < < < < < < | < < < < < < < < < < < | 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 | for(iSeg=nSeg-1; iSeg>=0; iSeg--){ fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]); } } assert( iIter==nSeg ); } /* If the above was successful, each component iterator now points ** to the first entry in its segment. In this case initialize the ** aFirst[] array. Or, if an error has occurred, free the iterator ** object and set the output variable to NULL. */ if( p->rc==SQLITE_OK ){ fts5MultiIterFinishSetup(p, pNew); }else{ fts5MultiIterFree(pNew); *ppOut = 0; } fts5MultiIterNew_post_check: assert( (*ppOut)!=0 || p->rc!=SQLITE_OK ); |
︙ | ︙ | |||
3850 3851 3852 3853 3854 3855 3856 | int bDesc, /* True for descending rowid order */ Fts5Iter **ppOut /* New object */ ){ Fts5Iter *pNew; pNew = fts5MultiIterAlloc(p, 2); if( pNew ){ Fts5SegIter *pIter = &pNew->aSeg[1]; | < | 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 | int bDesc, /* True for descending rowid order */ Fts5Iter **ppOut /* New object */ ){ Fts5Iter *pNew; pNew = fts5MultiIterAlloc(p, 2); if( pNew ){ Fts5SegIter *pIter = &pNew->aSeg[1]; pIter->flags = FTS5_SEGITER_ONETERM; if( pData->szLeaf>0 ){ pIter->pLeaf = pData; pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid); pIter->iEndofDoclist = pData->nn; pNew->aFirst[1].iFirst = 1; if( bDesc ){ |
︙ | ︙ | |||
3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 | */ static void fts5IndexDiscardData(Fts5Index *p){ assert( p->pHash || p->nPendingData==0 ); if( p->pHash ){ sqlite3Fts5HashClear(p->pHash); p->nPendingData = 0; p->nPendingRow = 0; } p->nContentlessDelete = 0; } /* ** Return the size of the prefix, in bytes, that buffer ** (pNew/<length-unknown>) shares with buffer (pOld/nOld). | > | 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 | */ static void fts5IndexDiscardData(Fts5Index *p){ assert( p->pHash || p->nPendingData==0 ); if( p->pHash ){ sqlite3Fts5HashClear(p->pHash); p->nPendingData = 0; p->nPendingRow = 0; p->flushRc = SQLITE_OK; } p->nContentlessDelete = 0; } /* ** Return the size of the prefix, in bytes, that buffer ** (pNew/<length-unknown>) shares with buffer (pOld/nOld). |
︙ | ︙ | |||
4213 4214 4215 4216 4217 4218 4219 | pDlidx->bPrevValid = 0; pDlidx->pgno++; }else{ bDone = 1; } if( pDlidx->bPrevValid ){ | | | 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 | pDlidx->bPrevValid = 0; pDlidx->pgno++; }else{ bDone = 1; } if( pDlidx->bPrevValid ){ iVal = (u64)iRowid - (u64)pDlidx->iPrev; }else{ i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno); assert( pDlidx->buf.n==0 ); sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, !bDone); sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iPgno); iVal = iRowid; } |
︙ | ︙ | |||
5133 5134 5135 5136 5137 5138 5139 | assert_nc( iSOP==pSeg->iLeafOffset ); iNextOff = pSeg->iLeafOffset + pSeg->nPos; } } iOff = iStart; | > > > > | | < | | | | > | > > | | 5253 5254 5255 5256 5257 5258 5259 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 | assert_nc( iSOP==pSeg->iLeafOffset ); iNextOff = pSeg->iLeafOffset + pSeg->nPos; } } iOff = iStart; /* If the position-list for the entry being removed flows over past ** the end of this page, delete the portion of the position-list on the ** next page and beyond. ** ** Set variable bLastInDoclist to true if this entry happens ** to be the last rowid in the doclist for its term. */ if( iNextOff>=iPgIdx ){ int pgno = pSeg->iLeafPgno+1; fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); iNextOff = iPgIdx; } if( pSeg->bDel==0 ){ if( iNextOff!=iPgIdx ){ /* Loop through the page-footer. If iNextOff (offset of the ** entry following the one we are removing) is equal to the ** offset of a key on this page, then the entry is the last ** in its doclist. */ int iKeyOff = 0; for(iIdx=0; iIdx<nIdx; /* no-op */){ u32 iVal = 0; iIdx += fts5GetVarint32(&aIdx[iIdx], iVal); iKeyOff += iVal; if( iKeyOff==iNextOff ){ bLastInDoclist = 1; |
︙ | ︙ | |||
5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 | ** mode. It edits the segments within the database described by argument ** pStruct to remove the entries for term zTerm, rowid iRowid. */ static void fts5FlushSecureDelete( Fts5Index *p, Fts5Structure *pStruct, const char *zTerm, i64 iRowid ){ const int f = FTS5INDEX_QUERY_SKIPHASH; | > < | 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 | ** mode. It edits the segments within the database described by argument ** pStruct to remove the entries for term zTerm, rowid iRowid. */ static void fts5FlushSecureDelete( Fts5Index *p, Fts5Structure *pStruct, const char *zTerm, int nTerm, i64 iRowid ){ const int f = FTS5INDEX_QUERY_SKIPHASH; Fts5Iter *pIter = 0; /* Used to find term instance */ fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); if( fts5MultiIterEof(p, pIter)==0 ){ i64 iThis = fts5MultiIterRowid(pIter); if( iThis<iRowid ){ fts5MultiIterNextFrom(p, pIter, iRowid); |
︙ | ︙ | |||
5407 5408 5409 5410 5411 5412 5413 | while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ const char *zTerm; /* Buffer containing term */ int nTerm; /* Size of zTerm in bytes */ const u8 *pDoclist; /* Pointer to doclist for this term */ int nDoclist; /* Size of doclist in bytes */ /* Get the term and doclist for this entry. */ | | < | 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 | while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ const char *zTerm; /* Buffer containing term */ int nTerm; /* Size of zTerm in bytes */ const u8 *pDoclist; /* Pointer to doclist for this term */ int nDoclist; /* Size of doclist in bytes */ /* Get the term and doclist for this entry. */ sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist); if( bSecureDelete==0 ){ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); if( p->rc!=SQLITE_OK ) break; assert( writer.bFirstRowidInPage==0 ); } if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ |
︙ | ︙ | |||
5438 5439 5440 5441 5442 5443 5444 | /* If in secure delete mode, and if this entry in the poslist is ** in fact a delete, then edit the existing segments directly ** using fts5FlushSecureDelete(). */ if( bSecureDelete ){ if( eDetail==FTS5_DETAIL_NONE ){ if( iOff<nDoclist && pDoclist[iOff]==0x00 ){ | | | | 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 | /* If in secure delete mode, and if this entry in the poslist is ** in fact a delete, then edit the existing segments directly ** using fts5FlushSecureDelete(). */ if( bSecureDelete ){ if( eDetail==FTS5_DETAIL_NONE ){ if( iOff<nDoclist && pDoclist[iOff]==0x00 ){ fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid); iOff++; if( iOff<nDoclist && pDoclist[iOff]==0x00 ){ iOff++; nDoclist = 0; }else{ continue; } } }else if( (pDoclist[iOff] & 0x01) ){ fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid); if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ iOff++; continue; } } } |
︙ | ︙ | |||
5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 | } /* ** Flush any data stored in the in-memory hash tables to the database. */ static void fts5IndexFlush(Fts5Index *p){ /* Unless it is empty, flush the hash table to disk */ if( p->nPendingData || p->nContentlessDelete ){ assert( p->pHash ); fts5FlushOneHash(p); if( p->rc==SQLITE_OK ){ sqlite3Fts5HashClear(p->pHash); p->nPendingData = 0; p->nPendingRow = 0; p->nContentlessDelete = 0; } } } static Fts5Structure *fts5IndexOptimizeStruct( Fts5Index *p, Fts5Structure *pStruct | > > > > > > | 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 | } /* ** Flush any data stored in the in-memory hash tables to the database. */ static void fts5IndexFlush(Fts5Index *p){ /* Unless it is empty, flush the hash table to disk */ if( p->flushRc ){ p->rc = p->flushRc; return; } if( p->nPendingData || p->nContentlessDelete ){ assert( p->pHash ); fts5FlushOneHash(p); if( p->rc==SQLITE_OK ){ sqlite3Fts5HashClear(p->pHash); p->nPendingData = 0; p->nPendingRow = 0; p->nContentlessDelete = 0; }else if( p->nPendingData || p->nContentlessDelete ){ p->flushRc = p->rc; } } } static Fts5Structure *fts5IndexOptimizeStruct( Fts5Index *p, Fts5Structure *pStruct |
︙ | ︙ | |||
6068 6069 6070 6071 6072 6073 6074 | static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ int bDesc, /* True for "ORDER BY rowid DESC" */ int iIdx, /* Index to scan for data */ u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ Fts5Colset *pColset, /* Restrict matches to these columns */ | | > | > > > > > > | 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 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 | static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ int bDesc, /* True for "ORDER BY rowid DESC" */ int iIdx, /* Index to scan for data */ u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ Fts5Colset *pColset, /* Restrict matches to these columns */ Fts5Iter **ppIter /* OUT: New iterator */ ){ Fts5Structure *pStruct; Fts5Buffer *aBuf; int nBuf = 32; int nMerge = 1; void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ xMerge = fts5MergeRowidLists; xAppend = fts5AppendRowid; }else{ nMerge = FTS5_MERGE_NLIST-1; nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ xMerge = fts5MergePrefixLists; xAppend = fts5AppendPoslist; } aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); pStruct = fts5StructureRead(p); assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); if( p->rc==SQLITE_OK ){ const int flags = FTS5INDEX_QUERY_SCAN | FTS5INDEX_QUERY_SKIPEMPTY | FTS5INDEX_QUERY_NOOUTPUT; int i; i64 iLastRowid = 0; Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ Fts5Data *pData; Fts5Buffer doclist; int bNewTerm = 1; memset(&doclist, 0, sizeof(doclist)); /* If iIdx is non-zero, then it is the number of a prefix-index for ** prefixes 1 character longer than the prefix being queried for. That ** index contains all the doclists required, except for the one ** corresponding to the prefix itself. That one is extracted from the ** main term index here. */ if( iIdx!=0 ){ int dummy = 0; const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; pToken[0] = FTS5_MAIN_PREFIX; fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1); fts5IterSetOutputCb(&p->rc, p1); for(; |
︙ | ︙ | |||
6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 | } fts5MultiIterFree(p1); } pToken[0] = FTS5_MAIN_PREFIX + iIdx; fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); fts5IterSetOutputCb(&p->rc, p1); for( /* no-op */ ; fts5MultiIterEof(p, p1)==0; fts5MultiIterNext2(p, p1, &bNewTerm) ){ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; int nTerm = pSeg->term.n; const u8 *pTerm = pSeg->term.p; p1->xSetOutputs(p1, pSeg); assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); if( bNewTerm ){ if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break; } if( p1->base.nData==0 ) continue; | > < | 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 | } fts5MultiIterFree(p1); } pToken[0] = FTS5_MAIN_PREFIX + iIdx; fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); fts5IterSetOutputCb(&p->rc, p1); for( /* no-op */ ; fts5MultiIterEof(p, p1)==0; fts5MultiIterNext2(p, p1, &bNewTerm) ){ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; int nTerm = pSeg->term.n; const u8 *pTerm = pSeg->term.p; p1->xSetOutputs(p1, pSeg); assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); if( bNewTerm ){ if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break; } if( p1->base.nData==0 ) continue; if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ int i1 = i*nMerge; int iStore; assert( i1+nMerge<=nBuf ); for(iStore=i1; iStore<i1+nMerge; iStore++){ if( aBuf[iStore].n==0 ){ |
︙ | ︙ | |||
6179 6180 6181 6182 6183 6184 6185 | } for(iFree=i; iFree<i+nMerge; iFree++){ fts5BufferFree(&aBuf[iFree]); } } fts5MultiIterFree(p1); | | | 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 | } for(iFree=i; iFree<i+nMerge; iFree++){ fts5BufferFree(&aBuf[iFree]); } } fts5MultiIterFree(p1); pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING); if( pData ){ pData->p = (u8*)&pData[1]; pData->nn = pData->szLeaf = doclist.n; if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); fts5MultiIterNew2(p, pData, bDesc, ppIter); } fts5BufferFree(&doclist); |
︙ | ︙ | |||
6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 | assert( p->pReader==0 ); fts5StructureInvalidate(p); sqlite3_finalize(p->pWriter); sqlite3_finalize(p->pDeleter); sqlite3_finalize(p->pIdxWriter); sqlite3_finalize(p->pIdxDeleter); sqlite3_finalize(p->pIdxSelect); sqlite3_finalize(p->pDataVersion); sqlite3_finalize(p->pDeleteFromIdx); sqlite3Fts5HashFree(p->pHash); sqlite3_free(p->zDataTbl); sqlite3_free(p); } return rc; | > | 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 | assert( p->pReader==0 ); fts5StructureInvalidate(p); sqlite3_finalize(p->pWriter); sqlite3_finalize(p->pDeleter); sqlite3_finalize(p->pIdxWriter); sqlite3_finalize(p->pIdxDeleter); sqlite3_finalize(p->pIdxSelect); sqlite3_finalize(p->pIdxNextSelect); sqlite3_finalize(p->pDataVersion); sqlite3_finalize(p->pDeleteFromIdx); sqlite3Fts5HashFree(p->pHash); sqlite3_free(p->zDataTbl); sqlite3_free(p); } return rc; |
︙ | ︙ | |||
6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 | nByte ); } } return rc; } /* ** Open a new iterator to iterate though all rowid that match the ** specified token or token prefix. */ int sqlite3Fts5IndexQuery( Fts5Index *p, /* FTS index to query */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 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 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 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 6775 6776 6777 6778 6779 6780 6781 6782 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 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 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 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 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 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 | nByte ); } } return rc; } /* ** pToken points to a buffer of size nToken bytes containing a search ** term, including the index number at the start, used on a tokendata=1 ** table. This function returns true if the term in buffer pBuf matches ** token pToken/nToken. */ static int fts5IsTokendataPrefix( Fts5Buffer *pBuf, const u8 *pToken, int nToken ){ return ( pBuf->n>=nToken && 0==memcmp(pBuf->p, pToken, nToken) && (pBuf->n==nToken || pBuf->p[nToken]==0x00) ); } /* ** Ensure the segment-iterator passed as the only argument points to EOF. */ static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ fts5DataRelease(pSeg->pLeaf); pSeg->pLeaf = 0; } /* ** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an ** array of these for each row it visits. Or, for an iterator used by an ** "ORDER BY rank" query, it accumulates an array of these for the entire ** query. ** ** Each instance in the array indicates the iterator (and therefore term) ** associated with position iPos of rowid iRowid. This is used by the ** xInstToken() API. */ struct Fts5TokenDataMap { i64 iRowid; /* Row this token is located in */ i64 iPos; /* Position of token */ int iIter; /* Iterator token was read from */ }; /* ** An object used to supplement Fts5Iter for tokendata=1 iterators. */ struct Fts5TokenDataIter { int nIter; int nIterAlloc; int nMap; int nMapAlloc; Fts5TokenDataMap *aMap; Fts5PoslistReader *aPoslistReader; int *aPoslistToIter; Fts5Iter *apIter[1]; }; /* ** This function appends iterator pAppend to Fts5TokenDataIter pIn and ** returns the result. */ static Fts5TokenDataIter *fts5AppendTokendataIter( Fts5Index *p, /* Index object (for error code) */ Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */ Fts5Iter *pAppend /* Append this iterator */ ){ Fts5TokenDataIter *pRet = pIn; if( p->rc==SQLITE_OK ){ if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter); Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); if( pNew==0 ){ p->rc = SQLITE_NOMEM; }else{ if( pIn==0 ) memset(pNew, 0, nByte); pRet = pNew; pNew->nIterAlloc = nAlloc; } } } if( p->rc ){ sqlite3Fts5IterClose((Fts5IndexIter*)pAppend); }else{ pRet->apIter[pRet->nIter++] = pAppend; } assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); return pRet; } /* ** Delete an Fts5TokenDataIter structure and its contents. */ static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ if( pSet ){ int ii; for(ii=0; ii<pSet->nIter; ii++){ fts5MultiIterFree(pSet->apIter[ii]); } sqlite3_free(pSet->aPoslistReader); sqlite3_free(pSet->aMap); sqlite3_free(pSet); } } /* ** Append a mapping to the token-map belonging to object pT. */ static void fts5TokendataIterAppendMap( Fts5Index *p, Fts5TokenDataIter *pT, int iIter, i64 iRowid, i64 iPos ){ if( p->rc==SQLITE_OK ){ if( pT->nMap==pT->nMapAlloc ){ int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; int nByte = nNew * sizeof(Fts5TokenDataMap); Fts5TokenDataMap *aNew; aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte); if( aNew==0 ){ p->rc = SQLITE_NOMEM; return; } pT->aMap = aNew; pT->nMapAlloc = nNew; } pT->aMap[pT->nMap].iRowid = iRowid; pT->aMap[pT->nMap].iPos = iPos; pT->aMap[pT->nMap].iIter = iIter; pT->nMap++; } } /* ** The iterator passed as the only argument must be a tokendata=1 iterator ** (pIter->pTokenDataIter!=0). This function sets the iterator output ** variables (pIter->base.*) according to the contents of the current ** row. */ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ int ii; int nHit = 0; i64 iRowid = SMALLEST_INT64; int iMin = 0; Fts5TokenDataIter *pT = pIter->pTokenDataIter; pIter->base.nData = 0; pIter->base.pData = 0; for(ii=0; ii<pT->nIter; ii++){ Fts5Iter *p = pT->apIter[ii]; if( p->base.bEof==0 ){ if( nHit==0 || p->base.iRowid<iRowid ){ iRowid = p->base.iRowid; nHit = 1; pIter->base.pData = p->base.pData; pIter->base.nData = p->base.nData; iMin = ii; }else if( p->base.iRowid==iRowid ){ nHit++; } } } if( nHit==0 ){ pIter->base.bEof = 1; }else{ int eDetail = pIter->pIndex->pConfig->eDetail; pIter->base.bEof = 0; pIter->base.iRowid = iRowid; if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1); }else if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ int nReader = 0; int nByte = 0; i64 iPrev = 0; /* Allocate array of iterators if they are not already allocated. */ if( pT->aPoslistReader==0 ){ pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero( &pIter->pIndex->rc, pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int)) ); if( pT->aPoslistReader==0 ) return; pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter]; } /* Populate an iterator for each poslist that will be merged */ for(ii=0; ii<pT->nIter; ii++){ Fts5Iter *p = pT->apIter[ii]; if( iRowid==p->base.iRowid ){ pT->aPoslistToIter[nReader] = ii; sqlite3Fts5PoslistReaderInit( p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++] ); nByte += p->base.nData; } } /* Ensure the output buffer is large enough */ if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){ return; } /* Ensure the token-mapping is large enough */ if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){ int nNew = (pT->nMapAlloc + nByte) * 2; Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc( pT->aMap, nNew*sizeof(Fts5TokenDataMap) ); if( aNew==0 ){ pIter->pIndex->rc = SQLITE_NOMEM; return; } pT->aMap = aNew; pT->nMapAlloc = nNew; } pIter->poslist.n = 0; while( 1 ){ i64 iMinPos = LARGEST_INT64; /* Find smallest position */ iMin = 0; for(ii=0; ii<nReader; ii++){ Fts5PoslistReader *pReader = &pT->aPoslistReader[ii]; if( pReader->bEof==0 ){ if( pReader->iPos<iMinPos ){ iMinPos = pReader->iPos; iMin = ii; } } } /* If all readers were at EOF, break out of the loop. */ if( iMinPos==LARGEST_INT64 ) break; sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos); sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]); if( eDetail==FTS5_DETAIL_FULL ){ pT->aMap[pT->nMap].iPos = iMinPos; pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin]; pT->aMap[pT->nMap].iRowid = iRowid; pT->nMap++; } } pIter->base.pData = pIter->poslist.p; pIter->base.nData = pIter->poslist.n; } } } /* ** The iterator passed as the only argument must be a tokendata=1 iterator ** (pIter->pTokenDataIter!=0). This function advances the iterator. If ** 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){ if( pIter && pIter->aSeg[0].pLeaf==0 ){ fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p); } } /* ** This function sets up an iterator to use for a non-prefix query on a ** tokendata=1 table. */ static Fts5Iter *fts5SetupTokendataIter( Fts5Index *p, /* FTS index to query */ const u8 *pToken, /* Buffer containing query term */ int nToken, /* Size of buffer pToken in bytes */ Fts5Colset *pColset /* Colset to filter on */ ){ Fts5Iter *pRet = 0; Fts5TokenDataIter *pSet = 0; Fts5Structure *pStruct = 0; const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN; Fts5Buffer bSeek = {0, 0, 0}; Fts5Buffer *pSmall = 0; fts5IndexFlush(p); pStruct = fts5StructureRead(p); while( p->rc==SQLITE_OK ){ Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0; Fts5Iter *pNew = 0; Fts5SegIter *pNewIter = 0; Fts5SegIter *pPrevIter = 0; int iLvl, iSeg, ii; pNew = fts5MultiIterAlloc(p, pStruct->nSegment); if( pSmall ){ fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p); fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0"); }else{ fts5BufferSet(&p->rc, &bSeek, nToken, pToken); } if( p->rc ){ sqlite3Fts5IterClose((Fts5IndexIter*)pNew); break; } pNewIter = &pNew->aSeg[0]; pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0); for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; int bDone = 0; if( pPrevIter ){ if( fts5BufferCompare(pSmall, &pPrevIter->term) ){ memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter)); memset(pPrevIter, 0, sizeof(Fts5SegIter)); bDone = 1; }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){ fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter); bDone = 1; } } if( bDone==0 ){ fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); } if( pPrevIter ){ if( pPrevIter->pTombArray ){ pNewIter->pTombArray = pPrevIter->pTombArray; pNewIter->pTombArray->nRef++; } }else{ fts5SegIterAllocTombstone(p, pNewIter); } pNewIter++; if( pPrevIter ) pPrevIter++; if( p->rc ) break; } } fts5TokendataSetTermIfEof(pPrev, pSmall); pNew->bSkipEmpty = 1; pNew->pColset = pColset; fts5IterSetOutputCb(&p->rc, pNew); /* Loop through all segments in the new iterator. Find the smallest ** term that any segment-iterator points to. Iterator pNew will be ** used for this term. Also, set any iterator that points to a term that ** does not match pToken/nToken to point to EOF */ pSmall = 0; for(ii=0; ii<pNew->nSeg; ii++){ Fts5SegIter *pII = &pNew->aSeg[ii]; if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){ fts5SegIterSetEOF(pII); } if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){ pSmall = &pII->term; } } /* If pSmall is still NULL at this point, then the new iterator does ** not point to any terms that match the query. So delete it and break ** out of the loop - all required iterators have been collected. */ if( pSmall==0 ){ sqlite3Fts5IterClose((Fts5IndexIter*)pNew); break; } /* Append this iterator to the set and continue. */ pSet = fts5AppendTokendataIter(p, pSet, pNew); } if( p->rc==SQLITE_OK && pSet ){ int ii; for(ii=0; ii<pSet->nIter; ii++){ Fts5Iter *pIter = pSet->apIter[ii]; int iSeg; for(iSeg=0; iSeg<pIter->nSeg; iSeg++){ pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM; } fts5MultiIterFinishSetup(p, pIter); } } if( p->rc==SQLITE_OK ){ pRet = fts5MultiIterAlloc(p, 0); } if( pRet ){ pRet->pTokenDataIter = pSet; if( pSet ){ fts5IterSetOutputsTokendata(pRet); }else{ pRet->base.bEof = 1; } }else{ fts5TokendataIterDelete(pSet); } fts5StructureRelease(pStruct); fts5BufferFree(&bSeek); return pRet; } /* ** Open a new iterator to iterate though all rowid that match the ** specified token or token prefix. */ int sqlite3Fts5IndexQuery( Fts5Index *p, /* FTS index to query */ |
︙ | ︙ | |||
6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 | /* If the QUERY_SCAN flag is set, all other flags must be clear. */ assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN ); if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ int iIdx = 0; /* Index to search */ int iPrefixIdx = 0; /* +1 prefix index */ if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); /* Figure out which index to search and set iIdx accordingly. If this ** is a prefix query for which there is no prefix index, set iIdx to ** greater than pConfig->nPrefix to indicate that the query will be ** satisfied by scanning multiple terms in the main index. ** ** If the QUERY_TEST_NOIDX flag was specified, then this must be a | > > > > > | 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 | /* If the QUERY_SCAN flag is set, all other flags must be clear. */ assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN ); if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ int iIdx = 0; /* Index to search */ int iPrefixIdx = 0; /* +1 prefix index */ int bTokendata = pConfig->bTokendata; if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){ bTokendata = 0; } /* Figure out which index to search and set iIdx accordingly. If this ** is a prefix query for which there is no prefix index, set iIdx to ** greater than pConfig->nPrefix to indicate that the query will be ** satisfied by scanning multiple terms in the main index. ** ** If the QUERY_TEST_NOIDX flag was specified, then this must be a |
︙ | ︙ | |||
6465 6466 6467 6468 6469 6470 6471 | for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){ int nIdxChar = pConfig->aPrefix[iIdx-1]; if( nIdxChar==nChar ) break; if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; } } | > > > | | 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 | for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){ int nIdxChar = pConfig->aPrefix[iIdx-1]; if( nIdxChar==nChar ) break; if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; } } if( bTokendata && iIdx==0 ){ buf.p[0] = '0'; pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); }else if( iIdx<=pConfig->nPrefix ){ /* Straight index lookup */ Fts5Structure *pStruct = fts5StructureRead(p); buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); if( pStruct ){ fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY, pColset, buf.p, nToken+1, -1, 0, &pRet ); |
︙ | ︙ | |||
6512 6513 6514 6515 6516 6517 6518 | */ /* ** Move to the next matching rowid. */ int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; assert( pIter->pIndex->rc==SQLITE_OK ); | > > > | > | 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 | */ /* ** Move to the next matching rowid. */ int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; assert( pIter->pIndex->rc==SQLITE_OK ); if( pIter->pTokenDataIter ){ fts5TokendataIterNext(pIter, 0, 0); }else{ fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); } return fts5IndexReturn(pIter->pIndex); } /* ** Move to the next matching term/rowid. Used by the fts5vocab module. */ int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){ |
︙ | ︙ | |||
6545 6546 6547 6548 6549 6550 6551 | /* ** Move to the next matching rowid that occurs at or after iMatch. The ** definition of "at or after" depends on whether this iterator iterates ** in ascending or descending rowid order. */ int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 | /* ** Move to the next matching rowid that occurs at or after iMatch. The ** definition of "at or after" depends on whether this iterator iterates ** in ascending or descending rowid order. */ int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; if( pIter->pTokenDataIter ){ fts5TokendataIterNext(pIter, 1, iMatch); }else{ fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); } return fts5IndexReturn(pIter->pIndex); } /* ** Return the current term. */ const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ int n; const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); assert_nc( z || n<=1 ); *pn = n-1; return (z ? &z[1] : 0); } /* ** This is used by xInstToken() to access the token at offset iOff, column ** iCol of row iRowid. The token is returned via output variables *ppOut ** and *pnOut. The iterator passed as the first argument must be a tokendata=1 ** iterator (pIter->pTokenDataIter!=0). */ int sqlite3Fts5IterToken( Fts5IndexIter *pIndexIter, i64 iRowid, int iCol, int iOff, const char **ppOut, int *pnOut ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; Fts5TokenDataIter *pT = pIter->pTokenDataIter; Fts5TokenDataMap *aMap = pT->aMap; i64 iPos = (((i64)iCol)<<32) + iOff; int i1 = 0; int i2 = pT->nMap; int iTest = 0; while( i2>i1 ){ iTest = (i1 + i2) / 2; if( aMap[iTest].iRowid<iRowid ){ i1 = iTest+1; }else if( aMap[iTest].iRowid>iRowid ){ i2 = iTest; }else{ if( aMap[iTest].iPos<iPos ){ if( aMap[iTest].iPos<0 ){ break; } i1 = iTest+1; }else if( aMap[iTest].iPos>iPos ){ i2 = iTest; }else{ break; } } } if( i2>i1 ){ Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; *ppOut = (const char*)pMap->aSeg[0].term.p+1; *pnOut = pMap->aSeg[0].term.n-1; } return SQLITE_OK; } /* ** Clear any existing entries from the token-map associated with the ** iterator passed as the only argument. */ void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; if( pIter && pIter->pTokenDataIter ){ pIter->pTokenDataIter->nMap = 0; } } /* ** Set a token-mapping for the iterator passed as the first argument. This ** is used in detail=column or detail=none mode when a token is requested ** using the xInstToken() API. In this case the caller tokenizers the ** current row and configures the token-mapping via multiple calls to this ** function. */ int sqlite3Fts5IndexIterWriteTokendata( Fts5IndexIter *pIndexIter, const char *pToken, int nToken, i64 iRowid, int iCol, int iOff ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; Fts5TokenDataIter *pT = pIter->pTokenDataIter; Fts5Index *p = pIter->pIndex; int ii; assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); assert( pIter->pTokenDataIter ); for(ii=0; ii<pT->nIter; ii++){ Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; } if( ii<pT->nIter ){ fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff); } return fts5IndexReturn(p); } /* ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery(). */ void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ if( pIndexIter ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; Fts5Index *pIndex = pIter->pIndex; fts5TokendataIterDelete(pIter->pTokenDataIter); fts5MultiIterFree(pIter); sqlite3Fts5IndexCloseReader(pIndex); } } /* ** Read and decode the "averages" record from the database. |
︙ | ︙ | |||
7074 7075 7076 7077 7078 7079 7080 | int n, /* Size of index key in bytes */ int flags, /* Flags for Fts5IndexQuery */ u64 *pCksum /* IN/OUT: Checksum value */ ){ int eDetail = p->pConfig->eDetail; u64 cksum = *pCksum; Fts5IndexIter *pIter = 0; | | > > | 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 | int n, /* Size of index key in bytes */ int flags, /* Flags for Fts5IndexQuery */ u64 *pCksum /* IN/OUT: Checksum value */ ){ int eDetail = p->pConfig->eDetail; u64 cksum = *pCksum; Fts5IndexIter *pIter = 0; int rc = sqlite3Fts5IndexQuery( p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter ); while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){ i64 rowid = pIter->iRowid; if( eDetail==FTS5_DETAIL_NONE ){ cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n); }else{ |
︙ | ︙ | |||
7241 7242 7243 7244 7245 7246 7247 | if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT; } fts5DataRelease(pLeaf); } } static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){ | | | | 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965 7966 | if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT; } fts5DataRelease(pLeaf); } } static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){ i64 iTermOff = 0; int ii; Fts5Buffer buf1 = {0,0,0}; Fts5Buffer buf2 = {0,0,0}; ii = pLeaf->szLeaf; while( ii<pLeaf->nn && p->rc==SQLITE_OK ){ int res; i64 iOff; int nIncr; ii += fts5GetVarint32(&pLeaf->p[ii], nIncr); iTermOff += nIncr; iOff = iTermOff; if( iOff>=pLeaf->szLeaf ){ |
︙ | ︙ | |||
7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 | } } sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); } } #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** The implementation of user-defined scalar function fts5_decode(). */ static void fts5DecodeFunction( sqlite3_context *pCtx, /* Function call context */ | > > > > > > > > > > > > > > > > > > | 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 | } } sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); } } #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){ int ii; fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1); if( *pRc==SQLITE_OK ){ for(ii=0; ii<pTerm->n; ii++){ if( pTerm->p[ii]==0x00 ){ pBuf->p[pBuf->n++] = '\\'; pBuf->p[pBuf->n++] = '0'; }else{ pBuf->p[pBuf->n++] = pTerm->p[ii]; } } pBuf->p[pBuf->n] = 0x00; } } #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** The implementation of user-defined scalar function fts5_decode(). */ static void fts5DecodeFunction( sqlite3_context *pCtx, /* Function call context */ |
︙ | ︙ | |||
7879 7880 7881 7882 7883 7884 7885 | while( iOff<szLeaf && rc==SQLITE_OK ){ int nAppend; /* Read the term data for the next term*/ iOff += fts5GetVarint32(&a[iOff], nAppend); term.n = nKeep; fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]); | | | < | 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 | while( iOff<szLeaf && rc==SQLITE_OK ){ int nAppend; /* Read the term data for the next term*/ iOff += fts5GetVarint32(&a[iOff], nAppend); term.n = nKeep; fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]); sqlite3Fts5BufferAppendPrintf(&rc, &s, " term="); fts5BufferAppendTerm(&rc, &s, &term); iOff += nAppend; /* Figure out where the doclist for this term ends */ if( iPgidxOff<n ){ int nIncr; iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nIncr); iTermOff += nIncr; |
︙ | ︙ | |||
7989 7990 7991 7992 7993 7994 7995 | if( iOff+nByte>n ){ rc = FTS5_CORRUPT; break; } fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]); iOff += nByte; | | | < | 8708 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 | if( iOff+nByte>n ){ rc = FTS5_CORRUPT; break; } fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]); iOff += nByte; sqlite3Fts5BufferAppendPrintf(&rc, &s, " term="); fts5BufferAppendTerm(&rc, &s, &term); iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff); } fts5BufferFree(&term); } decode_out: |
︙ | ︙ |
Changes to ext/fts5/fts5_main.c.
︙ | ︙ | |||
114 115 116 117 118 119 120 | struct Fts5FullTable { Fts5Table p; /* Public class members from fts5Int.h */ Fts5Storage *pStorage; /* Document store */ Fts5Global *pGlobal; /* Global (connection wide) data */ Fts5Cursor *pSortCsr; /* Sort data from this cursor */ int iSavepoint; /* Successful xSavepoint()+1 */ | | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | struct Fts5FullTable { Fts5Table p; /* Public class members from fts5Int.h */ Fts5Storage *pStorage; /* Document store */ Fts5Global *pGlobal; /* Global (connection wide) data */ Fts5Cursor *pSortCsr; /* Sort data from this cursor */ int iSavepoint; /* Successful xSavepoint()+1 */ #ifdef SQLITE_DEBUG struct Fts5TransactionState ts; #endif }; struct Fts5MatchPhrase { Fts5Buffer *pPoslist; /* Pointer to current poslist */ |
︙ | ︙ | |||
652 653 654 655 656 657 658 | bSeenGt = 1; } } } } idxStr[iIdxStr] = '\0'; | | > > > | | 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | bSeenGt = 1; } } } } idxStr[iIdxStr] = '\0'; /* Set idxFlags flags for the ORDER BY clause ** ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC". */ if( pInfo->nOrderBy==1 ){ int iSort = pInfo->aOrderBy[0].iColumn; if( iSort==(pConfig->nCol+1) && bSeenMatch ){ idxFlags |= FTS5_BI_ORDER_RANK; }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){ idxFlags |= FTS5_BI_ORDER_ROWID; } if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){ pInfo->orderByConsumed = 1; if( pInfo->aOrderBy[0].desc ){ idxFlags |= FTS5_BI_ORDER_DESC; } |
︙ | ︙ | |||
908 909 910 911 912 913 914 915 916 917 918 919 920 921 | Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc; assert( (pCsr->ePlan<3)== (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE) ); assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) ); if( pCsr->ePlan<3 ){ int bSkip = 0; if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid); CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr)); fts5CsrNewrow(pCsr); | > > > > > > > > > > | 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 | Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc; assert( (pCsr->ePlan<3)== (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE) ); assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) ); /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table, ** clear any token mappings accumulated at the fts5_index.c level. In ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH, ** we need to retain the mappings for the entire query. */ if( pCsr->ePlan==FTS5_PLAN_MATCH && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata ){ sqlite3Fts5ExprClearTokens(pCsr->pExpr); } if( pCsr->ePlan<3 ){ int bSkip = 0; if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid); CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr)); fts5CsrNewrow(pCsr); |
︙ | ︙ | |||
1569 1570 1571 1572 1573 1574 1575 | #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{ |
︙ | ︙ | |||
1894 1895 1896 1897 1898 1899 1900 | Fts5Context *pCtx, int iCol, const char **pz, int *pn ){ int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | > > > | | 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 | Fts5Context *pCtx, int iCol, const char **pz, int *pn ){ int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); if( iCol<0 || iCol>=pTab->pConfig->nCol ){ rc = SQLITE_RANGE; }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) || pCsr->ePlan==FTS5_PLAN_SPECIAL ){ *pz = 0; *pn = 0; }else{ rc = fts5SeekCursor(pCsr, 0); if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
1919 1920 1921 1922 1923 1924 1925 | const u8 **pa, int *pn ){ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; int rc = SQLITE_OK; int bLive = (pCsr->pSorter==0); | > > | < | 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 | const u8 **pa, int *pn ){ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; int rc = SQLITE_OK; int bLive = (pCsr->pSorter==0); if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ rc = SQLITE_RANGE; }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ Fts5PoslistPopulator *aPopulator; int i; aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive); if( aPopulator==0 ) rc = SQLITE_NOMEM; for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){ int n; const char *z; |
︙ | ︙ | |||
1944 1945 1946 1947 1948 1949 1950 | if( pCsr->pSorter ){ sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); } } CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST); } | > | | | | | | | | > > > > > | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 | if( pCsr->pSorter ){ sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); } } CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST); } if( rc==SQLITE_OK ){ if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ Fts5Sorter *pSorter = pCsr->pSorter; int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); *pn = pSorter->aIdx[iPhrase] - i1; *pa = &pSorter->aPoslist[i1]; }else{ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); } }else{ *pa = 0; *pn = 0; } return rc; } /* ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated ** correctly for the current view. Return SQLITE_OK if successful, or an |
︙ | ︙ | |||
2059 2060 2061 2062 2063 2064 2065 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; int rc = SQLITE_OK; if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ if( iIdx<0 || iIdx>=pCsr->nInstCount ){ rc = SQLITE_RANGE; | < < < < < < | 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; int rc = SQLITE_OK; if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ if( iIdx<0 || iIdx>=pCsr->nInstCount ){ rc = SQLITE_RANGE; }else{ *piPhrase = pCsr->aInst[iIdx*3]; *piCol = pCsr->aInst[iIdx*3 + 1]; *piOff = pCsr->aInst[iIdx*3 + 2]; } } return rc; |
︙ | ︙ | |||
2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 | } } } return rc; } static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); static const Fts5ExtensionApi sFts5Api = { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 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 | } } } return rc; } /* ** xQueryToken() API implemenetation. */ static int fts5ApiQueryToken( Fts5Context* pCtx, int iPhrase, int iToken, const char **ppOut, int *pnOut ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut); } /* ** xInstToken() API implemenetation. */ static int fts5ApiInstToken( Fts5Context *pCtx, int iIdx, int iToken, const char **ppOut, int *pnOut ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; int rc = SQLITE_OK; if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ if( iIdx<0 || iIdx>=pCsr->nInstCount ){ rc = SQLITE_RANGE; }else{ int iPhrase = pCsr->aInst[iIdx*3]; int iCol = pCsr->aInst[iIdx*3 + 1]; int iOff = pCsr->aInst[iIdx*3 + 2]; i64 iRowid = fts5CursorRowid(pCsr); rc = sqlite3Fts5ExprInstToken( pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut ); } } return rc; } static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); static const Fts5ExtensionApi sFts5Api = { 3, /* iVersion */ fts5ApiUserData, fts5ApiColumnCount, fts5ApiRowCount, fts5ApiColumnTotalSize, fts5ApiTokenize, fts5ApiPhraseCount, fts5ApiPhraseSize, fts5ApiInstCount, fts5ApiInst, fts5ApiRowid, fts5ApiColumnText, fts5ApiColumnSize, fts5ApiQueryPhrase, fts5ApiSetAuxdata, fts5ApiGetAuxdata, fts5ApiPhraseFirst, fts5ApiPhraseNext, fts5ApiPhraseFirstColumn, fts5ApiPhraseNextColumn, fts5ApiQueryToken, fts5ApiInstToken }; /* ** Implementation of API function xQueryPhrase(). */ static int fts5ApiQueryPhrase( Fts5Context *pCtx, |
︙ | ︙ | |||
2611 2612 2613 2614 2615 2616 2617 | */ static int fts5RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ int rc; Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | < < | | < < < < < < < < < < < < | | | < < | 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 | */ static int fts5RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ int rc; Fts5FullTable *pTab = (Fts5FullTable*)pVtab; rc = sqlite3Fts5StorageRename(pTab->pStorage, zName); return rc; } int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ fts5TripCursors((Fts5FullTable*)pTab); return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage); } /* ** The xSavepoint() method. ** ** Flush the contents of the pending-terms table to disk. */ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; int rc = SQLITE_OK; fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); if( rc==SQLITE_OK ){ pTab->iSavepoint = iSavepoint+1; } return rc; } /* ** The xRelease() method. ** ** This is a no-op. |
︙ | ︙ | |||
2681 2682 2683 2684 2685 2686 2687 | ** 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. |
︙ | ︙ | |||
2910 2911 2912 2913 2914 2915 2916 | } /* ** Run an integrity check on the FTS5 data structures. Return a string ** if anything is found amiss. Return a NULL pointer if everything is ** OK. */ | | < < < > | < < < < < > | > | | | 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 | } /* ** Run an integrity check on the FTS5 data structures. Return a string ** if anything is found amiss. Return a NULL pointer if everything is ** OK. */ static int fts5IntegrityMethod( 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, |
︙ | ︙ | |||
2968 2969 2970 2971 2972 2973 2974 | /* xRollback */ fts5RollbackMethod, /* xFindFunction */ fts5FindFunctionMethod, /* xRename */ fts5RenameMethod, /* xSavepoint */ fts5SavepointMethod, /* xRelease */ fts5ReleaseMethod, /* xRollbackTo */ fts5RollbackToMethod, /* xShadowName */ fts5ShadowName, | | | 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 | /* xRollback */ fts5RollbackMethod, /* xFindFunction */ fts5FindFunctionMethod, /* xRename */ fts5RenameMethod, /* xSavepoint */ fts5SavepointMethod, /* xRelease */ fts5ReleaseMethod, /* xRollbackTo */ fts5RollbackToMethod, /* xShadowName */ fts5ShadowName, /* xIntegrity */ fts5IntegrityMethod }; int rc; Fts5Global *pGlobal = 0; pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global)); if( pGlobal==0 ){ |
︙ | ︙ |
Changes to ext/fts5/fts5_storage.c.
︙ | ︙ | |||
669 670 671 672 673 674 675 | ctx.pStorage = p; rc = sqlite3Fts5StorageDeleteAll(p); if( rc==SQLITE_OK ){ rc = fts5StorageLoadTotals(p, 1); } if( rc==SQLITE_OK ){ | | | 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 | ctx.pStorage = p; rc = sqlite3Fts5StorageDeleteAll(p); if( rc==SQLITE_OK ){ rc = fts5StorageLoadTotals(p, 1); } if( rc==SQLITE_OK ){ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg); } while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){ i64 iRowid = sqlite3_column_int64(pScan, 0); sqlite3Fts5BufferZero(&buf); rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); |
︙ | ︙ |
Changes to ext/fts5/fts5_tcl.c.
︙ | ︙ | |||
240 241 242 243 244 245 246 247 248 249 250 251 252 253 | { "xQueryPhrase", 2, "PHRASE SCRIPT" }, /* 11 */ { "xSetAuxdata", 1, "VALUE" }, /* 12 */ { "xGetAuxdata", 1, "CLEAR" }, /* 13 */ { "xSetAuxdataInt", 1, "INTEGER" }, /* 14 */ { "xGetAuxdataInt", 1, "CLEAR" }, /* 15 */ { "xPhraseForeach", 4, "IPHRASE COLVAR OFFVAR SCRIPT" }, /* 16 */ { "xPhraseColumnForeach", 3, "IPHRASE COLVAR SCRIPT" }, /* 17 */ { 0, 0, 0} }; int rc; int iSub = 0; F5tApi *p = (F5tApi*)clientData; | > > > | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | { "xQueryPhrase", 2, "PHRASE SCRIPT" }, /* 11 */ { "xSetAuxdata", 1, "VALUE" }, /* 12 */ { "xGetAuxdata", 1, "CLEAR" }, /* 13 */ { "xSetAuxdataInt", 1, "INTEGER" }, /* 14 */ { "xGetAuxdataInt", 1, "CLEAR" }, /* 15 */ { "xPhraseForeach", 4, "IPHRASE COLVAR OFFVAR SCRIPT" }, /* 16 */ { "xPhraseColumnForeach", 3, "IPHRASE COLVAR SCRIPT" }, /* 17 */ { "xQueryToken", 2, "IPHRASE ITERM" }, /* 18 */ { "xInstToken", 2, "IDX ITERM" }, /* 19 */ { 0, 0, 0} }; int rc; int iSub = 0; F5tApi *p = (F5tApi*)clientData; |
︙ | ︙ | |||
492 493 494 495 496 497 498 499 500 501 502 503 504 505 | rc = Tcl_EvalObjEx(interp, pScript, 0); if( rc==TCL_CONTINUE ) rc = TCL_OK; if( rc!=TCL_OK ){ if( rc==TCL_BREAK ) rc = TCL_OK; break; } } break; } default: assert( 0 ); break; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | rc = Tcl_EvalObjEx(interp, pScript, 0); if( rc==TCL_CONTINUE ) rc = TCL_OK; if( rc!=TCL_OK ){ if( rc==TCL_BREAK ) rc = TCL_OK; break; } } break; } CASE(18, "xQueryToken") { const char *pTerm = 0; int nTerm = 0; int iPhrase = 0; int iTerm = 0; if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[3], &iTerm) ) return TCL_ERROR; rc = p->pApi->xQueryToken(p->pFts, iPhrase, iTerm, &pTerm, &nTerm); if( rc==SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(pTerm, nTerm)); } break; } CASE(19, "xInstToken") { const char *pTerm = 0; int nTerm = 0; int iIdx = 0; int iTerm = 0; if( Tcl_GetIntFromObj(interp, objv[2], &iIdx) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[3], &iTerm) ) return TCL_ERROR; rc = p->pApi->xInstToken(p->pFts, iIdx, iTerm, &pTerm, &nTerm); if( rc==SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(pTerm, nTerm)); } break; } default: assert( 0 ); break; |
︙ | ︙ | |||
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 | rc = sqlite3Fts5TestRegisterTok(db, pApi); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_ERROR; } return TCL_OK; } /* ** Entry point. */ int Fts5tcl_Init(Tcl_Interp *interp){ static struct Cmd { char *zName; Tcl_ObjCmdProc *xProc; int bTokenizeCtx; } aCmd[] = { { "sqlite3_fts5_create_tokenizer", f5tCreateTokenizer, 1 }, { "sqlite3_fts5_token", f5tTokenizerReturn, 1 }, { "sqlite3_fts5_tokenize", f5tTokenize, 0 }, { "sqlite3_fts5_create_function", f5tCreateFunction, 0 }, { "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt, 0 }, { "sqlite3_fts5_token_hash", f5tTokenHash, 0 }, { "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 }, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 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 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 | rc = sqlite3Fts5TestRegisterTok(db, pApi); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_ERROR; } return TCL_OK; } typedef struct OriginTextCtx OriginTextCtx; struct OriginTextCtx { sqlite3 *db; fts5_api *pApi; }; typedef struct OriginTextTokenizer OriginTextTokenizer; struct OriginTextTokenizer { Fts5Tokenizer *pTok; /* Underlying tokenizer object */ fts5_tokenizer tokapi; /* API implementation for pTok */ }; /* ** 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 ){ OriginTextCtx *p = (OriginTextCtx*)pCtx; OriginTextTokenizer *pTok = 0; void *pTokCtx = 0; int rc = SQLITE_OK; pTok = (OriginTextTokenizer*)sqlite3_malloc(sizeof(OriginTextTokenizer)); if( pTok==0 ){ rc = SQLITE_NOMEM; }else if( nArg<1 ){ rc = SQLITE_ERROR; }else{ /* Locate the underlying tokenizer */ rc = p->pApi->xFindTokenizer(p->pApi, azArg[0], &pTokCtx, &pTok->tokapi); } /* Create the new tokenizer instance */ if( rc==SQLITE_OK ){ rc = pTok->tokapi.xCreate(pTokCtx, &azArg[1], nArg-1, &pTok->pTok); } if( rc!=SQLITE_OK ){ sqlite3_free(pTok); pTok = 0; } *ppOut = (Fts5Tokenizer*)pTok; return rc; } static void f5tOrigintextDelete(Fts5Tokenizer *pTokenizer){ OriginTextTokenizer *p = (OriginTextTokenizer*)pTokenizer; if( p->pTok ){ p->tokapi.xDelete(p->pTok); } sqlite3_free(p); } typedef struct OriginTextCb OriginTextCb; struct OriginTextCb { void *pCtx; const char *pText; int nText; int (*xToken)(void *, int, const char *, int, int, int); char *aBuf; /* Buffer to use */ int nBuf; /* Allocated size of aBuf[] */ }; static int xOriginToken( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input */ ){ OriginTextCb *p = (OriginTextCb*)pCtx; int ret = 0; if( nToken==(iEnd-iStart) && 0==memcmp(pToken, &p->pText[iStart], nToken) ){ /* Token exactly matches document text. Pass it through as is. */ ret = p->xToken(p->pCtx, tflags, pToken, nToken, iStart, iEnd); }else{ int nReq = nToken + 1 + (iEnd-iStart); if( nReq>p->nBuf ){ sqlite3_free(p->aBuf); p->aBuf = sqlite3_malloc(nReq*2); if( p->aBuf==0 ) return SQLITE_NOMEM; p->nBuf = nReq*2; } memcpy(p->aBuf, pToken, nToken); p->aBuf[nToken] = '\0'; memcpy(&p->aBuf[nToken+1], &p->pText[iStart], iEnd-iStart); ret = p->xToken(p->pCtx, tflags, p->aBuf, nReq, iStart, iEnd); } return ret; } static int f5tOrigintextTokenize( Fts5Tokenizer *pTokenizer, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, int (*xToken)(void *, int, const char *, int, int, int) ){ OriginTextTokenizer *p = (OriginTextTokenizer*)pTokenizer; OriginTextCb cb; int ret; memset(&cb, 0, sizeof(cb)); cb.pCtx = pCtx; cb.pText = pText; cb.nText = nText; cb.xToken = xToken; ret = p->tokapi.xTokenize(p->pTok,(void*)&cb,flags,pText,nText,xOriginToken); sqlite3_free(cb.aBuf); return ret; } /* ** sqlite3_fts5_register_origintext DB ** ** Description... */ static int SQLITE_TCLAPI f5tRegisterOriginText( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3 *db = 0; fts5_api *pApi = 0; int rc; fts5_tokenizer tok = {0, 0, 0}; OriginTextCtx *pCtx = 0; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( f5tDbAndApi(interp, objv[1], &db, &pApi) ) return TCL_ERROR; pCtx = (OriginTextCtx*)ckalloc(sizeof(OriginTextCtx)); pCtx->db = db; pCtx->pApi = pApi; tok.xCreate = f5tOrigintextCreate; tok.xDelete = f5tOrigintextDelete; tok.xTokenize = f5tOrigintextTokenize; rc = pApi->xCreateTokenizer( pApi, "origintext", (void*)pCtx, &tok, f5tOrigintextTokenizerDelete ); Tcl_ResetResult(interp); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); return TCL_ERROR; } return TCL_OK; } /* ** Entry point. */ int Fts5tcl_Init(Tcl_Interp *interp){ static struct Cmd { char *zName; Tcl_ObjCmdProc *xProc; int bTokenizeCtx; } aCmd[] = { { "sqlite3_fts5_create_tokenizer", f5tCreateTokenizer, 1 }, { "sqlite3_fts5_token", f5tTokenizerReturn, 1 }, { "sqlite3_fts5_tokenize", f5tTokenize, 0 }, { "sqlite3_fts5_create_function", f5tCreateFunction, 0 }, { "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt, 0 }, { "sqlite3_fts5_token_hash", f5tTokenHash, 0 }, { "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 }, { "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok, 0 }, { "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 } }; int i; F5tTokenizerContext *pContext; pContext = (F5tTokenizerContext*)ckalloc(sizeof(F5tTokenizerContext)); memset(pContext, 0, sizeof(*pContext)); |
︙ | ︙ |
Changes to ext/fts5/fts5_tokenize.c.
︙ | ︙ | |||
224 225 226 227 228 229 230 231 232 233 234 235 236 237 | *zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F); \ *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \ *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \ } \ } #endif /* ifndef SQLITE_AMALGAMATION */ typedef struct Unicode61Tokenizer Unicode61Tokenizer; struct Unicode61Tokenizer { unsigned char aTokenChar[128]; /* ASCII range token characters */ char *aFold; /* Buffer to fold text into */ int nFold; /* Size of aFold[] in bytes */ int eRemoveDiacritic; /* True if remove_diacritics=1 is set */ | > > > > > > | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | *zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F); \ *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \ *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \ } \ } #endif /* ifndef SQLITE_AMALGAMATION */ #define FTS5_SKIP_UTF8(zIn) { \ if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \ while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \ } \ } typedef struct Unicode61Tokenizer Unicode61Tokenizer; struct Unicode61Tokenizer { unsigned char aTokenChar[128]; /* ASCII range token characters */ char *aFold; /* Buffer to fold text into */ int nFold; /* Size of aFold[] in bytes */ int eRemoveDiacritic; /* True if remove_diacritics=1 is set */ |
︙ | ︙ | |||
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 | /************************************************************************** ** Start of trigram implementation. */ typedef struct TrigramTokenizer TrigramTokenizer; struct TrigramTokenizer { int bFold; /* True to fold to lower-case */ }; /* ** Free a trigram tokenizer. */ static void fts5TriDelete(Fts5Tokenizer *p){ sqlite3_free(p); | > | 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 | /************************************************************************** ** Start of trigram implementation. */ typedef struct TrigramTokenizer TrigramTokenizer; struct TrigramTokenizer { int bFold; /* True to fold to lower-case */ int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */ }; /* ** Free a trigram tokenizer. */ static void fts5TriDelete(Fts5Tokenizer *p){ sqlite3_free(p); |
︙ | ︙ | |||
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 | TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); UNUSED_PARAM(pUnused); if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ int i; pNew->bFold = 1; for(i=0; rc==SQLITE_OK && i<nArg; i+=2){ const char *zArg = azArg[i+1]; if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){ rc = SQLITE_ERROR; }else{ pNew->bFold = (zArg[0]=='0'); } }else{ rc = SQLITE_ERROR; } } if( rc!=SQLITE_OK ){ fts5TriDelete((Fts5Tokenizer*)pNew); pNew = 0; } } *ppOut = (Fts5Tokenizer*)pNew; return rc; | > > > > > > > > > > > > | 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 | TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); UNUSED_PARAM(pUnused); if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ int i; pNew->bFold = 1; pNew->iFoldParam = 0; for(i=0; rc==SQLITE_OK && i<nArg; i+=2){ const char *zArg = azArg[i+1]; if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){ rc = SQLITE_ERROR; }else{ pNew->bFold = (zArg[0]=='0'); } }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ rc = SQLITE_ERROR; }else{ pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; } }else{ rc = SQLITE_ERROR; } } if( pNew->iFoldParam!=0 && pNew->bFold==0 ){ rc = SQLITE_ERROR; } if( rc!=SQLITE_OK ){ fts5TriDelete((Fts5Tokenizer*)pNew); pNew = 0; } } *ppOut = (Fts5Tokenizer*)pNew; return rc; |
︙ | ︙ | |||
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 | int unusedFlags, const char *pText, int nText, int (*xToken)(void*, int, const char*, int, int, int) ){ TrigramTokenizer *p = (TrigramTokenizer*)pTok; int rc = SQLITE_OK; char aBuf[32]; const unsigned char *zIn = (const unsigned char*)pText; const unsigned char *zEof = &zIn[nText]; u32 iCode; UNUSED_PARAM(unusedFlags); while( 1 ){ | > > > > > > > > > > > > > > > > > > > > > > > > | | < | > > | | < < | | | > > | < < | | | > | | < | | < < | > | < > | | 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 | int unusedFlags, const char *pText, int nText, int (*xToken)(void*, int, const char*, int, int, int) ){ TrigramTokenizer *p = (TrigramTokenizer*)pTok; int rc = SQLITE_OK; char aBuf[32]; char *zOut = aBuf; int ii; const unsigned char *zIn = (const unsigned char*)pText; const unsigned char *zEof = &zIn[nText]; u32 iCode; int aStart[3]; /* Input offset of each character in aBuf[] */ UNUSED_PARAM(unusedFlags); /* Populate aBuf[] with the characters for the first trigram. */ for(ii=0; ii<3; ii++){ do { aStart[ii] = zIn - (const unsigned char*)pText; READ_UTF8(zIn, zEof, iCode); if( iCode==0 ) return SQLITE_OK; if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); }while( iCode==0 ); WRITE_UTF8(zOut, iCode); } /* At the start of each iteration of this loop: ** ** aBuf: Contains 3 characters. The 3 characters of the next trigram. ** zOut: Points to the byte following the last character in aBuf. ** aStart[3]: Contains the byte offset in the input text corresponding ** to the start of each of the three characters in the buffer. */ assert( zIn<=zEof ); while( 1 ){ int iNext; /* Start of character following current tri */ const char *z1; /* Read characters from the input up until the first non-diacritic */ do { iNext = zIn - (const unsigned char*)pText; READ_UTF8(zIn, zEof, iCode); if( iCode==0 ) break; if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); }while( iCode==0 ); /* Pass the current trigram back to fts5 */ rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); if( iCode==0 || rc!=SQLITE_OK ) break; /* Remove the first character from buffer aBuf[]. Append the character ** with codepoint iCode. */ z1 = aBuf; FTS5_SKIP_UTF8(z1); memmove(aBuf, z1, zOut - z1); zOut -= (z1 - aBuf); WRITE_UTF8(zOut, iCode); /* Update the aStart[] array */ aStart[0] = aStart[1]; aStart[1] = aStart[2]; aStart[2] = iNext; } return rc; } /* ** Argument xCreate is a pointer to a constructor function for a tokenizer. |
︙ | ︙ | |||
1376 1377 1378 1379 1380 1381 1382 | */ int sqlite3Fts5TokenizerPattern( int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), Fts5Tokenizer *pTok ){ if( xCreate==fts5TriCreate ){ TrigramTokenizer *p = (TrigramTokenizer*)pTok; | > | > | 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 | */ int sqlite3Fts5TokenizerPattern( int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), Fts5Tokenizer *pTok ){ if( xCreate==fts5TriCreate ){ TrigramTokenizer *p = (TrigramTokenizer*)pTok; if( p->iFoldParam==0 ){ return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; } } return FTS5_PATTERN_NONE; } /* ** Register all built-in tokenizers with FTS5. */ |
︙ | ︙ |
Changes to ext/fts5/fts5_vocab.c.
︙ | ︙ | |||
625 626 627 628 629 630 631 | if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++]; if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++]; if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++]; if( pEq ){ zTerm = (const char *)sqlite3_value_text(pEq); nTerm = sqlite3_value_bytes(pEq); | | | 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 | if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++]; if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++]; if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++]; if( pEq ){ zTerm = (const char *)sqlite3_value_text(pEq); nTerm = sqlite3_value_bytes(pEq); f = FTS5INDEX_QUERY_NOTOKENDATA; }else{ if( pGe ){ zTerm = (const char *)sqlite3_value_text(pGe); nTerm = sqlite3_value_bytes(pGe); } if( pLe ){ const char *zCopy = (const char *)sqlite3_value_text(pLe); |
︙ | ︙ |
Changes to ext/fts5/test/fts5_common.tcl.
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | for {set i 0} {$i < [$cmd xPhraseCount]} {incr i} { $cmd xPhraseColumnForeach $i c { lappend res $i.$c } } set res } proc fts5_test_columnsize {cmd} { set res [list] for {set i 0} {$i < [$cmd xColumnCount]} {incr i} { lappend res [$cmd xColumnSize $i] } set res } proc fts5_test_columntext {cmd} { set res [list] for {set i 0} {$i < [$cmd xColumnCount]} {incr i} { lappend res [$cmd xColumnText $i] } set res | > > > > > > > > > > | 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 | for {set i 0} {$i < [$cmd xPhraseCount]} {incr i} { $cmd xPhraseColumnForeach $i c { lappend res $i.$c } } set res } proc fts5_collist {cmd iPhrase} { set res [list] $cmd xPhraseColumnForeach $iPhrase c { lappend res $c } set res } proc fts5_test_columnsize {cmd} { set res [list] for {set i 0} {$i < [$cmd xColumnCount]} {incr i} { lappend res [$cmd xColumnSize $i] } set res } proc fts5_columntext {cmd iCol} { $cmd xColumnText $iCol } proc fts5_test_columntext {cmd} { set res [list] for {set i 0} {$i < [$cmd xColumnCount]} {incr i} { lappend res [$cmd xColumnText $i] } set res |
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 | set cnt [list] for {set j 0} {$j < [$cmd xColumnCount]} {incr j} { lappend cnt 0 } $cmd xQueryPhrase $i [list test_queryphrase_cb cnt] lappend res $cnt } set res } proc fts5_test_phrasecount {cmd} { $cmd xPhraseCount } proc fts5_test_all {cmd} { set res [list] | > > > > > > > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | set cnt [list] for {set j 0} {$j < [$cmd xColumnCount]} {incr j} { lappend cnt 0 } $cmd xQueryPhrase $i [list test_queryphrase_cb cnt] lappend res $cnt } set res } proc fts5_queryphrase {cmd iPhrase} { set cnt [list] for {set j 0} {$j < [$cmd xColumnCount]} {incr j} { lappend cnt 0 } $cmd xQueryPhrase $iPhrase [list test_queryphrase_cb cnt] set cnt } proc fts5_test_phrasecount {cmd} { $cmd xPhraseCount } proc fts5_test_all {cmd} { set res [list] |
︙ | ︙ | |||
150 151 152 153 154 155 156 157 158 159 160 161 162 163 | fts5_test_collist fts5_test_tokenize fts5_test_rowcount fts5_test_all fts5_test_queryphrase fts5_test_phrasecount } { sqlite3_fts5_create_function $db $f $f } } proc fts5_segcount {tbl} { set N 0 | > > > | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | fts5_test_collist fts5_test_tokenize fts5_test_rowcount fts5_test_all fts5_test_queryphrase fts5_test_phrasecount fts5_columntext fts5_queryphrase fts5_collist } { sqlite3_fts5_create_function $db $f $f } } proc fts5_segcount {tbl} { set N 0 |
︙ | ︙ | |||
434 435 436 437 438 439 440 441 442 443 444 445 446 447 | error "not in foreach_detail_mode {...} block" } } proc detail_is_none {} { detail_check ; expr {$::detail == "none"} } proc detail_is_col {} { detail_check ; expr {$::detail == "col" } } proc detail_is_full {} { detail_check ; expr {$::detail == "full"} } #------------------------------------------------------------------------- # Convert a poslist of the type returned by fts5_test_poslist() to a # collist as returned by fts5_test_collist(). # proc fts5_poslist2collist {poslist} { set res [list] | > > > > > > > > > > > > > > | 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 | error "not in foreach_detail_mode {...} block" } } proc detail_is_none {} { detail_check ; expr {$::detail == "none"} } proc detail_is_col {} { detail_check ; expr {$::detail == "col" } } proc detail_is_full {} { detail_check ; expr {$::detail == "full"} } proc foreach_tokenizer_mode {prefix script} { set saved $::testprefix foreach {d mapping} { "" {} "-origintext" {, tokenize="origintext unicode61", tokendata=1} } { set s [string map [list %TOKENIZER% $mapping] $script] set ::testprefix "$prefix$d" reset_db sqlite3_fts5_register_origintext db uplevel $s } set ::testprefix $saved } #------------------------------------------------------------------------- # Convert a poslist of the type returned by fts5_test_poslist() to a # collist as returned by fts5_test_collist(). # proc fts5_poslist2collist {poslist} { set res [list] |
︙ | ︙ |
Changes to ext/fts5/test/fts5aa.test.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # If SQLITE_ENABLE_FTS5 is not defined, omit this file. ifcapable !fts5 { finish_test return } foreach_detail_mode $::testprefix { do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(a, b, c); SELECT name, sql FROM sqlite_master; } { t1 {CREATE VIRTUAL TABLE t1 USING fts5(a, b, c)} t1_data {CREATE TABLE 't1_data'(id INTEGER PRIMARY KEY, block BLOB)} | > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # If SQLITE_ENABLE_FTS5 is not defined, omit this file. ifcapable !fts5 { finish_test return } foreach_detail_mode $::testprefix { foreach_tokenizer_mode $::testprefix { do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(a, b, c); SELECT name, sql FROM sqlite_master; } { t1 {CREATE VIRTUAL TABLE t1 USING fts5(a, b, c)} t1_data {CREATE TABLE 't1_data'(id INTEGER PRIMARY KEY, block BLOB)} |
︙ | ︙ | |||
40 41 42 43 44 45 46 | SELECT name, sql FROM sqlite_master; } {} #------------------------------------------------------------------------- # do_execsql_test 2.0 { | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | SELECT name, sql FROM sqlite_master; } {} #------------------------------------------------------------------------- # do_execsql_test 2.0 { CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL% %TOKENIZER%); } do_execsql_test 2.1 { INSERT INTO t1 VALUES('a b c', 'd e f'); } do_test 2.2 { execsql { SELECT fts5_decode(id, block) FROM t1_data WHERE id==10 } |
︙ | ︙ | |||
69 70 71 72 73 74 75 76 | PRAGMA integrity_check(t1); } {ok ok} #------------------------------------------------------------------------- # reset_db do_execsql_test 3.0 { | > | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | PRAGMA integrity_check(t1); } {ok ok} #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 3.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); } foreach {i x y} { 1 {g f d b f} {h h e i a} 2 {f i g j e} {i j c f f} 3 {e e i f a} {e h f d f} 4 {h j f j i} {h a c f j} 5 {d b j c g} {f e i b e} |
︙ | ︙ | |||
93 94 95 96 97 98 99 100 | do_execsql_test 3.$i.3 { PRAGMA integrity_check(t1) } ok if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db do_execsql_test 4.0 { | > | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | do_execsql_test 3.$i.3 { PRAGMA integrity_check(t1) } ok if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 4.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } foreach {i x y} { 1 {g f d b f} {h h e i a} 2 {f i g j e} {i j c f f} 3 {e e i f a} {e h f d f} 4 {h j f j i} {h a c f j} |
︙ | ︙ | |||
117 118 119 120 121 122 123 124 | do_execsql_test 4.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') } if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db do_execsql_test 5.0 { | > | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | do_execsql_test 4.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') } if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 5.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } foreach {i x y} { 1 {dd abc abc abc abcde} {aaa dd ddd ddd aab} 2 {dd aab d aaa b} {abcde c aaa aaa aaa} 3 {abcde dd b b dd} {abc abc d abc ddddd} 4 {aaa abcde dddd dddd abcde} {abc b b abcde abc} |
︙ | ︙ | |||
141 142 143 144 145 146 147 148 | do_execsql_test 5.$i.2 { PRAGMA integrity_check(t1) } ok if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db do_execsql_test 6.0 { | > | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | do_execsql_test 5.$i.2 { PRAGMA integrity_check(t1) } ok if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 6.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } do_execsql_test 6.1 { INSERT INTO t1(rowid, x, y) VALUES(22, 'a b c', 'c b a'); REPLACE INTO t1(rowid, x, y) VALUES(22, 'd e f', 'f e d'); } |
︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 | 22 {l l l} {l l l} 23 {x y z} {x y z} } #------------------------------------------------------------------------- # reset_db expr srand(0) do_execsql_test 7.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y,z); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } proc doc {} { | > | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | 22 {l l l} {l l l} 23 {x y z} {x y z} } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db expr srand(0) do_execsql_test 7.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y,z); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } proc doc {} { |
︙ | ︙ | |||
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 | } {} if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db do_execsql_test 8.0 { CREATE VIRTUAL TABLE t1 USING fts5(x, prefix="1,2,3"); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } do_execsql_test 8.1 { INSERT INTO t1 VALUES('the quick brown fox'); INSERT INTO t1(t1) VALUES('integrity-check'); } #------------------------------------------------------------------------- # reset_db expr srand(0) do_execsql_test 9.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y,z, prefix="1,2,3"); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } | > > | 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 | } {} if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 8.0 { CREATE VIRTUAL TABLE t1 USING fts5(x, prefix="1,2,3"); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } do_execsql_test 8.1 { INSERT INTO t1 VALUES('the quick brown fox'); INSERT INTO t1(t1) VALUES('integrity-check'); } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db expr srand(0) do_execsql_test 9.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y,z, prefix="1,2,3"); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } |
︙ | ︙ | |||
276 277 278 279 280 281 282 283 | if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db do_execsql_test 10.0 { | > | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | if {[set_test_counter errors]} break } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 10.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); } set d10 { 1 {g f d b f} {h h e i a} 2 {f i g j e} {i j c f f} 3 {e e i f a} {e h f d f} 4 {h j f j i} {h a c f j} 5 {d b j c g} {f e i b e} |
︙ | ︙ | |||
310 311 312 313 314 315 316 | do_execsql_test 10.4.1 { DELETE FROM t1 } do_execsql_test 10.4.2 { INSERT INTO t1(t1) VALUES('integrity-check') } #------------------------------------------------------------------------- # do_catchsql_test 11.1 { | | | | | > | | 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 | do_execsql_test 10.4.1 { DELETE FROM t1 } do_execsql_test 10.4.2 { INSERT INTO t1(t1) VALUES('integrity-check') } #------------------------------------------------------------------------- # do_catchsql_test 11.1 { CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rank, detail=%DETAIL% %TOKENIZER%); } {1 {reserved fts5 column name: rank}} do_catchsql_test 11.2 { CREATE VIRTUAL TABLE rank USING fts5(a, b, c, detail=%DETAIL% %TOKENIZER%); } {1 {reserved fts5 table name: rank}} do_catchsql_test 11.3 { CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rowid, detail=%DETAIL% %TOKENIZER%); } {1 {reserved fts5 column name: rowid}} #------------------------------------------------------------------------- # do_execsql_test 12.1 { CREATE VIRTUAL TABLE t2 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); } {} do_catchsql_test 12.2 { SELECT t2 FROM t2 WHERE t2 MATCH '*stuff' } {1 {unknown special query: stuff}} do_test 12.3 { set res [db eval { SELECT t2 FROM t2 WHERE t2 MATCH '* reads ' }] string is integer $res } {1} #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 13.1 { CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(rowid, x) VALUES(1, 'o n e'), (2, 't w o'); } {} do_execsql_test 13.2 { SELECT rowid FROM t1 WHERE t1 MATCH 'o'; } {1 2} |
︙ | ︙ | |||
361 362 363 364 365 366 367 368 | do_execsql_test 13.6 { SELECT rowid FROM t1 WHERE t1 MATCH '""'; } {} #------------------------------------------------------------------------- # reset_db do_execsql_test 14.1 { | > | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | do_execsql_test 13.6 { SELECT rowid FROM t1 WHERE t1 MATCH '""'; } {} #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 14.1 { CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); WITH d(x,y) AS ( SELECT NULL, 'xyz xyz xyz xyz xyz xyz' UNION ALL SELECT NULL, 'xyz xyz xyz xyz xyz xyz' FROM d ) INSERT INTO t1 SELECT * FROM d LIMIT 200; |
︙ | ︙ | |||
445 446 447 448 449 450 451 452 | SELECT funk(), bm25(n1), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d' } {0 {{} -1e-06 {}}} # {1 {SQL logic error}} #------------------------------------------------------------------------- # reset_db do_execsql_test 17.1 { | > | > | > | | 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 | SELECT funk(), bm25(n1), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d' } {0 {{} -1e-06 {}}} # {1 {SQL logic error}} #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 17.1 { CREATE VIRTUAL TABLE b2 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO b2 VALUES('a'); INSERT INTO b2 VALUES('b'); INSERT INTO b2 VALUES('c'); } do_test 17.2 { set res [list] db eval { SELECT * FROM b2 ORDER BY rowid ASC } { lappend res [execsql { SELECT * FROM b2 ORDER BY rowid ASC }] } set res } {{a b c} {a b c} {a b c}} if {[string match n* %DETAIL%]==0} { reset_db sqlite3_fts5_register_origintext db do_execsql_test 17.3 { CREATE VIRTUAL TABLE c2 USING fts5(x, y, detail=%DETAIL% %TOKENIZER%); INSERT INTO c2 VALUES('x x x', 'x x x'); SELECT rowid FROM c2 WHERE c2 MATCH 'y:x'; } {1} } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db do_execsql_test 17.1 { CREATE VIRTUAL TABLE uio USING fts5(ttt, detail=%DETAIL% %TOKENIZER%); INSERT INTO uio VALUES(NULL); INSERT INTO uio SELECT NULL FROM uio; INSERT INTO uio SELECT NULL FROM uio; INSERT INTO uio SELECT NULL FROM uio; INSERT INTO uio SELECT NULL FROM uio; INSERT INTO uio SELECT NULL FROM uio; INSERT INTO uio SELECT NULL FROM uio; |
︙ | ︙ | |||
520 521 522 523 524 525 526 | do_execsql_test 17.9 { SELECT min(rowid), max(rowid), count(*) FROM uio WHERE rowid < 10; } {-9223372036854775808 9 10} #-------------------------------------------------------------------- # do_execsql_test 18.1 { | | | > | > | | | | | > > > | > | 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 | do_execsql_test 17.9 { SELECT min(rowid), max(rowid), count(*) FROM uio WHERE rowid < 10; } {-9223372036854775808 9 10} #-------------------------------------------------------------------- # do_execsql_test 18.1 { CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL% %TOKENIZER%); CREATE VIRTUAL TABLE t2 USING fts5(c, d, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1 VALUES('abc*', NULL); INSERT INTO t2 VALUES(1, 'abcdefg'); } do_execsql_test 18.2 { SELECT t1.rowid, t2.rowid FROM t1, t2 WHERE t2 MATCH t1.a AND t1.rowid = t2.c } {1 1} do_execsql_test 18.3 { SELECT t1.rowid, t2.rowid FROM t2, t1 WHERE t2 MATCH t1.a AND t1.rowid = t2.c } {1 1} #-------------------------------------------------------------------- # fts5 table in the temp schema. # reset_db sqlite3_fts5_register_origintext db do_execsql_test 19.0 { CREATE VIRTUAL TABLE temp.t1 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1 VALUES('x y z'); INSERT INTO t1 VALUES('w x 1'); SELECT rowid FROM t1 WHERE t1 MATCH 'x'; } {1 2} #-------------------------------------------------------------------- # Test that 6 and 7 byte varints can be read. # reset_db sqlite3_fts5_register_origintext db do_execsql_test 20.0 { CREATE VIRTUAL TABLE temp.tmp USING fts5(x, detail=%DETAIL% %TOKENIZER%); } set ::ids [list \ 0 [expr 1<<36] [expr 2<<36] [expr 1<<43] [expr 2<<43] ] do_test 20.1 { foreach id $::ids { execsql { INSERT INTO tmp(rowid, x) VALUES($id, 'x y z') } } execsql { SELECT rowid FROM tmp WHERE tmp MATCH 'y' } } $::ids #-------------------------------------------------------------------- # Test that a DROP TABLE may be executed within a transaction that # writes to an FTS5 table. # do_execsql_test 21.0 { CREATE TEMP TABLE t8(a, b); CREATE VIRTUAL TABLE ft USING fts5(x, detail=%DETAIL% %TOKENIZER%); } do_execsql_test 21.1 { BEGIN; INSERT INTO ft VALUES('a b c'); DROP TABLE t8; COMMIT; } do_execsql_test 22.0 { CREATE VIRTUAL TABLE t9 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO t9(rowid, x) VALUES(2, 'bbb'); BEGIN; INSERT INTO t9(rowid, x) VALUES(1, 'aaa'); DELETE FROM t9 WHERE rowid = 2; INSERT INTO t9(rowid, x) VALUES(3, 'bbb'); COMMIT; } do_execsql_test 22.1 { SELECT rowid FROM t9('a*') } {1} #------------------------------------------------------------------------- do_execsql_test 23.0 { CREATE VIRTUAL TABLE t10 USING fts5(x, detail=%DETAIL% %TOKENIZER%); CREATE TABLE t11(x); } do_execsql_test 23.1 { SELECT * FROM t11, t10 WHERE t11.x = t10.x AND t10.rowid IS NULL; } do_execsql_test 23.2 { SELECT * FROM t11, t10 WHERE t10.rowid IS NULL; } #------------------------------------------------------------------------- do_execsql_test 24.0 { CREATE VIRTUAL TABLE t12 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO t12 VALUES('aaaa'); } do_execsql_test 24.1 { BEGIN; DELETE FROM t12 WHERE rowid=1; SELECT * FROM t12('aaaa'); INSERT INTO t12 VALUES('aaaa'); END; } execsql_pp { SELECT rowid, hex(block) FROM t12_data } do_execsql_test 24.2 { INSERT INTO t12(t12) VALUES('integrity-check'); } do_execsql_test 24.3 { SELECT * FROM t12('aaaa'); } {aaaa} #------------------------------------------------------------------------- do_execsql_test 25.0 { CREATE VIRTUAL TABLE t13 USING fts5(x, detail=%DETAIL% %TOKENIZER%); } do_execsql_test 25.1 { BEGIN; INSERT INTO t13 VALUES('AAAA'); SELECT * FROM t13('BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*'); END; } } } expand_all_sql db finish_test |
Changes to ext/fts5/test/fts5aux.test.
︙ | ︙ | |||
329 330 331 332 333 334 335 336 337 | INSERT INTO x1 VALUES('one two one three one'); INSERT INTO x1 VALUES('one two three'); } do_execsql_test 11.2 { SELECT fts5_hitcount(x1) FROM x1('one') LIMIT 1; } {5} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | INSERT INTO x1 VALUES('one two one three one'); INSERT INTO x1 VALUES('one two three'); } do_execsql_test 11.2 { SELECT fts5_hitcount(x1) FROM x1('one') LIMIT 1; } {5} #------------------------------------------------------------------------- # Test that xColumnText returns SQLITE_RANGE when it should. # reset_db fts5_aux_test_functions db do_execsql_test 12.0 { CREATE VIRTUAL TABLE t1 USING fts5(a, b, c); INSERT INTO t1 VALUES('one', 'two', 'three'); INSERT INTO t1 VALUES('one', 'one', 'one'); INSERT INTO t1 VALUES('two', 'two', 'two'); INSERT INTO t1 VALUES('three', 'three', 'three'); } do_catchsql_test 12.1.1 { SELECT fts5_columntext(t1, -1) FROM t1('two'); } {1 SQLITE_RANGE} do_catchsql_test 12.1.2 { SELECT fts5_columntext(t1, 3) FROM t1('two'); } {1 SQLITE_RANGE} do_catchsql_test 12.1.2 { SELECT fts5_columntext(t1, 1) FROM t1('one AND two'); } {0 two} do_catchsql_test 12.2.1 { SELECT fts5_queryphrase(t1, -1) FROM t1('one AND two'); } {1 SQLITE_RANGE} do_catchsql_test 12.2.2 { SELECT fts5_queryphrase(t1, 2) FROM t1('one AND two'); } {1 SQLITE_RANGE} do_catchsql_test 12.2.3 { SELECT fts5_queryphrase(t1, 1) FROM t1('one AND two'); } {0 {{1 2 1}}} do_catchsql_test 12.3.1 { SELECT fts5_collist(t1, -1) FROM t1('one AND two'); } {1 SQLITE_RANGE} do_catchsql_test 12.3.2 { SELECT fts5_collist(t1, 2) FROM t1('one AND two'); } {1 SQLITE_RANGE} do_catchsql_test 12.3.3 { SELECT fts5_collist(t1, 1) FROM t1('one AND two'); } {0 1} finish_test |
Changes to ext/fts5/test/fts5content.test.
︙ | ︙ | |||
288 289 290 291 292 293 294 295 296 | } {1 {recursively defined fts5 content table}} do_catchsql_test 7.2.4 { SELECT count(*) FROM t1; } {1 {recursively defined fts5 content table}} do_catchsql_test 7.2.5 { SELECT * FROM t1('abc') ORDER BY rank; } {1 {recursively defined fts5 content table}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {1 {recursively defined fts5 content table}} do_catchsql_test 7.2.4 { SELECT count(*) FROM t1; } {1 {recursively defined fts5 content table}} do_catchsql_test 7.2.5 { SELECT * FROM t1('abc') ORDER BY rank; } {1 {recursively defined fts5 content table}} #--------------------------------------------------------------------------- # Check that if the content table is a view, and that view contains an # error, a reasonable error message is returned if the user tries to # read from the view via the fts5 table. # reset_db do_execsql_test 8.1 { CREATE VIEW a1 AS SELECT 1 AS r, text_value(1) AS t UNION ALL SELECT 2 AS r, text_value(2) AS t; CREATE VIRTUAL TABLE t1 USING fts5(t, content='a1', content_rowid='r'); } foreach {tn sql} { 1 "SELECT * FROM t1" 2 "INSERT INTO t1(t1) VALUES('rebuild')" 3 "SELECT * FROM t1 WHERE rowid=1" } { do_catchsql_test 8.2.$tn $sql {1 {no such function: text_value}} } proc text_value {i} { if {$i==1} { return "one" } if {$i==2} { return "two" } return "many" } db func text_value text_value do_execsql_test 8.3.1 { SELECT * FROM t1 } {one two} do_execsql_test 8.3.2 { INSERT INTO t1(t1) VALUES('rebuild') } do_execsql_test 8.3.3 { SELECT * FROM t1 WHERE rowid=1 } {one} do_execsql_test 8.3.4 { SELECT rowid FROM t1('two') } {2} finish_test |
Changes to ext/fts5/test/fts5corrupt5.test.
︙ | ︙ | |||
876 877 878 879 880 881 882 883 884 885 886 | do_execsql_test 5.1 { INSERT INTO t1(t1,rank) VALUES('secure-delete',1); } do_catchsql_test 5.4 { UPDATE t1 SET content=randomblob(500); } {1 {database disk image is malformed}} sqlite3_fts5_may_be_corrupt 0 finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 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 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 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 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 | do_execsql_test 5.1 { INSERT INTO t1(t1,rank) VALUES('secure-delete',1); } do_catchsql_test 5.4 { UPDATE t1 SET content=randomblob(500); } {1 {database disk image is malformed}} #------------------------------------------------------------------------- reset_db do_test 6.0 { sqlite3 db {} db deserialize [decode_hexdb { .open --hexdb | size 32768 pagesize 4096 filename crash-42fa37b694d45a.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 .....@ ........ | 96: 00 00 00 00 0d 00 00 00 07 0d d2 00 0f c4 0f 6d ...............m | 112: 0f 02 0e ab 0e 4e 0d f6 0d d2 00 00 00 00 00 00 .....N.......... | 3536: 00 00 22 07 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet | 3552: 32 74 32 07 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE | 3568: 20 74 32 28 78 29 56 06 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta | 3584: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c | 3600: 6f 6e 66 69 67 06 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB | 3616: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k | 3632: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v) | 3648: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 05 WITHOUT ROWID[. | 3664: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d | 3680: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize | 3696: 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't | 3712: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN | 3728: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE | 3744: 59 2c 20 73 7a 20 42 4c 4f 42 29 55 04 06 17 21 Y, sz BLOB)U...! | 3760: 21 01 77 74 61 62 6c 65 74 31 5f 63 6f 6e 74 65 !.wtablet1_conte | 3776: 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 04 43 52 45 ntt1_content.CRE | 3792: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 6f ATE TABLE 't1_co | 3808: 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 45 ntent'(id INTEGE | 3824: 52 20 50 52 49 4d 41 52 49 20 4b 45 59 2c 20 63 R PRIMARI KEY, c | 3840: 30 29 69 03 07 17 19 19 01 81 2d 74 61 62 6c 65 0)i.......-table | 3856: 74 31 5f 69 64 78 74 31 5f 69 64 78 03 43 52 45 t1_idxt1_idx.CRE | 3872: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 69 64 ATE TABLE 't1_id | 3888: 78 27 28 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 x'(segid, term, | 3904: 70 67 6e 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 pgno, PRIMARY KE | 3920: 59 28 73 65 67 69 64 2c 20 74 65 72 6d 29 29 20 Y(segid, term)) | 3936: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 55 02 07 WITHOUT ROWIDU.. | 3952: 17 1b 1b 01 81 01 74 61 62 6c 65 74 31 5f 64 61 ......tablet1_da | 3968: 74 61 74 31 5f 64 61 74 61 02 43 52 45 41 54 45 tat1_data.CREATE | 3984: 20 54 41 42 4c 45 20 27 74 31 5f 64 61 74 61 27 TABLE 't1_data' | 4000: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM | 4016: 41 52 b9 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 AR. KEY, block B | 4032: 4c 4f 42 29 3a 01 06 17 11 11 08 63 74 61 62 6c LOB):......ctabl | 4048: 65 74 31 74 31 43 52 45 41 54 45 20 56 49 52 54 et1t1CREATE VIRT | 4064: 55 41 4c 20 54 41 42 4c 45 20 74 31 20 55 53 49 UAL TABLE t1 USI | 4080: 4e 47 20 66 74 73 35 28 63 6f 6e 74 65 6e 74 29 NG fts5(content) | page 2 offset 4096 | 0: 0d 00 00 00 03 0f bd 00 0f e8 0f ef 0f bd f0 00 ................ | 16: 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ | 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 24 84 80 .............$.. | 4032: 80 80 80 01 03 00 4e 00 10 00 1e 06 30 61 62 61 ......N.....0aba | 4048: 63 6c 01 02 02 04 02 66 74 02 5f 02 04 04 6e 64 cl.....ft._...nd | 4064: 6f 6e 02 02 02 04 0a 07 05 01 03 00 10 03 03 0f on.............. | 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 11 ...$............ | page 3 offset 8192 | 0: 0a 00 00 00 01 0f 00 01 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 4 offset 12288 | 0: 0d 00 00 00 03 0f e0 00 0f f6 0f ec 0f e0 00 00 ................ | 4064: 0a 03 03 00 1b 61 62 61 6e 64 6f 6e 08 02 03 00 .....abandon.... | 4080: 17 61 62 61 66 74 08 01 03 00 17 61 62 61 63 6b .abaft.....aback | page 5 offset 16384 | 0: 0d 00 00 00 03 0f ee 00 0f fa 0f 00 00 00 00 00 ................ | 4064: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 03 ................ | 4080: 03 00 0e 01 04 02 03 00 0e 01 04 01 03 00 0e 01 ................ | page 6 offset 20480 | 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 7 offset 24576 | 0: 0d 00 00 10 03 0f d6 00 0f 00 00 00 00 00 00 00 ................ | 4048: 00 00 00 00 00 00 09 03 02 1b 72 65 62 75 69 6c ..........rebuil | 4064: 64 11 02 02 2b 69 6e 74 65 67 72 69 74 79 2d 63 d...+integrity-c | 4080: 68 65 63 6b 0a 01 02 1d 6f 70 74 69 6d 00 00 00 heck....optim... | page 8 offset 28672 | 0: 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ | end crash-42fa37b694d45a.db }]} {} do_execsql_test 6.1 { INSERT INTO t1(t1,rank) VALUES('secure-delete',1); } do_catchsql_test 6.2 { UPDATE t1 SET content=randomblob(500) WHERE t1; } {1 {constraint failed}} #------------------------------------------------------------------------- reset_db do_test 7.0 { sqlite3 db {} db deserialize [decode_hexdb { .open --hexdb | size 40960 pagesize 4096 filename crash-d8b4a99207c10b.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 0a .....@ ........ | 32: 00 00 00 00 00 00 00 00 00 00 00 0d 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 0d 0b 62 00 0f 97 0f 40 ..........b....@ | 112: 0e d5 0e 75 0e 18 0d c0 0d 66 0d 0f 0c a4 0c 44 ...u.....f.....D | 128: 0b ec 0b a7 0b 62 00 00 00 00 00 00 00 00 00 00 .....b.......... | 2912: 00 00 43 0d 06 17 11 11 08 75 74 61 62 6c 65 74 ..C......utablet | 2928: 34 74 34 43 52 45 41 54 45 20 56 49 52 54 55 41 4t4CREATE VIRTUA | 2944: 4c 20 54 41 42 4c 45 20 74 34 20 55 53 49 4e 47 L TABLE t4 USING | 2960: 20 66 74 73 35 76 6f 63 61 62 28 27 74 32 27 2c fts5vocab('t2', | 2976: 20 27 72 6f 77 27 29 43 0c 06 17 11 11 08 75 74 'row')C......ut | 2992: 61 62 6c 65 74 33 74 33 43 52 45 41 54 45 20 56 ablet3t3CREATE V | 3008: 49 52 54 55 41 4c 20 54 41 42 4c 45 20 74 33 20 IRTUAL TABLE t3 | 3024: 55 53 49 4e 47 20 66 74 73 35 76 6f 63 61 62 28 USING fts5vocab( | 3040: 27 74 31 27 2c 20 27 72 6f 77 27 29 56 0b 06 17 't1', 'row')V... | 3056: 1f 1f 01 7d 74 61 62 6c 65 74 32 5f 63 6f 6e 66 ....tablet2_conf | 3072: 69 67 74 32 5f 63 6f 6e 66 69 67 0a 43 52 45 41 igt2_config.CREA | 3088: 54 45 20 54 41 42 4c 45 20 27 74 32 5f 63 6f 6e TE TABLE 't2_con | 3104: 66 69 67 27 28 6b 20 50 52 49 4d 41 52 59 20 4b fig'(k PRIMARY K | 3120: 45 59 2c 20 76 29 20 57 49 54 48 4f 55 54 20 52 EY, v) WITHOUT R | 3136: 4f 57 49 44 5e 0a 07 17 21 21 01 81 07 74 61 62 OWID^...!!...tab | 3152: 6c 65 74 32 5f 63 6f 6e 74 65 6e 74 74 32 5f 63 let2_contentt2_c | 3168: 6f 6e 74 65 6e 74 09 43 52 45 41 54 45 20 54 41 ontent.CREATE TA | 3184: 42 4c 45 20 27 74 32 5f 63 6f 6e 74 65 6e 74 27 BLE 't2_content' | 3200: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM | 3216: 41 52 59 20 4b 45 59 2c 20 63 30 2c 20 63 31 2c ARY KEY, c0, c1, | 3232: 20 63 32 29 69 09 07 17 19 19 01 81 2d 74 61 62 c2)i.......-tab | 3248: 6c 65 74 32 5f 69 64 78 74 32 5f 69 64 78 08 43 let2_idxt2_idx.C | 3264: 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 32 5f REATE TABLE 't2_ | 3280: 69 64 78 27 28 73 65 67 69 64 2c 20 74 65 72 6d idx'(segid, term | 3296: 2c 20 70 67 6e 6f 2c 20 50 52 49 4d 41 52 59 20 , pgno, PRIMARY | 3312: 4b 45 59 28 73 65 67 69 64 2c 20 74 65 72 6d 29 KEY(segid, term) | 3328: 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 55 ) WITHOUT ROWIDU | 3344: 08 07 17 1b 1b 01 81 01 74 61 62 6c 65 74 32 5f ........tablet2_ | 3360: 64 61 74 61 74 32 5f 64 61 74 61 07 43 52 45 41 datat2_data.CREA | 3376: 54 45 20 54 41 42 4c 45 20 27 74 32 5f 64 61 74 TE TABLE 't2_dat | 3392: 61 27 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 a'(id INTEGER PR | 3408: 49 4d 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 6b IMARY KEY, block | 3424: 20 42 4c 4f 42 29 58 07 07 17 11 11 08 81 1d 74 BLOB)X........t | 3440: 61 62 6c 65 74 32 74 32 43 52 45 41 54 45 20 56 ablet2t2CREATE V | 3456: 49 52 54 55 41 4c 20 54 41 42 4c 45 20 74 32 20 IRTUAL TABLE t2 | 3472: 55 53 49 4e 47 20 66 74 73 35 28 27 61 27 2c 5b USING fts5('a',[ | 3488: 62 5d 2c 22 63 22 2c 64 65 74 61 69 6c 3d 6e 6f b],.c.,detail=no | 3504: 6e 65 2c 63 6f 6c 75 6d 6e 73 69 7a 65 3d 30 29 ne,columnsize=0) | 3520: 56 06 06 17 1f 1f 01 7d 74 61 62 6c 65 74 31 5f V.......tablet1_ | 3536: 63 6f 6e 66 69 67 74 31 5f 63 6f 6e 66 69 67 06 configt1_config. | 3552: 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 CREATE TABLE 't1 | 3568: 5f 63 6f 6e 66 69 67 27 28 6b 20 50 52 49 4d 41 _config'(k PRIMA | 3584: 52 59 20 4b 45 59 2c 20 76 29 20 57 49 54 48 4f RY KEY, v) WITHO | 3600: 55 54 20 52 4f 57 49 44 5b 05 07 17 21 21 01 81 UT ROWID[...!!.. | 3616: 01 74 61 62 6c 65 74 31 5f 64 6f 63 73 69 7a 65 .tablet1_docsize | 3632: 74 31 5f 64 6f 63 73 69 7a 65 05 43 52 45 41 54 t1_docsize.CREAT | 3648: 45 20 54 41 42 4c 45 20 27 74 31 5f 64 6f 63 73 E TABLE 't1_docs | 3664: 69 7a 65 27 28 69 64 20 49 4e 54 45 47 45 52 20 ize'(id INTEGER | 3680: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 73 7a 20 PRIMARY KEY, sz | 3696: 42 4c 4f 42 29 5e 04 07 17 21 21 01 81 07 74 61 BLOB)^...!!...ta | 3712: 62 6c 65 74 31 5f 63 6f 6e 74 65 6e 74 74 31 5f blet1_contentt1_ | 3728: 63 6f 6e 74 65 6e 74 04 43 52 45 41 54 45 20 54 content.CREATE T | 3744: 41 42 4c 45 20 27 74 31 5f 63 6f 6e 74 65 6e 74 ABLE 't1_content | 3760: 27 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 '(id INTEGER PRI | 3776: 4d 41 52 59 20 4b 45 59 2c 20 63 30 2c 20 63 31 MARY KEY, c0, c1 | 3792: 2c 20 63 32 29 69 03 07 17 19 19 01 81 2d 74 61 , c2)i.......-ta | 3808: 62 6c 65 74 31 5f 69 64 78 74 31 5f 69 64 78 03 blet1_idxt1_idx. | 3824: 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 CREATE TABLE 't1 | 3840: 5f 69 64 78 27 28 73 65 67 69 64 2c 20 74 65 72 _idx'(segid, ter | 3856: 6d 2c 20 70 67 6e 6f 2c 20 50 52 49 4d 41 52 59 m, pgno, PRIMARY | 3872: 20 4b 45 59 28 73 65 67 69 64 2c 20 74 65 72 6d KEY(segid, term | 3888: 29 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 )) WITHOUT ROWID | 3904: 55 02 07 17 1b 1b 01 81 01 74 61 62 6c 65 74 31 U........tablet1 | 3920: 5f 64 61 74 61 74 31 5f 64 61 74 61 02 43 52 45 _datat1_data.CRE | 3936: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 64 61 ATE TABLE 't1_da | 3952: 74 61 27 28 69 64 20 49 4e 54 45 47 45 52 20 50 ta'(id INTEGER P | 3968: 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 RIMARY KEY, bloc | 3984: 6b 20 42 4c 4f 42 29 67 01 07 17 11 11 08 81 3b k BLOB)g.......; | 4000: 74 61 62 6c 65 74 31 74 31 43 52 45 41 54 45 20 tablet1t1CREATE | 4016: 56 49 52 54 55 41 4c 20 54 41 42 4c 45 20 74 31 VIRTUAL TABLE t1 | 4032: 20 55 53 49 4e 47 20 66 74 73 35 28 61 2c 62 20 USING fts5(a,b | 4048: 75 6e 69 6e 64 65 78 65 64 2c 63 2c 74 6f 6b 65 unindexed,c,toke | 4064: 6e 69 7a 65 3d 22 70 6f 72 74 65 72 20 61 73 63 nize=.porter asc | 4080: 69 69 22 2c 74 6f 6b 65 6e 64 61 74 61 3d 31 29 ii.,tokendata=1) | page 2 offset 4096 | 0: 0d 0f 68 00 05 0f 13 00 0f e6 0f 13 0f a8 0f 7c ..h............| | 16: 0f 2a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .*.............. | 3856: 00 00 00 15 0a 03 00 30 00 00 00 00 01 03 03 00 .......0........ | 3872: 03 01 01 01 02 01 01 03 01 01 37 8c 80 80 80 80 ..........7..... | 3888: 01 03 00 74 00 00 00 2e 02 30 61 03 02 02 01 01 ...t.....0a..... | 3904: 62 03 02 03 01 01 63 03 02 04 01 01 67 03 06 01 b.....c.....g... | 3920: 02 02 01 01 68 03 06 01 02 03 01 01 69 03 06 01 ....h.......i... | 3936: 02 04 04 06 06 06 08 08 0f ef 00 14 2a 00 00 00 ............*... | 3952: 00 01 02 02 00 02 01 01 01 02 01 01 25 88 80 80 ............%... | 3968: 80 80 01 03 00 50 00 00 00 1f 02 30 67 02 08 02 .....P.....0g... | 3984: 01 02 02 01 01 68 02 08 03 01 02 03 01 01 69 02 .....h........i. | 4000: 08 04 01 02 04 04 09 09 37 84 80 80 80 7f f1 03 ........7....... | 4016: 00 74 00 00 00 2e 02 30 61 01 02 02 01 01 62 01 .t.....0a.....b. | 4032: 02 03 01 01 63 01 02 04 01 01 67 01 06 01 02 02 ....c.....g..... | 4048: 01 01 68 01 06 01 02 03 01 01 69 01 06 01 02 04 ..h.......i..... | 4064: 04 06 06 06 08 08 07 01 03 00 14 03 09 00 09 00 ................ | 4080: 00 00 11 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............ | page 3 offset 8192 | 0: 0a 00 00 00 03 0f ec 00 0f fa 0f f3 0f ec 00 00 ................ | 4064: 00 00 00 00 00 00 00 00 00 00 00 00 06 04 01 0c ................ | 4080: 01 03 02 06 04 01 0c 01 02 02 05 04 09 0c 01 02 ................ | page 4 offset 12288 | 0: 0d 00 00 00 03 0f be 00 0f ea 0f d4 0f be 00 00 ................ | 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 03 ................ | 4032: 05 00 17 17 17 61 20 62 20 63 67 20 68 20 69 67 .....a b cg h ig | 4048: 20 68 20 69 14 02 05 00 17 17 17 67 20 68 20 69 h i.......g h i | 4064: 61 20 62 20 63 67 20 68 20 69 14 01 05 00 17 17 a b cg h i...... | 4080: 17 61 20 62 20 63 64 20 65 20 66 67 20 68 20 69 .a b cd e fg h i | page 5 offset 16384 | 0: 0d 00 00 00 03 0f e8 00 0f f8 0f f0 0f e8 00 00 ................ | 4064: 00 00 00 00 00 00 00 00 06 03 03 00 12 03 00 03 ................ | 4080: 06 02 03 00 12 03 00 03 06 01 03 00 12 03 00 03 ................ | page 6 offset 20480 | 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 7 offset 24576 | 0: 0d 00 00 00 03 0f 9e 00 0f e6 0f ef 0f 9e 00 00 ................ | 3984: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 84 ..............A. | 4000: 80 80 80 80 01 04 00 81 06 00 00 00 34 02 30 61 ............4.0a | 4016: 01 01 01 01 01 62 01 01 01 01 01 63 01 01 01 01 .....b.....c.... | 4032: 01 64 01 01 01 65 01 01 01 66 01 01 01 67 01 01 .d...e...f...g.. | 4048: 01 01 01 68 01 01 01 01 01 69 01 01 01 04 06 06 ...h.....i...... | 4064: 06 04 04 04 06 06 07 01 03 00 14 03 09 09 09 0f ................ | 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............ | page 8 offset 28672 | 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................ | 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................ | page 9 offset 32768 | 0: 0d 00 00 00 03 0f be 00 0f ea 0f d4 0f be 00 00 ................ | 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 03 ................ | 4032: 05 00 17 17 17 61 20 62 20 63 67 20 68 20 69 67 .....a b cg h ig | 4048: 20 68 20 69 14 02 05 00 17 17 17 67 20 68 20 69 h i.......g h i | 4064: 61 20 62 20 63 67 20 68 20 69 14 01 05 00 17 17 a b cg h i...... | 4080: 17 61 20 62 20 63 64 20 65 20 66 67 20 68 20 69 .a b cd e fg h i | page 10 offset 36864 | 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. | end crash-d8b4a99207c10b.db }]} {} do_catchsql_test 7.1 { SELECT snippet(t1, -1, '.', '..', '[', ']'), highlight(t1, 2, '[', ']') FROM t1('g + h') WHERE rank MATCH 'bm25(1.0, 1.0)' ORDER BY rank; } {1 {database disk image is malformed}} #------------------------------------------------------------------------- reset_db do_test 8.0 { sqlite3 db {} db deserialize [decode_hexdb { .open --hexdb | size 20480 pagesize 4096 filename crash-d57c01958e48ab.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 05 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 05 0e 10 00 0f 97 0f 40 ...............@ | 112: 0e d5 0e 68 0e 10 01 00 00 00 00 00 00 00 00 00 ...h............ | 3600: 56 05 06 17 1f 1f 01 7d 74 61 62 6c 65 74 31 5f V.......tablet1_ | 3616: 63 6f 6e 66 69 67 74 31 5f 63 6f 6e 66 69 67 05 configt1_config. | 3632: 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 CREATE TABLE 't1 | 3648: 5f 63 6f 6e 66 69 67 27 28 6b 20 50 52 49 4d 41 _config'(k PRIMA | 3664: 52 59 20 4b 45 59 2c 20 76 29 20 57 49 54 48 4f RY KEY, v) WITHO | 3680: 55 54 20 52 4f 57 49 44 6b 04 07 17 21 21 01 81 UT ROWIDk...!!.. | 3696: 21 74 61 62 6c 65 74 31 5f 64 6f 63 73 69 7a 65 !tablet1_docsize | 3712: 74 31 5f 64 6f 63 73 69 7a 65 04 43 52 45 41 54 t1_docsize.CREAT | 3728: 45 20 54 41 42 4c 45 20 27 74 31 5f 64 6f 63 73 E TABLE 't1_docs | 3744: 69 7a 65 27 28 69 64 20 49 4e 54 45 47 45 52 20 ize'(id INTEGER | 3760: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 73 7a 20 PRIMARY KEY, sz | 3776: 42 4c 4f 42 2c 20 6f 72 69 67 69 6e 20 49 4e 54 BLOB, origin INT | 3792: 45 47 45 52 29 69 03 07 17 19 19 01 81 2d 74 61 EGER)i.......-ta | 3808: 62 6c 65 74 31 5f 69 64 78 74 31 5f 69 64 78 03 blet1_idxt1_idx. | 3824: 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 CREATE TABLE 't1 | 3840: 5f 69 64 78 27 28 73 65 67 69 64 2c 20 74 65 72 _idx'(segid, ter | 3856: 6d 2c 20 70 67 6e 6f 2c 20 50 52 49 4d 41 52 59 m, pgno, PRIMARY | 3872: 20 4b 45 59 28 73 65 67 69 64 2c 20 74 65 72 6d KEY(segid, term | 3888: 29 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 )) WITHOUT ROWID | 3904: 55 02 07 17 1b 1b 01 81 01 74 61 62 6c 65 74 31 U........tablet1 | 3920: 5f 64 61 74 61 74 31 5f 64 61 74 61 02 43 52 45 _datat1_data.CRE | 3936: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 64 61 ATE TABLE 't1_da | 3952: 74 61 27 28 69 64 20 49 4e 54 45 47 45 52 20 50 ta'(id INTEGER P | 3968: 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 RIMARY KEY, bloc | 3984: 6b 20 42 4c 4f 42 29 67 01 07 17 11 11 08 81 3b k BLOB)g.......; | 4000: 74 61 62 6c 65 74 31 74 31 43 52 45 41 54 45 20 tablet1t1CREATE | 4016: 56 49 52 54 55 41 4c 20 54 41 42 4c 45 20 74 31 VIRTUAL TABLE t1 | 4032: 20 55 53 49 4e 47 20 66 74 73 35 28 61 2c 20 62 USING fts5(a, b | 4048: 2c 20 63 6f 6e 74 65 6e 74 3d 27 27 2c 20 63 6f , content='', co | 4064: 6e 74 65 6e 74 6c 65 73 73 5f 64 65 6c 65 74 65 ntentless_delete | 4080: 3d 31 2c 20 74 6f 6b 65 6e 64 61 74 61 3d 31 29 =1, tokendata=1) | page 2 offset 4096 | 0: 0d 0f eb 00 03 0e 17 00 0f e2 0e 17 0e 31 00 00 .............1.. | 16: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ | 3600: 00 00 00 00 00 00 00 18 0a 03 00 36 00 00 00 00 ...........6.... | 3616: ff 00 00 01 01 01 01 00 01 01 01 01 01 01 00 00 ................ | 3632: 07 83 29 84 80 80 80 80 01 04 00 86 56 00 00 01 ..).........V... | 3648: 96 04 30 61 61 61 01 02 02 01 04 02 04 01 08 02 ..0aaa.......... | 3664: 04 04 04 01 10 02 04 04 04 04 04 04 04 01 20 02 .............. . | 3680: 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 01 ................ | 3696: 40 02 04 04 04 04 04 04 04 04 04 04 04 04 04 04 @............... | 3712: 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 ................ | 3728: 04 01 81 00 02 04 04 04 04 04 04 04 04 04 04 04 ................ | 3744: 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 ................ | 3760: 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 ................ | 3776: 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 ................ | 3792: 04 04 04 04 02 02 62 63 01 06 01 01 02 01 03 62 ......bc.......b | 3808: 62 62 02 02 03 01 04 03 06 01 08 03 06 06 06 01 bb.............. | 3824: 10 03 06 06 06 06 06 06 06 01 20 03 06 06 06 06 .......... ..... | 3840: 06 06 06 06 06 06 06 06 06 06 06 01 40 03 06 06 ............@... | 3856: 06 06 06 06 06 06 06 06 06 06 06 06 06 06 06 06 ................ | 3872: 06 06 06 06 06 06 06 06 06 06 16 06 06 02 02 63 ...............c | 3888: 64 02 06 01 01 02 01 03 63 63 63 03 02 05 01 04 d.......ccc..... | 3904: 05 0a 01 08 05 0a 0a 0a 01 10 05 0a 0a 0a 0a 0a ................ | 3920: 0a 0a 01 20 05 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ... ............ | 3936: 0a 0a 0a 0a 02 02 64 65 03 06 01 01 02 01 03 64 ......de.......d | 3952: 64 64 04 02 09 01 04 09 12 01 08 09 12 12 12 01 dd.............. | 3968: 10 09 12 12 12 12 12 12 12 02 02 65 66 04 06 01 ...........ef... | 3984: 01 02 01 03 65 65 65 05 02 11 01 04 11 22 01 08 ....eee......... | 4000: 11 22 22 22 02 02 66 67 05 06 01 01 02 01 03 66 ......fg.......f | 4016: 56 66 06 02 21 01 04 21 42 02 02 67 68 06 06 01 Vf..!..!B..gh... | 4032: 01 02 cb 03 67 67 67 07 02 41 02 02 68 69 07 06 ....ggg..A..hi.. | 4048: 01 01 02 04 81 13 09 50 09 2e 09 1c 09 12 09 0c .......P........ | 4064: 09 08 07 01 03 00 14 07 81 77 07 00 00 00 15 22 .........w...... | 4080: 00 00 00 00 ff 00 00 01 00 00 00 00 00 00 05 0c ................ | page 3 offset 8192 | 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................ | 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................ | page 4 offset 12288 | 0: 0d 00 00 00 07 0f c8 00 0f f8 0f f0 0f e8 0f e0 ................ | 16: 0f d8 0f d0 0f c8 00 00 00 00 00 00 00 00 00 00 ................ | 4032: 00 00 00 00 00 00 00 00 06 07 04 00 10 09 7f 01 ................ | 4048: 06 06 04 00 10 09 3f 01 06 05 04 00 10 09 1f 01 ......?......... | 4064: 06 04 04 00 10 09 0f 01 06 03 04 00 10 09 07 01 ................ | 4080: 06 02 04 00 10 09 03 01 06 01 04 00 10 09 01 01 ................ | page 5 offset 16384 | 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. | 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 |
Added ext/fts5/test/fts5faultH.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 | # 2010 June 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]] fts5_common.tcl] source $testdir/malloc_common.tcl set testprefix fts5faultG # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } set ::testprefix fts5faultH sqlite3_fts5_register_origintext db do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5( x, tokenize="origintext unicode61", tokendata=1 ); BEGIN; INSERT INTO t1 VALUES('oNe tWo thRee'); INSERT INTO t1 VALUES('One Two Three'); INSERT INTO t1 VALUES('onE twO threE'); COMMIT; BEGIN; INSERT INTO t1 VALUES('one two three'); INSERT INTO t1 VALUES('one two three'); INSERT INTO t1 VALUES('one two three'); COMMIT; } do_faultsim_test 1 -faults oom* -prep { } -body { execsql { SELECT rowid FROM t1('three'); } } -test { faultsim_integrity_check faultsim_test_result {0 {1 2 3 4 5 6}} } reset_db sqlite3_fts5_register_origintext db do_execsql_test 2.0 { CREATE VIRTUAL TABLE t1 USING fts5( x, tokenize="origintext unicode61", tokendata=1 ); INSERT INTO t1(t1, rank) VALUES('pgsz', 64); BEGIN; INSERT INTO t1(rowid, x) VALUES(10, 'aaa bbb BBB'); INSERT INTO t1(rowid, x) VALUES(12, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(13, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(14, 'bbb BBB bbb'); INSERT INTO t1(rowid, x) VALUES(15, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(16, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(17, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(18, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(19, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(20, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(21, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(22, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(23, 'bbb bbb bbb'); INSERT INTO t1(rowid, x) VALUES(24, 'aaa bbb BBB'); COMMIT; } do_faultsim_test 2 -faults oom* -prep { } -body { execsql { SELECT rowid FROM t1('BBB AND AAA'); } } -test { faultsim_integrity_check faultsim_test_result {0 {10 24}} } reset_db sqlite3_fts5_register_origintext db do_execsql_test 3.0 { CREATE VIRTUAL TABLE t1 USING fts5( x, tokenize="origintext unicode61", tokendata=1 ); INSERT INTO t1(t1, rank) VALUES('pgsz', 64); INSERT INTO t1(rowid, x) VALUES(9, 'bbb Bbb BBB'); BEGIN; INSERT INTO t1(rowid, x) VALUES(10, 'aaa bbb BBB'); INSERT INTO t1(rowid, x) VALUES(11, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(12, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(13, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(14, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(15, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(16, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(17, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(18, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(19, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(20, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(21, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(22, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(23, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(24, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(25, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(26, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(27, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(28, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(29, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(30, 'bbb Bbb BBB'); INSERT INTO t1(rowid, x) VALUES(31, 'bbb Bbb BBB'); 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.
︙ | ︙ | |||
87 88 89 90 91 92 93 | do_execsql_test 2.2.1 { CREATE TABLE t0(c0); CREATE VIRTUAL TABLE vt0 USING fts5(c0); BEGIN TRANSACTION; INSERT INTO vt0(c0) VALUES ('xyz'); } | < | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | do_execsql_test 2.2.1 { CREATE TABLE t0(c0); CREATE VIRTUAL TABLE vt0 USING fts5(c0); BEGIN TRANSACTION; INSERT INTO vt0(c0) VALUES ('xyz'); } do_execsql_test 2.2.2 { ALTER TABLE t0 RENAME TO t1; } do_execsql_test 2.2.3 { INSERT INTO vt0(vt0) VALUES('integrity-check'); } do_execsql_test 2.2.4 { |
︙ | ︙ | |||
496 497 498 499 500 501 502 | do_execsql_test 17.5 { SELECT c0 FROM t0 WHERE c0 GLOB '*f*'; } {assertionfaultproblem} do_execsql_test 17.5 { SELECT c0 FROM t0 WHERE c0 GLOB '*faul*'; } {assertionfaultproblem} | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_execsql_test 17.5 { SELECT c0 FROM t0 WHERE c0 GLOB '*f*'; } {assertionfaultproblem} do_execsql_test 17.5 { SELECT c0 FROM t0 WHERE c0 GLOB '*faul*'; } {assertionfaultproblem} #------------------------------------------------------------------------- reset_db do_execsql_test 18.0 { BEGIN; CREATE VIRTUAL TABLE t1 USING fts5(text); ALTER TABLE t1 RENAME TO t2; } 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 |
Added ext/fts5/test/fts5origintext.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 | # 2014 Jan 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. # #*********************************************************************** # # Tests focused on phrase queries. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5origintext # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } foreach_detail_mode $testprefix { sqlite3_fts5_register_origintext db do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize="origintext unicode61", detail=%DETAIL% ); CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); } do_execsql_test 1.1 { INSERT INTO ft VALUES('Hello world'); } do_execsql_test 1.2 { INSERT INTO ft(ft) VALUES('integrity-check'); } proc b {x} { string map [list "\0" "."] $x } db func b b do_execsql_test 1.3 { select b(term) from vocab; } { hello.Hello world } do_execsql_test 1.4 { SELECT rowid FROM ft('Hello'); } {1} #------------------------------------------------------------------------- reset_db # Return a random integer between 0 and n-1. # proc random {n} { expr {abs(int(rand()*$n))} } proc select_one {list} { set n [llength $list] lindex $list [random $n] } proc term {} { set first_letter { a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z } set term [select_one $first_letter] append term [random 100] } proc document {} { set nTerm [expr [random 5] + 5] set doc "" for {set ii 0} {$ii < $nTerm} {incr ii} { lappend doc [term] } set doc } db func document document sqlite3_fts5_register_origintext db do_execsql_test 2.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize="origintext unicode61", detail=%DETAIL% ); INSERT INTO ft(ft, rank) VALUES('pgsz', 128); CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); } do_test 2.1 { for {set ii 0} {$ii < 500} {incr ii} { execsql { INSERT INTO ft VALUES( document() ) } } } {} do_execsql_test 2.2 { INSERT INTO ft(ft) VALUES('integrity-check'); } do_execsql_test 2.3 { INSERT INTO ft(ft, rank) VALUES('merge', 16); } do_execsql_test 2.4 { INSERT INTO ft(ft) VALUES('integrity-check'); } do_execsql_test 2.5 { INSERT INTO ft(ft) VALUES('optimize'); } #------------------------------------------------------------------------- reset_db sqlite3_fts5_register_origintext db do_execsql_test 3.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize="origintext unicode61", detail=%DETAIL% ); CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); INSERT INTO ft(rowid, x) VALUES(1, 'hello'); INSERT INTO ft(rowid, x) VALUES(2, 'Hello'); INSERT INTO ft(rowid, x) VALUES(3, 'HELLO'); } #proc b {x} { string map [list "\0" "."] $x } #db func b b #execsql_pp { SELECT b(term) FROM vocab } do_execsql_test 3.1.1 { SELECT rowid FROM ft('hello') } 1 do_execsql_test 3.1.2 { SELECT rowid FROM ft('Hello') } 2 do_execsql_test 3.1.3 { SELECT rowid FROM ft('HELLO') } 3 do_execsql_test 3.2 { CREATE VIRTUAL TABLE ft2 USING fts5(x, tokenize="origintext unicode61", tokendata=1, detail=%DETAIL% ); CREATE VIRTUAL TABLE vocab2 USING fts5vocab(ft2, instance); INSERT INTO ft2(rowid, x) VALUES(1, 'hello'); INSERT INTO ft2(rowid, x) VALUES(2, 'Hello'); INSERT INTO ft2(rowid, x) VALUES(3, 'HELLO'); INSERT INTO ft2(rowid, x) VALUES(10, 'helloooo'); } #proc b {x} { string map [list "\0" "."] $x } #db func b b #execsql_pp { SELECT b(term) FROM vocab } do_execsql_test 3.3.1 { SELECT rowid FROM ft2('hello') } {1 2 3} do_execsql_test 3.3.2 { SELECT rowid FROM ft2('Hello') } {1 2 3} do_execsql_test 3.3.3 { SELECT rowid FROM ft2('HELLO') } {1 2 3} do_execsql_test 3.3.4 { SELECT rowid FROM ft2('hello*') } {1 2 3 10} #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db proc querytoken {cmd iPhrase iToken} { set txt [$cmd xQueryToken $iPhrase $iToken] string map [list "\0" "."] $txt } sqlite3_fts5_create_function db querytoken querytoken do_execsql_test 4.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize='origintext unicode61', tokendata=1, detail=%DETAIL% ); INSERT INTO ft VALUES('one two three four'); } do_execsql_test 4.1 { SELECT rowid, querytoken(ft, 0, 0) FROM ft('TwO') } {1 two.TwO} do_execsql_test 4.2 { SELECT rowid, querytoken(ft, 0, 0) FROM ft('one TWO ThreE') } {1 one} do_execsql_test 4.3 { SELECT rowid, querytoken(ft, 1, 0) FROM ft('one TWO ThreE') } {1 two.TWO} if {"%DETAIL%"=="full"} { # Phrase queries are only supported for detail=full. # do_execsql_test 4.4 { SELECT rowid, querytoken(ft, 0, 2) FROM ft('"one TWO ThreE"') } {1 three.ThreE} do_catchsql_test 4.5 { SELECT rowid, querytoken(ft, 0, 3) FROM ft('"one TWO ThreE"') } {1 SQLITE_RANGE} do_catchsql_test 4.6 { SELECT rowid, querytoken(ft, 1, 0) FROM ft('"one TWO ThreE"') } {1 SQLITE_RANGE} do_catchsql_test 4.7 { SELECT rowid, querytoken(ft, -1, 0) FROM ft('"one TWO ThreE"') } {1 SQLITE_RANGE} } #------------------------------------------------------------------------- # reset_db sqlite3_fts5_register_origintext db proc insttoken {cmd iIdx iToken} { set txt [$cmd xInstToken $iIdx $iToken] string map [list "\0" "."] $txt } sqlite3_fts5_create_function db insttoken insttoken fts5_aux_test_functions db do_execsql_test 5.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize='origintext unicode61', tokendata=1, detail=%DETAIL% ); INSERT INTO ft VALUES('one ONE One oNe oNE one'); } do_execsql_test 5.1 { SELECT insttoken(ft, 0, 0), insttoken(ft, 1, 0), insttoken(ft, 2, 0), insttoken(ft, 3, 0), insttoken(ft, 4, 0), insttoken(ft, 5, 0) FROM ft('one'); } { one one.ONE one.One one.oNe one.oNE one } do_execsql_test 5.2 { SELECT insttoken(ft, 1, 0) FROM ft('one'); } { one.ONE } do_execsql_test 5.3 { SELECT fts5_test_poslist(ft) FROM ft('one'); } { {0.0.0 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5} } #------------------------------------------------------------------------- # Test the xInstToken() API with: # # * a non tokendata=1 table. # * prefix queries. # reset_db sqlite3_fts5_register_origintext db do_execsql_test 6.0 { CREATE VIRTUAL TABLE ft USING fts5( x, y, tokenize='origintext unicode61', detail=%DETAIL% ); INSERT INTO ft VALUES('One Two', 'Three two'); INSERT INTO ft VALUES('three Three', 'one One'); } proc tokens {cmd} { set ret [list] for {set iTok 0} {$iTok < [$cmd xInstCount]} {incr iTok} { set txt [$cmd xInstToken $iTok 0] set txt [string map [list "\0" "."] $txt] lappend ret $txt } set ret } sqlite3_fts5_create_function db tokens tokens do_execsql_test 6.1 { SELECT rowid, tokens(ft) FROM ft('One'); } {1 one.One 2 one.One} do_execsql_test 6.2 { SELECT rowid, tokens(ft) FROM ft('on*'); } {1 {{}} 2 {{} {}}} do_execsql_test 6.3 { SELECT rowid, tokens(ft) FROM ft('Three*'); } {1 {{}} 2 {{}}} } finish_test |
Added ext/fts5/test/fts5origintext2.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 | # 2014 Jan 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. # #*********************************************************************** # # Tests focused on phrase queries. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5origintext2 # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } sqlite3_fts5_register_origintext db do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize="origintext unicode61", tokendata=1 ); } do_execsql_test 1.1 { BEGIN; INSERT INTO ft VALUES('Hello'); INSERT INTO ft VALUES('hello'); INSERT INTO ft VALUES('HELLO'); INSERT INTO ft VALUES('today'); INSERT INTO ft VALUES('today'); INSERT INTO ft VALUES('today'); INSERT INTO ft VALUES('World'); INSERT INTO ft VALUES('world'); INSERT INTO ft VALUES('WORLD'); COMMIT; } do_execsql_test 1.2 { SELECT rowid FROM ft('hello'); } {1 2 3} do_execsql_test 1.3 { SELECT rowid FROM ft('today'); } {4 5 6} do_execsql_test 1.4 { SELECT rowid FROM ft('world'); } {7 8 9} do_execsql_test 1.5 { SELECT count(*) FROM ft_data } 3 do_execsql_test 1.6 { DELETE FROM ft; INSERT INTO ft(ft, rank) VALUES('pgsz', 64); BEGIN; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 ) INSERT INTO ft SELECT 'Hello Hello Hello Hello Hello Hello Hello' FROM s; INSERT INTO ft VALUES ('hELLO hELLO hELLO'); INSERT INTO ft VALUES('today today today today today today today'); INSERT INTO ft VALUES('today today today today today today today'); INSERT INTO ft VALUES('today today today today today today today'); INSERT INTO ft VALUES('today today today today today today today'); INSERT INTO ft VALUES('today today today today today today today'); INSERT INTO ft VALUES('today today today today today today today'); INSERT INTO ft VALUES('World World World World World World World'); INSERT INTO ft VALUES('world world world world world world world'); INSERT INTO ft VALUES('WORLD WORLD WORLD WORLD WORLD WORLD WORLD'); INSERT INTO ft VALUES('World World World World World World World'); INSERT INTO ft VALUES('world world world world world world world'); INSERT INTO ft VALUES('WORLD WORLD WORLD WORLD WORLD WORLD WORLD'); COMMIT; } do_execsql_test 1.7 { SELECT count(*) FROM ft_data; } 23 do_execsql_test 1.8 { SELECT rowid FROM ft('hello') WHERE rowid>100; } {101} do_execsql_test 1.9 { DELETE FROM ft; INSERT INTO ft(ft) VALUES('optimize'); SELECT count(*) FROM ft_data; } {2} do_execsql_test 1.10 { BEGIN; INSERT INTO ft VALUES('Hello'); INSERT INTO ft VALUES('hello'); INSERT INTO ft VALUES('HELLO'); INSERT INTO ft VALUES('today'); INSERT INTO ft VALUES('today'); INSERT INTO ft VALUES('today'); INSERT INTO ft VALUES('World'); INSERT INTO ft VALUES('world'); INSERT INTO ft VALUES('WORLD'); } do_execsql_test 1.11 { SELECT rowid FROM ft('hello'); } {1 2 3} do_execsql_test 1.12 { SELECT rowid FROM ft('today'); } {4 5 6} do_execsql_test 1.13 { SELECT rowid FROM ft('world'); } {7 8 9} do_execsql_test 1.14 { SELECT rowid FROM ft('hello') ORDER BY rank; } {1 2 3} #------------------------------------------------------------------------ reset_db sqlite3_fts5_register_origintext db proc tokens {cmd} { set ret [list] for {set iTok 0} {$iTok < [$cmd xInstCount]} {incr iTok} { set txt [$cmd xInstToken $iTok 0] set txt [string map [list "\0" "."] $txt] lappend ret $txt } set ret } sqlite3_fts5_create_function db tokens tokens do_execsql_test 2.0 { CREATE VIRTUAL TABLE x1 USING fts5( v, tokenize="origintext unicode61", tokendata=1, detail=none ); INSERT INTO x1 VALUES('xxx Xxx XXX yyy YYY yyy'); INSERT INTO x1 VALUES('xxx yyy xxx yyy yyy yyy'); } do_execsql_test 2.1 { SELECT tokens(x1) FROM x1('xxx'); } { {xxx xxx.Xxx xxx.XXX} {xxx xxx} } do_execsql_test 2.2 { UPDATE x1_content SET c0 = 'xxx xxX xxx yyy yyy yyy' WHERE id=1; } do_execsql_test 2.3 { SELECT tokens(x1) FROM x1('xxx'); } { {xxx {} xxx} {xxx xxx} } finish_test |
Added ext/fts5/test/fts5origintext3.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 | # 2023 November 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. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Tests focused on phrase queries. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5origintext3 # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } foreach_detail_mode $testprefix { reset_db sqlite3_fts5_register_origintext db fts5_aux_test_functions db proc insttoken {cmd iIdx iToken} { set txt [$cmd xInstToken $iIdx $iToken] string map [list "\0" "."] $txt } sqlite3_fts5_create_function db insttoken insttoken do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize="origintext unicode61", tokendata=1, detail=%DETAIL% ); } do_execsql_test 1.1 { INSERT INTO ft VALUES('Hello world HELLO WORLD hello'); } do_execsql_test 1.2 { SELECT fts5_test_poslist(ft) FROM ft('hello'); } {{0.0.0 0.0.2 0.0.4}} do_execsql_test 1.3 { SELECT insttoken(ft, 0, 0), insttoken(ft, 1, 0), insttoken(ft, 2, 0) FROM ft('hello'); } {hello.Hello hello.HELLO hello} do_execsql_test 1.4 { SELECT insttoken(ft, 0, 0), insttoken(ft, 1, 0), insttoken(ft, 2, 0) FROM ft('hello') ORDER BY rank; } {hello.Hello hello.HELLO hello} do_execsql_test 1.5 { CREATE VIRTUAL TABLE ft2 USING fts5( x, tokenize="origintext unicode61", tokendata=1, detail=%DETAIL% ); INSERT INTO ft2(rowid, x) VALUES(1, 'ONE one two three ONE'); INSERT INTO ft2(rowid, x) VALUES(2, 'TWO one two three TWO'); INSERT INTO ft2(rowid, x) VALUES(3, 'THREE one two three THREE'); } do_execsql_test 1.6 { SELECT insttoken(ft2, 0, 0), rowid FROM ft2('three') ORDER BY rank; } {three.THREE 3 three 1 three 2} do_execsql_test 1.7 { INSERT INTO ft2(rowid, x) VALUES(10, 'aaa bbb BBB'); INSERT INTO ft2(rowid, x) VALUES(12, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(13, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(14, 'bbb BBB bbb'); INSERT INTO ft2(rowid, x) VALUES(15, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(16, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(17, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(18, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(19, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(20, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(21, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(22, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(23, 'bbb bbb bbb'); INSERT INTO ft2(rowid, x) VALUES(24, 'aaa bbb BBB'); } do_execsql_test 1.8 { SELECT rowid FROM ft2('aaa AND bbb'); } {10 24} do_execsql_test 1.9 { SELECT rowid FROM ft2('bbb AND aaa'); } {10 24} } finish_test |
Added ext/fts5/test/fts5origintext4.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 | # 2023 November 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. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Tests focused on phrase queries. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5origintext4 # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } # The tests below verify that a doclist-index is used to limit the number # of pages loaded into the cache. It does this by querying sqlite3_db_status() # for the amount of memory used by the pager cache. # # memsubsys1 effectively limits the page-cache to 24 pages. Which masks # the effect tested by the tests in this file. And "mmap" prevents the # cache from being used, also preventing these tests from working. # if {[permutation]=="memsubsys1" || [permutation]=="mmap"} { finish_test return } sqlite3_fts5_register_origintext db do_execsql_test 1.0 { PRAGMA page_size = 4096; CREATE VIRTUAL TABLE ft USING fts5( x, tokenize="origintext unicode61", tokendata=1 ); } do_execsql_test 1.1 { BEGIN; INSERT INTO ft SELECT 'the first thing'; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<90000 ) INSERT INTO ft SELECT 'The second thing' FROM s; INSERT INTO ft SELECT 'the first thing'; COMMIT; INSERT INTO ft(ft) VALUES('optimize'); } foreach {tn sql expr} { 1 { SELECT rowid FROM ft('the') } {$mem > 250000} 2 { SELECT rowid FROM ft('first') } {$mem < 50000} 3 { SELECT rowid FROM ft('the first') } {$mem < 50000} } { db close sqlite3 db test.db sqlite3_fts5_register_origintext db execsql $sql do_test 1.2.$tn { set mem [lindex [sqlite3_db_status db CACHE_USED 0] 1] expr $expr } 1 } proc b {x} { string map [list "\0" "."] $x } db func b b # execsql_pp { SELECT segid, b(term), pgno from ft_idx } finish_test |
Added ext/fts5/test/fts5origintext5.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 | # 2023 Dec 04 # # 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 tables that use both tokendata=1 and contentless_delete=1. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5origintext # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } # Return a random integer between 0 and n-1. # proc random {n} { expr {abs(int(rand()*$n))} } # Select an element of the list passed as the only argument at random and # return it. # proc select_one {list} { set n [llength $list] lindex $list [random $n] } # Given a term that consists entirely of alphabet characters, return all # permutations of the term using upper and lower case characters. e.g. # # "abc" -> {CBA cBA CbA cbA CBa cBa Cba cba} # proc casify {term {lRet {{}}}} { if {$term==""} { return $lRet } set t [string range $term 1 end] set f1 [string toupper [string range $term 0 0]] set f2 [string tolower [string range $term 0 0]] set ret [list] foreach x $lRet { lappend ret "$x$f1" lappend ret "$x$f2" } return [casify $t $ret] } proc vocab {} { list abc def ghi jkl mno pqr stu vwx yza } # Return a random 3 letter term. # proc term {} { if {[info exists ::expanded_vocab]==0} { foreach v [vocab] { lappend ::expanded_vocab {*}[casify $v] } } select_one $::expanded_vocab } # Return a document - between 3 and 10 terms. # proc document {} { set nTerm [expr [random 3] + 7] set doc "" for {set ii 0} {$ii < $nTerm} {incr ii} { lappend doc [term] } set doc } db func document document #------------------------------------------------------------------------- expr srand(6) set NDOC 200 set NLOOP 50 sqlite3_fts5_register_origintext db proc tokens {cmd} { set ret [list] for {set iTok 0} {$iTok < [$cmd xInstCount]} {incr iTok} { set txt [$cmd xInstToken $iTok 0] set txt [string map [list "\0" "."] $txt] lappend ret $txt } set ret } sqlite3_fts5_create_function db tokens tokens proc rankfunc {cmd} { $cmd xRowid } sqlite3_fts5_create_function db rankfunc rankfunc proc ctrl_tokens {term args} { set ret [list] set term [string tolower $term] foreach doc $args { foreach a $doc { if {[string tolower $a]==$term} { if {$a==$term} { lappend ret $a } else { lappend ret [string tolower $a].$a } } } } set ret } db func ctrl_tokens ctrl_tokens proc do_all_vocab_test {tn} { foreach ::v [concat [vocab] nnn] { set answer [execsql { SELECT id, ctrl_tokens($::v, x) FROM ctrl WHERE x LIKE '%' || $::v || '%' }] do_execsql_test $tn.$::v.1 { SELECT rowid, tokens(ft) FROM ft($::v) } $answer do_execsql_test $tn.$::v.2 { SELECT rowid, tokens(ft) FROM ft($::v) ORDER BY rank } $answer } } do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize="origintext unicode61", content=, contentless_delete=1, tokendata=1 ); CREATE TABLE ctrl(id INTEGER PRIMARY KEY, x TEXT); INSERT INTO ft(ft, rank) VALUES('pgsz', 64); INSERT INTO ft(ft, rank) VALUES('rank', 'rankfunc()'); } do_test 1.1 { for {set ii 0} {$ii < $NDOC} {incr ii} { set doc [document] execsql { INSERT INTO ft(rowid, x) VALUES($ii, $doc); INSERT INTO ctrl(id, x) VALUES($ii, $doc); } } } {} #execsql_pp { SELECT * FROM ctrl } #execsql_pp { SELECT * FROM ft } #fts5_aux_test_functions db #execsql_pp { SELECT rowid, tokens(ft), fts5_test_poslist(ft) FROM ft('ghi'); } do_all_vocab_test 1.2 for {set ii 0} {$ii < $NLOOP} {incr ii} { set lRowid [execsql { SELECT id FROM ctrl WHERE random() % 2 }] foreach r $lRowid { execsql { DELETE FROM ft WHERE rowid = $r } execsql { DELETE FROM ctrl WHERE rowid = $r } set doc [document] execsql { INSERT INTO ft(rowid, x) VALUES($r, $doc) } execsql { INSERT INTO ctrl(id, x) VALUES($r, $doc) } } do_all_vocab_test 1.3.$ii } #------------------------------------------------------------------------- do_execsql_test 2.0 { CREATE VIRTUAL TABLE ft2 USING fts5( x, y, tokenize="origintext unicode61", content=, contentless_delete=1, tokendata=1 ); CREATE TABLE ctrl2(id INTEGER PRIMARY KEY, x TEXT, y TEXT); INSERT INTO ft2(ft2, rank) VALUES('pgsz', 64); INSERT INTO ft2(ft2, rank) VALUES('rank', 'rankfunc()'); } do_test 2.1 { for {set ii 0} {$ii < $NDOC} {incr ii} { set doc1 [document] set doc2 [document] execsql { INSERT INTO ft2(rowid, x, y) VALUES($ii, $doc, $doc2); INSERT INTO ctrl2(id, x, y) VALUES($ii, $doc, $doc2); } } } {} proc do_all_vocab_test2 {tn} { foreach ::v [vocab] { set answer [execsql { SELECT id, ctrl_tokens($::v, x, y) FROM ctrl2 WHERE x LIKE '%' || $::v || '%' OR y LIKE '%' || $::v || '%'; }] do_execsql_test $tn.$::v.1 { SELECT rowid, tokens(ft2) FROM ft2($::v) } $answer do_execsql_test $tn.$::v.2 { SELECT rowid, tokens(ft2) FROM ft2($::v) ORDER BY rank } $answer } } do_all_vocab_test2 2.2 for {set ii 0} {$ii < $NLOOP} {incr ii} { set lRowid [execsql { SELECT id FROM ctrl2 WHERE random() % 2 }] foreach r $lRowid { execsql { DELETE FROM ft2 WHERE rowid = $r } execsql { DELETE FROM ctrl2 WHERE rowid = $r } set doc1 [document] set doc2 [document] execsql { INSERT INTO ft2(rowid, x, y) VALUES($r, $doc, $doc1) } execsql { INSERT INTO ctrl2(id, x, y) VALUES($r, $doc, $doc2) } } do_all_vocab_test 2.3.$ii } #------------------------------------------------------------------------- unset -nocomplain ::expanded_vocab proc vocab {} { list abcde fghij klmno } proc do_all_vocab_test3 {tn} { foreach ::v [concat [vocab] nnn] { set answer [execsql { SELECT rowid, ctrl_tokens($::v, w) FROM ctrl3 WHERE w LIKE '%' || $::v || '%' }] do_execsql_test $tn.$::v.1 { SELECT rowid, tokens(ft3) FROM ft3($::v) } $answer do_execsql_test $tn.$::v.2 { SELECT rowid, tokens(ft3) FROM ft3($::v) ORDER BY rank } $answer } } do_execsql_test 3.0 { CREATE VIRTUAL TABLE ft3 USING fts5( w, tokenize="origintext unicode61", content=, contentless_delete=1, tokendata=1 ); INSERT INTO ft3(ft3, rank) VALUES('rank', 'rankfunc()'); CREATE TABLE ctrl3(w); } do_execsql_test 3.1 { WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<2 ) INSERT INTO ctrl3 SELECT document() FROM s; INSERT INTO ft3(rowid, w) SELECT rowid, w FROM ctrl3; } do_all_vocab_test3 3.2 finish_test |
Changes to ext/fts5/test/fts5secure3.test.
︙ | ︙ | |||
82 83 84 85 86 87 88 | #execsql_pp { SELECT id, quote(block), fts5_decode(id, block) FROM t1_data; } #------------------------------------------------------------------------- # Tests with large/small rowid values. # | > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 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 | #execsql_pp { SELECT id, quote(block), fts5_decode(id, block) FROM t1_data; } #------------------------------------------------------------------------- # Tests with large/small rowid values. # foreach {tn cfg} { 1 "" 2 "INSERT INTO fff(fff, rank) VALUES('secure-delete', 1)" } { reset_db expr srand(0) set vocab { Popper Poppins Popsicle Porfirio Porrima Porsche Porter Portia Portland Portsmouth Portugal Portuguese Poseidon Post PostgreSQL Potemkin Potomac Potsdam Pottawatomie Potter Potts Pound Poussin Powell PowerPC PowerPoint Powers Powhatan Poznan Prada Prado Praetorian Prague Praia Prakrit Pratchett Pratt Pravda Praxiteles Preakness Precambrian Preminger Premyslid Prensa Prentice Pres Presbyterian Presbyterianism } proc newdoc {} { for {set i 0} {$i<8} {incr i} { lappend ret [lindex $::vocab [expr int(abs(rand()) * [llength $::vocab])]] } set ret } db func newdoc newdoc do_execsql_test 3.$tn.0 { CREATE VIRTUAL TABLE fff USING fts5(y); INSERT INTO fff(fff, rank) VALUES('pgsz', 64); WITH s(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM s WHERE x<1000 ) INSERT INTO fff(rowid, y) SELECT random() , newdoc() FROM s; WITH s(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM s WHERE x<1000 ) INSERT INTO fff(rowid, y) SELECT random() , newdoc() FROM s; WITH s(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM s WHERE x<1000 ) INSERT INTO fff(rowid, y) SELECT random() , newdoc() FROM s; } execsql $cfg proc lshuffle {in} { set out [list] while {[llength $in]>0} { set idx [expr int(abs(rand()) * [llength $in])] lappend out [lindex $in $idx] set in [lreplace $in $idx $idx] } set out } #dump fff set iTest 1 foreach ii [lshuffle [db eval {SELECT rowid FROM fff}]] { #if {$iTest==1} { dump fff } #if {$iTest==1} { breakpoint } do_execsql_test 3.$tn.1.$iTest.$ii { DELETE FROM fff WHERE rowid=$ii; } #if {$iTest==1} { dump fff } if {($iTest % 20)==0} { do_execsql_test 3.$tn.1.$iTest.$ii.ic { INSERT INTO fff(fff) VALUES('integrity-check'); } } #if {$iTest==1} { break } incr iTest } } #execsql_pp { SELECT rowid FROM fff('post') ORDER BY rowid ASC } #breakpoint #execsql_pp { # SELECT rowid FROM fff('post') ORDER BY rowid DESC #} |
︙ | ︙ |
Added ext/fts5/test/fts5secure8.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 | # 2023 Nov 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. # #************************************************************************* # source [file join [file dirname [info script]] fts5_common.tcl] ifcapable !fts5 { finish_test ; return } set ::testprefix fts5secure8 proc sql_repeat {txt n} { string repeat $txt $n } db func repeat sql_repeat do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5(x); INSERT INTO ft(ft, rank) VALUES('pgsz', 64); INSERT INTO ft(rowid, x) VALUES(100, 'hello world'); INSERT INTO ft(rowid, x) VALUES(200, 'one day'); BEGIN; INSERT INTO ft(rowid, x) VALUES(45, 'one two three'); UPDATE ft SET x = repeat('hello world ', 500) WHERE rowid=100; COMMIT } do_execsql_test 1.1 { INSERT INTO ft(ft, rank) VALUES('secure-delete', 1); DELETE FROM ft WHERE rowid=100; } do_execsql_test 1.2 { PRAGMA integrity_check; } {ok} finish_test |
Changes to ext/fts5/test/fts5simple2.test.
︙ | ︙ | |||
339 340 341 342 343 344 345 | CREATE VIRTUAL TABLE t2 USING fts5(x, y); BEGIN; INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); COMMIT; } | > | > | 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | CREATE VIRTUAL TABLE t2 USING fts5(x, y); BEGIN; INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); COMMIT; } do_execsql_test 17.1 { SELECT * FROM t2('y:a*') WHERE rowid BETWEEN 10 AND 20 } do_execsql_test 17.2 { BEGIN; INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); SELECT * FROM t2('y:a*') WHERE rowid BETWEEN 10 AND 20 ; } do_execsql_test 17.3 { COMMIT |
︙ | ︙ |
Changes to ext/fts5/test/fts5synonym2.test.
︙ | ︙ | |||
38 39 40 41 42 43 44 | } } list [sort_poslist $PL] $CL } sqlite3_fts5_create_function db fts5_test_bothlist fts5_test_bothlist | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | } } list [sort_poslist $PL] $CL } sqlite3_fts5_create_function db fts5_test_bothlist fts5_test_bothlist proc fts5_rowid {cmd} { expr [$cmd xRowid] } sqlite3_fts5_create_function db fts5_rowid fts5_rowid do_execsql_test 1.$tok.0.1 " CREATE VIRTUAL TABLE ss USING fts5(a, b, tokenize='tclnum $tok', detail=%DETAIL%); INSERT INTO ss(ss, rank) VALUES('rank', 'fts5_rowid()'); " |
︙ | ︙ |
Added ext/fts5/test/fts5tokenizer2.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 | # 2023 Nov 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. # #*********************************************************************** # # Tests focusing on the built-in fts5 tokenizers. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5tokenizer2 # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } sqlite3_fts5_create_tokenizer db tst get_tst_tokenizer proc get_tst_tokenizer {args} { return "tst_tokenizer" } proc tst_tokenizer {flags txt} { set token "" set lTok [list] foreach c [split $txt {}] { if {$token==""} { append token $c } else { set t1 [string is upper $token] set t2 [string is upper $c] if {$t1!=$t2} { lappend lTok $token set token "" } append token $c } } if {$token!=""} { lappend lTok $token } set iOff 0 foreach t $lTok { set n [string length $t] sqlite3_fts5_token $t $iOff [expr $iOff+$n] incr iOff $n } } do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(t, tokenize=tst); } do_execsql_test 1.1 { INSERT INTO t1 VALUES('AAdontBBmess'); } do_execsql_test 1.2 { SELECT snippet(t1, 0, '>', '<', '...', 4) FROM t1('BB'); } {AAdont>BB<mess} do_execsql_test 1.3 { SELECT highlight(t1, 0, '>', '<') FROM t1('BB'); } {AAdont>BB<mess} do_execsql_test 1.4 { SELECT highlight(t1, 0, '>', '<') FROM t1('AA'); } {>AA<dontBBmess} do_execsql_test 1.5 { SELECT highlight(t1, 0, '>', '<') FROM t1('dont'); } {AA>dont<BBmess} do_execsql_test 1.6 { SELECT highlight(t1, 0, '>', '<') FROM t1('mess'); } {AAdontBB>mess<} do_execsql_test 1.7 { SELECT highlight(t1, 0, '>', '<') FROM t1('BB mess'); } {AAdont>BBmess<} finish_test |
Added ext/fts5/test/fts5trigram2.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 | # 2023 October 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. # #************************************************************************* # # Tests for the fts5 "trigram" tokenizer. # source [file join [file dirname [info script]] fts5_common.tcl] ifcapable !fts5 { finish_test ; return } set ::testprefix fts5trigram2 do_execsql_test 1.0 " CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize='trigram remove_diacritics 1'); INSERT INTO t1 VALUES('abc\u0303defghijklm'); INSERT INTO t1 VALUES('a\u0303b\u0303c\u0303defghijklm'); " do_execsql_test 1.1 { SELECT highlight(t1, 0, '(', ')') FROM t1('abc'); } [list \ "(abc\u0303)defghijklm" \ "(a\u0303b\u0303c\u0303)defghijklm" \ ] do_execsql_test 1.2 { SELECT highlight(t1, 0, '(', ')') FROM t1('bcde'); } [list \ "a(bc\u0303de)fghijklm" \ "a\u0303(b\u0303c\u0303de)fghijklm" \ ] do_execsql_test 1.3 { SELECT highlight(t1, 0, '(', ')') FROM t1('cdef'); } [list \ "ab(c\u0303def)ghijklm" \ "a\u0303b\u0303(c\u0303def)ghijklm" \ ] do_execsql_test 1.4 { SELECT highlight(t1, 0, '(', ')') FROM t1('def'); } [list \ "abc\u0303(def)ghijklm" \ "a\u0303b\u0303c\u0303(def)ghijklm" \ ] #------------------------------------------------------------------------- do_catchsql_test 2.0 { CREATE VIRTUAL TABLE t2 USING fts5( z, tokenize='trigram case_sensitive 1 remove_diacritics 1' ); } {1 {error in tokenizer constructor}} do_execsql_test 2.1 { CREATE VIRTUAL TABLE t2 USING fts5( z, tokenize='trigram case_sensitive 0 remove_diacritics 1' ); } do_execsql_test 2.2 " INSERT INTO t2 VALUES('\u00E3bcdef'); INSERT INTO t2 VALUES('b\u00E3cdef'); INSERT INTO t2 VALUES('bc\u00E3def'); INSERT INTO t2 VALUES('bcd\u00E3ef'); " do_execsql_test 2.3 { SELECT highlight(t2, 0, '(', ')') FROM t2('abc'); } "(\u00E3bc)def" do_execsql_test 2.4 { SELECT highlight(t2, 0, '(', ')') FROM t2('bac'); } "(b\u00E3c)def" do_execsql_test 2.5 { SELECT highlight(t2, 0, '(', ')') FROM t2('bca'); } "(bc\u00E3)def" do_execsql_test 2.6 " SELECT highlight(t2, 0, '(', ')') FROM t2('\u00E3bc'); " "(\u00E3bc)def" #------------------------------------------------------------------------- do_execsql_test 3.0 { CREATE VIRTUAL TABLE t3 USING fts5( z, tokenize='trigram remove_diacritics 1' ); } {} do_execsql_test 3.1 " INSERT INTO t3 VALUES ('\u0303abc\u0303'); " do_execsql_test 3.2 { SELECT highlight(t3, 0, '(', ')') FROM t3('abc'); } "\u0303(abc\u0303)" #------------------------------------------------------------------------- do_execsql_test 4.0 { CREATE VIRTUAL TABLE t4 USING fts5(z, tokenize=trigram); } {} breakpoint do_execsql_test 4.1 { INSERT INTO t4 VALUES('ABCD'); } {} finish_test |
Changes to ext/fts5/test/fts5vocab2.test.
︙ | ︙ | |||
276 277 278 279 280 281 282 283 284 285 286 | } {1 {query aborted}} do_catchsql_test 5.2 { DELETE FROM t1 WHERE rowid>100; INSERT INTO t1 SELECT randomblob(3000) FROM v1 } {1 {query aborted}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {1 {query aborted}} do_catchsql_test 5.2 { DELETE FROM t1 WHERE rowid>100; INSERT INTO t1 SELECT randomblob(3000) FROM v1 } {1 {query aborted}} #------------------------------------------------------------------------- reset_db sqlite3_fts5_may_be_corrupt 1 do_execsql_test 6.0 { BEGIN TRANSACTION; CREATE VIRTUAL TABLE t1 USING fts5(a,b unindexed,c,tokenize="porter ascii",tokendata=1); REPLACE INTO t1_data VALUES(1,X'03090009'); REPLACE INTO t1_data VALUES(10,X'000000000103030003010101020101030101'); REPLACE INTO t1_data VALUES(137438953473,X'0000002e023061010202010162010203010163010204010167010601020201016801060102030101690106010204040606060808'); REPLACE INTO t1_data VALUES(274877906945,X'0000001f013067020802010202010168020803010203010169020804010204040909'); REPLACE INTO t1_data VALUES(412316860417,X'0000002e023061030202010162030203010163030204010167030601020201016803060102030101690306010204040606060808'); COMMIT; } do_execsql_test 6.1 { CREATE VIRTUAL TABLE t3 USING fts5vocab('t1', 'row'); } do_catchsql_test 6.2 { SELECT * FROM t3; } {1 {database disk image is malformed}} sqlite3_fts5_may_be_corrupt 0 finish_test |
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/GNUmakefile.
︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 | dir.tests := $(dir.src)/tests mkdir ?= mkdir -p $(dir.bld.c): $(mkdir) $@ javac.flags ?= -Xlint:unchecked -Xlint:deprecation java.flags ?= jnicheck ?= 1 ifeq (1,$(jnicheck)) java.flags += -Xcheck:jni endif classpath := $(dir.src) CLEAN_FILES := $(package.jar) | > > | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | dir.tests := $(dir.src)/tests mkdir ?= mkdir -p $(dir.bld.c): $(mkdir) $@ javac.flags ?= -Xlint:unchecked -Xlint:deprecation java.flags ?= javac.flags += -encoding utf8 # -------------^^^^^^^^^^^^^^ required for Windows builds jnicheck ?= 1 ifeq (1,$(jnicheck)) java.flags += -Xcheck:jni endif classpath := $(dir.src) CLEAN_FILES := $(package.jar) |
︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | .NOTPARALLEL: $(bin.version-info) $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile $(MAKE) -C $(dir.top) version-info # Be explicit about which Java files to compile so that we can work on # in-progress files without requiring them to be in a compilable statae. JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\ NotNull.java \ Nullable.java \ ) $(patsubst %,$(dir.src.capi)/%,\ AbstractCollationCallback.java \ AggregateFunction.java \ AuthorizerCallback.java \ AutoExtensionCallback.java \ BusyHandlerCallback.java \ CollationCallback.java \ CollationNeededCallback.java \ CommitHookCallback.java \ ConfigLogCallback.java \ | > | > > | 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 | .NOTPARALLEL: $(bin.version-info) $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile $(MAKE) -C $(dir.top) version-info # Be explicit about which Java files to compile so that we can work on # in-progress files without requiring them to be in a compilable statae. JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\ Experimental.java \ NotNull.java \ Nullable.java \ ) $(patsubst %,$(dir.src.capi)/%,\ AbstractCollationCallback.java \ AggregateFunction.java \ AuthorizerCallback.java \ AutoExtensionCallback.java \ BusyHandlerCallback.java \ CollationCallback.java \ CollationNeededCallback.java \ CommitHookCallback.java \ ConfigLogCallback.java \ ConfigSqlLogCallback.java \ NativePointerHolder.java \ OutputPointer.java \ PrepareMultiCallback.java \ PreupdateHookCallback.java \ ProgressHandlerCallback.java \ ResultCode.java \ RollbackHookCallback.java \ ScalarFunction.java \ SQLFunction.java \ CallbackProxy.java \ CApi.java \ TableColumnMetadata.java \ TraceV2Callback.java \ UpdateHookCallback.java \ ValueHolder.java \ WindowFunction.java \ XDestroyCallback.java \ sqlite3.java \ sqlite3_blob.java \ sqlite3_context.java \ sqlite3_stmt.java \ sqlite3_value.java \ ) $(patsubst %,$(dir.src.jni)/wrapper1/%,\ AggregateFunction.java \ ScalarFunction.java \ SqlFunction.java \ Sqlite.java \ SqliteException.java \ ValueHolder.java \ WindowFunction.java \ ) JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\ capi/Tester1.java \ wrapper1/Tester2.java \ ) ifeq (1,$(enable.fts5)) |
︙ | ︙ | |||
155 156 157 158 159 160 161 162 163 164 165 166 | ifeq (1,$(enable.tester)) JAVA_FILES += $(JAVA_FILES.tester) endif CLASS_FILES := define CLASSFILE_DEPS all: $(1).class CLASS_FILES += $(1).class endef $(foreach B,$(basename \ $(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.tester)),\ $(eval $(call CLASSFILE_DEPS,$(B)))) | > | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | ifeq (1,$(enable.tester)) JAVA_FILES += $(JAVA_FILES.tester) endif CLASS_FILES := define CLASSFILE_DEPS all: $(1).class $(1).class: $(1).java CLASS_FILES += $(1).class endef $(foreach B,$(basename \ $(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.tester)),\ $(eval $(call CLASSFILE_DEPS,$(B)))) $(CLASS_FILES): $(MAKEFILE) $(bin.javac) $(javac.flags) -h $(dir.bld.c) -cp $(classpath) $(JAVA_FILES) #.PHONY: classfiles ######################################################################## # Set up sqlite3.c and sqlite3.h... # |
︙ | ︙ | |||
222 223 224 225 226 227 228 | -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_BYTECODE_VTAB \ -DSQLITE_ENABLE_OFFSET_SQL_FUNC \ -DSQLITE_ENABLE_PREUPDATE_HOOK \ -DSQLITE_ENABLE_NORMALIZE \ | | > | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_BYTECODE_VTAB \ -DSQLITE_ENABLE_OFFSET_SQL_FUNC \ -DSQLITE_ENABLE_PREUPDATE_HOOK \ -DSQLITE_ENABLE_NORMALIZE \ -DSQLITE_ENABLE_SQLLOG \ -DSQLITE_ENABLE_COLUMN_METADATA endif ifeq (1,$(opt.debug)) SQLITE_OPT += -DSQLITE_DEBUG -g -DDEBUG -UNDEBUG else SQLITE_OPT += -Os endif |
︙ | ︙ | |||
312 313 314 315 316 317 318 | $(java.flags) -cp $(classpath) test.deps := $(CLASS_FILES) $(package.dll) test-one: $(test.deps) $(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags) $(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 $(Tester2.flags) test-sqllog: $(test.deps) @echo "Testing with -sqllog..." | | | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | $(java.flags) -cp $(classpath) test.deps := $(CLASS_FILES) $(package.dll) test-one: $(test.deps) $(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags) $(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 $(Tester2.flags) test-sqllog: $(test.deps) @echo "Testing with -sqllog..." $(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags) -sqllog test-mt: $(test.deps) @echo "Testing in multi-threaded mode:"; $(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 \ -t 7 -r 50 -shuffle $(Tester1.flags) $(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 \ -t 7 -r 50 -shuffle $(Tester2.flags) |
︙ | ︙ |
Changes to ext/jni/README.md.
︙ | ︙ | |||
12 13 14 15 16 17 18 | <https://sqlite.org/forum> > **FOREWARNING:** this subproject is very much in development and subject to any number of changes. Please do not rely on any information about its API until this disclaimer is removed. The JNI | | < | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <https://sqlite.org/forum> > **FOREWARNING:** this subproject is very much in development and subject to any number of changes. Please do not rely on any information about its API until this disclaimer is removed. The JNI bindings released with version 3.43 are a "tech preview." Once finalized, strong backward compatibility guarantees will apply. Project goals/requirements: - A [1-to-1(-ish) mapping of the C API](#1to1ish) to Java via JNI, insofar as cross-language semantics allow for. A closely-related goal is that [the C documentation](https://sqlite.org/c3ref/intro.html) should be usable as-is, insofar as possible, for the JNI binding. |
︙ | ︙ | |||
38 39 40 41 42 43 44 45 46 47 | insofar as they neither interfere with each other nor become a maintenance burden for the sqlite developers. Non-goals: - Creation of high-level OO wrapper APIs. Clients are free to create them off of the C-style API. - Support for mixed-mode operation, where client code accesses SQLite both via the Java-side API and the C API via their own native | > > > | | < | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | insofar as they neither interfere with each other nor become a maintenance burden for the sqlite developers. Non-goals: - Creation of high-level OO wrapper APIs. Clients are free to create them off of the C-style API. - Virtual tables are unlikely to be supported due to the amount of glue code needed to fit them into Java. - Support for mixed-mode operation, where client code accesses SQLite both via the Java-side API and the C API via their own native code. Such cases would be a minefield of potential mis-interactions between this project's JNI bindings and mixed-mode client code. Hello World ----------------------------------------------------------------------- ```java import org.sqlite.jni.*; |
︙ | ︙ | |||
119 120 121 122 123 124 125 | Where it makes sense to do so for usability, Java-side overloads are provided which accept or return data in alternative forms or provide sensible default argument values. In all such cases they are thin proxies around the corresponding C APIs and do not introduce new semantics. | | < < > > > | | | | | > | | 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 | Where it makes sense to do so for usability, Java-side overloads are provided which accept or return data in alternative forms or provide sensible default argument values. In all such cases they are thin proxies around the corresponding C APIs and do not introduce new semantics. In a few cases, Java-specific capabilities have been added in new APIs, all of which have "_java" somewhere in their names. Examples include: - `sqlite3_result_java_object()` - `sqlite3_column_java_object()` - `sqlite3_value_java_object()` which, as one might surmise, collectively enable the passing of arbitrary Java objects from user-defined SQL functions through to the caller. Golden Rule: Garbage Collection Cannot Free SQLite Resources ------------------------------------------------------------------------ It is important that all databases and prepared statement handles get cleaned up by client code. A database cannot be closed if it has open statement handles. `sqlite3_close()` fails if the db cannot be closed whereas `sqlite3_close_v2()` recognizes that case and marks the db as a "zombie," pending finalization when the library detects that all pending statements have been closed. Be aware that Java garbage collection _cannot_ close a database or finalize a prepared statement. Those things require explicit API calls. Classes for which it is sensible support Java's `AutoCloseable` interface so can be used with try-with-resources constructs. Golden Rule #2: _Never_ Throw from Callbacks (Unless...) ------------------------------------------------------------------------ All routines in this API, barring explicitly documented exceptions, retain C-like semantics. For example, they are not permitted to throw or propagate exceptions and must return error information (if any) via result codes or `null`. The only cases where the C-style APIs may throw is through client-side misuse, e.g. passing in a null where it may cause a `NullPointerException`. The APIs clearly mark function parameters which should not be null, but does not generally actively defend itself against such misuse. Some C-style APIs explicitly accept `null` as a no-op for usability's sake, and some of the JNI APIs deliberately return an error code, instead of segfaulting, when passed a `null`. Client-defined callbacks _must never throw exceptions_ unless _very explitly documented_ as being throw-safe. Exceptions are generally reserved for higher-level bindings which are constructed to specifically deal with them and ensure that they do not leak C-level resources. In some cases, callback handlers are permitted to throw, in which cases they get translated to C-level result codes and/or messages. If a callback which is not permitted to throw throws, its exception may trigger debug output but will otherwise be suppressed. |
︙ | ︙ | |||
288 289 290 291 292 293 294 | > Design question: does the encoding argument serve any purpose in Java? That's as-yet undetermined. If not, it will be removed. `SQLFunction` is not used directly, but is instead instantiated via one of its three subclasses: | | | | | | 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 | > Design question: does the encoding argument serve any purpose in Java? That's as-yet undetermined. If not, it will be removed. `SQLFunction` is not used directly, but is instead instantiated via one of its three subclasses: - `ScalarFunction` implements simple scalar functions using but a single callback. - `AggregateFunction` implements aggregate functions using two callbacks. - `WindowFunction` implements window functions using four callbacks. Search [`Tester1.java`](/file/ext/jni/src/org/sqlite/jni/capi/Tester1.java) for `SQLFunction` for how it's used. Reminder: see the disclaimer at the top of this document regarding the in-flux nature of this API. ### And so on... Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and `sqlite3_update_hook()`, use interfaces similar to those shown above. Despite the changes in signature, the JNI layer makes every effort to provide the same semantics as the C API documentation suggests. |
Changes to ext/jni/src/c/sqlite3-jni.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | ************************************************************************* ** This file implements the JNI bindings declared in ** org.sqlite.jni.capi.CApi (from which sqlite3-jni.h is generated). */ /* ** If you found this comment by searching the code for | > | | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ************************************************************************* ** This file implements the JNI bindings declared in ** org.sqlite.jni.capi.CApi (from which sqlite3-jni.h is generated). */ /* ** If you found this comment by searching the code for ** CallStaticObjectMethod because it appears in console output then ** you're probably the victim of an OpenJDK bug: ** ** https://bugs.openjdk.org/browse/JDK-8130659 ** ** It's known to happen with OpenJDK v8 but not with v19. It was ** triggered by this code long before it made any use of ** CallStaticObjectMethod(). */ /* ** Define any SQLITE_... config defaults we want if they aren't ** overridden by the builder. Please keep these alphabetized. */ |
︙ | ︙ | |||
86 87 88 89 90 91 92 | /* SQLITE_J... */ #ifdef SQLITE_JNI_FATAL_OOM #if !SQLITE_JNI_FATAL_OOM #undef SQLITE_JNI_FATAL_OOM #endif #endif | < < < < < < > > > > > > | 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 | /* SQLITE_J... */ #ifdef SQLITE_JNI_FATAL_OOM #if !SQLITE_JNI_FATAL_OOM #undef SQLITE_JNI_FATAL_OOM #endif #endif /**********************************************************************/ /* SQLITE_O... */ #ifndef SQLITE_OMIT_DEPRECATED # define SQLITE_OMIT_DEPRECATED 1 #endif #ifndef SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION 1 #endif #ifndef SQLITE_OMIT_SHARED_CACHE # define SQLITE_OMIT_SHARED_CACHE 1 #endif #ifdef SQLITE_OMIT_UTF16 /* UTF16 is required for java */ # undef SQLITE_OMIT_UTF16 1 #endif /**********************************************************************/ /* SQLITE_S... */ #ifndef SQLITE_STRICT_SUBTYPE # define SQLITE_STRICT_SUBTYPE 1 #endif /**********************************************************************/ /* SQLITE_T... */ #ifndef SQLITE_TEMP_STORE # define SQLITE_TEMP_STORE 2 #endif #ifndef SQLITE_THREADSAFE |
︙ | ︙ | |||
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | ** S3JniCast_L2P and P2L cast jlong (64-bit) to/from pointers. This is ** required for casting warning-free on 32-bit builds, where we ** otherwise get complaints that we're casting between different-sized ** int types. ** ** This use of intptr_t is the _only_ reason we require <stdint.h> ** which, in turn, requires building with -std=c99 (or later). */ #define S3JniCast_L2P(JLongAsPtr) (void*)((intptr_t)(JLongAsPtr)) #define S3JniCast_P2L(PTR) (jlong)((intptr_t)(PTR)) /* ** Shortcuts for the first 2 parameters to all JNI bindings. ** ** The type of the jSelf arg differs, but no docs seem to mention ** this: for static methods it's of type jclass and for non-static ** it's jobject. jobject actually works for all funcs, in the sense ** that it compiles and runs so long as we don't use jSelf (which is ** only rarely needed in this code), but to be pedantically correct we ** need the proper type in the signature. ** ** https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#jni_interface_functions_and_pointers */ | > > | | | 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 | ** S3JniCast_L2P and P2L cast jlong (64-bit) to/from pointers. This is ** required for casting warning-free on 32-bit builds, where we ** otherwise get complaints that we're casting between different-sized ** int types. ** ** This use of intptr_t is the _only_ reason we require <stdint.h> ** which, in turn, requires building with -std=c99 (or later). ** ** See also: the notes for LongPtrGet_T. */ #define S3JniCast_L2P(JLongAsPtr) (void*)((intptr_t)(JLongAsPtr)) #define S3JniCast_P2L(PTR) (jlong)((intptr_t)(PTR)) /* ** Shortcuts for the first 2 parameters to all JNI bindings. ** ** The type of the jSelf arg differs, but no docs seem to mention ** this: for static methods it's of type jclass and for non-static ** it's jobject. jobject actually works for all funcs, in the sense ** that it compiles and runs so long as we don't use jSelf (which is ** only rarely needed in this code), but to be pedantically correct we ** need the proper type in the signature. ** ** https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#jni_interface_functions_and_pointers */ #define JniArgsEnvObj JNIEnv * env, jobject jSelf #define JniArgsEnvClass JNIEnv * env, jclass jKlazz /* ** Helpers to account for -Xcheck:jni warnings about not having ** checked for exceptions. */ #define S3JniIfThrew if( (*env)->ExceptionCheck(env) ) #define S3JniExceptionClear (*env)->ExceptionClear(env) #define S3JniExceptionReport (*env)->ExceptionDescribe(env) |
︙ | ︙ | |||
653 654 655 656 657 658 659 660 661 662 663 664 665 666 | struct { jclass cLong /* global ref to java.lang.Long */; jclass cString /* global ref to java.lang.String */; jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; jmethodID ctorLong1 /* the Long(long) constructor */; jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; jmethodID stringGetBytes /* the String.getBytes(Charset) method */; } g; /* ** The list of Java-side auto-extensions ** (org.sqlite.jni.capi.AutoExtensionCallback objects). */ struct { S3JniAutoExtension *aExt /* The auto-extension list. It is | > > > > > > > > > > > > > > > | 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 | struct { jclass cLong /* global ref to java.lang.Long */; jclass cString /* global ref to java.lang.String */; jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; jmethodID ctorLong1 /* the Long(long) constructor */; jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; jmethodID stringGetBytes /* the String.getBytes(Charset) method */; /* ByteBuffer may or may not be supported via JNI on any given platform: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#nio_support We only store a ref to byteBuffer.klazz if JNI support for ByteBuffer is available (which we determine during static init). */ struct { jclass klazz /* global ref to java.nio.ByteBuffer */; jmethodID midAlloc /* ByteBuffer.allocateDirect() */; jmethodID midLimit /* ByteBuffer.limit() */; } byteBuffer; } g; /* ** The list of Java-side auto-extensions ** (org.sqlite.jni.capi.AutoExtensionCallback objects). */ struct { S3JniAutoExtension *aExt /* The auto-extension list. It is |
︙ | ︙ | |||
859 860 861 862 863 864 865 866 867 868 869 870 871 872 | s3jni__jbyteArray_bytes2(env, (jByteArray), (ptrToSz)) #define s3jni_jbyteArray_bytes(jByteArray) s3jni__jbyteArray_bytes2(env, (jByteArray), 0) #define s3jni_jbyteArray_release(jByteArray,jBytes) \ if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_ABORT) #define s3jni_jbyteArray_commit(jByteArray,jBytes) \ if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_COMMIT) /* ** Returns the current JNIEnv object. Fails fatally if it cannot find ** the object. */ static JNIEnv * s3jni_env(void){ JNIEnv * env = 0; if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | s3jni__jbyteArray_bytes2(env, (jByteArray), (ptrToSz)) #define s3jni_jbyteArray_bytes(jByteArray) s3jni__jbyteArray_bytes2(env, (jByteArray), 0) #define s3jni_jbyteArray_release(jByteArray,jBytes) \ if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_ABORT) #define s3jni_jbyteArray_commit(jByteArray,jBytes) \ if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_COMMIT) /* ** If jbb is-a java.nio.Buffer object and the JNI environment supports ** it, *pBuf is set to the buffer's memory and *pN is set to its ** limit() (as opposed to its capacity()). If jbb is NULL, not a ** Buffer, or the JNI environment does not support that operation, ** *pBuf is set to 0 and *pN is set to 0. ** ** Note that the length of the buffer can be larger than SQLITE_LIMIT ** but this function does not know what byte range of the buffer is ** required so cannot check for that violation. The caller is required ** to ensure that any to-be-bind()ed range fits within SQLITE_LIMIT. ** ** Sidebar: it is unfortunate that we cannot get ByteBuffer.limit() ** via a JNI method like we can for ByteBuffer.capacity(). We instead ** have to call back into Java to get the limit(). Depending on how ** the ByteBuffer is used, the limit and capacity might be the same, ** but when reusing a buffer, the limit may well change whereas the ** capacity is fixed. The problem with, e.g., read()ing blob data to a ** ByteBuffer's memory based on its capacity is that Java-level code ** is restricted to accessing the range specified in ** ByteBuffer.limit(). If we were to honor only the capacity, we ** could end up writing to, or reading from, parts of a ByteBuffer ** which client code itself cannot access without explicitly modifying ** the limit. The penalty we pay for this correctness is that we must ** call into Java to get the limit() of every ByteBuffer we work with. ** ** An alternative to having to call into ByteBuffer.limit() from here ** would be to add private native impls of all ByteBuffer-using ** methods, each of which adds a jint parameter which _must_ be set to ** theBuffer.limit() by public Java APIs which use those private impls ** to do the real work. */ static void s3jni__get_nio_buffer(JNIEnv * const env, jobject jbb, void **pBuf, jint * pN ){ *pBuf = 0; *pN = 0; if( jbb ){ *pBuf = (*env)->GetDirectBufferAddress(env, jbb); if( *pBuf ){ /* ** Maintenance reminder: do not use ** (*env)->GetDirectBufferCapacity(env,jbb), even though it ** would be much faster, for reasons explained in this ** function's comments. */ *pN = (*env)->CallIntMethod(env, jbb, SJG.g.byteBuffer.midLimit); S3JniExceptionIsFatal("Error calling ByteBuffer.limit() method."); } } } #define s3jni_get_nio_buffer(JOBJ,vpOut,jpOut) \ s3jni__get_nio_buffer(env,(JOBJ),(vpOut),(jpOut)) /* ** Returns the current JNIEnv object. Fails fatally if it cannot find ** the object. */ static JNIEnv * s3jni_env(void){ JNIEnv * env = 0; if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, |
︙ | ︙ | |||
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 | static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, int nP){ jstring const rv = p ? (*env)->NewString(env, (const jchar *)p, (jsize)(nP/2)) : NULL; s3jni_oom_check( p ? !!rv : 1 ); return rv; } /* ** Requires jx to be a Throwable. Calls its toString() method and ** returns its value converted to a UTF-8 string. The caller owns the ** returned string and must eventually sqlite3_free() it. Returns 0 ** if there is a problem fetching the info or on OOM. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 | static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, int nP){ jstring const rv = p ? (*env)->NewString(env, (const jchar *)p, (jsize)(nP/2)) : NULL; s3jni_oom_check( p ? !!rv : 1 ); return rv; } /* ** Creates a new ByteBuffer instance with a capacity of n. assert()s ** that SJG.g.byteBuffer.klazz is not 0 and n>0. */ static jobject s3jni__new_ByteBuffer(JNIEnv * const env, int n){ jobject rv = 0; assert( SJG.g.byteBuffer.klazz ); assert( SJG.g.byteBuffer.midAlloc ); assert( n > 0 ); rv = (*env)->CallStaticObjectMethod(env, SJG.g.byteBuffer.klazz, SJG.g.byteBuffer.midAlloc, (jint)n); S3JniIfThrew { S3JniExceptionReport; S3JniExceptionClear; } s3jni_oom_check( rv ); return rv; } /* ** If n>0 and sqlite3_jni_supports_nio() is true then this creates a ** new ByteBuffer object and copies n bytes from p to it. Returns NULL ** if n is 0, sqlite3_jni_supports_nio() is false, or on allocation ** error (unless fatal alloc failures are enabled). */ static jobject s3jni__blob_to_ByteBuffer(JNIEnv * const env, const void * p, int n){ jobject rv = NULL; assert( n >= 0 ); if( 0==n || !SJG.g.byteBuffer.klazz ){ return NULL; } rv = s3jni__new_ByteBuffer(env, n); if( rv ){ void * tgt = (*env)->GetDirectBufferAddress(env, rv); memcpy(tgt, p, (size_t)n); } return rv; } /* ** Requires jx to be a Throwable. Calls its toString() method and ** returns its value converted to a UTF-8 string. The caller owns the ** returned string and must eventually sqlite3_free() it. Returns 0 ** if there is a problem fetching the info or on OOM. ** |
︙ | ︙ | |||
1468 1469 1470 1471 1472 1473 1474 | ** ** sqlite3 * s = PtrGet_sqlite3_stmt(...) ** ** will work, despite the incorrect macro name, so long as the ** argument is a Java sqlite3 object, as this operation only has void ** pointers to work with. */ | | | | | | | | | > > > > > > > > > | | | | | | | 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 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 | ** ** sqlite3 * s = PtrGet_sqlite3_stmt(...) ** ** will work, despite the incorrect macro name, so long as the ** argument is a Java sqlite3 object, as this operation only has void ** pointers to work with. */ #define PtrGet_T(T,JOBJ) (T*)NativePointerHolder_get((JOBJ), S3JniNph(T)) #define PtrGet_sqlite3(JOBJ) PtrGet_T(sqlite3, (JOBJ)) #define PtrGet_sqlite3_backup(JOBJ) PtrGet_T(sqlite3_backup, (JOBJ)) #define PtrGet_sqlite3_blob(JOBJ) PtrGet_T(sqlite3_blob, (JOBJ)) #define PtrGet_sqlite3_context(JOBJ) PtrGet_T(sqlite3_context, (JOBJ)) #define PtrGet_sqlite3_stmt(JOBJ) PtrGet_T(sqlite3_stmt, (JOBJ)) #define PtrGet_sqlite3_value(JOBJ) PtrGet_T(sqlite3_value, (JOBJ)) /* ** LongPtrGet_T(X,Y) expects X to be an unqualified sqlite3 struct ** type name and Y to be a native pointer to such an object in the ** form of a jlong value. The jlong is simply cast to (X*). This ** approach is, as of 2023-09-27, supplanting the former approach. We ** now do the native pointer extraction in the Java side, rather than ** the C side, because it's reportedly significantly faster. The ** intptr_t part here is necessary for compatibility with (at least) ** ARM32. ** ** 2023-11-09: testing has not revealed any measurable performance ** difference between the approach of passing type T to C compared to ** passing pointer-to-T to C, and adding support for the latter ** everywhere requires sigificantly more code. As of this writing, the ** older/simpler approach is being applied except for (A) where the ** newer approach has already been applied and (B) hot-spot APIs where ** a difference of microseconds (i.e. below our testing measurement ** threshold) might add up. */ #define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)((JLongAsPtr))) #define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,(JLongAsPtr)) #define LongPtrGet_sqlite3_backup(JLongAsPtr) LongPtrGet_T(sqlite3_backup,(JLongAsPtr)) #define LongPtrGet_sqlite3_blob(JLongAsPtr) LongPtrGet_T(sqlite3_blob,(JLongAsPtr)) #define LongPtrGet_sqlite3_stmt(JLongAsPtr) LongPtrGet_T(sqlite3_stmt,(JLongAsPtr)) #define LongPtrGet_sqlite3_value(JLongAsPtr) LongPtrGet_T(sqlite3_value,(JLongAsPtr)) /* ** Extracts the new S3JniDb instance from the free-list, or allocates ** one if needed, associates it with pDb, and returns. Returns NULL ** on OOM. The returned object MUST, on success of the calling ** operation, subsequently be associated with jDb via ** NativePointerHolder_set() or freed using S3JniDb_set_aside(). */ |
︙ | ︙ | |||
1549 1550 1551 1552 1553 1554 1555 | /* ** Evaluates to the S3JniDb object for the given sqlite3 object, or ** NULL if pDb is NULL or was not initialized via the JNI interfaces. */ #define S3JniDb_from_c(sqlite3Ptr) \ ((sqlite3Ptr) ? S3JniDb_from_clientdata(sqlite3Ptr) : 0) #define S3JniDb_from_jlong(sqlite3PtrAsLong) \ | | | 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 | /* ** Evaluates to the S3JniDb object for the given sqlite3 object, or ** NULL if pDb is NULL or was not initialized via the JNI interfaces. */ #define S3JniDb_from_c(sqlite3Ptr) \ ((sqlite3Ptr) ? S3JniDb_from_clientdata(sqlite3Ptr) : 0) #define S3JniDb_from_jlong(sqlite3PtrAsLong) \ S3JniDb_from_c(LongPtrGet_T(sqlite3,sqlite3PtrAsLong)) /* ** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out ** AX. */ #define S3JniAutoExtension_clear(AX) S3JniHook_unref(AX); |
︙ | ︙ | |||
1666 1667 1668 1669 1670 1671 1672 | case SQLITE_UTF16LE: case SQLITE_UTF16BE: return 1; default: return 0; } } | | > | | 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 | case SQLITE_UTF16LE: case SQLITE_UTF16BE: return 1; default: return 0; } } /* For use with sqlite3_result_pointer(), sqlite3_value_pointer(), sqlite3_bind_java_object(), and sqlite3_column_java_object(). */ static const char * const s3jni__value_jref_key = "org.sqlite.jni.capi.ResultJavaVal"; /* ** If v is not NULL, it must be a jobject global reference. Its ** reference is relinquished. */ static void S3Jni_jobject_finalizer(void *v){ if( v ){ |
︙ | ︙ | |||
1876 1877 1878 1879 1880 1881 1882 | S3JniUnrefLocal(jcx); S3JniUnrefLocal(ja); return SQLITE_NOMEM; } /* ** Requires that jCx and jArgv are sqlite3_context | | > | | | > > | > > > > > > > > > > > > > > > > > > > | > | > | 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 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 | S3JniUnrefLocal(jcx); S3JniUnrefLocal(ja); return SQLITE_NOMEM; } /* ** Requires that jCx and jArgv are sqlite3_context ** resp. array-of-sqlite3_value values initialized by udf_args(). The ** latter will be 0-and-NULL for UDF types with no arguments. This ** function zeroes out the nativePointer member of jCx and each entry ** in jArgv. This is a safety-net precaution to avoid undefined ** behavior if a Java-side UDF holds a reference to its context or one ** of its arguments. This MUST be called from any function which ** successfully calls udf_args(), after calling the corresponding UDF ** and checking its exception status, or which Java-wraps a ** sqlite3_context for use with a UDF(ish) call. It MUST NOT be called ** in any other case. */ static void udf_unargs(JNIEnv *env, jobject jCx, int argc, jobjectArray jArgv){ int i = 0; assert(jCx); NativePointerHolder_set(S3JniNph(sqlite3_context), jCx, 0); for( ; i < argc; ++i ){ jobject jsv = (*env)->GetObjectArrayElement(env, jArgv, i); /* ** There is a potential Java-triggerable case of Undefined ** Behavior here, but it would require intentional misuse of the ** API: ** ** If a Java UDF grabs an sqlite3_value from its argv and then ** assigns that element to null, it becomes unreachable to us so ** we cannot clear out its pointer. That Java-side object's ** getNativePointer() will then refer to a stale value, so passing ** it into (e.g.) sqlite3_value_SOMETHING() would invoke UB. ** ** High-level wrappers can avoid that possibility if they do not ** expose sqlite3_value directly to clients (as is the case in ** org.sqlite.jni.wrapper1.SqlFunction). ** ** One potential (but expensive) workaround for this would be to ** privately store a duplicate argv array in each sqlite3_context ** wrapper object, and clear the native pointers from that copy. */ assert(jsv && "Someone illegally modified a UDF argument array."); if( jsv ){ NativePointerHolder_set(S3JniNph(sqlite3_value), jsv, 0); } } } /* ** Must be called immediately after a Java-side UDF callback throws. ** If translateToErr is true then it sets the exception's message in |
︙ | ︙ | |||
1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 | if( jcx ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); S3JniIfThrew{ rc = udf_report_exception(env, isFinal, cx, s->zFuncName, zFuncType); } S3JniUnrefLocal(jcx); }else{ if( isFinal ) sqlite3_result_error_nomem(cx); rc = SQLITE_NOMEM; } return rc; } | > | 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 | if( jcx ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); S3JniIfThrew{ rc = udf_report_exception(env, isFinal, cx, s->zFuncName, zFuncType); } udf_unargs(env, jcx, 0, 0); S3JniUnrefLocal(jcx); }else{ if( isFinal ) sqlite3_result_error_nomem(cx); rc = SQLITE_NOMEM; } return rc; } |
︙ | ︙ | |||
2053 2054 2055 2056 2057 2058 2059 | jstring const rv = (*env)->NewStringUTF( env, CName() ); \ s3jni_oom_check(rv); \ return rv; \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */ #define WRAP_INT_STMT(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt){ \ | | | | | | | | | | > > | 2199 2200 2201 2202 2203 2204 2205 2206 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 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 | jstring const rv = (*env)->NewStringUTF( env, CName() ); \ s3jni_oom_check(rv); \ return rv; \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */ #define WRAP_INT_STMT(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt){ \ return (jint)CName(LongPtrGet_sqlite3_stmt(jpStmt)); \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*,int)). */ #define WRAP_INT_STMT_INT(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt, jint n){ \ return (jint)CName(LongPtrGet_sqlite3_stmt(jpStmt), (int)n); \ } /** Create a trivial JNI wrapper for (boolean CName(sqlite3_stmt*)). */ #define WRAP_BOOL_STMT(JniNameSuffix,CName) \ JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jobject jStmt){ \ return CName(PtrGet_sqlite3_stmt(jStmt)) ? JNI_TRUE : JNI_FALSE; \ } /** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */ #define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt, jint ndx){ \ return s3jni_utf8_to_jstring( \ CName(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx), \ -1); \ } /** Create a trivial JNI wrapper for (boolean CName(sqlite3*)). */ #define WRAP_BOOL_DB(JniNameSuffix,CName) \ JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ return CName(LongPtrGet_sqlite3(jpDb)) ? JNI_TRUE : JNI_FALSE; \ } /** Create a trivial JNI wrapper for (int CName(sqlite3*)). */ #define WRAP_INT_DB(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ return (jint)CName(LongPtrGet_sqlite3(jpDb)); \ } /** Create a trivial JNI wrapper for (int64 CName(sqlite3*)). */ #define WRAP_INT64_DB(JniNameSuffix,CName) \ JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ return (jlong)CName(LongPtrGet_sqlite3(jpDb)); \ } /** Create a trivial JNI wrapper for (jstring CName(sqlite3*,int)). */ #define WRAP_STR_DB_INT(JniNameSuffix,CName) \ JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpDb, jint ndx){ \ return s3jni_utf8_to_jstring( \ CName(LongPtrGet_sqlite3(jpDb), (int)ndx), \ -1); \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_value*)). */ #define WRAP_INT_SVALUE(JniNameSuffix,CName,DfltOnNull) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSValue); \ return (jint)(sv ? CName(sv): DfltOnNull); \ } /** Create a trivial JNI wrapper for (boolean CName(sqlite3_value*)). */ #define WRAP_BOOL_SVALUE(JniNameSuffix,CName,DfltOnNull) \ JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSValue); \ return (jint)(sv ? CName(sv) : DfltOnNull) \ ? JNI_TRUE : JNI_FALSE; \ } WRAP_INT_DB(1changes, sqlite3_changes) WRAP_INT64_DB(1changes64, sqlite3_changes64) WRAP_INT_STMT(1clear_1bindings, sqlite3_clear_bindings) WRAP_INT_STMT_INT(1column_1bytes, sqlite3_column_bytes) WRAP_INT_STMT_INT(1column_1bytes16, sqlite3_column_bytes16) WRAP_INT_STMT(1column_1count, sqlite3_column_count) WRAP_STR_STMT_INT(1column_1decltype, sqlite3_column_decltype) WRAP_STR_STMT_INT(1column_1name, sqlite3_column_name) #ifdef SQLITE_ENABLE_COLUMN_METADATA WRAP_STR_STMT_INT(1column_1database_1name, sqlite3_column_database_name) WRAP_STR_STMT_INT(1column_1origin_1name, sqlite3_column_origin_name) WRAP_STR_STMT_INT(1column_1table_1name, sqlite3_column_table_name) #endif WRAP_INT_STMT_INT(1column_1type, sqlite3_column_type) WRAP_INT_STMT(1data_1count, sqlite3_data_count) WRAP_STR_DB_INT(1db_1name, sqlite3_db_name) WRAP_INT_DB(1error_1offset, sqlite3_error_offset) WRAP_INT_DB(1extended_1errcode, sqlite3_extended_errcode) WRAP_BOOL_DB(1get_1autocommit, sqlite3_get_autocommit) WRAP_MUTF8_VOID(1libversion, sqlite3_libversion) |
︙ | ︙ | |||
2177 2178 2179 2180 2181 2182 2183 | ? sqlite3_aggregate_context(pCx, (int)(initialize ? (int)sizeof(void*) : 0)) : 0; return S3JniCast_P2L(p); } | > | > | 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 | ? sqlite3_aggregate_context(pCx, (int)(initialize ? (int)sizeof(void*) : 0)) : 0; return S3JniCast_P2L(p); } /* ** Central auto-extension runner for auto-extensions created in Java. */ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, const struct sqlite3_api_routines *ignored){ int rc = 0; unsigned i, go = 1; JNIEnv * env = 0; S3JniDb * ps; S3JniEnv * jc; |
︙ | ︙ | |||
2314 2315 2316 2317 2318 2319 2320 | } S3JniApi(sqlite3_backup_finish(),jint,1backup_1finish)( JniArgsEnvClass, jlong jpBack ){ int rc = 0; if( jpBack!=0 ){ | | | | | 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 | } S3JniApi(sqlite3_backup_finish(),jint,1backup_1finish)( JniArgsEnvClass, jlong jpBack ){ int rc = 0; if( jpBack!=0 ){ rc = sqlite3_backup_finish( LongPtrGet_sqlite3_backup(jpBack) ); } return rc; } S3JniApi(sqlite3_backup_init(),jobject,1backup_1init)( JniArgsEnvClass, jlong jpDbDest, jstring jTDest, jlong jpDbSrc, jstring jTSrc ){ sqlite3 * const pDest = LongPtrGet_sqlite3(jpDbDest); sqlite3 * const pSrc = LongPtrGet_sqlite3(jpDbSrc); char * const zDest = s3jni_jstring_to_utf8(jTDest, 0); char * const zSrc = s3jni_jstring_to_utf8(jTSrc, 0); jobject rv = 0; if( pDest && pSrc && zDest && zSrc ){ sqlite3_backup * const pB = sqlite3_backup_init(pDest, zDest, pSrc, zSrc); |
︙ | ︙ | |||
2347 2348 2349 2350 2351 2352 2353 | sqlite3_free(zSrc); return rv; } S3JniApi(sqlite3_backup_pagecount(),jint,1backup_1pagecount)( JniArgsEnvClass, jlong jpBack ){ | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | 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 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 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 | sqlite3_free(zSrc); return rv; } S3JniApi(sqlite3_backup_pagecount(),jint,1backup_1pagecount)( JniArgsEnvClass, jlong jpBack ){ return sqlite3_backup_pagecount(LongPtrGet_sqlite3_backup(jpBack)); } S3JniApi(sqlite3_backup_remaining(),jint,1backup_1remaining)( JniArgsEnvClass, jlong jpBack ){ return sqlite3_backup_remaining(LongPtrGet_sqlite3_backup(jpBack)); } S3JniApi(sqlite3_backup_step(),jint,1backup_1step)( JniArgsEnvClass, jlong jpBack, jint nPage ){ return sqlite3_backup_step(LongPtrGet_sqlite3_backup(jpBack), (int)nPage); } S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( JniArgsEnvClass, jlong jpStmt, jint ndx, jbyteArray baData, jint nMax ){ jsize nBA = 0; jbyte * const pBuf = baData ? s3jni_jbyteArray_bytes2(baData, &nBA) : 0; int rc; if( pBuf ){ if( nMax>nBA ){ nMax = nBA; } rc = sqlite3_bind_blob(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT); s3jni_jbyteArray_release(baData, pBuf); }else{ rc = baData ? SQLITE_NOMEM : sqlite3_bind_null( LongPtrGet_sqlite3_stmt(jpStmt), ndx ); } return (jint)rc; } /** Helper for use with s3jni_setup_nio_args(). */ struct S3JniNioArgs { jobject jBuf; /* input - ByteBuffer */ jint iOffset; /* input - byte offset */ jint iHowMany; /* input - byte count to bind/read/write */ jint nBuf; /* output - jBuf's buffer size */ void * p; /* output - jBuf's buffer memory */ void * pStart; /* output - offset of p to bind/read/write */ int nOut; /* output - number of bytes from pStart to bind/read/write */ }; typedef struct S3JniNioArgs S3JniNioArgs; static const S3JniNioArgs S3JniNioArgs_empty = { 0,0,0,0,0,0,0 }; /* ** Internal helper for sqlite3_bind_nio_buffer(), ** sqlite3_result_nio_buffer(), and similar methods which take a ** ByteBuffer object as either input or output. Populates pArgs and ** returns 0 on success, non-0 if the operation should fail. The ** caller is required to check for SJG.g.byteBuffer.klazz!=0 before calling ** this and reporting it in a way appropriate for that routine. This ** function may assert() that SJG.g.byteBuffer.klazz is not 0. ** ** The (jBuffer, iOffset, iHowMany) arguments are the (ByteBuffer, offset, ** length) arguments to the bind/result method. ** ** If iHowMany is negative then it's treated as "until the end" and ** the calculated slice is trimmed to fit if needed. If iHowMany is ** positive and extends past the end of jBuffer then SQLITE_ERROR is ** returned. ** ** Returns 0 if everything looks to be in order, else some SQLITE_... ** result code */ static int s3jni_setup_nio_args( JNIEnv *env, S3JniNioArgs * pArgs, jobject jBuffer, jint iOffset, jint iHowMany ){ jlong iEnd = 0; const int bAllowTruncate = iHowMany<0; *pArgs = S3JniNioArgs_empty; pArgs->jBuf = jBuffer; pArgs->iOffset = iOffset; pArgs->iHowMany = iHowMany; assert( SJG.g.byteBuffer.klazz ); if( pArgs->iOffset<0 ){ return SQLITE_ERROR /* SQLITE_MISUSE or SQLITE_RANGE would fit better but we use SQLITE_ERROR for consistency with the code documented for a negative target blob offset in sqlite3_blob_read/write(). */; } s3jni_get_nio_buffer(pArgs->jBuf, &pArgs->p, &pArgs->nBuf); if( !pArgs->p ){ return SQLITE_MISUSE; }else if( pArgs->iOffset>=pArgs->nBuf ){ pArgs->pStart = 0; pArgs->nOut = 0; return 0; } assert( pArgs->nBuf > 0 ); assert( pArgs->iOffset < pArgs->nBuf ); iEnd = pArgs->iHowMany<0 ? pArgs->nBuf - pArgs->iOffset : pArgs->iOffset + pArgs->iHowMany; if( iEnd>(jlong)pArgs->nBuf ){ if( bAllowTruncate ){ iEnd = pArgs->nBuf - pArgs->iOffset; }else{ return SQLITE_ERROR /* again: for consistency with blob_read/write(), though SQLITE_MISUSE or SQLITE_RANGE would be a better fit. */; } } if( iEnd - pArgs->iOffset > (jlong)SQLITE_MAX_LENGTH ){ return SQLITE_TOOBIG; } assert( pArgs->iOffset >= 0 ); assert( iEnd > pArgs->iOffset ); pArgs->pStart = pArgs->p + pArgs->iOffset; pArgs->nOut = (int)(iEnd - pArgs->iOffset); assert( pArgs->nOut > 0 ); assert( (pArgs->pStart + pArgs->nOut) <= (pArgs->p + pArgs->nBuf) ); return 0; } S3JniApi(sqlite3_bind_nio_buffer(),jint,1bind_1nio_1buffer)( JniArgsEnvClass, jobject jpStmt, jint ndx, jobject jBuffer, jint iOffset, jint iN ){ sqlite3_stmt * pStmt = PtrGet_sqlite3_stmt(jpStmt); S3JniNioArgs args; int rc; if( !pStmt || !SJG.g.byteBuffer.klazz ) return SQLITE_MISUSE; rc = s3jni_setup_nio_args(env, &args, jBuffer, iOffset, iN); if(rc){ return rc; }else if( !args.pStart || !args.nOut ){ return sqlite3_bind_null(pStmt, ndx); } return sqlite3_bind_blob( pStmt, (int)ndx, args.pStart, args.nOut, SQLITE_TRANSIENT ); } S3JniApi(sqlite3_bind_double(),jint,1bind_1double)( JniArgsEnvClass, jlong jpStmt, jint ndx, jdouble val ){ return (jint)sqlite3_bind_double(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (double)val); } S3JniApi(sqlite3_bind_int(),jint,1bind_1int)( JniArgsEnvClass, jlong jpStmt, jint ndx, jint val ){ return (jint)sqlite3_bind_int(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)val); } S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)( JniArgsEnvClass, jlong jpStmt, jint ndx, jlong val ){ return (jint)sqlite3_bind_int64(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val); } /* ** Bind a new global ref to Object `val` using sqlite3_bind_pointer(). */ S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)( JniArgsEnvClass, jlong jpStmt, jint ndx, jobject val ){ sqlite3_stmt * const pStmt = LongPtrGet_sqlite3_stmt(jpStmt); int rc = SQLITE_MISUSE; if(pStmt){ jobject const rv = S3JniRefGlobal(val); if( rv ){ rc = sqlite3_bind_pointer(pStmt, ndx, rv, s3jni__value_jref_key, S3Jni_jobject_finalizer); }else if(val){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_bind_null(pStmt, ndx); } } return rc; } S3JniApi(sqlite3_bind_null(),jint,1bind_1null)( JniArgsEnvClass, jlong jpStmt, jint ndx ){ return (jint)sqlite3_bind_null(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx); } S3JniApi(sqlite3_bind_parameter_count(),jint,1bind_1parameter_1count)( JniArgsEnvClass, jlong jpStmt ){ return (jint)sqlite3_bind_parameter_count(LongPtrGet_sqlite3_stmt(jpStmt)); } S3JniApi(sqlite3_bind_parameter_index(),jint,1bind_1parameter_1index)( JniArgsEnvClass, jlong jpStmt, jbyteArray jName ){ int rc = 0; jbyte * const pBuf = s3jni_jbyteArray_bytes(jName); if( pBuf ){ rc = sqlite3_bind_parameter_index(LongPtrGet_sqlite3_stmt(jpStmt), (const char *)pBuf); s3jni_jbyteArray_release(jName, pBuf); } return rc; } S3JniApi(sqlite3_bind_parameter_name(),jstring,1bind_1parameter_1name)( JniArgsEnvClass, jlong jpStmt, jint ndx ){ const char *z = sqlite3_bind_parameter_name(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx); return z ? s3jni_utf8_to_jstring(z, -1) : 0; } /* ** Impl of sqlite3_bind_text/text16(). */ static int s3jni__bind_text(int is16, JNIEnv *env, jlong jpStmt, jint ndx, |
︙ | ︙ | |||
2476 2477 2478 2479 2480 2481 2482 | nMax = nBA; } /* Note that we rely on the Java layer having assured that baData is NUL-terminated if nMax is negative. In order to avoid UB for such cases, we do not expose the byte-limit arguments in the public API. */ rc = is16 | | | | | 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 | nMax = nBA; } /* Note that we rely on the Java layer having assured that baData is NUL-terminated if nMax is negative. In order to avoid UB for such cases, we do not expose the byte-limit arguments in the public API. */ rc = is16 ? sqlite3_bind_text16(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT) : sqlite3_bind_text(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (const char *)pBuf, (int)nMax, SQLITE_TRANSIENT); }else{ rc = baData ? sqlite3_bind_null(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx) : SQLITE_NOMEM; } s3jni_jbyteArray_release(baData, pBuf); return (jint)rc; } |
︙ | ︙ | |||
2507 2508 2509 2510 2511 2512 2513 | return s3jni__bind_text(1, env, jpStmt, ndx, baData, nMax); } S3JniApi(sqlite3_bind_value(),jint,1bind_1value)( JniArgsEnvClass, jlong jpStmt, jint ndx, jlong jpValue ){ int rc = 0; | | | | | | | | | 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 | return s3jni__bind_text(1, env, jpStmt, ndx, baData, nMax); } S3JniApi(sqlite3_bind_value(),jint,1bind_1value)( JniArgsEnvClass, jlong jpStmt, jint ndx, jlong jpValue ){ int rc = 0; sqlite3_stmt * pStmt = LongPtrGet_sqlite3_stmt(jpStmt); if( pStmt ){ sqlite3_value *v = LongPtrGet_sqlite3_value(jpValue); if( v ){ rc = sqlite3_bind_value(pStmt, (int)ndx, v); }else{ rc = sqlite3_bind_null(pStmt, (int)ndx); } }else{ rc = SQLITE_MISUSE; } return (jint)rc; } S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob)( JniArgsEnvClass, jlong jpStmt, jint ndx, jint n ){ return (jint)sqlite3_bind_zeroblob(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)n); } S3JniApi(sqlite3_bind_zeroblob64(),jint,1bind_1zeroblob64)( JniArgsEnvClass, jlong jpStmt, jint ndx, jlong n ){ return (jint)sqlite3_bind_zeroblob64(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_uint64)n); } S3JniApi(sqlite3_blob_bytes(),jint,1blob_1bytes)( JniArgsEnvClass, jlong jpBlob ){ return sqlite3_blob_bytes(LongPtrGet_sqlite3_blob(jpBlob)); } S3JniApi(sqlite3_blob_close(),jint,1blob_1close)( JniArgsEnvClass, jlong jpBlob ){ sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); return b ? (jint)sqlite3_blob_close(b) : SQLITE_MISUSE; } S3JniApi(sqlite3_blob_open(),jint,1blob_1open)( JniArgsEnvClass, jlong jpDb, jstring jDbName, jstring jTbl, jstring jCol, jlong jRowId, jint flags, jobject jOut ){ sqlite3 * const db = LongPtrGet_sqlite3(jpDb); sqlite3_blob * pBlob = 0; char * zDbName = 0, * zTableName = 0, * zColumnName = 0; int rc; if( !db || !jDbName || !jTbl || !jCol ) return SQLITE_MISUSE; zDbName = s3jni_jstring_to_utf8(jDbName,0); zTableName = zDbName ? s3jni_jstring_to_utf8(jTbl,0) : 0; |
︙ | ︙ | |||
2586 2587 2588 2589 2590 2591 2592 | S3JniApi(sqlite3_blob_read(),jint,1blob_1read)( JniArgsEnvClass, jlong jpBlob, jbyteArray jTgt, jint iOffset ){ jbyte * const pBa = s3jni_jbyteArray_bytes(jTgt); int rc = jTgt ? (pBa ? SQLITE_MISUSE : SQLITE_NOMEM) : SQLITE_MISUSE; if( pBa ){ jsize const nTgt = (*env)->GetArrayLength(env, jTgt); | | > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > | 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 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 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 | S3JniApi(sqlite3_blob_read(),jint,1blob_1read)( JniArgsEnvClass, jlong jpBlob, jbyteArray jTgt, jint iOffset ){ jbyte * const pBa = s3jni_jbyteArray_bytes(jTgt); int rc = jTgt ? (pBa ? SQLITE_MISUSE : SQLITE_NOMEM) : SQLITE_MISUSE; if( pBa ){ jsize const nTgt = (*env)->GetArrayLength(env, jTgt); rc = sqlite3_blob_read(LongPtrGet_sqlite3_blob(jpBlob), pBa, (int)nTgt, (int)iOffset); if( 0==rc ){ s3jni_jbyteArray_commit(jTgt, pBa); }else{ s3jni_jbyteArray_release(jTgt, pBa); } } return rc; } S3JniApi(sqlite3_blob_read_nio_buffer(),jint,1blob_1read_1nio_1buffer)( JniArgsEnvClass, jlong jpBlob, jint iSrcOff, jobject jBB, jint iTgtOff, jint iHowMany ){ sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); S3JniNioArgs args; int rc; if( !b || !SJG.g.byteBuffer.klazz || iHowMany<0 ){ return SQLITE_MISUSE; }else if( iTgtOff<0 || iSrcOff<0 ){ return SQLITE_ERROR /* for consistency with underlying sqlite3_blob_read() */; }else if( 0==iHowMany ){ return 0; } rc = s3jni_setup_nio_args(env, &args, jBB, iTgtOff, iHowMany); if(rc){ return rc; }else if( !args.pStart || !args.nOut ){ return 0; } assert( args.iHowMany>0 ); return sqlite3_blob_read( b, args.pStart, (int)args.nOut, (int)iSrcOff ); } S3JniApi(sqlite3_blob_reopen(),jint,1blob_1reopen)( JniArgsEnvClass, jlong jpBlob, jlong iNewRowId ){ return (jint)sqlite3_blob_reopen(LongPtrGet_sqlite3_blob(jpBlob), (sqlite3_int64)iNewRowId); } S3JniApi(sqlite3_blob_write(),jint,1blob_1write)( JniArgsEnvClass, jlong jpBlob, jbyteArray jBa, jint iOffset ){ sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); jbyte * const pBuf = b ? s3jni_jbyteArray_bytes(jBa) : 0; const jsize nBA = pBuf ? (*env)->GetArrayLength(env, jBa) : 0; int rc = SQLITE_MISUSE; if(b && pBuf){ rc = sqlite3_blob_write( b, pBuf, (int)nBA, (int)iOffset ); } s3jni_jbyteArray_release(jBa, pBuf); return (jint)rc; } S3JniApi(sqlite3_blob_write_nio_buffer(),jint,1blob_1write_1nio_1buffer)( JniArgsEnvClass, jlong jpBlob, jint iTgtOff, jobject jBB, jint iSrcOff, jint iHowMany ){ sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); S3JniNioArgs args; int rc; if( !b || !SJG.g.byteBuffer.klazz ){ return SQLITE_MISUSE; }else if( iTgtOff<0 || iSrcOff<0 ){ return SQLITE_ERROR /* for consistency with underlying sqlite3_blob_write() */; }else if( 0==iHowMany ){ return 0; } rc = s3jni_setup_nio_args(env, &args, jBB, iSrcOff, iHowMany); if(rc){ return rc; }else if( !args.pStart || !args.nOut ){ return 0; } return sqlite3_blob_write( b, args.pStart, (int)args.nOut, (int)iTgtOff ); } /* Central C-to-Java busy handler proxy. */ static int s3jni_busy_handler(void* pState, int n){ S3JniDb * const ps = (S3JniDb *)pState; int rc = 0; S3JniDeclLocal_env; S3JniHook hook; |
︙ | ︙ | |||
2816 2817 2818 2819 2820 2821 2822 | rc = sqlite3_collation_needed(ps->pDb, 0, 0); if( 0==rc ){ S3JniHook_unref(pHook); } }else{ jclass const klazz = (*env)->GetObjectClass(env, jHook); jmethodID const xCallback = (*env)->GetMethodID( | | | 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 | rc = sqlite3_collation_needed(ps->pDb, 0, 0); if( 0==rc ){ S3JniHook_unref(pHook); } }else{ jclass const klazz = (*env)->GetObjectClass(env, jHook); jmethodID const xCallback = (*env)->GetMethodID( env, klazz, "call", "(Lorg/sqlite/jni/capi/sqlite3;ILjava/lang/String;)V" ); S3JniUnrefLocal(klazz); S3JniIfThrew { rc = s3jni_db_exception(ps->pDb, SQLITE_MISUSE, "Cannot not find matching call() in " "CollationNeededCallback object."); }else{ |
︙ | ︙ | |||
2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 | } S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); const unsigned char * const p = stmt ? sqlite3_column_text(stmt, (int)ndx) : 0; const int n = p ? sqlite3_column_bytes(stmt, (int)ndx) : 0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 | } S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } S3JniApi(sqlite3_column_java_object(),jobject,1column_1java_1object)( JniArgsEnvClass, jlong jpStmt, jint ndx ){ sqlite3_stmt * const stmt = LongPtrGet_sqlite3_stmt(jpStmt); jobject rv = 0; if( stmt ){ sqlite3 * const db = sqlite3_db_handle(stmt); sqlite3_value * sv; sqlite3_mutex_enter(sqlite3_db_mutex(db)); sv = sqlite3_column_value(stmt, (int)ndx); if( sv ){ rv = S3JniRefLocal( sqlite3_value_pointer(sv, s3jni__value_jref_key) ); } sqlite3_mutex_leave(sqlite3_db_mutex(db)); } return rv; } S3JniApi(sqlite3_column_nio_buffer(),jobject,1column_1nio_1buffer)( JniArgsEnvClass, jobject jStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jStmt); jobject rv = 0; if( stmt ){ const void * const p = sqlite3_column_blob(stmt, (int)ndx); if( p ){ const int n = sqlite3_column_bytes(stmt, (int)ndx); rv = s3jni__blob_to_ByteBuffer(env, p, n); } } return rv; } S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); const unsigned char * const p = stmt ? sqlite3_column_text(stmt, (int)ndx) : 0; const int n = p ? sqlite3_column_bytes(stmt, (int)ndx) : 0; |
︙ | ︙ | |||
2921 2922 2923 2924 2925 2926 2927 | ? &ps->hooks.commit : &ps->hooks.rollback, &hook); if( hook.jObj ){ rc = isCommit ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback) : (int)((*env)->CallVoidMethod(env, hook.jObj, hook.midCallback), 0); S3JniIfThrew{ | | > > > | 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 | ? &ps->hooks.commit : &ps->hooks.rollback, &hook); if( hook.jObj ){ rc = isCommit ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback) : (int)((*env)->CallVoidMethod(env, hook.jObj, hook.midCallback), 0); S3JniIfThrew{ rc = s3jni_db_exception(ps->pDb, SQLITE_ERROR, isCommit ? "Commit hook callback threw" : "Rollback hook callback threw"); } S3JniHook_localundup(hook); } return rc; } /* C-to-Java commit hook wrapper. */ |
︙ | ︙ | |||
3024 3025 3026 3027 3028 3029 3030 | hypothetically faster to convert). */; const jboolean rc = 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; s3jni_mutf8_release(name, zUtf8); return rc; } | | | > | | 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 | hypothetically faster to convert). */; const jboolean rc = 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; s3jni_mutf8_release(name, zUtf8); return rc; } S3JniApi(sqlite3_complete(),jint,1complete)( JniArgsEnvClass, jbyteArray jSql ){ jbyte * const pBuf = s3jni_jbyteArray_bytes(jSql); const jsize nBA = pBuf ? (*env)->GetArrayLength(env, jSql) : 0; int rc; assert( (nBA>0 ? 0==pBuf[nBA-1] : (pBuf ? 0==*pBuf : 1)) && "Byte array is not NUL-terminated." ); rc = (pBuf && 0==pBuf[(nBA ? nBA-1 : 0)]) ? sqlite3_complete( (const char *)pBuf ) : (jSql ? SQLITE_NOMEM : SQLITE_MISUSE); s3jni_jbyteArray_release(jSql, pBuf); return rc; } S3JniApi(sqlite3_config() /*for a small subset of options.*/ sqlite3_config__enable()/* internal name to avoid name-mangling issues*/, jint,1config_1_1enable)(JniArgsEnvClass, jint n){ switch( n ){ case SQLITE_CONFIG_SINGLETHREAD: case SQLITE_CONFIG_MULTITHREAD: case SQLITE_CONFIG_SERIALIZED: return sqlite3_config( n ); default: return SQLITE_MISUSE; |
︙ | ︙ | |||
3071 3072 3073 3074 3075 3076 3077 | S3JniExceptionClear; } S3JniHook_localundup(hook); S3JniUnrefLocal(jArg1); } } | | > | | 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 | S3JniExceptionClear; } S3JniHook_localundup(hook); S3JniUnrefLocal(jArg1); } } S3JniApi(sqlite3_config() /* for SQLITE_CONFIG_LOG */ sqlite3_config__config_log() /* internal name */, jint, 1config_1_1CONFIG_1LOG )(JniArgsEnvClass, jobject jLog){ S3JniHook * const pHook = &SJG.hook.configlog; int rc = 0; S3JniGlobal_mutex_enter; if( !jLog ){ rc = sqlite3_config( SQLITE_CONFIG_LOG, NULL, NULL ); |
︙ | ︙ | |||
3146 3147 3148 3149 3150 3151 3152 | } //! Requirement of SQLITE_CONFIG_SQLLOG. void sqlite3_init_sqllog(void){ sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); } #endif | | > | | | 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 | } //! Requirement of SQLITE_CONFIG_SQLLOG. void sqlite3_init_sqllog(void){ sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); } #endif S3JniApi(sqlite3_config() /* for SQLITE_CONFIG_SQLLOG */ sqlite3_config__SQLLOG() /*internal name*/, jint, 1config_1_1SQLLOG )(JniArgsEnvClass, jobject jLog){ #ifndef SQLITE_ENABLE_SQLLOG return SQLITE_MISUSE; #else S3JniHook * const pHook = &SJG.hook.sqllog; int rc = 0; S3JniGlobal_mutex_enter; |
︙ | ︙ | |||
3408 3409 3410 3411 3412 3413 3414 | int pOut = 0; rc = sqlite3_db_config( ps->pDb, (int)op, onOff, &pOut ); if( 0==rc && jOut ){ OutputPointer_set_Int32(env, jOut, pOut); } break; } | < | 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 | int pOut = 0; rc = sqlite3_db_config( ps->pDb, (int)op, onOff, &pOut ); if( 0==rc && jOut ){ OutputPointer_set_Int32(env, jOut, pOut); } break; } default: rc = SQLITE_MISUSE; } return (jint)rc; } /* |
︙ | ︙ | |||
3471 3472 3473 3474 3475 3476 3477 | S3JniDb * const ps = S3JniDb_from_java(jDb); char *zDbName = jDbName ? s3jni_jstring_to_utf8( jDbName, 0 ) : 0; rc = sqlite3_db_readonly(ps ? ps->pDb : 0, zDbName); sqlite3_free(zDbName); return (jint)rc; } | | | 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 | S3JniDb * const ps = S3JniDb_from_java(jDb); char *zDbName = jDbName ? s3jni_jstring_to_utf8( jDbName, 0 ) : 0; rc = sqlite3_db_readonly(ps ? ps->pDb : 0, zDbName); sqlite3_free(zDbName); return (jint)rc; } S3JniApi(sqlite3_db_release_memory(),jint,1db_1release_1memory)( JniArgsEnvClass, jobject jDb ){ sqlite3 * const pDb = PtrGet_sqlite3(jDb); return pDb ? sqlite3_db_release_memory(pDb) : SQLITE_MISUSE; } S3JniApi(sqlite3_db_status(),jint,1db_1status)( |
︙ | ︙ | |||
3512 3513 3514 3515 3516 3517 3518 | additional level of internal encoding in sqlite3. The end effect should be identical to using errmsg16(), however. */; } S3JniApi(sqlite3_errstr(),jstring,1errstr)( JniArgsEnvClass, jint rcCode ){ | > > > > > > > | | | | 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 | additional level of internal encoding in sqlite3. The end effect should be identical to using errmsg16(), however. */; } S3JniApi(sqlite3_errstr(),jstring,1errstr)( JniArgsEnvClass, jint rcCode ){ jstring rv; const char * z = sqlite3_errstr((int)rcCode); if( !z ){ /* This hypothetically cannot happen, but we'll behave like the low-level library would in such a case... */ z = "unknown error"; } rv = (*env)->NewStringUTF(env, z) /* We know these values to be plain ASCII, so pose no MUTF-8 ** incompatibility */; s3jni_oom_check( rv ); return rv; } #ifndef SQLITE_ENABLE_NORMALIZE /* Dummy stub for sqlite3_normalized_sql(). Never called. */ static const char * sqlite3_normalized_sql(sqlite3_stmt *s){ |
︙ | ︙ | |||
3566 3567 3568 3569 3570 3571 3572 | #ifdef SQLITE_ENABLE_NORMALIZE return s3jni_xn_sql(0, env, jpStmt); #else return 0; #endif } | | > | > | | | 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 | #ifdef SQLITE_ENABLE_NORMALIZE return s3jni_xn_sql(0, env, jpStmt); #else return 0; #endif } S3JniApi(sqlite3_extended_result_codes(),jint,1extended_1result_1codes)( JniArgsEnvClass, jobject jpDb, jboolean onoff ){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); int const rc = pDb ? sqlite3_extended_result_codes(pDb, onoff ? 1 : 0) : SQLITE_MISUSE; return rc; } S3JniApi(sqlite3_finalize(),jint,1finalize)( JniArgsEnvClass, jlong jpStmt ){ return jpStmt ? sqlite3_finalize(LongPtrGet_sqlite3_stmt(jpStmt)) : 0; } S3JniApi(sqlite3_get_auxdata(),jobject,1get_1auxdata)( JniArgsEnvClass, jobject jCx, jint n ){ return sqlite3_get_auxdata(PtrGet_sqlite3_context(jCx), (int)n); |
︙ | ︙ | |||
3619 3620 3621 3622 3623 3624 3625 | } /* ** Uncaches the current JNIEnv from the S3JniGlobal state, clearing ** any resources owned by that cache entry and making that slot ** available for re-use. */ | > | > > > > > > > > > > > > > > > > > > > > > > > > | 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 | } /* ** Uncaches the current JNIEnv from the S3JniGlobal state, clearing ** any resources owned by that cache entry and making that slot ** available for re-use. */ S3JniApi(sqlite3_java_uncache_thread(), jboolean, 1java_1uncache_1thread)( JniArgsEnvClass ){ int rc; S3JniEnv_mutex_enter; rc = S3JniEnv_uncache(env); S3JniEnv_mutex_leave; return rc ? JNI_TRUE : JNI_FALSE; } S3JniApi(sqlite3_jni_db_error(), jint, 1jni_1db_1error)( JniArgsEnvClass, jobject jDb, jint jRc, jstring jStr ){ S3JniDb * const ps = S3JniDb_from_java(jDb); int rc = SQLITE_MISUSE; if( ps ){ char *zStr; zStr = jStr ? s3jni_jstring_to_utf8( jStr, 0) : NULL; rc = s3jni_db_error( ps->pDb, (int)jRc, zStr ); sqlite3_free(zStr); } return rc; } S3JniApi(sqlite3_jni_supports_nio(), jboolean,1jni_1supports_1nio)( JniArgsEnvClass ){ return SJG.g.byteBuffer.klazz ? JNI_TRUE : JNI_FALSE; } S3JniApi(sqlite3_keyword_check(),jboolean,1keyword_1check)( JniArgsEnvClass, jstring jWord ){ int nWord = 0; char * zWord = s3jni_jstring_to_utf8(jWord, &nWord); int rc = 0; |
︙ | ︙ | |||
3800 3801 3802 3803 3804 3805 3806 | assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); sqlite3_free(zVfs); return (jint)rc; } /* Proxy for the sqlite3_prepare[_v2/3]() family. */ | | > | | | | | 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 | assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); sqlite3_free(zVfs); return (jint)rc; } /* Proxy for the sqlite3_prepare[_v2/3]() family. */ static jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self, jlong jpDb, jbyteArray baSql, jint nMax, jint prepFlags, jobject jOutStmt, jobject outTail){ sqlite3_stmt * pStmt = 0; jobject jStmt = 0; const char * zTail = 0; sqlite3 * const pDb = LongPtrGet_sqlite3(jpDb); jbyte * const pBuf = pDb ? s3jni_jbyteArray_bytes(baSql) : 0; int rc = SQLITE_ERROR; assert(prepVersion==1 || prepVersion==2 || prepVersion==3); if( !pDb || !jOutStmt ){ rc = SQLITE_MISUSE; goto end; |
︙ | ︙ | |||
3962 3963 3964 3965 3966 3967 3968 | static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, const char *zTable, sqlite3_int64 nRowid){ return s3jni_updatepre_hook_impl(pState, NULL, opId, zDb, zTable, nRowid, 0); } #if !defined(SQLITE_ENABLE_PREUPDATE_HOOK) /* We need no-op impls for preupdate_{count,depth,blobwrite}() */ | | | | | 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 | static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, const char *zTable, sqlite3_int64 nRowid){ return s3jni_updatepre_hook_impl(pState, NULL, opId, zDb, zTable, nRowid, 0); } #if !defined(SQLITE_ENABLE_PREUPDATE_HOOK) /* We need no-op impls for preupdate_{count,depth,blobwrite}() */ S3JniApi(sqlite3_preupdate_blobwrite(),jint,1preupdate_1blobwrite)( JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } S3JniApi(sqlite3_preupdate_count(),jint,1preupdate_1count)( JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } S3JniApi(sqlite3_preupdate_depth(),jint,1preupdate_1depth)( JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } #endif /* !SQLITE_ENABLE_PREUPDATE_HOOK */ /* ** JNI wrapper for both sqlite3_update_hook() and ** sqlite3_preupdate_hook() (if isPre is true). */ |
︙ | ︙ | |||
4061 4062 4063 4064 4065 4066 4067 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ } /* Impl for sqlite3_preupdate_{new,old}(). */ static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jlong jpDb, jint iCol, jobject jOut){ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK | | | 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ } /* Impl for sqlite3_preupdate_{new,old}(). */ static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jlong jpDb, jint iCol, jobject jOut){ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK sqlite3 * const pDb = LongPtrGet_sqlite3(jpDb); int rc = SQLITE_MISUSE; if( pDb ){ sqlite3_value * pOut = 0; int (*fOrig)(sqlite3*,int,sqlite3_value**) = isNew ? sqlite3_preupdate_new : sqlite3_preupdate_old; rc = fOrig(pDb, (int)iCol, &pOut); if( 0==rc ){ |
︙ | ︙ | |||
4282 4283 4284 4285 4286 4287 4288 | S3JniApi(sqlite3_result_double(),void,1result_1double)( JniArgsEnvClass, jobject jpCx, jdouble v ){ sqlite3_result_double(PtrGet_sqlite3_context(jpCx), v); } S3JniApi(sqlite3_result_error(),void,1result_1error)( | | | 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 | S3JniApi(sqlite3_result_double(),void,1result_1double)( JniArgsEnvClass, jobject jpCx, jdouble v ){ sqlite3_result_double(PtrGet_sqlite3_context(jpCx), v); } S3JniApi(sqlite3_result_error(),void,1result_1error)( JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, jint eTextRep ){ const char * zUnspecified = "Unspecified error."; jsize const baLen = (*env)->GetArrayLength(env, baMsg); jbyte * const pjBuf = baMsg ? s3jni_jbyteArray_bytes(baMsg) : NULL; switch( pjBuf ? eTextRep : SQLITE_UTF8 ){ case SQLITE_UTF8: { const char *zMsg = pjBuf ? (const char *)pjBuf : zUnspecified; |
︙ | ︙ | |||
4347 4348 4349 4350 4351 4352 4353 | ){ sqlite3_context * pCx = PtrGet_sqlite3_context(jpCx); if( !pCx ) return; else if( v ){ jobject const rjv = S3JniRefGlobal(v); if( rjv ){ sqlite3_result_pointer(pCx, rjv, | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4725 4726 4727 4728 4729 4730 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 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 | ){ sqlite3_context * pCx = PtrGet_sqlite3_context(jpCx); if( !pCx ) return; else if( v ){ jobject const rjv = S3JniRefGlobal(v); if( rjv ){ sqlite3_result_pointer(pCx, rjv, s3jni__value_jref_key, S3Jni_jobject_finalizer); }else{ sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx)); } }else{ sqlite3_result_null(PtrGet_sqlite3_context(jpCx)); } } S3JniApi(sqlite3_result_nio_buffer(),void,1result_1nio_1buffer)( JniArgsEnvClass, jobject jpCtx, jobject jBuffer, jint iOffset, jint iN ){ sqlite3_context * pCx = PtrGet_sqlite3_context(jpCtx); int rc; S3JniNioArgs args; if( !pCx ){ return; }else if( !SJG.g.byteBuffer.klazz ){ sqlite3_result_error( pCx, "This JVM does not support JNI access to ByteBuffers.", -1 ); return; } rc = s3jni_setup_nio_args(env, &args, jBuffer, iOffset, iN); if(rc){ if( iOffset<0 ){ sqlite3_result_error(pCx, "Start index may not be negative.", -1); }else if( SQLITE_TOOBIG==rc ){ sqlite3_result_error_toobig(pCx); }else{ sqlite3_result_error( pCx, "Invalid arguments to sqlite3_result_nio_buffer().", -1 ); } }else if( !args.pStart || !args.nOut ){ sqlite3_result_null(pCx); }else{ sqlite3_result_blob(pCx, args.pStart, args.nOut, SQLITE_TRANSIENT); } } S3JniApi(sqlite3_result_null(),void,1result_1null)( JniArgsEnvClass, jobject jpCx ){ sqlite3_result_null(PtrGet_sqlite3_context(jpCx)); } S3JniApi(sqlite3_result_subtype(),void,1result_1subtype)( JniArgsEnvClass, jobject jpCx, jint v ){ sqlite3_result_subtype(PtrGet_sqlite3_context(jpCx), (unsigned int)v); } S3JniApi(sqlite3_result_text(),void,1result_1text)( JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax ){ return result_blob_text(0, SQLITE_UTF8, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); } |
︙ | ︙ | |||
4535 4536 4537 4538 4539 4540 4541 | } S3JniHook_mutex_leave; /* Free up env cache. */ S3JniEnv_mutex_enter; { while( SJG.envCache.aHead ){ S3JniEnv_uncache( SJG.envCache.aHead->env ); } } S3JniEnv_mutex_leave; | < < < < < < < | < < < < < | | 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 | } S3JniHook_mutex_leave; /* Free up env cache. */ S3JniEnv_mutex_enter; { while( SJG.envCache.aHead ){ S3JniEnv_uncache( SJG.envCache.aHead->env ); } } S3JniEnv_mutex_leave; /* Do not clear S3JniGlobal.jvm or S3JniGlobal.g: it's legal to ** restart the lib. */ return sqlite3_shutdown(); } S3JniApi(sqlite3_status(),jint,1status)( JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh, jboolean reset ){ |
︙ | ︙ | |||
4590 4591 4592 4593 4594 4595 4596 | } static int s3jni_strlike_glob(int isLike, JNIEnv *const env, jbyteArray baG, jbyteArray baT, jint escLike){ int rc = 0; jbyte * const pG = s3jni_jbyteArray_bytes(baG); | | | 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 | } static int s3jni_strlike_glob(int isLike, JNIEnv *const env, jbyteArray baG, jbyteArray baT, jint escLike){ int rc = 0; jbyte * const pG = s3jni_jbyteArray_bytes(baG); jbyte * const pT = s3jni_jbyteArray_bytes(baT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = isLike ? sqlite3_strlike((const char *)pG, (const char *)pT, (unsigned int)escLike) : sqlite3_strglob((const char *)pG, (const char *)pT); |
︙ | ︙ | |||
4629 4630 4631 4632 4633 4634 4635 | zSql = sqlite3_sql(pStmt); rv = s3jni_utf8_to_jstring( zSql, -1); } return rv; } S3JniApi(sqlite3_step(),jint,1step)( | | | | | 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 | zSql = sqlite3_sql(pStmt); rv = s3jni_utf8_to_jstring( zSql, -1); } return rv; } S3JniApi(sqlite3_step(),jint,1step)( JniArgsEnvClass, jlong jpStmt ){ sqlite3_stmt * const pStmt = LongPtrGet_sqlite3_stmt(jpStmt); return pStmt ? (jint)sqlite3_step(pStmt) : (jint)SQLITE_MISUSE; } S3JniApi(sqlite3_table_column_metadata(),jint,1table_1column_1metadata)( JniArgsEnvClass, jobject jDb, jstring jDbName, jstring jTableName, jstring jColumnName, jobject jDataType, jobject jCollSeq, jobject jNotNull, jobject jPrimaryKey, jobject jAutoinc ){ sqlite3 * const db = PtrGet_sqlite3(jDb); char * zDbName = 0, * zTableName = 0, * zColumnName = 0; const char * pzCollSeq = 0; |
︙ | ︙ | |||
4811 4812 4813 4814 4815 4816 4817 | return s3jni_updatepre_hook(env, 0, jpDb, jHook); } S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( JniArgsEnvClass, jlong jpSVal ){ | | | | | | | | | | | | | > > > > > > > > > > > > > > > | | | | 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 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 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 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 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 | return s3jni_updatepre_hook(env, 0, jpDb, jHook); } S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); const jbyte * pBytes = sv ? sqlite3_value_blob(sv) : 0; int const nLen = pBytes ? sqlite3_value_bytes(sv) : 0; s3jni_oom_check( nLen ? !!pBytes : 1 ); return pBytes ? s3jni_new_jbyteArray(pBytes, nLen) : NULL; } S3JniApi(sqlite3_value_bytes(),jint,1value_1bytes)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return sv ? sqlite3_value_bytes(sv) : 0; } S3JniApi(sqlite3_value_bytes16(),jint,1value_1bytes16)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return sv ? sqlite3_value_bytes16(sv) : 0; } S3JniApi(sqlite3_value_double(),jdouble,1value_1double)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return (jdouble) (sv ? sqlite3_value_double(sv) : 0.0); } S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); sqlite3_value * const sd = sv ? sqlite3_value_dup(sv) : 0; jobject rv = sd ? new_java_sqlite3_value(env, sd) : 0; if( sd && !rv ) { /* OOM */ sqlite3_value_free(sd); } return rv; } S3JniApi(sqlite3_value_free(),void,1value_1free)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); if( sv ){ sqlite3_value_free(sv); } } S3JniApi(sqlite3_value_int(),jint,1value_1int)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return (jint) (sv ? sqlite3_value_int(sv) : 0); } S3JniApi(sqlite3_value_int64(),jlong,1value_1int64)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return (jlong) (sv ? sqlite3_value_int64(sv) : 0LL); } S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return sv ? sqlite3_value_pointer(sv, s3jni__value_jref_key) : 0; } S3JniApi(sqlite3_value_nio_buffer(),jobject,1value_1nio_1buffer)( JniArgsEnvClass, jobject jVal ){ sqlite3_value * const sv = PtrGet_sqlite3_value(jVal); jobject rv = 0; if( sv ){ const void * const p = sqlite3_value_blob(sv); if( p ){ const int n = sqlite3_value_bytes(sv); rv = s3jni__blob_to_ByteBuffer(env, p, n); } } return rv; } S3JniApi(sqlite3_value_text(),jbyteArray,1value_1text)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); const unsigned char * const p = sv ? sqlite3_value_text(sv) : 0; int const n = p ? sqlite3_value_bytes(sv) : 0; return p ? s3jni_new_jbyteArray(p, n) : 0; } #if 0 // this impl might prove useful. S3JniApi(sqlite3_value_text(),jstring,1value_1text)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); const unsigned char * const p = sv ? sqlite3_value_text(sv) : 0; int const n = p ? sqlite3_value_bytes(sv) : 0; return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0; } #endif S3JniApi(sqlite3_value_text16(),jstring,1value_1text16)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); const int n = sv ? sqlite3_value_bytes16(sv) : 0; const void * const p = sv ? sqlite3_value_text16(sv) : 0; return p ? s3jni_text16_to_jstring(env, p, n) : 0; } JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ MARKER(("\nVarious bits of internal info:\n")); |
︙ | ︙ | |||
5495 5496 5497 5498 5499 5500 5501 | } JniDeclFtsXA(jlong,xRowid)(JniArgsEnvObj,jobject jCtx){ Fts5ExtDecl; return (jlong)ext->xRowid(PtrGet_Fts5Context(jCtx)); } | | | 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 | } JniDeclFtsXA(jlong,xRowid)(JniArgsEnvObj,jobject jCtx){ Fts5ExtDecl; return (jlong)ext->xRowid(PtrGet_Fts5Context(jCtx)); } JniDeclFtsXA(jint,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ Fts5ExtDecl; int rc; S3JniFts5AuxData * pAux; pAux = s3jni_malloc( sizeof(*pAux)); if( !pAux ){ if( jAux ){ |
︙ | ︙ | |||
5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 | SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); s3jni_oom_fatal( SJG.autoExt.mutex ); #if S3JNI_METRICS_MUTEX SJG.metrics.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); s3jni_oom_fatal( SJG.metrics.mutex ); #endif sqlite3_shutdown() /* So that it becomes legal for Java-level code to call ** sqlite3_config(). */; } | > > > > > > > > > > > > > > > > > > > > > > | 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 | SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); s3jni_oom_fatal( SJG.autoExt.mutex ); #if S3JNI_METRICS_MUTEX SJG.metrics.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); s3jni_oom_fatal( SJG.metrics.mutex ); #endif { /* Test whether this JVM supports direct memory access via ByteBuffer. */ unsigned char buf[16] = {0}; jobject bb = (*env)->NewDirectByteBuffer(env, buf, 16); if( bb ){ SJG.g.byteBuffer.klazz = S3JniRefGlobal((*env)->GetObjectClass(env, bb)); SJG.g.byteBuffer.midAlloc = (*env)->GetStaticMethodID( env, SJG.g.byteBuffer.klazz, "allocateDirect", "(I)Ljava/nio/ByteBuffer;" ); S3JniExceptionIsFatal("Error getting ByteBuffer.allocateDirect() method."); SJG.g.byteBuffer.midLimit = (*env)->GetMethodID( env, SJG.g.byteBuffer.klazz, "limit", "()I" ); S3JniExceptionIsFatal("Error getting ByteBuffer.limit() method."); S3JniUnrefLocal(bb); }else{ SJG.g.byteBuffer.klazz = 0; SJG.g.byteBuffer.midAlloc = 0; } } sqlite3_shutdown() /* So that it becomes legal for Java-level code to call ** sqlite3_config(). */; } |
Changes to ext/jni/src/c/sqlite3-jni.h.
︙ | ︙ | |||
423 424 425 426 427 428 429 | #define org_sqlite_jni_capi_CApi_SQLITE_OPEN_PRIVATECACHE 262144L #undef org_sqlite_jni_capi_CApi_SQLITE_OPEN_NOFOLLOW #define org_sqlite_jni_capi_CApi_SQLITE_OPEN_NOFOLLOW 16777216L #undef org_sqlite_jni_capi_CApi_SQLITE_OPEN_EXRESCODE #define org_sqlite_jni_capi_CApi_SQLITE_OPEN_EXRESCODE 33554432L #undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_PERSISTENT #define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_PERSISTENT 1L | < < | 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | #define org_sqlite_jni_capi_CApi_SQLITE_OPEN_PRIVATECACHE 262144L #undef org_sqlite_jni_capi_CApi_SQLITE_OPEN_NOFOLLOW #define org_sqlite_jni_capi_CApi_SQLITE_OPEN_NOFOLLOW 16777216L #undef org_sqlite_jni_capi_CApi_SQLITE_OPEN_EXRESCODE #define org_sqlite_jni_capi_CApi_SQLITE_OPEN_EXRESCODE 33554432L #undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_PERSISTENT #define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_PERSISTENT 1L #undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NO_VTAB #define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NO_VTAB 4L #undef org_sqlite_jni_capi_CApi_SQLITE_OK #define org_sqlite_jni_capi_CApi_SQLITE_OK 0L #undef org_sqlite_jni_capi_CApi_SQLITE_ERROR #define org_sqlite_jni_capi_CApi_SQLITE_ERROR 1L #undef org_sqlite_jni_capi_CApi_SQLITE_INTERNAL |
︙ | ︙ | |||
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 | #define org_sqlite_jni_capi_CApi_SQLITE_TXN_READ 1L #undef org_sqlite_jni_capi_CApi_SQLITE_TXN_WRITE #define org_sqlite_jni_capi_CApi_SQLITE_TXN_WRITE 2L #undef org_sqlite_jni_capi_CApi_SQLITE_DETERMINISTIC #define org_sqlite_jni_capi_CApi_SQLITE_DETERMINISTIC 2048L #undef org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY #define org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY 524288L #undef org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS #define org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS 2097152L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE #define org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE 1L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_EQ #define org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_EQ 2L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_GT #define org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_GT 4L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_LE | > > > > | 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 | #define org_sqlite_jni_capi_CApi_SQLITE_TXN_READ 1L #undef org_sqlite_jni_capi_CApi_SQLITE_TXN_WRITE #define org_sqlite_jni_capi_CApi_SQLITE_TXN_WRITE 2L #undef org_sqlite_jni_capi_CApi_SQLITE_DETERMINISTIC #define org_sqlite_jni_capi_CApi_SQLITE_DETERMINISTIC 2048L #undef org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY #define org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY 524288L #undef org_sqlite_jni_capi_CApi_SQLITE_SUBTYPE #define org_sqlite_jni_capi_CApi_SQLITE_SUBTYPE 1048576L #undef org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS #define org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS 2097152L #undef org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE #define org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE 16777216L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE #define org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE 1L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_EQ #define org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_EQ 2L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_GT #define org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_GT 4L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_LE |
︙ | ︙ | |||
771 772 773 774 775 776 777 778 779 780 781 782 783 784 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_java_uncache_thread * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1java_1uncache_1thread (JNIEnv *, jclass); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_aggregate_context * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Z)J */ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1aggregate_1context (JNIEnv *, jclass, jobject, jboolean); | > > > > > > > > > > > > > > > > | 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 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_java_uncache_thread * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1java_1uncache_1thread (JNIEnv *, jclass); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_jni_supports_nio * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1jni_1supports_1nio (JNIEnv *, jclass); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_jni_db_error * Signature: (Lorg/sqlite/jni/capi/sqlite3;ILjava/lang/String;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1jni_1db_1error (JNIEnv *, jclass, jobject, jint, jstring); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_aggregate_context * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Z)J */ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1aggregate_1context (JNIEnv *, jclass, jobject, jboolean); |
︙ | ︙ | |||
867 868 869 870 871 872 873 874 875 876 877 878 879 880 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_bind_java_object * Signature: (JILjava/lang/Object;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1java_1object (JNIEnv *, jclass, jlong, jint, jobject); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_bind_null * Signature: (JI)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1null (JNIEnv *, jclass, jlong, jint); | > > > > > > > > | 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_bind_java_object * Signature: (JILjava/lang/Object;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1java_1object (JNIEnv *, jclass, jlong, jint, jobject); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_bind_nio_buffer * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;ILjava/nio/ByteBuffer;II)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1nio_1buffer (JNIEnv *, jclass, jobject, jint, jobject, jint, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_bind_null * Signature: (JI)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1null (JNIEnv *, jclass, jlong, jint); |
︙ | ︙ | |||
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 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_blob_read * Signature: (J[BI)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1read (JNIEnv *, jclass, jlong, jbyteArray, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_blob_reopen * Signature: (JJ)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1reopen (JNIEnv *, jclass, jlong, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_blob_write * Signature: (J[BI)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1write (JNIEnv *, jclass, jlong, jbyteArray, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_busy_handler * Signature: (JLorg/sqlite/jni/capi/BusyHandlerCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1busy_1handler (JNIEnv *, jclass, jlong, jobject); | > > > > > > > > > > > > > > > > | 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 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_blob_read * Signature: (J[BI)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1read (JNIEnv *, jclass, jlong, jbyteArray, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_blob_read_nio_buffer * Signature: (JILjava/nio/ByteBuffer;II)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1read_1nio_1buffer (JNIEnv *, jclass, jlong, jint, jobject, jint, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_blob_reopen * Signature: (JJ)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1reopen (JNIEnv *, jclass, jlong, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_blob_write * Signature: (J[BI)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1write (JNIEnv *, jclass, jlong, jbyteArray, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_blob_write_nio_buffer * Signature: (JILjava/nio/ByteBuffer;II)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1write_1nio_1buffer (JNIEnv *, jclass, jlong, jint, jobject, jint, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_busy_handler * Signature: (JLorg/sqlite/jni/capi/BusyHandlerCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1busy_1handler (JNIEnv *, jclass, jlong, jobject); |
︙ | ︙ | |||
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_count * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1count (JNIEnv *, jclass, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_decltype * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1decltype (JNIEnv *, jclass, jlong, jint); | > > > > > > > > | 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_count * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1count (JNIEnv *, jclass, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_database_name * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1database_1name (JNIEnv *, jclass, jlong, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_decltype * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1decltype (JNIEnv *, jclass, jlong, jint); |
︙ | ︙ | |||
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_int64 * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;I)J */ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1int64 (JNIEnv *, jclass, jobject, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_name * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1name (JNIEnv *, jclass, jlong, jint); /* * Class: org_sqlite_jni_capi_CApi | > > > > > > > > | | | | | 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 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_int64 * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;I)J */ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1int64 (JNIEnv *, jclass, jobject, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_java_object * Signature: (JI)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1java_1object (JNIEnv *, jclass, jlong, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_name * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1name (JNIEnv *, jclass, jlong, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_nio_buffer * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;I)Ljava/nio/ByteBuffer; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1nio_1buffer (JNIEnv *, jclass, jobject, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_origin_name * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1origin_1name |
︙ | ︙ | |||
1221 1222 1223 1224 1225 1226 1227 | * Signature: ([B)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1complete (JNIEnv *, jclass, jbyteArray); /* * Class: org_sqlite_jni_capi_CApi | | | | | | | | | | 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 | * Signature: ([B)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1complete (JNIEnv *, jclass, jbyteArray); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_config__enable * Signature: (I)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1config_1_1enable (JNIEnv *, jclass, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_config__CONFIG_LOG * Signature: (Lorg/sqlite/jni/capi/ConfigLogCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1config_1_1CONFIG_1LOG (JNIEnv *, jclass, jobject); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_config__SQLLOG * Signature: (Lorg/sqlite/jni/capi/ConfigSqlLogCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1config_1_1SQLLOG (JNIEnv *, jclass, jobject); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_context_db_handle * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;)Lorg/sqlite/jni/capi/sqlite3; */ |
︙ | ︙ | |||
1390 1391 1392 1393 1394 1395 1396 | */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1errcode (JNIEnv *, jclass, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_extended_result_codes | | | | 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 | */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1errcode (JNIEnv *, jclass, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_extended_result_codes * Signature: (Lorg/sqlite/jni/capi/sqlite3;Z)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1result_1codes (JNIEnv *, jclass, jobject, jboolean); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_get_autocommit * Signature: (J)Z */ |
︙ | ︙ | |||
1675 1676 1677 1678 1679 1680 1681 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_error_code * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;I)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1error_1code (JNIEnv *, jclass, jobject, jint); | < < < < < < < < | 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_error_code * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;I)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1error_1code (JNIEnv *, jclass, jobject, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_int * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;I)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1int (JNIEnv *, jclass, jobject, jint); |
︙ | ︙ | |||
1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_java_object * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Ljava/lang/Object;)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1java_1object (JNIEnv *, jclass, jobject, jobject); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_value * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Lorg/sqlite/jni/capi/sqlite3_value;)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1value (JNIEnv *, jclass, jobject, jobject); | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_java_object * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Ljava/lang/Object;)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1java_1object (JNIEnv *, jclass, jobject, jobject); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_nio_buffer * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Ljava/nio/ByteBuffer;II)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1nio_1buffer (JNIEnv *, jclass, jobject, jobject, jint, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_null * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1null (JNIEnv *, jclass, jobject); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_subtype * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;I)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1subtype (JNIEnv *, jclass, jobject, jint); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_value * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Lorg/sqlite/jni/capi/sqlite3_value;)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1value (JNIEnv *, jclass, jobject, jobject); |
︙ | ︙ | |||
1846 1847 1848 1849 1850 1851 1852 | */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1status64 (JNIEnv *, jclass, jint, jobject, jobject, jboolean); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_step | | | | 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 | */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1status64 (JNIEnv *, jclass, jint, jobject, jobject, jboolean); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_step * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1step (JNIEnv *, jclass, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_stmt_busy * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;)Z */ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1stmt_1busy |
︙ | ︙ | |||
2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_value_java_object * Signature: (J)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1value_1java_1object (JNIEnv *, jclass, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_value_nochange * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1value_1nochange (JNIEnv *, jclass, jlong); | > > > > > > > > | 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 | * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_value_java_object * Signature: (J)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1value_1java_1object (JNIEnv *, jclass, jlong); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_value_nio_buffer * Signature: (Lorg/sqlite/jni/capi/sqlite3_value;)Ljava/nio/ByteBuffer; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1value_1nio_1buffer (JNIEnv *, jclass, jobject); /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_value_nochange * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1value_1nochange (JNIEnv *, jclass, jlong); |
︙ | ︙ |
Added ext/jni/src/org/sqlite/jni/annotation/Experimental.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 2023-09-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 houses the Experimental annotation for the sqlite3 C API. */ package org.sqlite.jni.annotation; import java.lang.annotation.*; /** This annotation is for flagging methods, constructors, and types which are expressly experimental and subject to any amount of change or outright removal. Client code should not rely on such features. */ @Documented @Retention(RetentionPolicy.SOURCE) @Target({ ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE }) public @interface Experimental{} |
Changes to ext/jni/src/org/sqlite/jni/annotation/NotNull.java.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2023-09-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. ** ************************************************************************* | | > | | | | | | | | | > > > > > > > > > > > | | | | | 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 | /* ** 2023-09-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 houses the NotNull annotation for the sqlite3 C API. */ package org.sqlite.jni.annotation; import java.lang.annotation.*; /** This annotation is for flagging parameters which may not legally be null or point to closed/finalized C-side resources. <p>In the case of Java types which map directly to C struct types (e.g. {@link org.sqlite.jni.capi.sqlite3}, {@link org.sqlite.jni.capi.sqlite3_stmt}, and {@link org.sqlite.jni.capi.sqlite3_context}), a closed/finalized resource is also considered to be null for purposes this annotation because the C-side effect of passing such a handle is the same as if null is passed.</p> <p>When used in the context of Java interfaces which are called from the C APIs, this annotation communicates that the C API will never pass a null value to the callback for that parameter.</p> <p>Passing a null, for this annotation's definition of null, for any parameter marked with this annoation specifically invokes undefined behavior (see below).</p> <p>Passing 0 (i.e. C NULL) or a negative value for any long-type parameter marked with this annoation specifically invokes undefined behavior (see below). Such values are treated as C pointers in the JNI layer.</p> <p><b>Undefined behaviour:</b> the JNI build uses the {@code SQLITE_ENABLE_API_ARMOR} build flag, meaning that the C code invoked with invalid NULL pointers and the like will not invoke undefined behavior in the conventional C sense, but may, for example, return result codes which are not documented for the affected APIs or may otherwise behave unpredictably. In no known cases will such arguments result in C-level code dereferencing a NULL pointer or accessing out-of-bounds (or otherwise invalid) memory. In other words, they may cause unexpected behavior but should never cause an outright crash or security issue.</p> <p>Note that the C-style API does not throw any exceptions on its own because it has a no-throw policy in order to retain its C-style semantics, but it may trigger NullPointerExceptions (or similar) if passed a null for a parameter flagged with this annotation.</p> <p>This annotation is informational only. No policy is in place to programmatically ensure that NotNull is conformed to in client code.</p> <p>This annotation is solely for the use by the classes in the org.sqlite.jni package and subpackages, but is made public so that javadoc will link to it from the annotated functions. It is not part of the public API and client-level code must not rely on it.</p> */ @Documented @Retention(RetentionPolicy.SOURCE) @Target(ElementType.PARAMETER) public @interface NotNull{} |
Changes to ext/jni/src/org/sqlite/jni/annotation/Nullable.java.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2023-09-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. ** ************************************************************************* | | > | | | | 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 | /* ** 2023-09-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 houses the Nullable annotation for the sqlite3 C API. */ package org.sqlite.jni.annotation; import java.lang.annotation.*; /** This annotation is for flagging parameters which may legally be null, noting that they may behave differently if passed null but are prepared to expect null as a value. When used in the context of callback methods which are called into from the C APIs, this annotation communicates that the C API may pass a null value to the callback. <p>This annotation is solely for the use by the classes in this package but is made public so that javadoc will link to it from the annotated functions. It is not part of the public API and client-level code must not rely on it. */ @Documented @Retention(RetentionPolicy.SOURCE) @Target(ElementType.PARAMETER) public @interface Nullable{} |
Changes to ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java.
︙ | ︙ | |||
37 38 39 40 41 42 43 44 45 | public abstract void xFinal(sqlite3_context cx); /** Optionally override to be notified when the UDF is finalized by SQLite. */ public void xDestroy() {} /** Per-invocation state for the UDF. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | 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 | public abstract void xFinal(sqlite3_context cx); /** Optionally override to be notified when the UDF is finalized by SQLite. */ public void xDestroy() {} /** PerContextState assists aggregate and window functions in managing their accumulator state across calls to the UDF's callbacks. <p>T must be of a type which can be legally stored as a value in java.util.HashMap<KeyType,T>. <p>If a given aggregate or window function is called multiple times in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)..., then the clients need some way of knowing which call is which so that they can map their state between their various UDF callbacks and reset it via xFinal(). This class takes care of such mappings. <p>This class works by mapping sqlite3_context.getAggregateContext() to a single piece of state, of a client-defined type (the T part of this class), which persists across a "matching set" of the UDF's callbacks. <p>This class is a helper providing commonly-needed functionality - it is not required for use with aggregate or window functions. Client UDFs are free to perform such mappings using custom approaches. The provided {@link AggregateFunction} and {@link WindowFunction} classes use this. */ public static final class PerContextState<T> { private final java.util.Map<Long,ValueHolder<T>> map = new java.util.HashMap<>(); /** Should be called from a UDF's xStep(), xValue(), and xInverse() methods, passing it that method's first argument and an initial value for the persistent state. If there is currently no mapping for the given context within the map, one is created using the given initial value, else the existing one is used and the 2nd argument is ignored. It returns a ValueHolder<T> which can be used to modify that state directly without requiring that the client update the underlying map's entry. <p>The caller is obligated to eventually call takeAggregateState() to clear the mapping. */ public ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){ final Long key = cx.getAggregateContext(true); ValueHolder<T> rc = null==key ? null : map.get(key); if( null==rc ){ map.put(key, rc = new ValueHolder<>(initialValue)); } return rc; } /** Should be called from a UDF's xFinal() method and passed that method's first argument. This function removes the value associated with cx.getAggregateContext() from the map and returns it, returning null if no other UDF method has been called to set up such a mapping. The latter condition will be the case if a UDF is used in a statement which has no result rows. */ public T takeAggregateState(sqlite3_context cx){ final ValueHolder<T> h = map.remove(cx.getAggregateContext(false)); return null==h ? null : h.value; } } /** Per-invocation state for the UDF. */ private final PerContextState<T> map = new PerContextState<>(); /** To be called from the implementation's xStep() method, as well as the xValue() and xInverse() methods of the {@link WindowFunction} subclass, to fetch the current per-call UDF state. On the first call to this method for any given sqlite3_context argument, the context is set to the given initial value. On all other |
︙ | ︙ |
Changes to ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java.
︙ | ︙ | |||
16 17 18 19 20 21 22 | /** Callback for use with {@link CApi#sqlite3_set_authorizer}. */ public interface AuthorizerCallback extends CallbackProxy { /** Must function as described for the C-level | | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /** Callback for use with {@link CApi#sqlite3_set_authorizer}. */ public interface AuthorizerCallback extends CallbackProxy { /** Must function as described for the C-level sqlite3_set_authorizer() callback. If it throws, the error is converted to a db-level error and the exception is suppressed. */ int call(int opId, @Nullable String s1, @Nullable String s2, @Nullable String s3, @Nullable String s4); } |
Changes to ext/jni/src/org/sqlite/jni/capi/CApi.java.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2023-07-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. ** ************************************************************************* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** 2023-07-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 declares the main JNI bindings for the sqlite3 C API. */ package org.sqlite.jni.capi; import java.nio.charset.StandardCharsets; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.Documented; |
︙ | ︙ | |||
28 29 30 31 32 33 34 | <pre>{@code import static org.sqlite.jni.capi.CApi.*; }</pre> <p>The C-side part can be found in sqlite3-jni.c. | < < < < < < < < < | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <pre>{@code import static org.sqlite.jni.capi.CApi.*; }</pre> <p>The C-side part can be found in sqlite3-jni.c. <p>Only functions which materially differ from their C counterparts are documented here, and only those material differences are documented. The C documentation is otherwise applicable for these APIs: <p><a href="https://sqlite.org/c3ref/intro.html">https://sqlite.org/c3ref/intro.html</a> |
︙ | ︙ | |||
74 75 76 77 78 79 80 | <li>C functions which take C-style strings without a length argument require special care when taking input from Java. In particular, Java strings converted to byte arrays for encoding purposes are not NUL-terminated, and conversion to a Java byte array must sometimes be careful to add one. Functions which take a length do not require this so long as the length is provided. Search the CApi class | | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | <li>C functions which take C-style strings without a length argument require special care when taking input from Java. In particular, Java strings converted to byte arrays for encoding purposes are not NUL-terminated, and conversion to a Java byte array must sometimes be careful to add one. Functions which take a length do not require this so long as the length is provided. Search the CApi class for "\0" for examples. </ul> <p>Further reading: <p><a href="https://stackoverflow.com/questions/57419723">https://stackoverflow.com/questions/57419723</a> <p><a href="https://stackoverflow.com/questions/7921016">https://stackoverflow.com/questions/7921016</a> |
︙ | ︙ | |||
124 125 126 127 128 129 130 | required. Additional threads must call this before ending or they will leak cache entries in the C heap, which in turn may keep numerous Java-side global references active. <p>This routine returns false without side effects if the current JNIEnv is not cached, else returns true, but this information is primarily for testing of the JNI bindings and is not information | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | required. Additional threads must call this before ending or they will leak cache entries in the C heap, which in turn may keep numerous Java-side global references active. <p>This routine returns false without side effects if the current JNIEnv is not cached, else returns true, but this information is primarily for testing of the JNI bindings and is not information which client-level code can use to make any informed decisions. Its return type and semantics are not considered stable and may change at any time. */ public static native boolean sqlite3_java_uncache_thread(); /** Returns true if this JVM has JNI-level support for C-level direct memory access using java.nio.ByteBuffer, else returns false. */ @Experimental public static native boolean sqlite3_jni_supports_nio(); /** For internal use only. Sets the given db's error code and (optionally) string. If rc is 0, it defaults to SQLITE_ERROR. On success it returns rc. On error it may return a more serious code, such as SQLITE_NOMEM. Returns SQLITE_MISUSE if db is null. */ static native int sqlite3_jni_db_error(@NotNull sqlite3 db, int rc, @Nullable String msg); /** Convenience overload which uses e.toString() as the error message. */ static int sqlite3_jni_db_error(@NotNull sqlite3 db, int rc, @NotNull Exception e){ return sqlite3_jni_db_error(db, rc, e.toString()); } ////////////////////////////////////////////////////////////////////// // Maintenance reminder: please keep the sqlite3_.... functions // alphabetized. The SQLITE_... values. on the other hand, are // grouped by category. /** |
︙ | ︙ | |||
169 170 171 172 173 174 175 | execute for the current database. That is, doing so will result in unpredictable, but not undefined, behavior. <p>See the AutoExtension class docs for more information. */ public static native int sqlite3_auto_extension(@NotNull AutoExtensionCallback callback); | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 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 | execute for the current database. That is, doing so will result in unpredictable, but not undefined, behavior. <p>See the AutoExtension class docs for more information. */ public static native int sqlite3_auto_extension(@NotNull AutoExtensionCallback callback); private static native int sqlite3_backup_finish(@NotNull long ptrToBackup); public static int sqlite3_backup_finish(@NotNull sqlite3_backup b){ return null==b ? 0 : sqlite3_backup_finish(b.clearNativePointer()); } private static native sqlite3_backup sqlite3_backup_init( @NotNull long ptrToDbDest, @NotNull String destTableName, @NotNull long ptrToDbSrc, @NotNull String srcTableName ); public static sqlite3_backup sqlite3_backup_init( @NotNull sqlite3 dbDest, @NotNull String destTableName, @NotNull sqlite3 dbSrc, @NotNull String srcTableName ){ return sqlite3_backup_init( dbDest.getNativePointer(), destTableName, dbSrc.getNativePointer(), srcTableName ); } private static native int sqlite3_backup_pagecount(@NotNull long ptrToBackup); public static int sqlite3_backup_pagecount(@NotNull sqlite3_backup b){ return sqlite3_backup_pagecount(b.getNativePointer()); } private static native int sqlite3_backup_remaining(@NotNull long ptrToBackup); public static int sqlite3_backup_remaining(@NotNull sqlite3_backup b){ return sqlite3_backup_remaining(b.getNativePointer()); } private static native int sqlite3_backup_step(@NotNull long ptrToBackup, int nPage); public static int sqlite3_backup_step(@NotNull sqlite3_backup b, int nPage){ return sqlite3_backup_step(b.getNativePointer(), nPage); } private static native int sqlite3_bind_blob( @NotNull long ptrToStmt, int ndx, @Nullable byte[] data, int n ); /** If n is negative, SQLITE_MISUSE is returned. If n>data.length then n is silently truncated to data.length. */ public static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n ){ return sqlite3_bind_blob(stmt.getNativePointer(), ndx, data, n); } public static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data ){ return (null==data) ? sqlite3_bind_null(stmt.getNativePointer(), ndx) : sqlite3_bind_blob(stmt.getNativePointer(), ndx, data, data.length); } /** 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 ); public static int sqlite3_bind_double( @NotNull sqlite3_stmt stmt, int ndx, double v ){ return sqlite3_bind_double(stmt.getNativePointer(), ndx, v); } private static native int sqlite3_bind_int( @NotNull long ptrToStmt, int ndx, int v ); public static int sqlite3_bind_int( @NotNull sqlite3_stmt stmt, int ndx, int v ){ return sqlite3_bind_int(stmt.getNativePointer(), ndx, v); } private static native int sqlite3_bind_int64( @NotNull long ptrToStmt, int ndx, long v ); public static int sqlite3_bind_int64(@NotNull sqlite3_stmt stmt, int ndx, long v){ return sqlite3_bind_int64( stmt.getNativePointer(), ndx, v ); } private static native int sqlite3_bind_java_object( @NotNull long ptrToStmt, int ndx, @Nullable Object o ); /** Binds the contents of the given buffer object as a blob. The byte range of the buffer may be restricted by providing a start index and a number of bytes. beginPos may not be negative. Negative howMany is interpretated as the remainder of the buffer past the given start position, up to the buffer's limit() (as opposed its capacity()). If beginPos+howMany would extend past the limit() of the buffer then SQLITE_ERROR is returned. If any of the following are true, this function behaves like sqlite3_bind_null(): the buffer is null, beginPos is at or past the end of the buffer, howMany is 0, or the calculated slice of the blob has a length of 0. If ndx is out of range, it returns SQLITE_RANGE, as documented for sqlite3_bind_blob(). If beginPos is negative or if sqlite3_jni_supports_nio() returns false then SQLITE_MISUSE is returned. Note that this function is bound (as it were) by the SQLITE_LIMIT_LENGTH constraint and SQLITE_TOOBIG is returned if the resulting slice of the buffer exceeds that limit. This function does not modify the buffer's streaming-related cursors. If the buffer is modified in a separate thread while this operation is running, results are undefined and will likely result in corruption of the bound data or a segmentation fault. Design note: this function should arguably take a java.nio.Buffer 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 sqlite3_bind_null(). @see #sqlite3_result_java_object */ public static int sqlite3_bind_java_object( @NotNull sqlite3_stmt stmt, int ndx, @Nullable Object o ){ return sqlite3_bind_java_object(stmt.getNativePointer(), ndx, o); } private static native int sqlite3_bind_null(@NotNull long ptrToStmt, int ndx); public static int sqlite3_bind_null(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_bind_null(stmt.getNativePointer(), ndx); } private static native int sqlite3_bind_parameter_count(@NotNull long ptrToStmt); public static int sqlite3_bind_parameter_count(@NotNull sqlite3_stmt stmt){ return sqlite3_bind_parameter_count(stmt.getNativePointer()); } /** Requires that paramName be a NUL-terminated UTF-8 string. |
︙ | ︙ | |||
305 306 307 308 309 310 311 | public static int sqlite3_bind_parameter_index( @NotNull sqlite3_stmt stmt, @NotNull String paramName ){ final byte[] utf8 = nulTerminateUtf8(paramName); return null==utf8 ? 0 : sqlite3_bind_parameter_index(stmt.getNativePointer(), utf8); } | | | | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | public static int sqlite3_bind_parameter_index( @NotNull sqlite3_stmt stmt, @NotNull String paramName ){ final byte[] utf8 = nulTerminateUtf8(paramName); return null==utf8 ? 0 : sqlite3_bind_parameter_index(stmt.getNativePointer(), utf8); } private static native String sqlite3_bind_parameter_name( @NotNull long ptrToStmt, int index ); public static String sqlite3_bind_parameter_name(@NotNull sqlite3_stmt stmt, int index){ return sqlite3_bind_parameter_name(stmt.getNativePointer(), index); } private static native int sqlite3_bind_text( @NotNull long ptrToStmt, int ndx, @Nullable byte[] utf8, int maxBytes ); /** Works like the C-level sqlite3_bind_text() but assumes SQLITE_TRANSIENT for the final C API parameter. The byte array is assumed to be in UTF-8 encoding. |
︙ | ︙ | |||
357 358 359 360 361 362 363 | @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] utf8 ){ return ( null==utf8 ) ? sqlite3_bind_null(stmt.getNativePointer(), ndx) : sqlite3_bind_text(stmt.getNativePointer(), ndx, utf8, utf8.length); } | | | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] utf8 ){ return ( null==utf8 ) ? sqlite3_bind_null(stmt.getNativePointer(), ndx) : sqlite3_bind_text(stmt.getNativePointer(), ndx, utf8, utf8.length); } private static native int sqlite3_bind_text16( @NotNull long ptrToStmt, int ndx, @Nullable byte[] data, int maxBytes ); /** Identical to the sqlite3_bind_text() overload with the same signature but requires that its input be encoded in UTF-16 in platform byte order. |
︙ | ︙ | |||
398 399 400 401 402 403 404 | @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data ){ return (null == data) ? sqlite3_bind_null(stmt.getNativePointer(), ndx) : sqlite3_bind_text16(stmt.getNativePointer(), ndx, data, data.length); } | | | | | | | | | 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 | @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data ){ return (null == data) ? sqlite3_bind_null(stmt.getNativePointer(), ndx) : sqlite3_bind_text16(stmt.getNativePointer(), ndx, data, data.length); } private static native int sqlite3_bind_value(@NotNull long ptrToStmt, int ndx, long ptrToValue); /** Functions like the C-level sqlite3_bind_value(), or sqlite3_bind_null() if val is null. */ public static int sqlite3_bind_value(@NotNull sqlite3_stmt stmt, int ndx, sqlite3_value val){ return sqlite3_bind_value(stmt.getNativePointer(), ndx, null==val ? 0L : val.getNativePointer()); } private static native int sqlite3_bind_zeroblob(@NotNull long ptrToStmt, int ndx, int n); public static int sqlite3_bind_zeroblob(@NotNull sqlite3_stmt stmt, int ndx, int n){ return sqlite3_bind_zeroblob(stmt.getNativePointer(), ndx, n); } private static native int sqlite3_bind_zeroblob64( @NotNull long ptrToStmt, int ndx, long n ); public static int sqlite3_bind_zeroblob64(@NotNull sqlite3_stmt stmt, int ndx, long n){ return sqlite3_bind_zeroblob64(stmt.getNativePointer(), ndx, n); } private static native int sqlite3_blob_bytes(@NotNull long ptrToBlob); public static int sqlite3_blob_bytes(@NotNull sqlite3_blob blob){ return sqlite3_blob_bytes(blob.getNativePointer()); } private static native int sqlite3_blob_close(@Nullable long ptrToBlob); public static int sqlite3_blob_close(@Nullable sqlite3_blob blob){ return null==blob ? 0 : sqlite3_blob_close(blob.clearNativePointer()); } private static native int sqlite3_blob_open( @NotNull long ptrToDb, @NotNull String dbName, @NotNull String tableName, @NotNull String columnName, long iRow, int flags, @NotNull OutputPointer.sqlite3_blob out ); public static int sqlite3_blob_open( @NotNull sqlite3 db, @NotNull String dbName, |
︙ | ︙ | |||
463 464 465 466 467 468 469 | long iRow, int flags ){ final OutputPointer.sqlite3_blob out = new OutputPointer.sqlite3_blob(); sqlite3_blob_open(db.getNativePointer(), dbName, tableName, columnName, iRow, flags, out); return out.take(); }; | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | < < < | | < | < | | | | | > > > > > > > > > | > | > > > > > > > > > | > | > > > > > > > > > > > > > | | | > > > > > > > > > > > | > > > | > > > | 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 941 942 943 944 945 946 947 948 949 950 951 952 953 | long iRow, int flags ){ final OutputPointer.sqlite3_blob out = new OutputPointer.sqlite3_blob(); sqlite3_blob_open(db.getNativePointer(), dbName, tableName, columnName, iRow, flags, out); return out.take(); }; private static native int sqlite3_blob_read( @NotNull long ptrToBlob, @NotNull byte[] target, int srcOffset ); /** As per C's sqlite3_blob_read(), but writes its output to the given byte array. Note that the final argument is the offset of the source buffer, not the target array. */ public static int sqlite3_blob_read( @NotNull sqlite3_blob src, @NotNull byte[] target, int srcOffset ){ return sqlite3_blob_read(src.getNativePointer(), target, srcOffset); } /** An internal level of indirection. */ @Experimental private static native int sqlite3_blob_read_nio_buffer( @NotNull long ptrToBlob, int srcOffset, @NotNull java.nio.ByteBuffer tgt, int tgtOffset, int howMany ); /** Reads howMany bytes from offset srcOffset of src into position tgtOffset of tgt. 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 ) : SQLITE_MISUSE; } /** Convenience overload which reads howMany bytes from position srcOffset of src and returns the result as a new ByteBuffer. srcOffset may not be negative. If howMany is negative, it is treated as all bytes following srcOffset. 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 ); } /** Convenience overload which uses 0 for both src and tgt offsets and reads a number of bytes equal to the smaller of sqlite3_blob_bytes(src) and tgt.limit(). On success it sets tgt.limit() to the number of bytes read. On 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; final int rc = sqlite3_blob_read_nio_buffer( src.getNativePointer(), 0, tgt, 0, nRead ); if( 0==rc && nTgt!=nRead ) tgt.limit( nRead ); return rc; } private static native int sqlite3_blob_reopen( @NotNull long ptrToBlob, long newRowId ); public static int sqlite3_blob_reopen(@NotNull sqlite3_blob b, long newRowId){ return sqlite3_blob_reopen(b.getNativePointer(), newRowId); } private static native int sqlite3_blob_write( @NotNull long ptrToBlob, @NotNull byte[] bytes, int iOffset ); public static int sqlite3_blob_write( @NotNull sqlite3_blob b, @NotNull byte[] bytes, int iOffset ){ return sqlite3_blob_write(b.getNativePointer(), bytes, iOffset); } /** An internal level of indirection. */ @Experimental private static native int sqlite3_blob_write_nio_buffer( @NotNull long ptrToBlob, int tgtOffset, @NotNull java.nio.ByteBuffer src, int srcOffset, int howMany ); /** Writes howMany bytes of memory from offset srcOffset of the src buffer at position tgtOffset of b. If howMany is negative then it's equivalent to the number of bytes remaining starting at srcOffset. Returns SQLITE_MISUSE if tgt is null or sqlite3_jni_supports_nio() returns false. 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 ); } /** Overload alias for sqlite3_blob_write_nio_buffer(). */ @Experimental public static int sqlite3_blob_write( @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 ); } /** 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 ); } private static native int sqlite3_busy_handler( @NotNull long ptrToDb, @Nullable BusyHandlerCallback handler ); /** As for the C-level function of the same name, with a BusyHandlerCallback instance in place of a callback function. Pass it a null handler to clear the busy handler. */ public static int sqlite3_busy_handler( @NotNull sqlite3 db, @Nullable BusyHandlerCallback handler ){ return sqlite3_busy_handler(db.getNativePointer(), handler); } private static native int sqlite3_busy_timeout(@NotNull long ptrToDb, int ms); public static int sqlite3_busy_timeout(@NotNull sqlite3 db, int ms){ return sqlite3_busy_timeout(db.getNativePointer(), ms); } public static native boolean sqlite3_cancel_auto_extension( @NotNull AutoExtensionCallback ax ); private static native int sqlite3_changes(@NotNull long ptrToDb); public static int sqlite3_changes(@NotNull sqlite3 db){ return sqlite3_changes(db.getNativePointer()); } private static native long sqlite3_changes64(@NotNull long ptrToDb); public static long sqlite3_changes64(@NotNull sqlite3 db){ return sqlite3_changes64(db.getNativePointer()); } private static native int sqlite3_clear_bindings(@NotNull long ptrToStmt); public static int sqlite3_clear_bindings(@NotNull sqlite3_stmt stmt){ return sqlite3_clear_bindings(stmt.getNativePointer()); } private static native int sqlite3_close(@Nullable long ptrToDb); public static int sqlite3_close(@Nullable sqlite3 db){ return null==db ? 0 : sqlite3_close(db.clearNativePointer()); } private static native int sqlite3_close_v2(@Nullable long ptrToDb); public static int sqlite3_close_v2(@Nullable sqlite3 db){ return null==db ? 0 : sqlite3_close_v2(db.clearNativePointer()); } public static native byte[] sqlite3_column_blob( @NotNull sqlite3_stmt stmt, int ndx ); private static native int sqlite3_column_bytes(@NotNull long ptrToStmt, int ndx); public static int sqlite3_column_bytes(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_bytes(stmt.getNativePointer(), ndx); } private static native int sqlite3_column_bytes16(@NotNull long ptrToStmt, int ndx); public static int sqlite3_column_bytes16(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_bytes16(stmt.getNativePointer(), ndx); } private static native int sqlite3_column_count(@NotNull long ptrToStmt); public static int sqlite3_column_count(@NotNull sqlite3_stmt stmt){ return sqlite3_column_count(stmt.getNativePointer()); } private static native String sqlite3_column_database_name(@NotNull long ptrToStmt, int ndx); /** Only available if built with SQLITE_ENABLE_COLUMN_METADATA. */ public static String sqlite3_column_database_name(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_database_name(stmt.getNativePointer(), ndx); } private static native String sqlite3_column_decltype(@NotNull long ptrToStmt, int ndx); public static String sqlite3_column_decltype(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_decltype(stmt.getNativePointer(), ndx); } public static native double sqlite3_column_double( @NotNull sqlite3_stmt stmt, int ndx ); public static native int sqlite3_column_int( @NotNull sqlite3_stmt stmt, int ndx ); public static native long sqlite3_column_int64( @NotNull sqlite3_stmt stmt, int ndx ); private static native Object sqlite3_column_java_object( @NotNull long ptrToStmt, int ndx ); /** If the given result column was bound with sqlite3_bind_java_object() or sqlite3_result_java_object() then that object is returned, else null is returned. This routine requires locking the owning database's mutex briefly in order to extract the object in a thread-safe way. */ public static Object sqlite3_column_java_object( @NotNull sqlite3_stmt stmt, int ndx ){ return sqlite3_column_java_object( stmt.getNativePointer(), ndx ); } /** If the two-parameter overload of sqlite3_column_java_object() returns non-null and the returned value is an instance of T then that object is returned, else null is returned. */ @SuppressWarnings("unchecked") public static <T> T sqlite3_column_java_object( @NotNull sqlite3_stmt stmt, int ndx, @NotNull Class<T> type ){ final Object o = sqlite3_column_java_object(stmt, ndx); return type.isInstance(o) ? (T)o : null; } private static native String sqlite3_column_name(@NotNull long ptrToStmt, int ndx); public static String sqlite3_column_name(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_name(stmt.getNativePointer(), ndx); } /** 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. */ public static String sqlite3_column_origin_name(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_origin_name(stmt.getNativePointer(), ndx); } private static native String sqlite3_column_table_name(@NotNull long ptrToStmt, int ndx); /** Only available if built with SQLITE_ENABLE_COLUMN_METADATA. */ public static String sqlite3_column_table_name(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_table_name(stmt.getNativePointer(), ndx); } /** Functions identially to the C API, and this note is just to stress that the returned bytes are encoded as UTF-8. It returns |
︙ | ︙ | |||
668 669 670 671 672 673 674 | // default: break; // } // } // sqlite3_value_free(v); // return rv; // } | | | | | 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 1033 1034 1035 | // default: break; // } // } // sqlite3_value_free(v); // return rv; // } private static native int sqlite3_column_type(@NotNull long ptrToStmt, int ndx); public static int sqlite3_column_type(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_type(stmt.getNativePointer(), ndx); } public static native sqlite3_value sqlite3_column_value( @NotNull sqlite3_stmt stmt, int ndx ); private static native int sqlite3_collation_needed( @NotNull long ptrToDb, @Nullable CollationNeededCallback callback ); /** This functions like C's sqlite3_collation_needed16() because Java's string type is inherently compatible with that interface. */ public static int sqlite3_collation_needed( @NotNull sqlite3 db, @Nullable CollationNeededCallback callback ){ return sqlite3_collation_needed(db.getNativePointer(), callback); } private static native CommitHookCallback sqlite3_commit_hook( @NotNull long ptrToDb, @Nullable CommitHookCallback hook ); public static CommitHookCallback sqlite3_commit_hook( @NotNull sqlite3 db, @Nullable CommitHookCallback hook ){ return sqlite3_commit_hook(db.getNativePointer(), hook); |
︙ | ︙ | |||
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 | Unlike the C API, this returns SQLITE_MISUSE if its argument is null (as opposed to invoking UB). */ public static int sqlite3_complete(@NotNull String sql){ return sqlite3_complete( nulTerminateUtf8(sql) ); } /** <p>Works like in the C API with the exception that it only supports the following subset of configution flags: <p>SQLITE_CONFIG_SINGLETHREAD SQLITE_CONFIG_MULTITHREAD SQLITE_CONFIG_SERIALIZED <p>Others may be added in the future. It returns SQLITE_MISUSE if given an argument it does not handle. <p>Note that sqlite3_config() is not threadsafe with regards to the rest of the library. This must not be called when any other library APIs are being called. */ | > > > > > > > > > > > > > > > > > > | > > | | > > | > > | 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 1120 1121 1122 1123 1124 1125 1126 1127 | Unlike the C API, this returns SQLITE_MISUSE if its argument is null (as opposed to invoking UB). */ public static int sqlite3_complete(@NotNull String sql){ return sqlite3_complete( nulTerminateUtf8(sql) ); } /** Internal level of indirection for sqlite3_config(int). */ private static native int sqlite3_config__enable(int op); /** Internal level of indirection for sqlite3_config(ConfigLogCallback). */ private static native int sqlite3_config__CONFIG_LOG( @Nullable ConfigLogCallback logger ); /** Internal level of indirection for sqlite3_config(ConfigSqlLogCallback). */ private static native int sqlite3_config__SQLLOG( @Nullable ConfigSqlLogCallback logger ); /** <p>Works like in the C API with the exception that it only supports the following subset of configution flags: <p>SQLITE_CONFIG_SINGLETHREAD SQLITE_CONFIG_MULTITHREAD SQLITE_CONFIG_SERIALIZED <p>Others may be added in the future. It returns SQLITE_MISUSE if given an argument it does not handle. <p>Note that sqlite3_config() is not threadsafe with regards to the rest of the library. This must not be called when any other library APIs are being called. */ public static int sqlite3_config(int op){ return sqlite3_config__enable(op); } /** If the native library was built with SQLITE_ENABLE_SQLLOG defined then this acts as a proxy for C's sqlite3_config(SQLITE_CONFIG_SQLLOG,...). This sets or clears the logger. If installation of a logger fails, any previous logger is retained. <p>If not built with SQLITE_ENABLE_SQLLOG defined, this returns SQLITE_MISUSE. <p>Note that sqlite3_config() is not threadsafe with regards to the rest of the library. This must not be called when any other library APIs are being called. */ public static int sqlite3_config( @Nullable ConfigSqlLogCallback logger ){ return sqlite3_config__SQLLOG(logger); } /** The sqlite3_config() overload for handling the SQLITE_CONFIG_LOG option. */ public static int sqlite3_config( @Nullable ConfigLogCallback logger ){ return sqlite3_config__CONFIG_LOG(logger); } /** Unlike the C API, this returns null if its argument is null (as opposed to invoking UB). */ public static native sqlite3 sqlite3_context_db_handle( @NotNull sqlite3_context cx |
︙ | ︙ | |||
791 792 793 794 795 796 797 | functionName arguments are null (as opposed to invoking UB). */ public static native int sqlite3_create_function( @NotNull sqlite3 db, @NotNull String functionName, int nArg, int eTextRep, @NotNull SQLFunction func ); | | | | 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 | functionName arguments are null (as opposed to invoking UB). */ public static native int sqlite3_create_function( @NotNull sqlite3 db, @NotNull String functionName, int nArg, int eTextRep, @NotNull SQLFunction func ); private static native int sqlite3_data_count(@NotNull long ptrToStmt); public static int sqlite3_data_count(@NotNull sqlite3_stmt stmt){ return sqlite3_data_count(stmt.getNativePointer()); } /** Overload for sqlite3_db_config() calls which take (int,int*) variadic arguments. Returns SQLITE_MISUSE if op is not one of the SQLITE_DBCONFIG_... options which uses this call form. <p>Unlike the C API, this returns SQLITE_MISUSE if its db argument is null (as opposed to invoking UB). */ public static native int sqlite3_db_config( @NotNull sqlite3 db, int op, int onOff, @Nullable OutputPointer.Int32 out ); /** Overload for sqlite3_db_config() calls which take a (const char*) |
︙ | ︙ | |||
826 827 828 829 830 831 832 | private static native String sqlite3_db_name(@NotNull long ptrToDb, int ndx); public static String sqlite3_db_name(@NotNull sqlite3 db, int ndx){ return null==db ? null : sqlite3_db_name(db.getNativePointer(), ndx); } | < | | | | | | | 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 | private static native String sqlite3_db_name(@NotNull long ptrToDb, int ndx); public static String sqlite3_db_name(@NotNull sqlite3 db, int ndx){ return null==db ? null : sqlite3_db_name(db.getNativePointer(), ndx); } public static native String sqlite3_db_filename( @NotNull sqlite3 db, @NotNull String dbName ); public static native sqlite3 sqlite3_db_handle(@NotNull sqlite3_stmt stmt); public static native int sqlite3_db_readonly(@NotNull sqlite3 db, String dbName); public static native int sqlite3_db_release_memory(sqlite3 db); public static native int sqlite3_db_status( @NotNull sqlite3 db, int op, @NotNull OutputPointer.Int32 pCurrent, @NotNull OutputPointer.Int32 pHighwater, boolean reset ); public static native int sqlite3_errcode(@NotNull sqlite3 db); public static native String sqlite3_errmsg(@NotNull sqlite3 db); private static native int sqlite3_error_offset(@NotNull long ptrToDb); /** Note that the returned byte offset values assume UTF-8-encoded inputs, so won't always match character offsets in Java Strings. */ public static int sqlite3_error_offset(@NotNull sqlite3 db){ return sqlite3_error_offset(db.getNativePointer()); } public static native String sqlite3_errstr(int resultCode); public static native String sqlite3_expanded_sql(@NotNull sqlite3_stmt stmt); private static native int sqlite3_extended_errcode(@NotNull long ptrToDb); public static int sqlite3_extended_errcode(@NotNull sqlite3 db){ return sqlite3_extended_errcode(db.getNativePointer()); } public static native int sqlite3_extended_result_codes( @NotNull sqlite3 db, boolean on ); private static native boolean sqlite3_get_autocommit(@NotNull long ptrToDb); public static boolean sqlite3_get_autocommit(@NotNull sqlite3 db){ return sqlite3_get_autocommit(db.getNativePointer()); } public static native Object sqlite3_get_auxdata( @NotNull sqlite3_context cx, int n ); private static native int sqlite3_finalize(long ptrToStmt); public static int sqlite3_finalize(@NotNull sqlite3_stmt stmt){ return null==stmt ? 0 : sqlite3_finalize(stmt.clearNativePointer()); } public static native int sqlite3_initialize(); |
︙ | ︙ | |||
1162 1163 1164 1165 1166 1167 1168 | /** A convenience wrapper around sqlite3_prepare_v3() which accepts an arbitrary amount of input provided as a UTF-8-encoded byte array. It loops over the input bytes looking for statements. Each one it finds is passed to p.call(), passing ownership of it to that function. If p.call() returns 0, looping | | > > | > > | | | | | | > | > > > | 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 | /** A convenience wrapper around sqlite3_prepare_v3() which accepts an arbitrary amount of input provided as a UTF-8-encoded byte array. It loops over the input bytes looking for statements. Each one it finds is passed to p.call(), passing ownership of it to that function. If p.call() returns 0, looping continues, else the loop stops and p.call()'s result code is returned. If preparation of any given segment fails, looping stops and that result code is returned. <p>If p.call() throws, the exception is converted to a db-level error and a non-0 code is returned, in order to retain the C-style error semantics of the API. <p>How each statement is handled, including whether it is finalized or not, is up to the callback object. e.g. the callback might collect them for later use. If it does not collect them then it must finalize them. See PrepareMultiCallback.Finalize for a simple proxy which does that. */ public static int sqlite3_prepare_multi( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int prepFlags, @NotNull PrepareMultiCallback p){ final OutputPointer.Int32 oTail = new OutputPointer.Int32(); int pos = 0, n = 1; byte[] sqlChunk = sqlUtf8; int rc = 0; final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); while( 0==rc && pos<sqlChunk.length ){ sqlite3_stmt stmt = null; if( pos>0 ){ sqlChunk = Arrays.copyOfRange(sqlChunk, pos, sqlChunk.length); } if( 0==sqlChunk.length ) break; rc = sqlite3_prepare_v3(db, sqlChunk, prepFlags, outStmt, oTail); if( 0!=rc ) break; pos = oTail.value; stmt = outStmt.take(); if( null==stmt ){ // empty statement (whitespace/comments) continue; } try{ rc = p.call(stmt); }catch(Exception e){ rc = sqlite3_jni_db_error( db, SQLITE_ERROR, e ); } } return rc; } /** Convenience overload which accepts its SQL as a String and uses no statement-preparation flags. |
︙ | ︙ | |||
1252 1253 1254 1255 1256 1257 1258 | */ public static int sqlite3_prepare_multi( @NotNull sqlite3 db, @NotNull String[] sql, @NotNull PrepareMultiCallback p){ return sqlite3_prepare_multi(db, sql, 0, p); } | | | | | | > > > > > > > > > | > > > | 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 | */ public static int sqlite3_prepare_multi( @NotNull sqlite3 db, @NotNull String[] sql, @NotNull PrepareMultiCallback p){ return sqlite3_prepare_multi(db, sql, 0, p); } private static native int sqlite3_preupdate_blobwrite(@NotNull long ptrToDb); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this acts as a proxy for C's sqlite3_preupdate_blobwrite(), else it returns SQLITE_MISUSE with no side effects. */ public static int sqlite3_preupdate_blobwrite(@NotNull sqlite3 db){ return sqlite3_preupdate_blobwrite(db.getNativePointer()); } private static native int sqlite3_preupdate_count(@NotNull long ptrToDb); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this acts as a proxy for C's sqlite3_preupdate_count(), else it returns SQLITE_MISUSE with no side effects. */ public static int sqlite3_preupdate_count(@NotNull sqlite3 db){ return sqlite3_preupdate_count(db.getNativePointer()); } private static native int sqlite3_preupdate_depth(@NotNull long ptrToDb); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this acts as a proxy for C's sqlite3_preupdate_depth(), else it returns SQLITE_MISUSE with no side effects. */ public static int sqlite3_preupdate_depth(@NotNull sqlite3 db){ return sqlite3_preupdate_depth(db.getNativePointer()); } private static native PreupdateHookCallback sqlite3_preupdate_hook( @NotNull long ptrToDb, @Nullable PreupdateHookCallback hook ); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this acts as a proxy for C's sqlite3_preupdate_hook(), else it returns null with no side effects. */ public static PreupdateHookCallback sqlite3_preupdate_hook( @NotNull sqlite3 db, @Nullable PreupdateHookCallback hook ){ return sqlite3_preupdate_hook(db.getNativePointer(), hook); } private static native int sqlite3_preupdate_new(@NotNull long ptrToDb, int col, @NotNull OutputPointer.sqlite3_value out); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this acts as a proxy for C's sqlite3_preupdate_new(), else it returns SQLITE_MISUSE with no side effects. WARNING: client code _must not_ hold a reference to the returned sqlite3_value object beyond the scope of the preupdate hook in which this function is called. Doing so will leave the client holding a stale pointer, the address of which could point to anything at all after the pre-update hook is complete. This API has no way to record such objects and clear/invalidate them at the end of a pre-update hook. We "could" add infrastructure to do so, but would require significant levels of bookkeeping. */ public static int sqlite3_preupdate_new(@NotNull sqlite3 db, int col, @NotNull OutputPointer.sqlite3_value out){ return sqlite3_preupdate_new(db.getNativePointer(), col, out); } /** Convenience wrapper for the 3-arg sqlite3_preupdate_new() which returns null on error. */ public static sqlite3_value sqlite3_preupdate_new(@NotNull sqlite3 db, int col){ final OutputPointer.sqlite3_value out = new OutputPointer.sqlite3_value(); sqlite3_preupdate_new(db.getNativePointer(), col, out); return out.take(); } private static native int sqlite3_preupdate_old(@NotNull long ptrToDb, int col, @NotNull OutputPointer.sqlite3_value out); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this acts as a proxy for C's sqlite3_preupdate_old(), else it returns SQLITE_MISUSE with no side effects. WARNING: see warning in sqlite3_preupdate_new() regarding the potential for stale sqlite3_value handles. */ public static int sqlite3_preupdate_old(@NotNull sqlite3 db, int col, @NotNull OutputPointer.sqlite3_value out){ return sqlite3_preupdate_old(db.getNativePointer(), col, out); } /** |
︙ | ︙ | |||
1374 1375 1376 1377 1378 1379 1380 | /** The main sqlite3_result_error() impl of which all others are proxies. eTextRep must be one of SQLITE_UTF8 or SQLITE_UTF16 and msg must be encoded correspondingly. Any other eTextRep value results in the C-level sqlite3_result_error() being called with a complaint about the invalid argument. */ | | | 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 | /** The main sqlite3_result_error() impl of which all others are proxies. eTextRep must be one of SQLITE_UTF8 or SQLITE_UTF16 and msg must be encoded correspondingly. Any other eTextRep value results in the C-level sqlite3_result_error() being called with a complaint about the invalid argument. */ private static native void sqlite3_result_error( @NotNull sqlite3_context cx, @NotNull byte[] msg, int eTextRep ); public static void sqlite3_result_error( @NotNull sqlite3_context cx, @NotNull byte[] utf8 ){ sqlite3_result_error(cx, utf8, SQLITE_UTF8); |
︙ | ︙ | |||
1428 1429 1430 1431 1432 1433 1434 | @NotNull sqlite3_context cx ); public static native void sqlite3_result_error_code( @NotNull sqlite3_context cx, int c ); | < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | @NotNull sqlite3_context cx ); public static native void sqlite3_result_error_code( @NotNull sqlite3_context cx, int c ); public static native void sqlite3_result_int( @NotNull sqlite3_context cx, int v ); public static native void sqlite3_result_int64( @NotNull sqlite3_context cx, long v ); /** Binds the SQL result to the given object, or {@link #sqlite3_result_null} if {@code o} is null. Use {@link #sqlite3_value_java_object} to fetch it. <p>This is implemented in terms of C's sqlite3_result_pointer(), but that function is not exposed to JNI because (A) cross-language semantic mismatch and (B) Java doesn't need that argument for its intended purpose (type safety). @see #sqlite3_value_java_object @see #sqlite3_bind_java_object */ public static native void sqlite3_result_java_object( @NotNull sqlite3_context cx, @NotNull Object o ); /** Similar to sqlite3_bind_nio_buffer(), this works like sqlite3_result_blob() but accepts a java.nio.ByteBuffer as its input source. See sqlite3_bind_nio_buffer() for the semantics of the second and subsequent arguments. If cx is null then this function will silently fail. If sqlite3_jni_supports_nio() returns false or iBegin is negative, an error result is set. If (begin+n) extends beyond the end of the buffer, it is silently truncated to fit. If any of the following apply, this function behaves like 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 ); public static void sqlite3_result_set( @NotNull sqlite3_context cx, @NotNull Boolean v ){ sqlite3_result_int(cx, v ? 1 : 0); } |
︙ | ︙ | |||
1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 | public static void sqlite3_result_set( @NotNull sqlite3_context cx, @Nullable byte[] blob ){ if( null==blob ) sqlite3_result_null(cx); else sqlite3_result_blob(cx, blob, blob.length); } public static native void sqlite3_result_value( @NotNull sqlite3_context cx, @NotNull sqlite3_value v ); public static native void sqlite3_result_zeroblob( @NotNull sqlite3_context cx, int n | > > > > | 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 | public static void sqlite3_result_set( @NotNull sqlite3_context cx, @Nullable byte[] blob ){ if( null==blob ) sqlite3_result_null(cx); else sqlite3_result_blob(cx, blob, blob.length); } public static native void sqlite3_result_subtype( @NotNull sqlite3_context cx, int val ); public static native void sqlite3_result_value( @NotNull sqlite3_context cx, @NotNull sqlite3_value v ); public static native void sqlite3_result_zeroblob( @NotNull sqlite3_context cx, int n |
︙ | ︙ | |||
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 | ); public static void sqlite3_result_blob( @NotNull sqlite3_context cx, @Nullable byte[] blob ){ sqlite3_result_blob(cx, blob, (int)(null==blob ? 0 : blob.length)); } /** Binds the given text using C's sqlite3_result_blob64() unless: <ul> <li>@param blob is null: translates to sqlite3_result_null()</li> | > > > > > > > > > > > > > > > > > > > > > > > | 1953 1954 1955 1956 1957 1958 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 1986 1987 1988 1989 | ); public static void sqlite3_result_blob( @NotNull sqlite3_context cx, @Nullable byte[] blob ){ sqlite3_result_blob(cx, blob, (int)(null==blob ? 0 : blob.length)); } /** 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: <ul> <li>@param blob is null: translates to sqlite3_result_null()</li> |
︙ | ︙ | |||
1603 1604 1605 1606 1607 1608 1609 | } /** Binds the given text using C's sqlite3_result_text64() unless: <ul> | | > | 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 | } /** Binds the given text using C's sqlite3_result_text64() unless: <ul> <li>text is null: translates to a call to {@link #sqlite3_result_null}</li> <li>text is too large: translates to a call to {@link #sqlite3_result_error_toobig}</li> <li>The @param encoding argument has an invalid value: translates to {@link sqlite3_result_error_code} with code SQLITE_FORMAT.</li> |
︙ | ︙ | |||
1647 1648 1649 1650 1651 1652 1653 | if(null == text) sqlite3_result_null(cx); else{ final byte[] b = text.getBytes(StandardCharsets.UTF_16); sqlite3_result_text64(cx, b, b.length, SQLITE_UTF16); } } | | | 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 | if(null == text) sqlite3_result_null(cx); else{ final byte[] b = text.getBytes(StandardCharsets.UTF_16); sqlite3_result_text64(cx, b, b.length, SQLITE_UTF16); } } private static native RollbackHookCallback sqlite3_rollback_hook( @NotNull long ptrToDb, @Nullable RollbackHookCallback hook ); public static RollbackHookCallback sqlite3_rollback_hook( @NotNull sqlite3 db, @Nullable RollbackHookCallback hook ){ return sqlite3_rollback_hook(db.getNativePointer(), hook); |
︙ | ︙ | |||
1699 1700 1701 1702 1703 1704 1705 | ); public static native int sqlite3_status64( int op, @NotNull OutputPointer.Int64 pCurrent, @NotNull OutputPointer.Int64 pHighwater, boolean reset ); | > > | > > | | | | | | | | 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 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 2203 2204 2205 2206 2207 2208 2209 | ); public static native int sqlite3_status64( int op, @NotNull OutputPointer.Int64 pCurrent, @NotNull OutputPointer.Int64 pHighwater, boolean reset ); private static native int sqlite3_step(@NotNull long ptrToStmt); public static int sqlite3_step(@NotNull sqlite3_stmt stmt){ return null==stmt ? SQLITE_MISUSE : sqlite3_step(stmt.getNativePointer()); } public static native boolean sqlite3_stmt_busy(@NotNull sqlite3_stmt stmt); private static native int sqlite3_stmt_explain(@NotNull long ptrToStmt, int op); public static int sqlite3_stmt_explain(@NotNull sqlite3_stmt stmt, int op){ return null==stmt ? SQLITE_MISUSE : sqlite3_stmt_explain(stmt.getNativePointer(), op); } private static native int sqlite3_stmt_isexplain(@NotNull long ptrToStmt); public static int sqlite3_stmt_isexplain(@NotNull sqlite3_stmt stmt){ return null==stmt ? 0 : sqlite3_stmt_isexplain(stmt.getNativePointer()); } public static native boolean sqlite3_stmt_readonly(@NotNull sqlite3_stmt stmt); public static native int sqlite3_stmt_status( @NotNull sqlite3_stmt stmt, int op, boolean reset ); /** Internal impl of the public sqlite3_strglob() method. Neither argument may be null and both must be NUL-terminated UTF-8. This overload is private because: (A) to keep users from inadvertently passing non-NUL-terminated byte arrays (an easy thing to do). (B) it is cheaper to NUL-terminate the String-to-byte-array conversion in the Java implementation (sqlite3_strglob(String,String)) than to do that in C, so that signature is the public-facing one. */ private static native int sqlite3_strglob( @NotNull byte[] glob, @NotNull byte[] nulTerminatedUtf8 ); public static int sqlite3_strglob( @NotNull String glob, @NotNull String txt ){ return sqlite3_strglob(nulTerminateUtf8(glob), nulTerminateUtf8(txt)); } /** The LIKE counterpart of the private sqlite3_strglob() method. */ private static native int sqlite3_strlike( @NotNull byte[] glob, @NotNull byte[] nulTerminatedUtf8, int escChar ); public static int sqlite3_strlike( @NotNull String glob, @NotNull String txt, char escChar ){ return sqlite3_strlike(nulTerminateUtf8(glob), nulTerminateUtf8(txt), (int)escChar); } private static native int sqlite3_system_errno(@NotNull long ptrToDb); public static int sqlite3_system_errno(@NotNull sqlite3 db){ return sqlite3_system_errno(db.getNativePointer()); } public static native int sqlite3_table_column_metadata( @NotNull sqlite3 db, @NotNull String zDbName, |
︙ | ︙ | |||
1805 1806 1807 1808 1809 1810 1811 | return 0==sqlite3_table_column_metadata( db, zDbName, zTableName, zColumnName, out ) ? out : null; } public static native int sqlite3_threadsafe(); | | | | 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 | return 0==sqlite3_table_column_metadata( db, zDbName, zTableName, zColumnName, out ) ? out : null; } public static native int sqlite3_threadsafe(); private static native int sqlite3_total_changes(@NotNull long ptrToDb); public static int sqlite3_total_changes(@NotNull sqlite3 db){ return sqlite3_total_changes(db.getNativePointer()); } private static native long sqlite3_total_changes64(@NotNull long ptrToDb); public static long sqlite3_total_changes64(@NotNull sqlite3 db){ return sqlite3_total_changes64(db.getNativePointer()); } /** Works like C's sqlite3_trace_v2() except that the 3rd argument to that |
︙ | ︙ | |||
1834 1835 1836 1837 1838 1839 1840 | @NotNull sqlite3 db, int traceMask, @Nullable TraceV2Callback tracer ); public static native int sqlite3_txn_state( @NotNull sqlite3 db, @Nullable String zSchema ); | | | | | | | | | | | | | | | > > > > > > > > > > > | | | | | | | 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 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 | @NotNull sqlite3 db, int traceMask, @Nullable TraceV2Callback tracer ); public static native int sqlite3_txn_state( @NotNull sqlite3 db, @Nullable String zSchema ); private static native UpdateHookCallback sqlite3_update_hook( @NotNull long ptrToDb, @Nullable UpdateHookCallback hook ); public static UpdateHookCallback sqlite3_update_hook( @NotNull sqlite3 db, @Nullable UpdateHookCallback hook ){ return sqlite3_update_hook(db.getNativePointer(), hook); } /* Note that: void * sqlite3_user_data(sqlite3_context*) Is not relevant in the JNI binding, as its feature is replaced by the ability to pass an object, including any relevant state, to sqlite3_create_function(). */ private static native byte[] sqlite3_value_blob(@NotNull long ptrToValue); public static byte[] sqlite3_value_blob(@NotNull sqlite3_value v){ return sqlite3_value_blob(v.getNativePointer()); } private static native int sqlite3_value_bytes(@NotNull long ptrToValue); public static int sqlite3_value_bytes(@NotNull sqlite3_value v){ return sqlite3_value_bytes(v.getNativePointer()); } private static native int sqlite3_value_bytes16(@NotNull long ptrToValue); public static int sqlite3_value_bytes16(@NotNull sqlite3_value v){ return sqlite3_value_bytes16(v.getNativePointer()); } private static native double sqlite3_value_double(@NotNull long ptrToValue); public static double sqlite3_value_double(@NotNull sqlite3_value v){ return sqlite3_value_double(v.getNativePointer()); } private static native sqlite3_value sqlite3_value_dup(@NotNull long ptrToValue); public static sqlite3_value sqlite3_value_dup(@NotNull sqlite3_value v){ return sqlite3_value_dup(v.getNativePointer()); } private static native int sqlite3_value_encoding(@NotNull long ptrToValue); public static int sqlite3_value_encoding(@NotNull sqlite3_value v){ return sqlite3_value_encoding(v.getNativePointer()); } private static native void sqlite3_value_free(@Nullable long ptrToValue); public static void sqlite3_value_free(@Nullable sqlite3_value v){ if( null!=v ) sqlite3_value_free(v.clearNativePointer()); } private static native boolean sqlite3_value_frombind(@NotNull long ptrToValue); public static boolean sqlite3_value_frombind(@NotNull sqlite3_value v){ return sqlite3_value_frombind(v.getNativePointer()); } private static native int sqlite3_value_int(@NotNull long ptrToValue); public static int sqlite3_value_int(@NotNull sqlite3_value v){ return sqlite3_value_int(v.getNativePointer()); } private static native long sqlite3_value_int64(@NotNull long ptrToValue); public static long sqlite3_value_int64(@NotNull sqlite3_value v){ return sqlite3_value_int64(v.getNativePointer()); } private static native Object sqlite3_value_java_object(@NotNull long ptrToValue); /** If the given value was set using {@link #sqlite3_result_java_object} then this function returns that object, else it returns null. <p>It is up to the caller to inspect the object to determine its type, and cast it if necessary. */ public static Object sqlite3_value_java_object(@NotNull sqlite3_value v){ return sqlite3_value_java_object(v.getNativePointer()); } /** A variant of sqlite3_value_java_object() which returns the fetched object cast to T if the object is an instance of the given Class, else it returns null. */ @SuppressWarnings("unchecked") public static <T> T sqlite3_value_java_object(@NotNull sqlite3_value v, @NotNull Class<T> type){ final Object o = sqlite3_value_java_object(v); return type.isInstance(o) ? (T)o : null; } /** 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()); } private static native int sqlite3_value_numeric_type(@NotNull long ptrToValue); public static int sqlite3_value_numeric_type(@NotNull sqlite3_value v){ return sqlite3_value_numeric_type(v.getNativePointer()); } private static native int sqlite3_value_subtype(@NotNull long ptrToValue); public static int sqlite3_value_subtype(@NotNull sqlite3_value v){ return sqlite3_value_subtype(v.getNativePointer()); } private static native byte[] sqlite3_value_text(@NotNull long ptrToValue); /** Functions identially to the C API, and this note is just to stress that the returned bytes are encoded as UTF-8. It returns null if the underlying C-level sqlite3_value_text() returns NULL or on allocation error. */ public static byte[] sqlite3_value_text(@NotNull sqlite3_value v){ return sqlite3_value_text(v.getNativePointer()); } private static native String sqlite3_value_text16(@NotNull long ptrToValue); public static String sqlite3_value_text16(@NotNull sqlite3_value v){ return sqlite3_value_text16(v.getNativePointer()); } private static native int sqlite3_value_type(@NotNull long ptrToValue); public static int sqlite3_value_type(@NotNull sqlite3_value v){ return sqlite3_value_type(v.getNativePointer()); } /** This is NOT part of the public API. It exists solely as a place |
︙ | ︙ | |||
2251 2252 2253 2254 2255 2256 2257 | public static final int SQLITE_OPEN_PRIVATECACHE = 0x00040000 /* Ok for sqlite3_open_v2() */; //public static final int SQLITE_OPEN_WAL = 0x00080000 /* VFS only */; public static final int SQLITE_OPEN_NOFOLLOW = 0x01000000 /* Ok for sqlite3_open_v2() */; public static final int SQLITE_OPEN_EXRESCODE = 0x02000000 /* Extended result codes */; // prepare flags public static final int SQLITE_PREPARE_PERSISTENT = 1; | < | 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 | public static final int SQLITE_OPEN_PRIVATECACHE = 0x00040000 /* Ok for sqlite3_open_v2() */; //public static final int SQLITE_OPEN_WAL = 0x00080000 /* VFS only */; public static final int SQLITE_OPEN_NOFOLLOW = 0x01000000 /* Ok for sqlite3_open_v2() */; public static final int SQLITE_OPEN_EXRESCODE = 0x02000000 /* Extended result codes */; // prepare flags public static final int SQLITE_PREPARE_PERSISTENT = 1; public static final int SQLITE_PREPARE_NO_VTAB = 4; // result codes public static final int SQLITE_OK = 0; public static final int SQLITE_ERROR = 1; public static final int SQLITE_INTERNAL = 2; public static final int SQLITE_PERM = 3; |
︙ | ︙ | |||
2407 2408 2409 2410 2411 2412 2413 | // transaction state public static final int SQLITE_TXN_NONE = 0; public static final int SQLITE_TXN_READ = 1; public static final int SQLITE_TXN_WRITE = 2; // udf flags | | | > | > | 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 | // transaction state public static final int SQLITE_TXN_NONE = 0; public static final int SQLITE_TXN_READ = 1; public static final int SQLITE_TXN_WRITE = 2; // udf flags public static final int SQLITE_DETERMINISTIC = 0x000000800; public static final int SQLITE_DIRECTONLY = 0x000080000; public static final int SQLITE_SUBTYPE = 0x000100000; public static final int SQLITE_INNOCUOUS = 0x000200000; public static final int SQLITE_RESULT_SUBTYPE = 0x001000000; // virtual tables public static final int SQLITE_INDEX_SCAN_UNIQUE = 1; public static final int SQLITE_INDEX_CONSTRAINT_EQ = 2; public static final int SQLITE_INDEX_CONSTRAINT_GT = 4; public static final int SQLITE_INDEX_CONSTRAINT_LE = 8; public static final int SQLITE_INDEX_CONSTRAINT_LT = 16; |
︙ | ︙ | |||
2438 2439 2440 2441 2442 2443 2444 | public static final int SQLITE_VTAB_INNOCUOUS = 2; public static final int SQLITE_VTAB_DIRECTONLY = 3; public static final int SQLITE_VTAB_USES_ALL_SCHEMAS = 4; public static final int SQLITE_ROLLBACK = 1; public static final int SQLITE_FAIL = 3; public static final int SQLITE_REPLACE = 5; static { | < < > > | 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 | public static final int SQLITE_VTAB_INNOCUOUS = 2; public static final int SQLITE_VTAB_DIRECTONLY = 3; public static final int SQLITE_VTAB_USES_ALL_SCHEMAS = 4; public static final int SQLITE_ROLLBACK = 1; public static final int SQLITE_FAIL = 3; public static final int SQLITE_REPLACE = 5; static { init(); } /* Must come after static init(). */ private static final boolean JNI_SUPPORTS_NIO = sqlite3_jni_supports_nio(); } |
Changes to ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java.
︙ | ︙ | |||
20 21 22 23 24 25 26 | <p>Unless very explicitely documented otherwise, callbacks must never throw. Any which do throw but should not might trigger debug output regarding the error, but the exception will not be propagated. For callback interfaces which support returning error info to the core, the JNI binding will convert any exceptions to C-level error information. For callback interfaces which do not | > | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <p>Unless very explicitely documented otherwise, callbacks must never throw. Any which do throw but should not might trigger debug output regarding the error, but the exception will not be propagated. For callback interfaces which support returning error info to the core, the JNI binding will convert any exceptions to C-level error information. For callback interfaces which do not support returning error information, all exceptions will necessarily be suppressed in order to retain the C-style no-throw semantics and avoid invoking undefined behavior in the C layer. <p>Callbacks of this style follow a common naming convention: <p>1) They use the UpperCamelCase form of the C function they're proxying for, minus the {@code sqlite3_} prefix, plus a {@code Callback} suffix. e.g. {@code sqlite3_busy_handler()}'s callback is named {@code BusyHandlerCallback}. Exceptions are made where that |
︙ | ︙ |
Changes to ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java.
︙ | ︙ | |||
17 18 19 20 21 22 23 | Callback for use with {@link CApi#sqlite3_collation_needed}. */ public interface CollationNeededCallback extends CallbackProxy { /** Has the same semantics as the C-level sqlite3_create_collation() callback. | > | | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 | Callback for use with {@link CApi#sqlite3_collation_needed}. */ public interface CollationNeededCallback extends CallbackProxy { /** Has the same semantics as the C-level sqlite3_create_collation() callback. <p>Because the C API has no mechanism for reporting errors from this callbacks, any exceptions thrown by this callback are suppressed. */ void call(sqlite3 db, int eTextRep, String collationName); } |
Changes to ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java.
︙ | ︙ | |||
15 16 17 18 19 20 21 | /** Callback for use with {@link CApi#sqlite3_commit_hook}. */ public interface CommitHookCallback extends CallbackProxy { /** Works as documented for the C-level sqlite3_commit_hook() | | > | 15 16 17 18 19 20 21 22 23 24 25 26 | /** Callback for use with {@link CApi#sqlite3_commit_hook}. */ public interface CommitHookCallback extends CallbackProxy { /** Works as documented for the C-level sqlite3_commit_hook() callback. If it throws, the exception is translated into a db-level error. */ int call(); } |
Added ext/jni/src/org/sqlite/jni/capi/ConfigSqlLogCallback.java.
> > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 2023-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. ** ************************************************************************* ** This file is part of the JNI bindings for the sqlite3 C API. */ package org.sqlite.jni.capi; /** A callback for use with sqlite3_config(). */ public interface ConfigSqlLogCallback { /** Must function as described for a C-level callback for {@link CApi#sqlite3_config(ConfigSqlLogCallback)}, with the slight signature change. */ void call(sqlite3 db, String msg, int msgType ); } |
Deleted ext/jni/src/org/sqlite/jni/capi/ConfigSqllogCallback.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to ext/jni/src/org/sqlite/jni/capi/OutputPointer.java.
︙ | ︙ | |||
224 225 226 227 228 229 230 231 | /** Initializes with the value v. */ public ByteArray(byte[] v){value = v;} /** Returns the current value. */ public final byte[] get(){return value;} /** Sets the current value. */ public final void set(byte[] v){value = v;} } } | > > > > > > > > > > > > > > > > > > > > > > | 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 | /** Initializes with the value v. */ public ByteArray(byte[] v){value = v;} /** Returns the current value. */ public final byte[] get(){return value;} /** Sets the current value. */ public final void set(byte[] v){value = v;} } /** Output pointer for use with native routines which return blobs via java.nio.ByteBuffer. See {@link org.sqlite.jni.capi.CApi#sqlite3_jni_supports_nio} */ public static final class ByteBuffer { /** This is public for ease of use. Accessors are provided for consistency with the higher-level types. */ public java.nio.ByteBuffer value; /** Initializes with the value null. */ public ByteBuffer(){this(null);} /** Initializes with the value v. */ public ByteBuffer(java.nio.ByteBuffer v){value = v;} /** Returns the current value. */ public final java.nio.ByteBuffer get(){return value;} /** Sets the current value. */ public final void set(java.nio.ByteBuffer v){value = v;} } } |
Changes to ext/jni/src/org/sqlite/jni/capi/PrepareMultiCallback.java.
︙ | ︙ | |||
21 22 23 24 25 26 27 | /** Gets passed a sqlite3_stmt which it may handle in arbitrary ways, transfering ownership of it to this function. sqlite3_prepare_multi() will _not_ finalize st - it is up to the call() implementation how st is handled. | | > > > | | 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 | /** Gets passed a sqlite3_stmt which it may handle in arbitrary ways, transfering ownership of it to this function. sqlite3_prepare_multi() will _not_ finalize st - it is up to the call() implementation how st is handled. Must return 0 on success or an SQLITE_... code on error. If it throws, sqlite3_prepare_multi() will transform the exception into a db-level error in order to retain the C-style error semantics of the API. See the {@link Finalize} class for a wrapper which finalizes the statement after calling a proxy PrepareMultiCallback. */ int call(sqlite3_stmt st); /** A PrepareMultiCallback impl which wraps a separate impl and finalizes any sqlite3_stmt passed to its callback. */ public static final class Finalize implements PrepareMultiCallback { private final PrepareMultiCallback p; /** p is the proxy to call() when this.call() is called. */ public Finalize( PrepareMultiCallback p ){ this.p = p; } /** |
︙ | ︙ |
Changes to ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java.
︙ | ︙ | |||
15 16 17 18 19 20 21 | /** Callback for use with {@link CApi#sqlite3_preupdate_hook}. */ public interface PreupdateHookCallback extends CallbackProxy { /** Must function as described for the C-level sqlite3_preupdate_hook() | | > | 15 16 17 18 19 20 21 22 23 24 25 26 27 | /** Callback for use with {@link CApi#sqlite3_preupdate_hook}. */ public interface PreupdateHookCallback extends CallbackProxy { /** Must function as described for the C-level sqlite3_preupdate_hook() callback. If it throws, the exception is translated to a db-level error and the exception is suppressed. */ void call(sqlite3 db, int op, String dbName, String dbTable, long iKey1, long iKey2 ); } |
Changes to ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java.
︙ | ︙ | |||
14 15 16 17 18 19 20 | package org.sqlite.jni.capi; /** Callback for use with {@link CApi#sqlite3_rollback_hook}. */ public interface RollbackHookCallback extends CallbackProxy { /** | | | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 | package org.sqlite.jni.capi; /** Callback for use with {@link CApi#sqlite3_rollback_hook}. */ public interface RollbackHookCallback extends CallbackProxy { /** Must function as documented for the C-level sqlite3_rollback_hook() callback. If it throws, the exception is translated into a db-level error. */ void call(); } |
Changes to ext/jni/src/org/sqlite/jni/capi/SQLFunction.java.
︙ | ︙ | |||
29 30 31 32 33 34 35 | UDFs, so long as they conform to the public interfaces defined by those three classes. The JNI layer only actively relies on the SQLFunction base class and the method names and signatures used by the UDF callback interfaces. */ public interface SQLFunction { | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 29 30 31 32 33 34 35 36 | UDFs, so long as they conform to the public interfaces defined by those three classes. The JNI layer only actively relies on the SQLFunction base class and the method names and signatures used by the UDF callback interfaces. */ public interface SQLFunction { } |
Changes to ext/jni/src/org/sqlite/jni/capi/Tester1.java.
︙ | ︙ | |||
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | Annotation for Tester1 tests which mark those which must be skipped in multi-threaded mode. */ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) @interface SingleThreadOnly{} public class Tester1 implements Runnable { //! True when running in multi-threaded mode. private static boolean mtMode = false; //! True to sleep briefly between tests. private static boolean takeNaps = false; //! True to shuffle the order of the tests. private static boolean shuffle = false; //! True to dump the list of to-run tests to stdout. | > > > > > > > > | | 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 | Annotation for Tester1 tests which mark those which must be skipped in multi-threaded mode. */ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) @interface SingleThreadOnly{} /** Annotation for Tester1 tests which must only be run if sqlite3_jni_supports_nio() is true. */ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) @interface RequiresJniNio{} public class Tester1 implements Runnable { //! True when running in multi-threaded mode. private static boolean mtMode = false; //! True to sleep briefly between tests. private static boolean takeNaps = false; //! True to shuffle the order of the tests. private static boolean shuffle = false; //! True to dump the list of to-run tests to stdout. private static int listRunTests = 0; //! True to squelch all out() and outln() output. private static boolean quietMode = false; //! Total number of runTests() calls. private static int nTestRuns = 0; //! List of test*() methods to run. private static List<java.lang.reflect.Method> testMethods = null; //! List of exceptions collected by run() |
︙ | ︙ | |||
323 324 325 326 327 328 329 | sqlite3_finalize(stmt); affirm(0 == stmt.getNativePointer()); } } rc = sqlite3_prepare_v3(db, "INSERT INTO t2(a) VALUES(1),(2),(3)", | | | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | sqlite3_finalize(stmt); affirm(0 == stmt.getNativePointer()); } } rc = sqlite3_prepare_v3(db, "INSERT INTO t2(a) VALUES(1),(2),(3)", 0, outStmt); affirm(0 == rc); stmt = outStmt.get(); affirm(0 != stmt.getNativePointer()); sqlite3_finalize(stmt); affirm(0 == stmt.getNativePointer() ); affirm( 0==sqlite3_errcode(db) ); |
︙ | ︙ | |||
378 379 380 381 382 383 384 385 386 387 388 389 390 391 | affirm(sqlite3_changes(db) > changes); affirm(sqlite3_total_changes(db) > changesT); affirm(sqlite3_changes64(db) > changes64); affirm(sqlite3_total_changes64(db) > changesT64); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); affirm( sqlite3_stmt_readonly(stmt) ); affirm( !sqlite3_stmt_busy(stmt) ); int total2 = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ affirm( sqlite3_stmt_busy(stmt) ); total2 += sqlite3_column_int(stmt, 0); sqlite3_value sv = sqlite3_column_value(stmt, 0); affirm( null != sv ); affirm( 0 != sv.getNativePointer() ); | > > > > > > > > > | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | affirm(sqlite3_changes(db) > changes); affirm(sqlite3_total_changes(db) > changesT); affirm(sqlite3_changes64(db) > changes64); affirm(sqlite3_total_changes64(db) > changesT64); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); affirm( sqlite3_stmt_readonly(stmt) ); affirm( !sqlite3_stmt_busy(stmt) ); if( sqlite3_compileoption_used("ENABLE_COLUMN_METADATA") ){ /* Unlike in native C code, JNI won't trigger an UnsatisfiedLinkError until these are called (on Linux, at least). */ affirm("t".equals(sqlite3_column_table_name(stmt,0))); affirm("main".equals(sqlite3_column_database_name(stmt,0))); affirm("a".equals(sqlite3_column_origin_name(stmt,0))); } int total2 = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ affirm( sqlite3_stmt_busy(stmt) ); total2 += sqlite3_column_int(stmt, 0); sqlite3_value sv = sqlite3_column_value(stmt, 0); affirm( null != sv ); affirm( 0 != sv.getNativePointer() ); |
︙ | ︙ | |||
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 | affirm(SQLITE_DONE==rc); sqlite3_reset(stmt); } sqlite3_finalize(stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); StringBuilder sbuf = new StringBuilder(); n = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ final sqlite3_value sv = sqlite3_value_dup(sqlite3_column_value(stmt,0)); final String txt = sqlite3_column_text16(stmt, 0); sbuf.append( txt ); affirm( txt.equals(new String( sqlite3_column_text(stmt, 0), StandardCharsets.UTF_8 )) ); affirm( txt.length() < sqlite3_value_bytes(sv) ); affirm( txt.equals(new String( sqlite3_value_text(sv), StandardCharsets.UTF_8)) ); affirm( txt.length() == sqlite3_value_bytes16(sv)/2 ); affirm( txt.equals(sqlite3_value_text16(sv)) ); sqlite3_value_free(sv); ++n; } sqlite3_finalize(stmt); affirm(3 == n); affirm("w😃rldhell🤩!🤩".equals(sbuf.toString())); | > > > > > > > > > > | 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 | affirm(SQLITE_DONE==rc); sqlite3_reset(stmt); } sqlite3_finalize(stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); StringBuilder sbuf = new StringBuilder(); n = 0; final boolean tryNio = sqlite3_jni_supports_nio(); while( SQLITE_ROW == sqlite3_step(stmt) ){ final sqlite3_value sv = sqlite3_value_dup(sqlite3_column_value(stmt,0)); final String txt = sqlite3_column_text16(stmt, 0); sbuf.append( txt ); affirm( txt.equals(new String( sqlite3_column_text(stmt, 0), StandardCharsets.UTF_8 )) ); affirm( txt.length() < sqlite3_value_bytes(sv) ); affirm( txt.equals(new String( sqlite3_value_text(sv), StandardCharsets.UTF_8)) ); affirm( txt.length() == sqlite3_value_bytes16(sv)/2 ); affirm( txt.equals(sqlite3_value_text16(sv)) ); if( tryNio ){ java.nio.ByteBuffer bu = sqlite3_value_nio_buffer(sv); byte ba[] = sqlite3_value_blob(sv); affirm( ba.length == bu.capacity() ); int i = 0; for( byte b : ba ){ affirm( b == bu.get(i++) ); } } sqlite3_value_free(sv); ++n; } sqlite3_finalize(stmt); affirm(3 == n); affirm("w😃rldhell🤩!🤩".equals(sbuf.toString())); |
︙ | ︙ | |||
543 544 545 546 547 548 549 550 551 552 553 554 555 556 | ++n; } sqlite3_finalize(stmt); affirm(1 == n); affirm(total == 0x32 + 0x33 + 0x34); sqlite3_close_v2(db); } private void testSql(){ sqlite3 db = createNewDb(); sqlite3_stmt stmt = prepare(db, "SELECT 1"); affirm( "SELECT 1".equals(sqlite3_sql(stmt)) ); sqlite3_finalize(stmt); stmt = prepare(db, "SELECT ?"); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ++n; } sqlite3_finalize(stmt); affirm(1 == n); affirm(total == 0x32 + 0x33 + 0x34); sqlite3_close_v2(db); } @RequiresJniNio private void testBindByteBuffer(){ /* TODO: these tests need to be much more extensive to check the begin/end range handling. */ java.nio.ByteBuffer zeroCheck = java.nio.ByteBuffer.allocateDirect(0); affirm( null != zeroCheck ); zeroCheck = null; sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a)"); final java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocateDirect(10); buf.put((byte)0x31)/*note that we'll skip this one*/ .put((byte)0x32) .put((byte)0x33) .put((byte)0x34) .put((byte)0x35)/*we'll skip this one too*/; final int expectTotal = buf.get(1) + buf.get(2) + buf.get(3); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); affirm( SQLITE_ERROR == sqlite3_bind_blob(stmt, 1, buf, -1, 0), "Buffer offset may not be negative." ); affirm( 0 == sqlite3_bind_blob(stmt, 1, buf, 1, 3) ); affirm( SQLITE_DONE == sqlite3_step(stmt) ); sqlite3_finalize(stmt); stmt = prepare(db, "SELECT a FROM t;"); int total = 0; affirm( SQLITE_ROW == sqlite3_step(stmt) ); byte blob[] = sqlite3_column_blob(stmt, 0); java.nio.ByteBuffer nioBlob = sqlite3_column_nio_buffer(stmt, 0); affirm(3 == blob.length); affirm(blob.length == nioBlob.capacity()); affirm(blob.length == nioBlob.limit()); int i = 0; for(byte b : blob){ affirm( i<=3 ); affirm(b == buf.get(1 + i)); affirm(b == nioBlob.get(i)); ++i; total += b; } affirm( SQLITE_DONE == sqlite3_step(stmt) ); sqlite3_finalize(stmt); affirm(total == expectTotal); SQLFunction func = new ScalarFunction(){ public void xFunc(sqlite3_context cx, sqlite3_value[] args){ sqlite3_result_blob(cx, buf, 1, 3); } }; affirm( 0 == sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func) ); stmt = prepare(db, "SELECT myfunc()"); affirm( SQLITE_ROW == sqlite3_step(stmt) ); blob = sqlite3_column_blob(stmt, 0); affirm(3 == blob.length); i = 0; total = 0; for(byte b : blob){ affirm( i<=3 ); affirm(b == buf.get(1 + i++)); total += b; } affirm( SQLITE_DONE == sqlite3_step(stmt) ); sqlite3_finalize(stmt); affirm(total == expectTotal); sqlite3_close_v2(db); } private void testSql(){ sqlite3 db = createNewDb(); sqlite3_stmt stmt = prepare(db, "SELECT 1"); affirm( "SELECT 1".equals(sqlite3_sql(stmt)) ); sqlite3_finalize(stmt); stmt = prepare(db, "SELECT ?"); |
︙ | ︙ | |||
589 590 591 592 593 594 595 | public void xDestroy() { // Just demonstrates that xDestroy is called. ++xDestroyCalled.value; } }; final CollationNeededCallback collLoader = new CollationNeededCallback(){ @Override | | | | 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | public void xDestroy() { // Just demonstrates that xDestroy is called. ++xDestroyCalled.value; } }; final CollationNeededCallback collLoader = new CollationNeededCallback(){ @Override public void call(sqlite3 dbArg, int eTextRep, String collationName){ affirm(dbArg == db/* as opposed to a temporary object*/); sqlite3_create_collation(dbArg, "reversi", eTextRep, myCollation); } }; int rc = sqlite3_collation_needed(db, collLoader); affirm( 0 == rc ); rc = sqlite3_collation_needed(db, collLoader); affirm( 0 == rc /* Installing the same object again is a no-op */); sqlite3_stmt stmt = prepare(db, "SELECT a FROM t ORDER BY a COLLATE reversi"); |
︙ | ︙ | |||
799 800 801 802 803 804 805 806 807 | sqlite3_stmt stmt = prepare(db, "select myfunc(?)"); affirm( 0 != stmt.getNativePointer() ); affirm( testResult.value == db ); rc = sqlite3_bind_java_object(stmt, 1, boundObj); affirm( 0==rc ); int n = 0; if( SQLITE_ROW == sqlite3_step(stmt) ){ final sqlite3_value v = sqlite3_column_value(stmt, 0); affirm( testResult.value == sqlite3_value_java_object(v) ); | > > > > | | | | > > > > > > > > > > > > > | 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 | sqlite3_stmt stmt = prepare(db, "select myfunc(?)"); affirm( 0 != stmt.getNativePointer() ); affirm( testResult.value == db ); rc = sqlite3_bind_java_object(stmt, 1, boundObj); affirm( 0==rc ); int n = 0; if( SQLITE_ROW == sqlite3_step(stmt) ){ affirm( testResult.value == sqlite3_column_java_object(stmt, 0) ); affirm( testResult.value == sqlite3_column_java_object(stmt, 0, sqlite3.class) ); affirm( null == sqlite3_column_java_object(stmt, 0, sqlite3_stmt.class) ); affirm( null == sqlite3_column_java_object(stmt,1) ); final sqlite3_value v = sqlite3_column_value(stmt, 0); affirm( testResult.value == sqlite3_value_java_object(v) ); affirm( testResult.value == sqlite3_value_java_object(v, sqlite3.class) ); affirm( testResult.value == sqlite3_value_java_object(v, testResult.value.getClass()) ); affirm( testResult.value == sqlite3_value_java_object(v, Object.class) ); affirm( null == sqlite3_value_java_object(v, String.class) ); ++n; } sqlite3_finalize(stmt); affirm( 1 == n ); affirm( 0==sqlite3_db_release_memory(db) ); sqlite3_close_v2(db); } private void testUdfAggregate(){ final sqlite3 db = createNewDb(); final ValueHolder<Boolean> xFinalNull = // To confirm that xFinal() is called with no aggregate state // when the corresponding result set is empty. new ValueHolder<>(false); final ValueHolder<sqlite3_value[]> neverEverDoThisInClientCode = new ValueHolder<>(null); final ValueHolder<sqlite3_context> neverEverDoThisInClientCode2 = new ValueHolder<>(null); SQLFunction func = new AggregateFunction<Integer>(){ @Override public void xStep(sqlite3_context cx, sqlite3_value[] args){ if( null==neverEverDoThisInClientCode.value ){ /* !!!NEVER!!! hold a reference to an sqlite3_value or sqlite3_context object like this in client code! They are ONLY legal for the duration of their single call. We do it here ONLY to test that the defenses against clients doing this are working. */ neverEverDoThisInClientCode.value = args; } final ValueHolder<Integer> agg = this.getAggregateState(cx, 0); agg.value += sqlite3_value_int(args[0]); affirm( agg == this.getAggregateState(cx, 0) ); } @Override public void xFinal(sqlite3_context cx){ if( null==neverEverDoThisInClientCode2.value ){ neverEverDoThisInClientCode2.value = cx; } final Integer v = this.takeAggregateState(cx); if(null == v){ xFinalNull.value = true; sqlite3_result_null(cx); }else{ sqlite3_result_int(cx, v); } |
︙ | ︙ | |||
853 854 855 856 857 858 859 860 861 862 863 864 865 866 | affirm( 6 == v ); int v2 = sqlite3_column_int(stmt, 1); affirm( 30+v == v2 ); ++n; } affirm( 1==n ); affirm(!xFinalNull.value); sqlite3_reset(stmt); affirm( 1==sqlite3_stmt_status(stmt, SQLITE_STMTSTATUS_RUN, false) ); // Ensure that the accumulator is reset on subsequent calls... n = 0; if( SQLITE_ROW == sqlite3_step(stmt) ){ final int v = sqlite3_column_int(stmt, 0); affirm( 6 == v ); | > > > > | 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | affirm( 6 == v ); int v2 = sqlite3_column_int(stmt, 1); affirm( 30+v == v2 ); ++n; } affirm( 1==n ); affirm(!xFinalNull.value); affirm( null!=neverEverDoThisInClientCode.value ); affirm( null!=neverEverDoThisInClientCode2.value ); affirm( 0<neverEverDoThisInClientCode.value.length ); affirm( 0==neverEverDoThisInClientCode2.value.getNativePointer() ); sqlite3_reset(stmt); affirm( 1==sqlite3_stmt_status(stmt, SQLITE_STMTSTATUS_RUN, false) ); // Ensure that the accumulator is reset on subsequent calls... n = 0; if( SQLITE_ROW == sqlite3_step(stmt) ){ final int v = sqlite3_column_int(stmt, 0); affirm( 6 == v ); |
︙ | ︙ | |||
921 922 923 924 925 926 927 | "('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)" }); final sqlite3_stmt stmt = prepare(db, "SELECT x, winsumint(y) OVER ("+ "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+ ") AS sum_y "+ "FROM twin ORDER BY x;"); | < | 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | "('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)" }); final sqlite3_stmt stmt = prepare(db, "SELECT x, winsumint(y) OVER ("+ "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+ ") AS sum_y "+ "FROM twin ORDER BY x;"); int n = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ final String s = sqlite3_column_text16(stmt, 0); final int i = sqlite3_column_int(stmt, 1); switch(++n){ case 1: affirm( "a".equals(s) && 9==i ); break; case 2: affirm( "b".equals(s) && 12==i ); break; |
︙ | ︙ | |||
1024 1025 1026 1027 1028 1029 1030 | sqlite3_close_v2(db); affirm( 7 == counter.value ); } @SingleThreadOnly /* because threads inherently break this test */ private static void testBusy(){ final String dbName = "_busy-handler.db"; | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > | > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > | | 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 | sqlite3_close_v2(db); affirm( 7 == counter.value ); } @SingleThreadOnly /* because threads inherently break this test */ private static void testBusy(){ final String dbName = "_busy-handler.db"; try{ final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); int rc = sqlite3_open(dbName, outDb); ++metrics.dbOpen; affirm( 0 == rc ); final sqlite3 db1 = outDb.get(); execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)"); rc = sqlite3_open(dbName, outDb); ++metrics.dbOpen; affirm( 0 == rc ); affirm( outDb.get() != db1 ); final sqlite3 db2 = outDb.get(); affirm( "main".equals( sqlite3_db_name(db1, 0) ) ); rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo"); affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) ); affirm( "foo".equals( sqlite3_db_name(db1, 0) ) ); affirm( SQLITE_MISUSE == sqlite3_db_config(db1, 0, 0, null) ); final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0); BusyHandlerCallback handler = new BusyHandlerCallback(){ @Override public int call(int n){ //outln("busy handler #"+n); return n > 2 ? 0 : ++xBusyCalled.value; } }; rc = sqlite3_busy_handler(db2, handler); affirm(0 == rc); // Force a locked condition... execSql(db1, "BEGIN EXCLUSIVE"); rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt); affirm( SQLITE_BUSY == rc); affirm( null == outStmt.get() ); affirm( 3 == xBusyCalled.value ); sqlite3_close_v2(db1); sqlite3_close_v2(db2); }finally{ try{(new java.io.File(dbName)).delete();} catch(Exception e){/* ignore */} } } private void testProgress(){ final sqlite3 db = createNewDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); sqlite3_progress_handler(db, 1, new ProgressHandlerCallback(){ |
︙ | ︙ | |||
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 | execSql(db, "SELECT 1; SELECT 2;"); affirm( nOld == counter.value ); sqlite3_close_v2(db); } private void testCommitHook(){ final sqlite3 db = createNewDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); final ValueHolder<Integer> hookResult = new ValueHolder<>(0); final CommitHookCallback theHook = new CommitHookCallback(){ @Override public int call(){ ++counter.value; return hookResult.value; } | > | 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 | execSql(db, "SELECT 1; SELECT 2;"); affirm( nOld == counter.value ); sqlite3_close_v2(db); } private void testCommitHook(){ final sqlite3 db = createNewDb(); sqlite3_extended_result_codes(db, true); final ValueHolder<Integer> counter = new ValueHolder<>(0); final ValueHolder<Integer> hookResult = new ValueHolder<>(0); final CommitHookCallback theHook = new CommitHookCallback(){ @Override public int call(){ ++counter.value; return hookResult.value; } |
︙ | ︙ | |||
1131 1132 1133 1134 1135 1136 1137 | affirm( 4 == counter.value ); oldHook = sqlite3_commit_hook(db, theHook); affirm( newHook == oldHook ); execSql(db, "BEGIN; update t set a='i' where a='h'; COMMIT;"); affirm( 5 == counter.value ); hookResult.value = SQLITE_ERROR; int rc = execSql(db, false, "BEGIN; update t set a='j' where a='i'; COMMIT;"); | | | 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 | affirm( 4 == counter.value ); oldHook = sqlite3_commit_hook(db, theHook); affirm( newHook == oldHook ); execSql(db, "BEGIN; update t set a='i' where a='h'; COMMIT;"); affirm( 5 == counter.value ); hookResult.value = SQLITE_ERROR; int rc = execSql(db, false, "BEGIN; update t set a='j' where a='i'; COMMIT;"); affirm( SQLITE_CONSTRAINT_COMMITHOOK == rc ); affirm( 6 == counter.value ); sqlite3_close_v2(db); } private void testUpdateHook(){ final sqlite3 db = createNewDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); |
︙ | ︙ | |||
1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 | execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); sqlite3_set_authorizer(db, auth); execSql(db, "UPDATE t SET a=1"); affirm( 1 == counter.value ); authRc.value = SQLITE_DENY; int rc = execSql(db, false, "UPDATE t SET a=2"); affirm( SQLITE_AUTH==rc ); // TODO: expand these tests considerably sqlite3_close(db); } @SingleThreadOnly /* because multiple threads legitimately make these results unpredictable */ private synchronized void testAutoExtension(){ | > > > | 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 | execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); sqlite3_set_authorizer(db, auth); execSql(db, "UPDATE t SET a=1"); affirm( 1 == counter.value ); authRc.value = SQLITE_DENY; int rc = execSql(db, false, "UPDATE t SET a=2"); affirm( SQLITE_AUTH==rc ); sqlite3_set_authorizer(db, null); rc = execSql(db, false, "UPDATE t SET a=2"); affirm( 0==rc ); // TODO: expand these tests considerably sqlite3_close(db); } @SingleThreadOnly /* because multiple threads legitimately make these results unpredictable */ private synchronized void testAutoExtension(){ |
︙ | ︙ | |||
1411 1412 1413 1414 1415 1416 1417 | } affirm( err!=null ); affirm( err.getMessage().indexOf(toss.value)>0 ); toss.value = null; val.value = 0; final AutoExtensionCallback ax2 = new AutoExtensionCallback(){ | | | 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 | } affirm( err!=null ); affirm( err.getMessage().indexOf(toss.value)>0 ); toss.value = null; val.value = 0; final AutoExtensionCallback ax2 = new AutoExtensionCallback(){ @Override public int call(sqlite3 db){ ++val.value; return 0; } }; rc = sqlite3_auto_extension( ax2 ); affirm( 0 == rc ); sqlite3_close(createNewDb()); |
︙ | ︙ | |||
1622 1623 1624 1625 1626 1627 1628 | sqlite3_stmt stmt = prepare(db,"SELECT length(a), a FROM t ORDER BY a"); affirm( SQLITE_ROW == sqlite3_step(stmt) ); affirm( 3 == sqlite3_column_int(stmt,0) ); affirm( "def".equals(sqlite3_column_text16(stmt,1)) ); sqlite3_finalize(stmt); b = sqlite3_blob_open(db, "main", "t", "a", | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 1877 1878 1879 1880 1881 | sqlite3_stmt stmt = prepare(db,"SELECT length(a), a FROM t ORDER BY a"); affirm( SQLITE_ROW == sqlite3_step(stmt) ); affirm( 3 == sqlite3_column_int(stmt,0) ); affirm( "def".equals(sqlite3_column_text16(stmt,1)) ); sqlite3_finalize(stmt); b = sqlite3_blob_open(db, "main", "t", "a", sqlite3_last_insert_rowid(db), 0); affirm( null!=b ); rc = sqlite3_blob_reopen(b, 2); affirm( 0==rc ); final byte[] tgt = new byte[3]; rc = sqlite3_blob_read(b, tgt, 0); affirm( 0==rc ); affirm( 100==tgt[0] && 101==tgt[1] && 102==tgt[2], "DEF" ); rc = sqlite3_blob_close(b); affirm( 0==rc ); if( !sqlite3_jni_supports_nio() ){ outln("WARNING: skipping tests for ByteBuffer-using sqlite3_blob APIs ", "because this platform lacks that support."); sqlite3_close_v2(db); return; } /* Sanity checks for the java.nio.ByteBuffer-taking overloads of sqlite3_blob_read/write(). */ execSql(db, "UPDATE t SET a=zeroblob(10)"); b = sqlite3_blob_open(db, "main", "t", "a", 1, 1); affirm( null!=b ); java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocateDirect(10); for( byte i = 0; i < 10; ++i ){ bb.put((int)i, (byte)(48+i & 0xff)); } rc = sqlite3_blob_write(b, 1, bb, 1, 10); affirm( rc==SQLITE_ERROR, "b length < (srcOffset + bb length)" ); rc = sqlite3_blob_write(b, -1, bb); affirm( rc==SQLITE_ERROR, "Target offset may not be negative" ); rc = sqlite3_blob_write(b, 0, bb, -1, -1); affirm( rc==SQLITE_ERROR, "Source offset may not be negative" ); rc = sqlite3_blob_write(b, 1, bb, 1, 8); affirm( rc==0 ); // b's contents: 0 49 50 51 52 53 54 55 56 0 // ascii: 0 '1' '2' '3' '4' '5' '6' '7' '8' 0 byte br[] = new byte[10]; java.nio.ByteBuffer bbr = java.nio.ByteBuffer.allocateDirect(bb.limit()); rc = sqlite3_blob_read( b, br, 0 ); affirm( rc==0 ); rc = sqlite3_blob_read( b, bbr ); affirm( rc==0 ); java.nio.ByteBuffer bbr2 = sqlite3_blob_read_nio_buffer(b, 0, 12); affirm( null==bbr2, "Read size is too big"); bbr2 = sqlite3_blob_read_nio_buffer(b, -1, 3); affirm( null==bbr2, "Source offset is negative"); bbr2 = sqlite3_blob_read_nio_buffer(b, 5, 6); affirm( null==bbr2, "Read pos+size is too big"); bbr2 = sqlite3_blob_read_nio_buffer(b, 4, 7); affirm( null==bbr2, "Read pos+size is too big"); bbr2 = sqlite3_blob_read_nio_buffer(b, 4, 6); affirm( null!=bbr2 ); java.nio.ByteBuffer bbr3 = java.nio.ByteBuffer.allocateDirect(2 * bb.limit()); java.nio.ByteBuffer bbr4 = java.nio.ByteBuffer.allocateDirect(5); rc = sqlite3_blob_read( b, bbr3 ); affirm( rc==0 ); rc = sqlite3_blob_read( b, bbr4 ); affirm( rc==0 ); affirm( sqlite3_blob_bytes(b)==bbr3.limit() ); affirm( 5==bbr4.limit() ); sqlite3_blob_close(b); affirm( 0==br[0] ); affirm( 0==br[9] ); affirm( 0==bbr.get(0) ); affirm( 0==bbr.get(9) ); affirm( bbr2.limit() == 6 ); affirm( 0==bbr3.get(0) ); { Exception ex = null; try{ bbr3.get(11); } catch(Exception e){ex = e;} affirm( ex instanceof IndexOutOfBoundsException, "bbr3.limit() was reset by read()" ); ex = null; } affirm( 0==bbr4.get(0) ); for( int i = 1; i < 9; ++i ){ affirm( br[i] == 48 + i ); affirm( br[i] == bbr.get(i) ); affirm( br[i] == bbr3.get(i) ); if( i>3 ){ affirm( br[i] == bbr2.get(i-4) ); } if( i < bbr4.limit() ){ affirm( br[i] == bbr4.get(i) ); } } sqlite3_close_v2(db); } private void testPrepareMulti(){ final sqlite3 db = createNewDb(); final String[] sql = { "create table t(","a)", "; insert into t(a) values(1),(2),(3);", "select a from t;" }; final List<sqlite3_stmt> liStmt = new ArrayList<sqlite3_stmt>(); final PrepareMultiCallback proxy = new PrepareMultiCallback.StepAll(); final ValueHolder<String> toss = new ValueHolder<>(null); PrepareMultiCallback m = new PrepareMultiCallback() { @Override public int call(sqlite3_stmt st){ liStmt.add(st); if( null!=toss.value ){ throw new RuntimeException(toss.value); } return proxy.call(st); } }; int rc = sqlite3_prepare_multi(db, sql, m); affirm( 0==rc ); affirm( liStmt.size() == 3 ); for( sqlite3_stmt st : liStmt ){ sqlite3_finalize(st); } toss.value = "This is an exception."; rc = sqlite3_prepare_multi(db, "SELECT 1", m); affirm( SQLITE_ERROR==rc ); affirm( sqlite3_errmsg(db).indexOf(toss.value)>0 ); sqlite3_close_v2(db); } /* Copy/paste/rename this to add new tests. */ private void _testTemplate(){ final sqlite3 db = createNewDb(); sqlite3_stmt stmt = prepare(db,"SELECT 1"); |
︙ | ︙ | |||
1694 1695 1696 1697 1698 1699 1700 | if(false) showCompileOption(); List<java.lang.reflect.Method> mlist = testMethods; affirm( null!=mlist ); if( shuffle ){ mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) ); java.util.Collections.shuffle(mlist); } | | | 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 | if(false) showCompileOption(); List<java.lang.reflect.Method> mlist = testMethods; affirm( null!=mlist ); if( shuffle ){ mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) ); java.util.Collections.shuffle(mlist); } if( (!fromThread && listRunTests>0) || listRunTests>1 ){ synchronized(this.getClass()){ if( !fromThread ){ out("Initial test"," list: "); for(java.lang.reflect.Method m : testMethods){ out(m.getName()+" "); } outln(); |
︙ | ︙ | |||
1755 1756 1757 1758 1759 1760 1761 1762 1763 | -r|-repeat N: repeats the tests in a loop N times, each one consisting of the -thread value's threads. -shuffle: randomizes the order of most of the test functions. -naps: sleep small random intervals between tests in order to add some chaos for cross-thread contention. -list-tests: outputs the list of tests being run, minus some | > | > > | 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 | -r|-repeat N: repeats the tests in a loop N times, each one consisting of the -thread value's threads. -shuffle: randomizes the order of most of the test functions. -naps: sleep small random intervals between tests in order to add some chaos for cross-thread contention. -list-tests: outputs the list of tests being run, minus some which are hard-coded. In multi-threaded mode, use this twice to to emit the list run by each thread (which may differ from the initial list, in particular if -shuffle is used). -fail: forces an exception to be thrown during the test run. Use with -shuffle to make its appearance unpredictable. -v: emit some developer-mode info at the end. */ public static void main(String[] args) throws Exception { |
︙ | ︙ | |||
1786 1787 1788 1789 1790 1791 1792 | }else if(arg.equals("t") || arg.equals("thread")){ nThread = Integer.parseInt(args[i++]); }else if(arg.equals("r") || arg.equals("repeat")){ nRepeat = Integer.parseInt(args[i++]); }else if(arg.equals("shuffle")){ shuffle = true; }else if(arg.equals("list-tests")){ | | | | | 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 | }else if(arg.equals("t") || arg.equals("thread")){ nThread = Integer.parseInt(args[i++]); }else if(arg.equals("r") || arg.equals("repeat")){ nRepeat = Integer.parseInt(args[i++]); }else if(arg.equals("shuffle")){ shuffle = true; }else if(arg.equals("list-tests")){ ++listRunTests; }else if(arg.equals("fail")){ forceFail = true; }else if(arg.equals("sqllog")){ sqlLog = true; }else if(arg.equals("configlog")){ configLog = true; }else if(arg.equals("naps")){ takeNaps = true; }else if(arg.equals("q") || arg.equals("quiet")){ squelchTestOutput = true; }else{ throw new IllegalArgumentException("Unhandled flag:"+arg); } } } if( sqlLog ){ if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){ final ConfigSqlLogCallback log = new ConfigSqlLogCallback() { @Override public void call(sqlite3 db, String msg, int op){ switch(op){ case 0: outln("Opening db: ",db); break; case 1: outln("SQL ",db,": ",msg); break; case 2: outln("Closing db: ",db); break; } } }; int rc = sqlite3_config( log ); affirm( 0==rc ); rc = sqlite3_config( (ConfigSqlLogCallback)null ); affirm( 0==rc ); rc = sqlite3_config( log ); affirm( 0==rc ); }else{ outln("WARNING: -sqllog is not active because library was built ", "without SQLITE_ENABLE_SQLLOG."); } |
︙ | ︙ | |||
1854 1855 1856 1857 1858 1859 1860 1861 1862 | int nSkipped = 0; for(final java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){ final String name = m.getName(); if( name.equals("testFail") ){ if( forceFail ){ testMethods.add(m); } }else if( !m.isAnnotationPresent( ManualTest.class ) ){ if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){ | > > > > > > | < < < < | 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 | int nSkipped = 0; for(final java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){ final String name = m.getName(); if( name.equals("testFail") ){ if( forceFail ){ testMethods.add(m); } }else if( m.isAnnotationPresent( RequiresJniNio.class ) && !sqlite3_jni_supports_nio() ){ outln("Skipping test for lack of JNI java.nio.ByteBuffer support: ", name,"()\n"); ++nSkipped; }else if( !m.isAnnotationPresent( ManualTest.class ) ){ if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){ out("Skipping test in multi-thread mode: ",name,"()\n"); ++nSkipped; }else if( name.startsWith("test") ){ testMethods.add(m); } } } } final long timeStart = System.currentTimeMillis(); int nLoop = 0; switch( sqlite3_threadsafe() ){ /* Sanity checking */ case 0: affirm( SQLITE_ERROR==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), |
︙ | ︙ | |||
1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 | default: affirm( false, "Unhandled SQLITE_THREADSAFE value." ); } outln("libversion_number: ", sqlite3_libversion_number(),"\n", sqlite3_libversion(),"\n",SQLITE_SOURCE_ID,"\n", "SQLITE_THREADSAFE=",sqlite3_threadsafe()); final boolean showLoopCount = (nRepeat>1 && nThread>1); if( showLoopCount ){ outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each."); } if( takeNaps ) outln("Napping between tests is enabled."); for( int n = 0; n < nRepeat; ++n ){ ++nLoop; | > | 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 | default: affirm( false, "Unhandled SQLITE_THREADSAFE value." ); } outln("libversion_number: ", sqlite3_libversion_number(),"\n", sqlite3_libversion(),"\n",SQLITE_SOURCE_ID,"\n", "SQLITE_THREADSAFE=",sqlite3_threadsafe()); outln("JVM NIO support? ",sqlite3_jni_supports_nio() ? "YES" : "NO"); final boolean showLoopCount = (nRepeat>1 && nThread>1); if( showLoopCount ){ outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each."); } if( takeNaps ) outln("Napping between tests is enabled."); for( int n = 0; n < nRepeat; ++n ){ ++nLoop; |
︙ | ︙ |
Changes to ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java.
︙ | ︙ | |||
15 16 17 18 19 20 21 | /** Callback for use with {@link CApi#sqlite3_update_hook}. */ public interface UpdateHookCallback extends CallbackProxy { /** Must function as described for the C-level sqlite3_update_hook() | | > | 15 16 17 18 19 20 21 22 23 24 25 26 | /** Callback for use with {@link CApi#sqlite3_update_hook}. */ public interface UpdateHookCallback extends CallbackProxy { /** Must function as described for the C-level sqlite3_update_hook() callback. If it throws, the exception is translated into a db-level error. */ void call(int opId, String dbName, String tableName, long rowId); } |
Changes to ext/jni/src/org/sqlite/jni/capi/ValueHolder.java.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2023-10-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. ** ************************************************************************* | > | | > | 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 | /* ** 2023-10-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. ** ************************************************************************* ** This file contains the ValueHolder utility class for the sqlite3 ** JNI bindings. */ package org.sqlite.jni.capi; /** A helper class which simply holds a single value. Its primary use is for communicating values out of anonymous classes, as doing so requires a "final" reference, as well as communicating aggregate SQL function state across calls to such functions. */ public class ValueHolder<T> { public T value; public ValueHolder(){} public ValueHolder(T v){value = v;} } |
Changes to ext/jni/src/org/sqlite/jni/capi/sqlite3.java.
︙ | ︙ | |||
34 35 36 37 38 39 40 | return sqlite3.class.getSimpleName() +"@"+String.format("0x%08x",ptr) +"["+((null == fn) ? "<unnamed>" : fn)+"]" ; } @Override public void close(){ | | | 34 35 36 37 38 39 40 41 42 43 | return sqlite3.class.getSimpleName() +"@"+String.format("0x%08x",ptr) +"["+((null == fn) ? "<unnamed>" : fn)+"]" ; } @Override public void close(){ CApi.sqlite3_close_v2(this); } } |
Changes to ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java.
︙ | ︙ | |||
21 22 23 24 25 26 27 | */ public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob> implements AutoCloseable { // Only invoked from JNI. private sqlite3_blob(){} @Override public void close(){ | | < | 21 22 23 24 25 26 27 28 29 30 | */ public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob> implements AutoCloseable { // Only invoked from JNI. private sqlite3_blob(){} @Override public void close(){ CApi.sqlite3_blob_close(this); } } |
Changes to ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java.
︙ | ︙ | |||
21 22 23 24 25 26 27 | */ public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt> implements AutoCloseable { // Only invoked from JNI. private sqlite3_stmt(){} @Override public void close(){ | | | 21 22 23 24 25 26 27 28 29 30 | */ public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt> implements AutoCloseable { // Only invoked from JNI. private sqlite3_stmt(){} @Override public void close(){ CApi.sqlite3_finalize(this); } } |
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/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; | < < < < | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; /** EXPERIMENTAL/INCOMPLETE/UNTESTED A SqlFunction implementation for aggregate functions. The T type represents the type of data accumulated by this aggregate while it works. e.g. a SUM()-like UDF might use Integer or Long and a |
︙ | ︙ | |||
46 47 48 49 50 51 52 53 54 | public abstract void xFinal(SqlFunction.Arguments args); /** Optionally override to be notified when the UDF is finalized by SQLite. */ public void xDestroy() {} /** Per-invocation state for the UDF. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | 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 | public abstract void xFinal(SqlFunction.Arguments args); /** Optionally override to be notified when the UDF is finalized by SQLite. */ public void xDestroy() {} /** PerContextState assists aggregate and window functions in managing their accumulator state across calls to the UDF's callbacks. <p>T must be of a type which can be legally stored as a value in java.util.HashMap<KeyType,T>. <p>If a given aggregate or window function is called multiple times in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)..., then the clients need some way of knowing which call is which so that they can map their state between their various UDF callbacks and reset it via xFinal(). This class takes care of such mappings. <p>This class works by mapping sqlite3_context.getAggregateContext() to a single piece of state, of a client-defined type (the T part of this class), which persists across a "matching set" of the UDF's callbacks. <p>This class is a helper providing commonly-needed functionality - it is not required for use with aggregate or window functions. Client UDFs are free to perform such mappings using custom approaches. The provided {@link AggregateFunction} and {@link WindowFunction} classes use this. */ public static final class PerContextState<T> { private final java.util.Map<Long,ValueHolder<T>> map = new java.util.HashMap<>(); /** Should be called from a UDF's xStep(), xValue(), and xInverse() methods, passing it that method's first argument and an initial value for the persistent state. If there is currently no mapping for the given context within the map, one is created using the given initial value, else the existing one is used and the 2nd argument is ignored. It returns a ValueHolder<T> which can be used to modify that state directly without requiring that the client update the underlying map's entry. <p>The caller is obligated to eventually call takeAggregateState() to clear the mapping. */ public ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){ final Long key = args.getContext().getAggregateContext(true); ValueHolder<T> rc = null==key ? null : map.get(key); if( null==rc ){ map.put(key, rc = new ValueHolder<>(initialValue)); } return rc; } /** Should be called from a UDF's xFinal() method and passed that method's first argument. This function removes the value associated with with the arguments' aggregate context from the map and returns it, returning null if no other UDF method has been called to set up such a mapping. The latter condition will be the case if a UDF is used in a statement which has no result rows. */ public T takeAggregateState(SqlFunction.Arguments args){ final ValueHolder<T> h = map.remove(args.getContext().getAggregateContext(false)); return null==h ? null : h.value; } } /** Per-invocation state for the UDF. */ private final PerContextState<T> map = new PerContextState<>(); /** To be called from the implementation's xStep() method, as well as the xValue() and xInverse() methods of the {@link WindowFunction} subclass, to fetch the current per-call UDF state. On the first call to this method for any given sqlite3_context argument, the context is set to the given initial value. On all other |
︙ | ︙ |
Changes to ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /** Base marker interface for SQLite's three types of User-Defined SQL Functions (UDFs): Scalar, Aggregate, and Window functions. */ public interface SqlFunction { /** The Arguments type is an abstraction on top of the lower-level UDF function argument types. It provides _most_ of the functionality of the lower-level interface, insofar as possible without "leaking" those types into this API. */ public final static class Arguments implements Iterable<SqlFunction.Arguments.Arg>{ | > > > > > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /** Base marker interface for SQLite's three types of User-Defined SQL Functions (UDFs): Scalar, Aggregate, and Window functions. */ public interface SqlFunction { public static final int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC; public static final int INNOCUOUS = CApi.SQLITE_INNOCUOUS; public static final int DIRECTONLY = CApi.SQLITE_DIRECTONLY; public static final int SUBTYPE = CApi.SQLITE_SUBTYPE; public static final int RESULT_SUBTYPE = CApi.SQLITE_RESULT_SUBTYPE; public static final int UTF8 = CApi.SQLITE_UTF8; public static final int UTF16 = CApi.SQLITE_UTF16; /** The Arguments type is an abstraction on top of the lower-level UDF function argument types. It provides _most_ of the functionality of the lower-level interface, insofar as possible without "leaking" those types into this API. */ public final static class Arguments implements Iterable<SqlFunction.Arguments.Arg>{ |
︙ | ︙ | |||
41 42 43 44 45 46 47 | AggregateAdapter. Passing null for the args is equivalent to passing a length-0 array. */ Arguments(sqlite3_context cx, sqlite3_value args[]){ this.cx = cx; | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | AggregateAdapter. Passing null for the args is equivalent to passing a length-0 array. */ Arguments(sqlite3_context cx, sqlite3_value args[]){ this.cx = cx; this.args = args==null ? new sqlite3_value[0] : args; this.length = this.args.length; } /** Returns the sqlite3_value at the given argument index or throws an IllegalArgumentException exception if ndx is out of range. */ private sqlite3_value valueAt(int ndx){ if(ndx<0 || ndx>=args.length){ throw new IllegalArgumentException( "SQL function argument index "+ndx+" is out of range." ); } return args[ndx]; } //! Returns the underlying sqlite3_context for these arguments. sqlite3_context getContext(){return cx;} /** Returns the Sqlite (db) object associated with this UDF call, or null if the UDF is somehow called without such an object or the db has been closed in an untimely manner (e.g. closed by a UDF call). */ public Sqlite getDb(){ return Sqlite.fromNative( CApi.sqlite3_context_db_handle(cx) ); } public int getArgCount(){ return args.length; } public int getInt(int argNdx){return CApi.sqlite3_value_int(valueAt(argNdx));} public long getInt64(int argNdx){return CApi.sqlite3_value_int64(valueAt(argNdx));} public double getDouble(int argNdx){return CApi.sqlite3_value_double(valueAt(argNdx));} public byte[] getBlob(int argNdx){return CApi.sqlite3_value_blob(valueAt(argNdx));} public byte[] getText(int argNdx){return CApi.sqlite3_value_text(valueAt(argNdx));} public String getText16(int argNdx){return CApi.sqlite3_value_text16(valueAt(argNdx));} public int getBytes(int argNdx){return CApi.sqlite3_value_bytes(valueAt(argNdx));} public int getBytes16(int argNdx){return CApi.sqlite3_value_bytes16(valueAt(argNdx));} public Object getObject(int argNdx){return CApi.sqlite3_value_java_object(valueAt(argNdx));} public <T> T getObject(int argNdx, Class<T> type){ return CApi.sqlite3_value_java_object(valueAt(argNdx), type); } public int getType(int argNdx){return CApi.sqlite3_value_type(valueAt(argNdx));} public int getSubtype(int argNdx){return CApi.sqlite3_value_subtype(valueAt(argNdx));} public int getNumericType(int argNdx){return CApi.sqlite3_value_numeric_type(valueAt(argNdx));} public int getNoChange(int argNdx){return CApi.sqlite3_value_nochange(valueAt(argNdx));} public boolean getFromBind(int argNdx){return CApi.sqlite3_value_frombind(valueAt(argNdx));} public int getEncoding(int argNdx){return CApi.sqlite3_value_encoding(valueAt(argNdx));} public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); } public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); } public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); } public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);} public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);} public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);} public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);} public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);} public void resultNull(){CApi.sqlite3_result_null(cx);} /** Analog to sqlite3_result_value(), using the Value object at the given argument index. */ public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));} public void resultSubtype(int subtype){CApi.sqlite3_result_subtype(cx, subtype);} public void resultZeroBlob(long n){ // Throw on error? If n is too big, // sqlite3_result_error_toobig() is automatically called. CApi.sqlite3_result_zeroblob64(cx, n); } public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);} public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);} public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);} public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);} public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);} /** Callbacks should invoke this on OOM errors, instead of throwing OutOfMemoryError, because the latter cannot be propagated through the C API. */ public void resultNoMem(){CApi.sqlite3_result_error_nomem(cx);} /** Analog to sqlite3_set_auxdata() but throws if argNdx is out of range. */ public void setAuxData(int argNdx, Object o){ /* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html The value of the N parameter to these interfaces should be non-negative. Future enhancements may make use of negative N values to define new kinds of function caching behavior. */ valueAt(argNdx); CApi.sqlite3_set_auxdata(cx, argNdx, o); } /** Analog to sqlite3_get_auxdata() but throws if argNdx is out of range. */ public Object getAuxData(int argNdx){ valueAt(argNdx); return CApi.sqlite3_get_auxdata(cx, argNdx); } /** Represents a single SqlFunction argument. Primarily intended for use with the Arguments class's Iterable interface. */ public final static class Arg { private final Arguments a; private final int ndx; /* Only for use by the Arguments class. */ private Arg(Arguments a, int ndx){ |
︙ | ︙ | |||
68 69 70 71 72 73 74 | public double getDouble(){return a.getDouble(ndx);} public byte[] getBlob(){return a.getBlob(ndx);} public byte[] getText(){return a.getText(ndx);} public String getText16(){return a.getText16(ndx);} public int getBytes(){return a.getBytes(ndx);} public int getBytes16(){return a.getBytes16(ndx);} public Object getObject(){return a.getObject(ndx);} | | < < < < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 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 | public double getDouble(){return a.getDouble(ndx);} public byte[] getBlob(){return a.getBlob(ndx);} public byte[] getText(){return a.getText(ndx);} public String getText16(){return a.getText16(ndx);} public int getBytes(){return a.getBytes(ndx);} public int getBytes16(){return a.getBytes16(ndx);} public Object getObject(){return a.getObject(ndx);} public <T> T getObject(Class<T> type){ return a.getObject(ndx, type); } public int getType(){return a.getType(ndx);} public Object getAuxData(){return a.getAuxData(ndx);} public void setAuxData(Object o){a.setAuxData(ndx, o);} } @Override public java.util.Iterator<SqlFunction.Arguments.Arg> iterator(){ final Arg[] proxies = new Arg[args.length]; for( int i = 0; i < args.length; ++i ){ proxies[i] = new Arg(this, i); } return java.util.Arrays.stream(proxies).iterator(); } } /** Internal-use adapter for wrapping this package's ScalarFunction for use with the org.sqlite.jni.capi.ScalarFunction interface. */ static final class ScalarAdapter extends org.sqlite.jni.capi.ScalarFunction { private final ScalarFunction impl; ScalarAdapter(ScalarFunction impl){ this.impl = impl; } /** Proxies this.impl.xFunc(), adapting the call arguments to that function's signature. If the proxy throws, it's translated to sqlite_result_error() with the exception's message. |
︙ | ︙ | |||
257 258 259 260 261 262 263 | } } /** Internal-use adapter for wrapping this package's AggregateFunction for use with the org.sqlite.jni.capi.AggregateFunction interface. */ | > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } } /** Internal-use adapter for wrapping this package's AggregateFunction for use with the org.sqlite.jni.capi.AggregateFunction interface. */ static /*cannot be final without duplicating the whole body in WindowAdapter*/ class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction { private final AggregateFunction impl; AggregateAdapter(AggregateFunction impl){ this.impl = impl; } /** Proxies this.impl.xStep(), adapting the call arguments to that function's signature. If the proxied function throws, it is translated to sqlite_result_error() with the exception's message. */ public void xStep(sqlite3_context cx, sqlite3_value[] args){ try{ impl.xStep( new SqlFunction.Arguments(cx, args) ); }catch(Exception e){ CApi.sqlite3_result_error(cx, e); } } /** As for the xFinal() argument of the C API's sqlite3_create_function(). If the proxied function throws, it is translated into a sqlite3_result_error(). */ public void xFinal(sqlite3_context cx){ try{ impl.xFinal( new SqlFunction.Arguments(cx, null) ); }catch(Exception e){ CApi.sqlite3_result_error(cx, e); } } public void xDestroy(){ impl.xDestroy(); } } /** Internal-use adapter for wrapping this package's WindowFunction for use with the org.sqlite.jni.capi.WindowFunction interface. */ static final class WindowAdapter extends AggregateAdapter { private final WindowFunction impl; WindowAdapter(WindowFunction impl){ super(impl); this.impl = impl; } /** Proxies this.impl.xInverse(), adapting the call arguments to that function's signature. If the proxied function throws, it is translated to sqlite_result_error() with the exception's message. */ public void xInverse(sqlite3_context cx, sqlite3_value[] args){ try{ impl.xInverse( new SqlFunction.Arguments(cx, args) ); }catch(Exception e){ CApi.sqlite3_result_error(cx, e); } } /** As for the xValue() argument of the C API's sqlite3_create_window_function(). If the proxied function throws, it is translated into a sqlite3_result_error(). */ public void xValue(sqlite3_context cx){ try{ impl.xValue( new SqlFunction.Arguments(cx, null) ); }catch(Exception e){ CApi.sqlite3_result_error(cx, e); } } public void xDestroy(){ impl.xDestroy(); } } } |
Changes to ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; import java.nio.charset.StandardCharsets; | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > | > | > > | | > | > > > > > > > > > > > > > > > > | > > > | > > | > > | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > | > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 941 942 943 944 945 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 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 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 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 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 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 1496 1497 1498 1499 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 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 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 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 1877 1878 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 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 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 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; import java.nio.charset.StandardCharsets; import org.sqlite.jni.capi.CApi; import org.sqlite.jni.capi.sqlite3; import org.sqlite.jni.capi.sqlite3_stmt; import org.sqlite.jni.capi.sqlite3_backup; import org.sqlite.jni.capi.sqlite3_blob; import org.sqlite.jni.capi.OutputPointer; import java.nio.ByteBuffer; /** This class represents a database connection, analog to the C-side sqlite3 class but with added argument validation, exceptions, and similar "smoothing of sharp edges" to make the API safe to use from Java. It also acts as a namespace for other types for which individual instances are tied to a specific database connection. */ public final class Sqlite implements AutoCloseable { private sqlite3 db; private static final boolean JNI_SUPPORTS_NIO = CApi.sqlite3_jni_supports_nio(); // Result codes public static final int OK = CApi.SQLITE_OK; public static final int ERROR = CApi.SQLITE_ERROR; public static final int INTERNAL = CApi.SQLITE_INTERNAL; public static final int PERM = CApi.SQLITE_PERM; public static final int ABORT = CApi.SQLITE_ABORT; public static final int BUSY = CApi.SQLITE_BUSY; public static final int LOCKED = CApi.SQLITE_LOCKED; public static final int NOMEM = CApi.SQLITE_NOMEM; public static final int READONLY = CApi.SQLITE_READONLY; public static final int INTERRUPT = CApi.SQLITE_INTERRUPT; public static final int IOERR = CApi.SQLITE_IOERR; public static final int CORRUPT = CApi.SQLITE_CORRUPT; public static final int NOTFOUND = CApi.SQLITE_NOTFOUND; public static final int FULL = CApi.SQLITE_FULL; public static final int CANTOPEN = CApi.SQLITE_CANTOPEN; public static final int PROTOCOL = CApi.SQLITE_PROTOCOL; public static final int EMPTY = CApi.SQLITE_EMPTY; public static final int SCHEMA = CApi.SQLITE_SCHEMA; public static final int TOOBIG = CApi.SQLITE_TOOBIG; public static final int CONSTRAINT = CApi. SQLITE_CONSTRAINT; public static final int MISMATCH = CApi.SQLITE_MISMATCH; public static final int MISUSE = CApi.SQLITE_MISUSE; public static final int NOLFS = CApi.SQLITE_NOLFS; public static final int AUTH = CApi.SQLITE_AUTH; public static final int FORMAT = CApi.SQLITE_FORMAT; public static final int RANGE = CApi.SQLITE_RANGE; public static final int NOTADB = CApi.SQLITE_NOTADB; public static final int NOTICE = CApi.SQLITE_NOTICE; public static final int WARNING = CApi.SQLITE_WARNING; public static final int ROW = CApi.SQLITE_ROW; public static final int DONE = CApi.SQLITE_DONE; public static final int ERROR_MISSING_COLLSEQ = CApi.SQLITE_ERROR_MISSING_COLLSEQ; public static final int ERROR_RETRY = CApi.SQLITE_ERROR_RETRY; public static final int ERROR_SNAPSHOT = CApi.SQLITE_ERROR_SNAPSHOT; public static final int IOERR_READ = CApi.SQLITE_IOERR_READ; public static final int IOERR_SHORT_READ = CApi.SQLITE_IOERR_SHORT_READ; public static final int IOERR_WRITE = CApi.SQLITE_IOERR_WRITE; public static final int IOERR_FSYNC = CApi.SQLITE_IOERR_FSYNC; public static final int IOERR_DIR_FSYNC = CApi.SQLITE_IOERR_DIR_FSYNC; public static final int IOERR_TRUNCATE = CApi.SQLITE_IOERR_TRUNCATE; public static final int IOERR_FSTAT = CApi.SQLITE_IOERR_FSTAT; public static final int IOERR_UNLOCK = CApi.SQLITE_IOERR_UNLOCK; public static final int IOERR_RDLOCK = CApi.SQLITE_IOERR_RDLOCK; public static final int IOERR_DELETE = CApi.SQLITE_IOERR_DELETE; public static final int IOERR_BLOCKED = CApi.SQLITE_IOERR_BLOCKED; public static final int IOERR_NOMEM = CApi.SQLITE_IOERR_NOMEM; public static final int IOERR_ACCESS = CApi.SQLITE_IOERR_ACCESS; public static final int IOERR_CHECKRESERVEDLOCK = CApi.SQLITE_IOERR_CHECKRESERVEDLOCK; public static final int IOERR_LOCK = CApi.SQLITE_IOERR_LOCK; public static final int IOERR_CLOSE = CApi.SQLITE_IOERR_CLOSE; public static final int IOERR_DIR_CLOSE = CApi.SQLITE_IOERR_DIR_CLOSE; public static final int IOERR_SHMOPEN = CApi.SQLITE_IOERR_SHMOPEN; public static final int IOERR_SHMSIZE = CApi.SQLITE_IOERR_SHMSIZE; public static final int IOERR_SHMLOCK = CApi.SQLITE_IOERR_SHMLOCK; public static final int IOERR_SHMMAP = CApi.SQLITE_IOERR_SHMMAP; public static final int IOERR_SEEK = CApi.SQLITE_IOERR_SEEK; public static final int IOERR_DELETE_NOENT = CApi.SQLITE_IOERR_DELETE_NOENT; public static final int IOERR_MMAP = CApi.SQLITE_IOERR_MMAP; public static final int IOERR_GETTEMPPATH = CApi.SQLITE_IOERR_GETTEMPPATH; public static final int IOERR_CONVPATH = CApi.SQLITE_IOERR_CONVPATH; public static final int IOERR_VNODE = CApi.SQLITE_IOERR_VNODE; public static final int IOERR_AUTH = CApi.SQLITE_IOERR_AUTH; public static final int IOERR_BEGIN_ATOMIC = CApi.SQLITE_IOERR_BEGIN_ATOMIC; public static final int IOERR_COMMIT_ATOMIC = CApi.SQLITE_IOERR_COMMIT_ATOMIC; public static final int IOERR_ROLLBACK_ATOMIC = CApi.SQLITE_IOERR_ROLLBACK_ATOMIC; public static final int IOERR_DATA = CApi.SQLITE_IOERR_DATA; public static final int IOERR_CORRUPTFS = CApi.SQLITE_IOERR_CORRUPTFS; public static final int LOCKED_SHAREDCACHE = CApi.SQLITE_LOCKED_SHAREDCACHE; public static final int LOCKED_VTAB = CApi.SQLITE_LOCKED_VTAB; public static final int BUSY_RECOVERY = CApi.SQLITE_BUSY_RECOVERY; public static final int BUSY_SNAPSHOT = CApi.SQLITE_BUSY_SNAPSHOT; public static final int BUSY_TIMEOUT = CApi.SQLITE_BUSY_TIMEOUT; public static final int CANTOPEN_NOTEMPDIR = CApi.SQLITE_CANTOPEN_NOTEMPDIR; public static final int CANTOPEN_ISDIR = CApi.SQLITE_CANTOPEN_ISDIR; public static final int CANTOPEN_FULLPATH = CApi.SQLITE_CANTOPEN_FULLPATH; public static final int CANTOPEN_CONVPATH = CApi.SQLITE_CANTOPEN_CONVPATH; public static final int CANTOPEN_SYMLINK = CApi.SQLITE_CANTOPEN_SYMLINK; public static final int CORRUPT_VTAB = CApi.SQLITE_CORRUPT_VTAB; public static final int CORRUPT_SEQUENCE = CApi.SQLITE_CORRUPT_SEQUENCE; public static final int CORRUPT_INDEX = CApi.SQLITE_CORRUPT_INDEX; public static final int READONLY_RECOVERY = CApi.SQLITE_READONLY_RECOVERY; public static final int READONLY_CANTLOCK = CApi.SQLITE_READONLY_CANTLOCK; public static final int READONLY_ROLLBACK = CApi.SQLITE_READONLY_ROLLBACK; public static final int READONLY_DBMOVED = CApi.SQLITE_READONLY_DBMOVED; public static final int READONLY_CANTINIT = CApi.SQLITE_READONLY_CANTINIT; public static final int READONLY_DIRECTORY = CApi.SQLITE_READONLY_DIRECTORY; public static final int ABORT_ROLLBACK = CApi.SQLITE_ABORT_ROLLBACK; public static final int CONSTRAINT_CHECK = CApi.SQLITE_CONSTRAINT_CHECK; public static final int CONSTRAINT_COMMITHOOK = CApi.SQLITE_CONSTRAINT_COMMITHOOK; public static final int CONSTRAINT_FOREIGNKEY = CApi.SQLITE_CONSTRAINT_FOREIGNKEY; public static final int CONSTRAINT_FUNCTION = CApi.SQLITE_CONSTRAINT_FUNCTION; public static final int CONSTRAINT_NOTNULL = CApi.SQLITE_CONSTRAINT_NOTNULL; public static final int CONSTRAINT_PRIMARYKEY = CApi.SQLITE_CONSTRAINT_PRIMARYKEY; public static final int CONSTRAINT_TRIGGER = CApi.SQLITE_CONSTRAINT_TRIGGER; public static final int CONSTRAINT_UNIQUE = CApi.SQLITE_CONSTRAINT_UNIQUE; public static final int CONSTRAINT_VTAB = CApi.SQLITE_CONSTRAINT_VTAB; public static final int CONSTRAINT_ROWID = CApi.SQLITE_CONSTRAINT_ROWID; public static final int CONSTRAINT_PINNED = CApi.SQLITE_CONSTRAINT_PINNED; public static final int CONSTRAINT_DATATYPE = CApi.SQLITE_CONSTRAINT_DATATYPE; public static final int NOTICE_RECOVER_WAL = CApi.SQLITE_NOTICE_RECOVER_WAL; public static final int NOTICE_RECOVER_ROLLBACK = CApi.SQLITE_NOTICE_RECOVER_ROLLBACK; public static final int WARNING_AUTOINDEX = CApi.SQLITE_WARNING_AUTOINDEX; public static final int AUTH_USER = CApi.SQLITE_AUTH_USER; public static final int OK_LOAD_PERMANENTLY = CApi.SQLITE_OK_LOAD_PERMANENTLY; // sqlite3_open() flags public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE; public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE; public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE; // transaction state public static final int TXN_NONE = CApi.SQLITE_TXN_NONE; public static final int TXN_READ = CApi.SQLITE_TXN_READ; public static final int TXN_WRITE = CApi.SQLITE_TXN_WRITE; // sqlite3_status() ops public static final int STATUS_MEMORY_USED = CApi.SQLITE_STATUS_MEMORY_USED; public static final int STATUS_PAGECACHE_USED = CApi.SQLITE_STATUS_PAGECACHE_USED; public static final int STATUS_PAGECACHE_OVERFLOW = CApi.SQLITE_STATUS_PAGECACHE_OVERFLOW; public static final int STATUS_MALLOC_SIZE = CApi.SQLITE_STATUS_MALLOC_SIZE; public static final int STATUS_PARSER_STACK = CApi.SQLITE_STATUS_PARSER_STACK; public static final int STATUS_PAGECACHE_SIZE = CApi.SQLITE_STATUS_PAGECACHE_SIZE; public static final int STATUS_MALLOC_COUNT = CApi.SQLITE_STATUS_MALLOC_COUNT; // sqlite3_db_status() ops public static final int DBSTATUS_LOOKASIDE_USED = CApi.SQLITE_DBSTATUS_LOOKASIDE_USED; public static final int DBSTATUS_CACHE_USED = CApi.SQLITE_DBSTATUS_CACHE_USED; public static final int DBSTATUS_SCHEMA_USED = CApi.SQLITE_DBSTATUS_SCHEMA_USED; public static final int DBSTATUS_STMT_USED = CApi.SQLITE_DBSTATUS_STMT_USED; public static final int DBSTATUS_LOOKASIDE_HIT = CApi.SQLITE_DBSTATUS_LOOKASIDE_HIT; public static final int DBSTATUS_LOOKASIDE_MISS_SIZE = CApi.SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE; public static final int DBSTATUS_LOOKASIDE_MISS_FULL = CApi.SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL; public static final int DBSTATUS_CACHE_HIT = CApi.SQLITE_DBSTATUS_CACHE_HIT; public static final int DBSTATUS_CACHE_MISS = CApi.SQLITE_DBSTATUS_CACHE_MISS; public static final int DBSTATUS_CACHE_WRITE = CApi.SQLITE_DBSTATUS_CACHE_WRITE; public static final int DBSTATUS_DEFERRED_FKS = CApi.SQLITE_DBSTATUS_DEFERRED_FKS; public static final int DBSTATUS_CACHE_USED_SHARED = CApi.SQLITE_DBSTATUS_CACHE_USED_SHARED; public static final int DBSTATUS_CACHE_SPILL = CApi.SQLITE_DBSTATUS_CACHE_SPILL; // Limits public static final int LIMIT_LENGTH = CApi.SQLITE_LIMIT_LENGTH; public static final int LIMIT_SQL_LENGTH = CApi.SQLITE_LIMIT_SQL_LENGTH; public static final int LIMIT_COLUMN = CApi.SQLITE_LIMIT_COLUMN; public static final int LIMIT_EXPR_DEPTH = CApi.SQLITE_LIMIT_EXPR_DEPTH; public static final int LIMIT_COMPOUND_SELECT = CApi.SQLITE_LIMIT_COMPOUND_SELECT; public static final int LIMIT_VDBE_OP = CApi.SQLITE_LIMIT_VDBE_OP; public static final int LIMIT_FUNCTION_ARG = CApi.SQLITE_LIMIT_FUNCTION_ARG; public static final int LIMIT_ATTACHED = CApi.SQLITE_LIMIT_ATTACHED; public static final int LIMIT_LIKE_PATTERN_LENGTH = CApi.SQLITE_LIMIT_LIKE_PATTERN_LENGTH; public static final int LIMIT_VARIABLE_NUMBER = CApi.SQLITE_LIMIT_VARIABLE_NUMBER; public static final int LIMIT_TRIGGER_DEPTH = CApi.SQLITE_LIMIT_TRIGGER_DEPTH; public static final int LIMIT_WORKER_THREADS = CApi.SQLITE_LIMIT_WORKER_THREADS; // sqlite3_prepare_v3() flags public static final int PREPARE_PERSISTENT = CApi.SQLITE_PREPARE_PERSISTENT; public static final int PREPARE_NO_VTAB = CApi.SQLITE_PREPARE_NO_VTAB; // sqlite3_trace_v2() flags public static final int TRACE_STMT = CApi.SQLITE_TRACE_STMT; public static final int TRACE_PROFILE = CApi.SQLITE_TRACE_PROFILE; public static final int TRACE_ROW = CApi.SQLITE_TRACE_ROW; public static final int TRACE_CLOSE = CApi.SQLITE_TRACE_CLOSE; public static final int TRACE_ALL = TRACE_STMT | TRACE_PROFILE | TRACE_ROW | TRACE_CLOSE; // sqlite3_db_config() ops public static final int DBCONFIG_ENABLE_FKEY = CApi.SQLITE_DBCONFIG_ENABLE_FKEY; public static final int DBCONFIG_ENABLE_TRIGGER = CApi.SQLITE_DBCONFIG_ENABLE_TRIGGER; public static final int DBCONFIG_ENABLE_FTS3_TOKENIZER = CApi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER; public static final int DBCONFIG_ENABLE_LOAD_EXTENSION = CApi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION; public static final int DBCONFIG_NO_CKPT_ON_CLOSE = CApi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE; public static final int DBCONFIG_ENABLE_QPSG = CApi.SQLITE_DBCONFIG_ENABLE_QPSG; public static final int DBCONFIG_TRIGGER_EQP = CApi.SQLITE_DBCONFIG_TRIGGER_EQP; public static final int DBCONFIG_RESET_DATABASE = CApi.SQLITE_DBCONFIG_RESET_DATABASE; public static final int DBCONFIG_DEFENSIVE = CApi.SQLITE_DBCONFIG_DEFENSIVE; public static final int DBCONFIG_WRITABLE_SCHEMA = CApi.SQLITE_DBCONFIG_WRITABLE_SCHEMA; public static final int DBCONFIG_LEGACY_ALTER_TABLE = CApi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE; public static final int DBCONFIG_DQS_DML = CApi.SQLITE_DBCONFIG_DQS_DML; public static final int DBCONFIG_DQS_DDL = CApi.SQLITE_DBCONFIG_DQS_DDL; public static final int DBCONFIG_ENABLE_VIEW = CApi.SQLITE_DBCONFIG_ENABLE_VIEW; public static final int DBCONFIG_LEGACY_FILE_FORMAT = CApi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT; public static final int DBCONFIG_TRUSTED_SCHEMA = CApi.SQLITE_DBCONFIG_TRUSTED_SCHEMA; public static final int DBCONFIG_STMT_SCANSTATUS = CApi.SQLITE_DBCONFIG_STMT_SCANSTATUS; public static final int DBCONFIG_REVERSE_SCANORDER = CApi.SQLITE_DBCONFIG_REVERSE_SCANORDER; // sqlite3_config() ops public static final int CONFIG_SINGLETHREAD = CApi.SQLITE_CONFIG_SINGLETHREAD; public static final int CONFIG_MULTITHREAD = CApi.SQLITE_CONFIG_MULTITHREAD; public static final int CONFIG_SERIALIZED = CApi.SQLITE_CONFIG_SERIALIZED; // Encodings public static final int UTF8 = CApi.SQLITE_UTF8; public static final int UTF16 = CApi.SQLITE_UTF16; public static final int UTF16LE = CApi.SQLITE_UTF16LE; public static final int UTF16BE = CApi.SQLITE_UTF16BE; /* We elide the UTF16_ALIGNED from this interface because it is irrelevant for the Java interface. */ // SQL data type IDs public static final int INTEGER = CApi.SQLITE_INTEGER; public static final int FLOAT = CApi.SQLITE_FLOAT; public static final int TEXT = CApi.SQLITE_TEXT; public static final int BLOB = CApi.SQLITE_BLOB; public static final int NULL = CApi.SQLITE_NULL; // Authorizer codes. public static final int DENY = CApi.SQLITE_DENY; public static final int IGNORE = CApi.SQLITE_IGNORE; public static final int CREATE_INDEX = CApi.SQLITE_CREATE_INDEX; public static final int CREATE_TABLE = CApi.SQLITE_CREATE_TABLE; public static final int CREATE_TEMP_INDEX = CApi.SQLITE_CREATE_TEMP_INDEX; public static final int CREATE_TEMP_TABLE = CApi.SQLITE_CREATE_TEMP_TABLE; public static final int CREATE_TEMP_TRIGGER = CApi.SQLITE_CREATE_TEMP_TRIGGER; public static final int CREATE_TEMP_VIEW = CApi.SQLITE_CREATE_TEMP_VIEW; public static final int CREATE_TRIGGER = CApi.SQLITE_CREATE_TRIGGER; public static final int CREATE_VIEW = CApi.SQLITE_CREATE_VIEW; public static final int DELETE = CApi.SQLITE_DELETE; public static final int DROP_INDEX = CApi.SQLITE_DROP_INDEX; public static final int DROP_TABLE = CApi.SQLITE_DROP_TABLE; public static final int DROP_TEMP_INDEX = CApi.SQLITE_DROP_TEMP_INDEX; public static final int DROP_TEMP_TABLE = CApi.SQLITE_DROP_TEMP_TABLE; public static final int DROP_TEMP_TRIGGER = CApi.SQLITE_DROP_TEMP_TRIGGER; public static final int DROP_TEMP_VIEW = CApi.SQLITE_DROP_TEMP_VIEW; public static final int DROP_TRIGGER = CApi.SQLITE_DROP_TRIGGER; public static final int DROP_VIEW = CApi.SQLITE_DROP_VIEW; public static final int INSERT = CApi.SQLITE_INSERT; public static final int PRAGMA = CApi.SQLITE_PRAGMA; public static final int READ = CApi.SQLITE_READ; public static final int SELECT = CApi.SQLITE_SELECT; public static final int TRANSACTION = CApi.SQLITE_TRANSACTION; public static final int UPDATE = CApi.SQLITE_UPDATE; public static final int ATTACH = CApi.SQLITE_ATTACH; public static final int DETACH = CApi.SQLITE_DETACH; public static final int ALTER_TABLE = CApi.SQLITE_ALTER_TABLE; public static final int REINDEX = CApi.SQLITE_REINDEX; public static final int ANALYZE = CApi.SQLITE_ANALYZE; public static final int CREATE_VTABLE = CApi.SQLITE_CREATE_VTABLE; public static final int DROP_VTABLE = CApi.SQLITE_DROP_VTABLE; public static final int FUNCTION = CApi.SQLITE_FUNCTION; public static final int SAVEPOINT = CApi.SQLITE_SAVEPOINT; public static final int RECURSIVE = CApi.SQLITE_RECURSIVE; //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ this.db = db; } /** Maps org.sqlite.jni.capi.sqlite3 to Sqlite instances. */ private static final java.util.Map<org.sqlite.jni.capi.sqlite3, Sqlite> nativeToWrapper = new java.util.HashMap<>(); /** When any given thread is done using the SQLite library, calling this will free up any native-side resources which may be associated specifically with that thread. This is not strictly necessary, in particular in applications which only use SQLite from a single thread, but may help free some otherwise errant resources. Calling into SQLite from a given thread after this has been called in that thread is harmless. The library will simply start to re-cache certain state for that thread. Contrariwise, failing to call this will effectively leak a small amount of cached state for the thread, which may add up to significant amounts if the application uses SQLite from many threads. This must never be called while actively using SQLite from this thread, e.g. from within a query loop or a callback which is operating on behalf of the library. */ static void uncacheThread(){ CApi.sqlite3_java_uncache_thread(); } /** Returns the Sqlite object associated with the given sqlite3 object, or null if there is no such mapping. */ static Sqlite fromNative(sqlite3 low){ synchronized(nativeToWrapper){ return nativeToWrapper.get(low); } } /** Returns a newly-opened db connection or throws SqliteException if opening fails. All arguments are as documented for sqlite3_open_v2(). Design question: do we want static factory functions or should this be reformulated as a constructor? */ public static Sqlite open(String filename, int flags, String vfsName){ final OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); final int rc = CApi.sqlite3_open_v2(filename, out, flags, vfsName); final sqlite3 n = out.take(); if( 0!=rc ){ if( null==n ) throw new SqliteException(rc); final SqliteException ex = new SqliteException(n); n.close(); throw ex; } final Sqlite rv = new Sqlite(n); synchronized(nativeToWrapper){ nativeToWrapper.put(n, rv); } runAutoExtensions(rv); return rv; } public static Sqlite open(String filename, int flags){ return open(filename, flags, null); } public static Sqlite open(String filename){ return open(filename, OPEN_READWRITE|OPEN_CREATE, null); } public static String libVersion(){ return CApi.sqlite3_libversion(); } public static int libVersionNumber(){ return CApi.sqlite3_libversion_number(); } public static String libSourceId(){ return CApi.sqlite3_sourceid(); } /** Returns the value of the native library's build-time value of the SQLITE_THREADSAFE build option. */ public static int libThreadsafe(){ return CApi.sqlite3_threadsafe(); } /** Analog to sqlite3_compileoption_get(). */ public static String compileOptionGet(int n){ return CApi.sqlite3_compileoption_get(n); } /** Analog to sqlite3_compileoption_used(). */ public static boolean compileOptionUsed(String optName){ return CApi.sqlite3_compileoption_used(optName); } private static boolean hasNormalizeSql = compileOptionUsed("ENABLE_NORMALIZE"); private static boolean hasSqlLog = compileOptionUsed("ENABLE_SQLLOG"); /** Throws UnsupportedOperationException if check is false. flag is expected to be the name of an SQLITE_ENABLE_... build flag. */ private static void checkSupported(boolean check, String flag){ if( !check ){ throw new UnsupportedOperationException( "Library was built without "+flag ); } } /** Analog to sqlite3_complete(). */ public static boolean isCompleteStatement(String sql){ switch(CApi.sqlite3_complete(sql)){ case 0: return false; case CApi.SQLITE_MISUSE: throw new IllegalArgumentException("Input may not be null."); case CApi.SQLITE_NOMEM: throw new OutOfMemoryError(); default: return true; } } public static int keywordCount(){ return CApi.sqlite3_keyword_count(); } public static boolean keywordCheck(String word){ return CApi.sqlite3_keyword_check(word); } public static String keywordName(int index){ return CApi.sqlite3_keyword_name(index); } public static boolean strglob(String glob, String txt){ return 0==CApi.sqlite3_strglob(glob, txt); } public static boolean strlike(String glob, String txt, char escChar){ return 0==CApi.sqlite3_strlike(glob, txt, escChar); } /** Output object for use with status() and libStatus(). */ public static final class Status { /** The current value for the requested status() or libStatus() metric. */ long current; /** The peak value for the requested status() or libStatus() metric. */ long peak; }; /** As per sqlite3_status64(), but returns its current and high-water results as a Status object. Throws if the first argument is not one of the STATUS_... constants. */ public static Status libStatus(int op, boolean resetStats){ org.sqlite.jni.capi.OutputPointer.Int64 pCurrent = new org.sqlite.jni.capi.OutputPointer.Int64(); org.sqlite.jni.capi.OutputPointer.Int64 pHighwater = new org.sqlite.jni.capi.OutputPointer.Int64(); checkRcStatic( CApi.sqlite3_status64(op, pCurrent, pHighwater, resetStats) ); final Status s = new Status(); s.current = pCurrent.value; s.peak = pHighwater.value; return s; } /** As per sqlite3_db_status(), but returns its current and high-water results as a Status object. Throws if the first argument is not one of the DBSTATUS_... constants or on any other misuse. */ public Status status(int op, boolean resetStats){ org.sqlite.jni.capi.OutputPointer.Int32 pCurrent = new org.sqlite.jni.capi.OutputPointer.Int32(); org.sqlite.jni.capi.OutputPointer.Int32 pHighwater = new org.sqlite.jni.capi.OutputPointer.Int32(); checkRc( CApi.sqlite3_db_status(thisDb(), op, pCurrent, pHighwater, resetStats) ); final Status s = new Status(); s.current = pCurrent.value; s.peak = pHighwater.value; return s; } @Override public void close(){ if(null!=this.db){ synchronized(nativeToWrapper){ nativeToWrapper.remove(this.db); } this.db.close(); this.db = null; } } /** Returns this object's underlying native db handle, or null if this instance has been closed. This is very specifically not public. */ sqlite3 nativeHandle(){ return this.db; } private sqlite3 thisDb(){ if( null==db || 0==db.getNativePointer() ){ throw new IllegalArgumentException("This database instance is closed."); } return this.db; } // private byte[] stringToUtf8(String s){ // return s==null ? null : s.getBytes(StandardCharsets.UTF_8); // } /** If rc!=0, throws an SqliteException. If this db is currently opened and has non-0 sqlite3_errcode(), the error state is extracted from it, else only the string form of rc is used. It is the caller's responsibility to filter out non-error codes such as SQLITE_ROW and SQLITE_DONE before calling this. As a special case, if rc is SQLITE_NOMEM, an OutOfMemoryError is thrown. */ private void checkRc(int rc){ if( 0!=rc ){ if( CApi.SQLITE_NOMEM==rc ){ throw new OutOfMemoryError(); }else if( null==db || 0==CApi.sqlite3_errcode(db) ){ throw new SqliteException(rc); }else{ throw new SqliteException(db); } } } /** Like checkRc() but behaves as if that function were called with a null db object. */ private static void checkRcStatic(int rc){ if( 0!=rc ){ if( CApi.SQLITE_NOMEM==rc ){ throw new OutOfMemoryError(); }else{ throw new SqliteException(rc); } } } /** Toggles the use of extended result codes on or off. By default they are turned off, but they can be enabled by default by including the OPEN_EXRESCODE flag when opening a database. Because this API reports db-side errors using exceptions, enabling this may change the values returned by SqliteException.errcode(). */ public void useExtendedResultCodes(boolean on){ checkRc( CApi.sqlite3_extended_result_codes(thisDb(), on) ); } /** Analog to sqlite3_prepare_v3(), this prepares the first SQL statement from the given input string and returns it as a Stmt. It throws an SqliteException if preparation fails or an IllegalArgumentException if the input is empty (e.g. contains only comments or whitespace). The first argument must be SQL input in UTF-8 encoding. prepFlags must be 0 or a bitmask of the PREPARE_... constants. For processing multiple statements from a single input, use prepareMulti(). Design note: though the C-level API succeeds with a null statement object for empty inputs, that approach is cumbersome to use in higher-level APIs because every prepared statement has to be checked for null before using it. */ public Stmt prepare(byte utf8Sql[], int prepFlags){ final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); final int rc = CApi.sqlite3_prepare_v3(thisDb(), utf8Sql, prepFlags, out); checkRc(rc); final sqlite3_stmt q = out.take(); if( null==q ){ /* The C-level API treats input which is devoid of SQL statements (e.g. all comments or an empty string) as success but returns a NULL sqlite3_stmt object. In higher-level APIs, wrapping a "successful NULL" object that way is tedious to use because it forces clients and/or wrapper-level code to check for that unusual case. In practice, higher-level bindings are generally better-served by treating empty SQL input as an error. */ throw new IllegalArgumentException("Input contains no SQL statements."); } return new Stmt(this, q); } /** Equivalent to prepare(X, prepFlags), where X is sql.getBytes(StandardCharsets.UTF_8). */ public Stmt prepare(String sql, int prepFlags){ return prepare( sql.getBytes(StandardCharsets.UTF_8), prepFlags ); } /** Equivalent to prepare(sql, 0). */ public Stmt prepare(String sql){ return prepare(sql, 0); } /** Callback type for use with prepareMulti(). */ public interface PrepareMulti { /** Gets passed a Stmt which it may handle in arbitrary ways. Ownership of st is passed to this function. It must throw on error. */ void call(Sqlite.Stmt st); } /** A PrepareMulti implementation which calls another PrepareMulti object and then finalizes its statement. */ public static class PrepareMultiFinalize implements PrepareMulti { private final PrepareMulti pm; /** Proxies the given PrepareMulti via this object's call() method. */ public PrepareMultiFinalize(PrepareMulti proxy){ this.pm = proxy; } /** Passes st to the call() method of the object this one proxies, then finalizes st, propagating any exceptions from call() after finalizing st. */ @Override public void call(Stmt st){ try{ pm.call(st); } finally{ st.finalizeStmt(); } } } /** Equivalent to prepareMulti(sql,0,visitor). */ public void prepareMulti(String sql, PrepareMulti visitor){ prepareMulti( sql, 0, visitor ); } /** Equivallent to prepareMulti(X,prepFlags,visitor), where X is sql.getBytes(StandardCharsets.UTF_8). */ public void prepareMulti(String sql, int prepFlags, PrepareMulti visitor){ prepareMulti(sql.getBytes(StandardCharsets.UTF_8), prepFlags, visitor); } /** A variant of prepare() which can handle multiple SQL statements in a single input string. For each statement in the given string, the statement is passed to visitor.call() a single time, passing ownership of the statement to that function. This function does not step() or close() statements - those operations are left to caller or the visitor function. Unlike prepare(), this function does not fail if the input contains only whitespace or SQL comments. In that case it is up to the caller to arrange for that to be an error (if desired). PrepareMultiFinalize offers a proxy which finalizes each statement after it is passed to another client-defined visitor. Be aware that certain legal SQL constructs may fail in the preparation phase, before the corresponding statement can be stepped. Most notably, authorizer checks which disallow access to something in a statement behave that way. */ public void prepareMulti(byte sqlUtf8[], int prepFlags, PrepareMulti visitor){ int pos = 0, n = 1; byte[] sqlChunk = sqlUtf8; final org.sqlite.jni.capi.OutputPointer.sqlite3_stmt outStmt = new org.sqlite.jni.capi.OutputPointer.sqlite3_stmt(); final org.sqlite.jni.capi.OutputPointer.Int32 oTail = new org.sqlite.jni.capi.OutputPointer.Int32(); while( pos < sqlChunk.length ){ sqlite3_stmt stmt = null; if( pos>0 ){ sqlChunk = java.util.Arrays.copyOfRange(sqlChunk, pos, sqlChunk.length); } if( 0==sqlChunk.length ) break; checkRc( CApi.sqlite3_prepare_v3(db, sqlChunk, prepFlags, outStmt, oTail) ); pos = oTail.value; stmt = outStmt.take(); if( null==stmt ){ /* empty statement, e.g. only comments or whitespace, was parsed. */ continue; } visitor.call(new Stmt(this, stmt)); } } public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f){ int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, new SqlFunction.ScalarAdapter(f)); if( 0!=rc ) throw new SqliteException(db); } public void createFunction(String name, int nArg, ScalarFunction f){ this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); } public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f){ int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, new SqlFunction.AggregateAdapter(f)); if( 0!=rc ) throw new SqliteException(db); } public void createFunction(String name, int nArg, AggregateFunction f){ this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); } public void createFunction(String name, int nArg, int eTextRep, WindowFunction f){ int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, new SqlFunction.WindowAdapter(f)); if( 0!=rc ) throw new SqliteException(db); } public void createFunction(String name, int nArg, WindowFunction f){ this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); } public long changes(){ return CApi.sqlite3_changes64(thisDb()); } public long totalChanges(){ return CApi.sqlite3_total_changes64(thisDb()); } public long lastInsertRowId(){ return CApi.sqlite3_last_insert_rowid(thisDb()); } public void setLastInsertRowId(long rowId){ CApi.sqlite3_set_last_insert_rowid(thisDb(), rowId); } public void interrupt(){ CApi.sqlite3_interrupt(thisDb()); } public boolean isInterrupted(){ return CApi.sqlite3_is_interrupted(thisDb()); } public boolean isAutoCommit(){ return CApi.sqlite3_get_autocommit(thisDb()); } /** Analog to sqlite3_txn_state(). Returns one of TXN_NONE, TXN_READ, or TXN_WRITE to denote this database's current transaction state for the given schema name (or the most restrictive state of any schema if zSchema is null). */ public int transactionState(String zSchema){ return CApi.sqlite3_txn_state(thisDb(), zSchema); } /** Analog to sqlite3_db_name(). Returns null if passed an unknown index. */ public String dbName(int dbNdx){ return CApi.sqlite3_db_name(thisDb(), dbNdx); } /** Analog to sqlite3_db_filename(). Returns null if passed an unknown db name. */ public String dbFileName(String dbName){ return CApi.sqlite3_db_filename(thisDb(), dbName); } /** Analog to sqlite3_db_config() for the call forms which take one of the boolean-type db configuration flags (namely the DBCONFIG_... constants defined in this class). On success it returns the result of that underlying call. Throws on error. */ public boolean dbConfig(int op, boolean on){ org.sqlite.jni.capi.OutputPointer.Int32 pOut = new org.sqlite.jni.capi.OutputPointer.Int32(); checkRc( CApi.sqlite3_db_config(thisDb(), op, on ? 1 : 0, pOut) ); return pOut.get()!=0; } /** Analog to the variant of sqlite3_db_config() for configuring the SQLITE_DBCONFIG_MAINDBNAME option. Throws on error. */ public void setMainDbName(String name){ checkRc( CApi.sqlite3_db_config(thisDb(), CApi.SQLITE_DBCONFIG_MAINDBNAME, name) ); } /** Analog to sqlite3_db_readonly() but throws an SqliteException with result code SQLITE_NOTFOUND if given an unknown database name. */ public boolean readOnly(String dbName){ final int rc = CApi.sqlite3_db_readonly(thisDb(), dbName); if( 0==rc ) return false; else if( rc>0 ) return true; throw new SqliteException(CApi.SQLITE_NOTFOUND); } /** Analog to sqlite3_db_release_memory(). */ public void releaseMemory(){ CApi.sqlite3_db_release_memory(thisDb()); } /** Analog to sqlite3_release_memory(). */ public static int libReleaseMemory(int n){ return CApi.sqlite3_release_memory(n); } /** Analog to sqlite3_limit(). limitId must be one of the LIMIT_... constants. Returns the old limit for the given option. If newLimit is negative, it returns the old limit without modifying the limit. If sqlite3_limit() returns a negative value, this function throws an SqliteException with the SQLITE_RANGE result code but no further error info (because that case does not qualify as a db-level error). Such errors may indicate an invalid argument value or an invalid range for newLimit (the underlying function does not differentiate between those). */ public int limit(int limitId, int newLimit){ final int rc = CApi.sqlite3_limit(thisDb(), limitId, newLimit); if( rc<0 ){ throw new SqliteException(CApi.SQLITE_RANGE); } return rc; } /** Analog to sqlite3_errstr(). */ static String errstr(int resultCode){ return CApi.sqlite3_errstr(resultCode); } /** A wrapper object for use with tableColumnMetadata(). They are created and populated only via that interface. */ public final class TableColumnMetadata { Boolean pNotNull = null; Boolean pPrimaryKey = null; Boolean pAutoinc = null; String pzCollSeq = null; String pzDataType = null; private TableColumnMetadata(){} public String getDataType(){ return pzDataType; } public String getCollation(){ return pzCollSeq; } public boolean isNotNull(){ return pNotNull; } public boolean isPrimaryKey(){ return pPrimaryKey; } public boolean isAutoincrement(){ return pAutoinc; } } /** Returns data about a database, table, and (optionally) column (which may be null), as per sqlite3_table_column_metadata(). Throws if passed invalid arguments, else returns the result as a new TableColumnMetadata object. */ TableColumnMetadata tableColumnMetadata( String zDbName, String zTableName, String zColumnName ){ org.sqlite.jni.capi.OutputPointer.String pzDataType = new org.sqlite.jni.capi.OutputPointer.String(); org.sqlite.jni.capi.OutputPointer.String pzCollSeq = new org.sqlite.jni.capi.OutputPointer.String(); org.sqlite.jni.capi.OutputPointer.Bool pNotNull = new org.sqlite.jni.capi.OutputPointer.Bool(); org.sqlite.jni.capi.OutputPointer.Bool pPrimaryKey = new org.sqlite.jni.capi.OutputPointer.Bool(); org.sqlite.jni.capi.OutputPointer.Bool pAutoinc = new org.sqlite.jni.capi.OutputPointer.Bool(); final int rc = CApi.sqlite3_table_column_metadata( thisDb(), zDbName, zTableName, zColumnName, pzDataType, pzCollSeq, pNotNull, pPrimaryKey, pAutoinc ); checkRc(rc); TableColumnMetadata rv = new TableColumnMetadata(); rv.pzDataType = pzDataType.value; rv.pzCollSeq = pzCollSeq.value; rv.pNotNull = pNotNull.value; rv.pPrimaryKey = pPrimaryKey.value; rv.pAutoinc = pAutoinc.value; return rv; } public interface TraceCallback { /** Called by sqlite3 for various tracing operations, as per sqlite3_trace_v2(). Note that this interface elides the 2nd argument to the native trace callback, as that role is better filled by instance-local state. <p>These callbacks may throw, in which case their exceptions are converted to C-level error information. <p>The 2nd argument to this function, if non-null, will be a an Sqlite or Sqlite.Stmt object, depending on the first argument (see below). <p>The final argument to this function is the "X" argument documented for sqlite3_trace() and sqlite3_trace_v2(). Its type depends on value of the first argument: <p>- SQLITE_TRACE_STMT: pNative is a Sqlite.Stmt. pX is a String containing the prepared SQL. <p>- SQLITE_TRACE_PROFILE: pNative is a sqlite3_stmt. pX is a Long holding an approximate number of nanoseconds the statement took to run. <p>- SQLITE_TRACE_ROW: pNative is a sqlite3_stmt. pX is null. <p>- SQLITE_TRACE_CLOSE: pNative is a sqlite3. pX is null. */ void call(int traceFlag, Object pNative, Object pX); } /** Analog to sqlite3_trace_v2(). traceMask must be a mask of the TRACE_... constants. Pass a null callback to remove tracing. Throws on error. */ public void trace(int traceMask, TraceCallback callback){ final Sqlite self = this; final org.sqlite.jni.capi.TraceV2Callback tc = (null==callback) ? null : new org.sqlite.jni.capi.TraceV2Callback(){ @SuppressWarnings("unchecked") @Override public int call(int flag, Object pNative, Object pX){ switch(flag){ case TRACE_ROW: case TRACE_PROFILE: case TRACE_STMT: callback.call(flag, Sqlite.Stmt.fromNative((sqlite3_stmt)pNative), pX); break; case TRACE_CLOSE: callback.call(flag, self, pX); break; } return 0; } }; checkRc( CApi.sqlite3_trace_v2(thisDb(), traceMask, tc) ); }; /** Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to create new instances. */ public static final class Stmt implements AutoCloseable { private Sqlite _db = null; private sqlite3_stmt stmt = null; /** Only called by the prepare() factory functions. */ Stmt(Sqlite db, sqlite3_stmt stmt){ this._db = db; this.stmt = stmt; synchronized(nativeToWrapper){ nativeToWrapper.put(this.stmt, this); } } sqlite3_stmt nativeHandle(){ return stmt; } /** Maps org.sqlite.jni.capi.sqlite3_stmt to Stmt instances. */ private static final java.util.Map<org.sqlite.jni.capi.sqlite3_stmt, Stmt> nativeToWrapper = new java.util.HashMap<>(); /** Returns the Stmt object associated with the given sqlite3_stmt object, or null if there is no such mapping. */ static Stmt fromNative(sqlite3_stmt low){ synchronized(nativeToWrapper){ return nativeToWrapper.get(low); } } /** If this statement is still opened, its low-level handle is returned, else an IllegalArgumentException is thrown. */ private sqlite3_stmt thisStmt(){ if( null==stmt || 0==stmt.getNativePointer() ){ throw new IllegalArgumentException("This Stmt has been finalized."); } return stmt; } /** Throws if n is out of range of this statement's result column count. Intended to be used by the columnXyz() methods. */ private sqlite3_stmt checkColIndex(int n){ if(n<0 || n>=columnCount()){ throw new IllegalArgumentException("Column index "+n+" is out of range."); } return thisStmt(); } /** Corresponds to sqlite3_finalize(), but we cannot override the name finalize() here because this one requires a different signature. It does not throw on error here because "destructors do not throw." If it returns non-0, the object is still finalized, but the result code is an indication that something went wrong in a prior call into the statement's API, as documented for sqlite3_finalize(). */ public int finalizeStmt(){ int rc = 0; if( null!=stmt ){ synchronized(nativeToWrapper){ nativeToWrapper.remove(this.stmt); } CApi.sqlite3_finalize(stmt); stmt = null; _db = null; } return rc; } @Override public void close(){ finalizeStmt(); } /** Throws if rc is any value other than 0, SQLITE_ROW, or SQLITE_DONE, else returns rc. Error state for the exception is extracted from this statement object (if it's opened) or the string form of rc. */ private int checkRc(int rc){ switch(rc){ case 0: case CApi.SQLITE_ROW: case CApi.SQLITE_DONE: return rc; default: if( null==stmt ) throw new SqliteException(rc); else throw new SqliteException(this); } } /** Works like sqlite3_step() but returns true for SQLITE_ROW, false for SQLITE_DONE, and throws SqliteException for any other result. */ public boolean step(){ switch(checkRc(CApi.sqlite3_step(thisStmt()))){ case CApi.SQLITE_ROW: return true; case CApi.SQLITE_DONE: return false; default: throw new IllegalStateException( "This \"cannot happen\": all possible result codes were checked already." ); } } /** Works like sqlite3_step(), returning the same result codes as that function unless throwOnError is true, in which case it will throw an SqliteException for any result codes other than Sqlite.ROW or Sqlite.DONE. The utility of this overload over the no-argument one is the ability to handle BUSY and LOCKED errors more easily. */ public int step(boolean throwOnError){ final int rc = (null==stmt) ? Sqlite.MISUSE : CApi.sqlite3_step(stmt); return throwOnError ? checkRc(rc) : rc; } /** Returns the Sqlite which prepared this statement, or null if this statement has been finalized. */ public Sqlite getDb(){ return this._db; } /** Works like sqlite3_reset() but throws on error. */ public void reset(){ checkRc(CApi.sqlite3_reset(thisStmt())); } public boolean isBusy(){ return CApi.sqlite3_stmt_busy(thisStmt()); } public boolean isReadOnly(){ return CApi.sqlite3_stmt_readonly(thisStmt()); } public String sql(){ return CApi.sqlite3_sql(thisStmt()); } public String expandedSql(){ return CApi.sqlite3_expanded_sql(thisStmt()); } /** Analog to sqlite3_stmt_explain() but throws if op is invalid. */ public void explain(int op){ checkRc(CApi.sqlite3_stmt_explain(thisStmt(), op)); } /** Analog to sqlite3_stmt_isexplain(). */ public int isExplain(){ return CApi.sqlite3_stmt_isexplain(thisStmt()); } /** Analog to sqlite3_normalized_sql(), but throws UnsupportedOperationException if the library was built without the SQLITE_ENABLE_NORMALIZE flag. */ public String normalizedSql(){ Sqlite.checkSupported(hasNormalizeSql, "SQLITE_ENABLE_NORMALIZE"); return CApi.sqlite3_normalized_sql(thisStmt()); } public void clearBindings(){ CApi.sqlite3_clear_bindings( thisStmt() ); } public void bindInt(int ndx, int val){ checkRc(CApi.sqlite3_bind_int(thisStmt(), ndx, val)); } public void bindInt64(int ndx, long val){ checkRc(CApi.sqlite3_bind_int64(thisStmt(), ndx, val)); } public void bindDouble(int ndx, double val){ checkRc(CApi.sqlite3_bind_double(thisStmt(), ndx, val)); } public void bindObject(int ndx, Object o){ checkRc(CApi.sqlite3_bind_java_object(thisStmt(), ndx, o)); } public void bindNull(int ndx){ checkRc(CApi.sqlite3_bind_null(thisStmt(), ndx)); } public int bindParameterCount(){ return CApi.sqlite3_bind_parameter_count(thisStmt()); } public int bindParameterIndex(String paramName){ return CApi.sqlite3_bind_parameter_index(thisStmt(), paramName); } public String bindParameterName(int ndx){ return CApi.sqlite3_bind_parameter_name(thisStmt(), ndx); } public void bindText(int ndx, byte[] utf8){ checkRc(CApi.sqlite3_bind_text(thisStmt(), ndx, utf8)); } public void bindText(int ndx, String asUtf8){ checkRc(CApi.sqlite3_bind_text(thisStmt(), ndx, asUtf8)); } public void bindText16(int ndx, byte[] utf16){ checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, utf16)); } public void bindText16(int ndx, String asUtf16){ checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, asUtf16)); } public void bindZeroBlob(int ndx, int n){ checkRc(CApi.sqlite3_bind_zeroblob(thisStmt(), ndx, n)); } public void bindBlob(int ndx, byte[] bytes){ checkRc(CApi.sqlite3_bind_blob(thisStmt(), ndx, bytes)); } public byte[] columnBlob(int ndx){ return CApi.sqlite3_column_blob( checkColIndex(ndx), ndx ); } public byte[] columnText(int ndx){ return CApi.sqlite3_column_text( checkColIndex(ndx), ndx ); } public String columnText16(int ndx){ return CApi.sqlite3_column_text16( checkColIndex(ndx), ndx ); } public int columnBytes(int ndx){ return CApi.sqlite3_column_bytes( checkColIndex(ndx), ndx ); } public int columnBytes16(int ndx){ return CApi.sqlite3_column_bytes16( checkColIndex(ndx), ndx ); } public int columnInt(int ndx){ return CApi.sqlite3_column_int( checkColIndex(ndx), ndx ); } public long columnInt64(int ndx){ return CApi.sqlite3_column_int64( checkColIndex(ndx), ndx ); } public double columnDouble(int ndx){ return CApi.sqlite3_column_double( checkColIndex(ndx), ndx ); } public int columnType(int ndx){ return CApi.sqlite3_column_type( checkColIndex(ndx), ndx ); } public String columnDeclType(int ndx){ return CApi.sqlite3_column_decltype( checkColIndex(ndx), ndx ); } /** Analog to sqlite3_column_count() but throws if this statement has been finalized. */ public int columnCount(){ /* We cannot reliably cache the column count in a class member because an ALTER TABLE from a separate statement can invalidate that count and we have no way, short of installing a COMMIT handler or the like, of knowing when to re-read it. We cannot install such a handler without interfering with a client's ability to do so. */ return CApi.sqlite3_column_count(thisStmt()); } public int columnDataCount(){ return CApi.sqlite3_data_count( thisStmt() ); } public Object columnObject(int ndx){ return CApi.sqlite3_column_java_object( checkColIndex(ndx), ndx ); } public <T> T columnObject(int ndx, Class<T> type){ return CApi.sqlite3_column_java_object( checkColIndex(ndx), ndx, type ); } public String columnName(int ndx){ return CApi.sqlite3_column_name( checkColIndex(ndx), ndx ); } public String columnDatabaseName(int ndx){ return CApi.sqlite3_column_database_name( checkColIndex(ndx), ndx ); } public String columnOriginName(int ndx){ return CApi.sqlite3_column_origin_name( checkColIndex(ndx), ndx ); } public String columnTableName(int ndx){ return CApi.sqlite3_column_table_name( checkColIndex(ndx), ndx ); } } /* Stmt class */ /** Interface for auto-extensions, as per the sqlite3_auto_extension() API. Design note: the chicken/egg timing of auto-extension execution requires that this feature be entirely re-implemented in Java because the C-level API has no access to the Sqlite type so cannot pass on an object of that type while the database is being opened. One side effect of this reimplementation is that this class's list of auto-extensions is 100% independent of the C-level list so, e.g., clearAutoExtensions() will have no effect on auto-extensions added via the C-level API and databases opened from that level of API will not be passed to this level's AutoExtension instances. */ public interface AutoExtension { public void call(Sqlite db); } private static final java.util.Set<AutoExtension> autoExtensions = new java.util.LinkedHashSet<>(); /** Passes db to all auto-extensions. If any one of them throws, db.close() is called before the exception is propagated. */ private static void runAutoExtensions(Sqlite db){ AutoExtension list[]; synchronized(autoExtensions){ /* Avoid that modifications to the AutoExtension list from within auto-extensions affect this execution of this list. */ list = autoExtensions.toArray(new AutoExtension[0]); } try { for( AutoExtension ax : list ) ax.call(db); }catch(Exception e){ db.close(); throw e; } } /** Analog to sqlite3_auto_extension(), adds the given object to the list of auto-extensions if it is not already in that list. The given object will be run as part of Sqlite.open(), and passed the being-opened database. If the extension throws then open() will fail. This API does not guaranty whether or not manipulations made to the auto-extension list from within auto-extension callbacks will affect the current traversal of the auto-extension list. Whether or not they do is unspecified and subject to change between versions. e.g. if an AutoExtension calls addAutoExtension(), whether or not the new extension will be run on the being-opened database is undefined. Note that calling Sqlite.open() from an auto-extension will necessarily result in recursion loop and (eventually) a stack overflow. */ public static void addAutoExtension( AutoExtension e ){ if( null==e ){ throw new IllegalArgumentException("AutoExtension may not be null."); } synchronized(autoExtensions){ autoExtensions.add(e); } } /** Removes the given object from the auto-extension list if it is in that list, otherwise this has no side-effects beyond briefly locking that list. */ public static void removeAutoExtension( AutoExtension e ){ synchronized(autoExtensions){ autoExtensions.remove(e); } } /** Removes all auto-extensions which were added via addAutoExtension(). */ public static void clearAutoExtensions(){ synchronized(autoExtensions){ autoExtensions.clear(); } } /** Encapsulates state related to the sqlite3 backup API. Use Sqlite.initBackup() to create new instances. */ public static final class Backup implements AutoCloseable { private sqlite3_backup b = null; private Sqlite dbTo = null; private Sqlite dbFrom = null; Backup(Sqlite dbDest, String schemaDest,Sqlite dbSrc, String schemaSrc){ this.dbTo = dbDest; this.dbFrom = dbSrc; b = CApi.sqlite3_backup_init(dbDest.nativeHandle(), schemaDest, dbSrc.nativeHandle(), schemaSrc); if(null==b) toss(); } private void toss(){ int rc = CApi.sqlite3_errcode(dbTo.nativeHandle()); if(0!=rc) throw new SqliteException(dbTo); rc = CApi.sqlite3_errcode(dbFrom.nativeHandle()); if(0!=rc) throw new SqliteException(dbFrom); throw new SqliteException(CApi.SQLITE_ERROR); } private sqlite3_backup getNative(){ if( null==b ) throw new IllegalStateException("This Backup is already closed."); return b; } /** If this backup is still active, this completes the backup and frees its native resources, otherwise it this is a no-op. */ public void finish(){ if( null!=b ){ CApi.sqlite3_backup_finish(b); b = null; dbTo = null; dbFrom = null; } } /** Equivalent to finish(). */ @Override public void close(){ this.finish(); } /** Analog to sqlite3_backup_step(). Returns 0 if stepping succeeds or, Sqlite.DONE if the end is reached, Sqlite.BUSY if one of the databases is busy, Sqlite.LOCKED if one of the databases is locked, and throws for any other result code or if this object has been closed. Note that BUSY and LOCKED are not necessarily permanent errors, so do not trigger an exception. */ public int step(int pageCount){ final int rc = CApi.sqlite3_backup_step(getNative(), pageCount); switch(rc){ case 0: case Sqlite.DONE: case Sqlite.BUSY: case Sqlite.LOCKED: return rc; default: toss(); return CApi.SQLITE_ERROR/*not reached*/; } } /** Analog to sqlite3_backup_pagecount(). */ public int pageCount(){ return CApi.sqlite3_backup_pagecount(getNative()); } /** Analog to sqlite3_backup_remaining(). */ public int remaining(){ return CApi.sqlite3_backup_remaining(getNative()); } } /** Analog to sqlite3_backup_init(). If schemaSrc is null, "main" is assumed. Throws if either this db or dbSrc (the source db) are not opened, if either of schemaDest or schemaSrc are null, or if the underlying call to sqlite3_backup_init() fails. The returned object must eventually be cleaned up by either arranging for it to be auto-closed (e.g. using try-with-resources) or by calling its finish() method. */ public Backup initBackup(String schemaDest, Sqlite dbSrc, String schemaSrc){ thisDb(); dbSrc.thisDb(); if( null==schemaSrc || null==schemaDest ){ throw new IllegalArgumentException( "Neither the source nor destination schema name may be null." ); } return new Backup(this, schemaDest, dbSrc, schemaSrc); } /** Callback type for use with createCollation(). */ public interface Collation { /** Called by the SQLite core to compare inputs. Implementations must compare its two arguments using memcmp(3) semantics. Warning: the SQLite core has no mechanism for reporting errors from custom collations and its workflow does not accommodate propagation of exceptions from callbacks. Any exceptions thrown from collations will be silently supressed and sorting results will be unpredictable. */ int call(byte[] lhs, byte[] rhs); } /** Analog to sqlite3_create_collation(). Throws if name is null or empty, c is null, or the encoding flag is invalid. The encoding must be one of the UTF8, UTF16, UTF16LE, or UTF16BE constants. */ public void createCollation(String name, int encoding, Collation c){ thisDb(); if( null==name || 0==name.length()){ throw new IllegalArgumentException("Collation name may not be null or empty."); } if( null==c ){ throw new IllegalArgumentException("Collation may not be null."); } switch(encoding){ case UTF8: case UTF16: case UTF16LE: case UTF16BE: break; default: throw new IllegalArgumentException("Invalid Collation encoding."); } checkRc( CApi.sqlite3_create_collation( thisDb(), name, encoding, new org.sqlite.jni.capi.CollationCallback(){ @Override public int call(byte[] lhs, byte[] rhs){ try{return c.call(lhs, rhs);} catch(Exception e){return 0;} } @Override public void xDestroy(){} } ) ); } /** Callback for use with onCollationNeeded(). */ public interface CollationNeeded { /** Must behave as documented for the callback for sqlite3_collation_needed(). Warning: the C API has no mechanism for reporting or propagating errors from this callback, so any exceptions it throws are suppressed. */ void call(Sqlite db, int encoding, String collationName); } /** Sets up the given object to be called by the SQLite core when it encounters a collation name which it does not know. Pass a null object to disconnect the object from the core. This replaces any existing collation-needed loader, or is a no-op if the given object is already registered. Throws if registering the loader fails. */ public void onCollationNeeded( CollationNeeded cn ){ org.sqlite.jni.capi.CollationNeededCallback cnc = null; if( null!=cn ){ cnc = new org.sqlite.jni.capi.CollationNeededCallback(){ @Override public void call(sqlite3 db, int encoding, String collationName){ final Sqlite xdb = Sqlite.fromNative(db); if(null!=xdb) cn.call(xdb, encoding, collationName); } }; } checkRc( CApi.sqlite3_collation_needed(thisDb(), cnc) ); } /** Callback for use with busyHandler(). */ public interface BusyHandler { /** Must function as documented for the C-level sqlite3_busy_handler() callback argument, minus the (void*) argument the C-level function requires. If this function throws, it is translated to a database-level error. */ int call(int n); } /** Analog to sqlite3_busy_timeout(). */ public void setBusyTimeout(int ms){ checkRc(CApi.sqlite3_busy_timeout(thisDb(), ms)); } /** Analog to sqlite3_busy_handler(). If b is null then any current handler is cleared. */ public void setBusyHandler( BusyHandler b ){ org.sqlite.jni.capi.BusyHandlerCallback bhc = null; if( null!=b ){ bhc = new org.sqlite.jni.capi.BusyHandlerCallback(){ @Override public int call(int n){ return b.call(n); } }; } checkRc( CApi.sqlite3_busy_handler(thisDb(), bhc) ); } public interface CommitHook { /** Must behave as documented for the C-level sqlite3_commit_hook() callback. If it throws, the exception is translated into a db-level error. */ int call(); } /** A level of indirection to permit setCommitHook() to have similar semantics as the C API, returning the previous hook. The caveat is that if the low-level API is used to install a hook, it will have a different hook type than Sqlite.CommitHook so setCommitHook() will return null instead of that object. */ private static class CommitHookProxy implements org.sqlite.jni.capi.CommitHookCallback { final CommitHook commitHook; CommitHookProxy(CommitHook ch){ this.commitHook = ch; } @Override public int call(){ return commitHook.call(); } } /** Analog to sqlite3_commit_hook(). Returns the previous hook, if any (else null). Throws if this db is closed. Minor caveat: if a commit hook is set on this object's underlying db handle using the lower-level SQLite API, this function may return null when replacing it, despite there being a hook, because it will have a different callback type. So long as the handle is only manipulated via the high-level API, this caveat does not apply. */ public CommitHook setCommitHook( CommitHook c ){ CommitHookProxy chp = null; if( null!=c ){ chp = new CommitHookProxy(c); } final org.sqlite.jni.capi.CommitHookCallback rv = CApi.sqlite3_commit_hook(thisDb(), chp); return (rv instanceof CommitHookProxy) ? ((CommitHookProxy)rv).commitHook : null; } public interface RollbackHook { /** Must behave as documented for the C-level sqlite3_rollback_hook() callback. If it throws, the exception is translated into a db-level error. */ void call(); } /** A level of indirection to permit setRollbackHook() to have similar semantics as the C API, returning the previous hook. The caveat is that if the low-level API is used to install a hook, it will have a different hook type than Sqlite.RollbackHook so setRollbackHook() will return null instead of that object. */ private static class RollbackHookProxy implements org.sqlite.jni.capi.RollbackHookCallback { final RollbackHook rollbackHook; RollbackHookProxy(RollbackHook ch){ this.rollbackHook = ch; } @Override public void call(){rollbackHook.call();} } /** Analog to sqlite3_rollback_hook(). Returns the previous hook, if any (else null). Throws if this db is closed. Minor caveat: if a rollback hook is set on this object's underlying db handle using the lower-level SQLite API, this function may return null when replacing it, despite there being a hook, because it will have a different callback type. So long as the handle is only manipulated via the high-level API, this caveat does not apply. */ public RollbackHook setRollbackHook( RollbackHook c ){ RollbackHookProxy chp = null; if( null!=c ){ chp = new RollbackHookProxy(c); } final org.sqlite.jni.capi.RollbackHookCallback rv = CApi.sqlite3_rollback_hook(thisDb(), chp); return (rv instanceof RollbackHookProxy) ? ((RollbackHookProxy)rv).rollbackHook : null; } public interface UpdateHook { /** Must function as described for the C-level sqlite3_update_hook() callback. */ void call(int opId, String dbName, String tableName, long rowId); } /** A level of indirection to permit setUpdateHook() to have similar semantics as the C API, returning the previous hook. The caveat is that if the low-level API is used to install a hook, it will have a different hook type than Sqlite.UpdateHook so setUpdateHook() will return null instead of that object. */ private static class UpdateHookProxy implements org.sqlite.jni.capi.UpdateHookCallback { final UpdateHook updateHook; UpdateHookProxy(UpdateHook ch){ this.updateHook = ch; } @Override public void call(int opId, String dbName, String tableName, long rowId){ updateHook.call(opId, dbName, tableName, rowId); } } /** Analog to sqlite3_update_hook(). Returns the previous hook, if any (else null). Throws if this db is closed. Minor caveat: if a update hook is set on this object's underlying db handle using the lower-level SQLite API, this function may return null when replacing it, despite there being a hook, because it will have a different callback type. So long as the handle is only manipulated via the high-level API, this caveat does not apply. */ public UpdateHook setUpdateHook( UpdateHook c ){ UpdateHookProxy chp = null; if( null!=c ){ chp = new UpdateHookProxy(c); } final org.sqlite.jni.capi.UpdateHookCallback rv = CApi.sqlite3_update_hook(thisDb(), chp); return (rv instanceof UpdateHookProxy) ? ((UpdateHookProxy)rv).updateHook : null; } /** Callback interface for use with setProgressHandler(). */ public interface ProgressHandler { /** Must behave as documented for the C-level sqlite3_progress_handler() callback. If it throws, the exception is translated into a db-level error. */ int call(); } /** Analog to sqlite3_progress_handler(), sets the current progress handler or clears it if p is null. Note that this API, in contrast to setUpdateHook(), setRollbackHook(), and setCommitHook(), cannot return the previous handler. That inconsistency is part of the lower-level C API. */ public void setProgressHandler( int n, ProgressHandler p ){ org.sqlite.jni.capi.ProgressHandlerCallback phc = null; if( null!=p ){ phc = new org.sqlite.jni.capi.ProgressHandlerCallback(){ @Override public int call(){ return p.call(); } }; } CApi.sqlite3_progress_handler( thisDb(), n, phc ); } /** Callback for use with setAuthorizer(). */ public interface Authorizer { /** Must function as described for the C-level sqlite3_set_authorizer() callback. If it throws, the error is converted to a db-level error and the exception is suppressed. */ int call(int opId, String s1, String s2, String s3, String s4); } /** Analog to sqlite3_set_authorizer(), this sets the current authorizer callback, or clears if it passed null. */ public void setAuthorizer( Authorizer a ) { org.sqlite.jni.capi.AuthorizerCallback ac = null; if( null!=a ){ ac = new org.sqlite.jni.capi.AuthorizerCallback(){ @Override public int call(int opId, String s1, String s2, String s3, String s4){ return a.call(opId, s1, s2, s3, s4); } }; } checkRc( CApi.sqlite3_set_authorizer( thisDb(), ac ) ); } /** Object type for use with blobOpen() */ public final class Blob implements AutoCloseable { private Sqlite db; private sqlite3_blob b; Blob(Sqlite db, sqlite3_blob b){ this.db = db; this.b = b; } /** If this blob is still opened, its low-level handle is returned, else an IllegalArgumentException is thrown. */ private sqlite3_blob thisBlob(){ if( null==b || 0==b.getNativePointer() ){ throw new IllegalArgumentException("This Blob has been finalized."); } return b; } /** Analog to sqlite3_blob_close(). */ @Override public void close(){ if( null!=b ){ CApi.sqlite3_blob_close(b); b = null; db = null; } } /** Throws if the JVM does not have JNI-level support for ByteBuffer. */ private void checkNio(){ if( !Sqlite.JNI_SUPPORTS_NIO ){ throw new UnsupportedOperationException( "This JVM does not support JNI access to ByteBuffer." ); } } /** Analog to sqlite3_blob_reopen() but throws on error. */ public void reopen(long newRowId){ db.checkRc( CApi.sqlite3_blob_reopen(thisBlob(), newRowId) ); } /** Analog to sqlite3_blob_write() but throws on error. */ public void write( byte[] bytes, int atOffset ){ db.checkRc( CApi.sqlite3_blob_write(thisBlob(), bytes, atOffset) ); } /** Analog to sqlite3_blob_read() but throws on error. */ public void read( byte[] dest, int atOffset ){ db.checkRc( CApi.sqlite3_blob_read(thisBlob(), dest, atOffset) ); } /** Analog to sqlite3_blob_bytes(). */ public int bytes(){ return CApi.sqlite3_blob_bytes(thisBlob()); } } /** Analog to sqlite3_blob_open(). Returns a Blob object for the given database, table, column, and rowid. The blob is opened for read-write mode if writeable is true, else it is read-only. The returned object must eventually be freed, before this database is closed, by either arranging for it to be auto-closed or calling its close() method. Throws on error. */ public Blob blobOpen(String dbName, String tableName, String columnName, long iRow, boolean writeable){ final OutputPointer.sqlite3_blob out = new OutputPointer.sqlite3_blob(); checkRc( CApi.sqlite3_blob_open(thisDb(), dbName, tableName, columnName, iRow, writeable ? 1 : 0, out) ); return new Blob(this, out.take()); } /** Callback for use with libConfigLog(). */ public interface ConfigLog { /** Must function as described for a C-level callback for sqlite3_config()'s SQLITE_CONFIG_LOG callback, with the slight signature change. Any exceptions thrown from this callback are necessarily suppressed. */ void call(int errCode, String msg); } /** Analog to sqlite3_config() with the SQLITE_CONFIG_LOG option, this sets or (if log is null) clears the current logger. */ public static void libConfigLog(ConfigLog log){ final org.sqlite.jni.capi.ConfigLogCallback l = null==log ? null : new org.sqlite.jni.capi.ConfigLogCallback() { @Override public void call(int errCode, String msg){ log.call(errCode, msg); } }; checkRcStatic(CApi.sqlite3_config(l)); } /** Callback for use with libConfigSqlLog(). */ public interface ConfigSqlLog { /** Must function as described for a C-level callback for sqlite3_config()'s SQLITE_CONFIG_SQLLOG callback, with the slight signature change. Any exceptions thrown from this callback are necessarily suppressed. */ void call(Sqlite db, String msg, int msgType); } /** Analog to sqlite3_config() with the SQLITE_CONFIG_SQLLOG option, this sets or (if log is null) clears the current logger. If SQLite is built without SQLITE_ENABLE_SQLLOG defined then this will throw an UnsupportedOperationException. */ public static void libConfigSqlLog(ConfigSqlLog log){ Sqlite.checkSupported(hasNormalizeSql, "SQLITE_ENABLE_SQLLOG"); final org.sqlite.jni.capi.ConfigSqlLogCallback l = null==log ? null : new org.sqlite.jni.capi.ConfigSqlLogCallback() { @Override public void call(sqlite3 db, String msg, int msgType){ try{ log.call(fromNative(db), msg, msgType); }catch(Exception e){ /* Suppressed */ } } }; checkRcStatic(CApi.sqlite3_config(l)); } /** Analog to the C-level sqlite3_config() with one of the SQLITE_CONFIG_... constants defined as CONFIG_... in this class. Throws on error, including passing of an unknown option or if a specified option is not supported by the underlying build of the SQLite library. */ public static void libConfigOp( int op ){ checkRcStatic(CApi.sqlite3_config(op)); } } |
Changes to ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; | | | | | | | > > > | | | | | | | | | | 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 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; import org.sqlite.jni.capi.CApi; import org.sqlite.jni.capi.sqlite3; /** A wrapper for communicating C-level (sqlite3*) instances with Java. These wrappers do not own their associated pointer, they simply provide a type-safe way to communicate it between Java and C via JNI. */ public final class SqliteException extends java.lang.RuntimeException { private int errCode = CApi.SQLITE_ERROR; private int xerrCode = CApi.SQLITE_ERROR; private int errOffset = -1; private int sysErrno = 0; /** Records the given error string and uses SQLITE_ERROR for both the error code and extended error code. */ public SqliteException(String msg){ super(msg); } /** Uses sqlite3_errstr(sqlite3ResultCode) for the error string and sets both the error code and extended error code to the given value. This approach includes no database-level information and systemErrno() will be 0, so is intended only for use with sqlite3 APIs for which a result code is not an error but which the higher-level wrapper should treat as one. */ public SqliteException(int sqlite3ResultCode){ super(CApi.sqlite3_errstr(sqlite3ResultCode)); errCode = xerrCode = sqlite3ResultCode; } /** Records the current error state of db (which must not be null and must refer to an opened db object). Note that this does not close the db. Design note: closing the db on error is really only useful during a failed db-open operation, and the place(s) where that can happen are inside this library, not client-level code. */ SqliteException(sqlite3 db){ super(CApi.sqlite3_errmsg(db)); errCode = CApi.sqlite3_errcode(db); xerrCode = CApi.sqlite3_extended_errcode(db); errOffset = CApi.sqlite3_error_offset(db); sysErrno = CApi.sqlite3_system_errno(db); } /** Records the current error state of db (which must not be null and must refer to an open database). */ public SqliteException(Sqlite db){ this(db.nativeHandle()); } public SqliteException(Sqlite.Stmt stmt){ this(stmt.getDb()); } public int errcode(){ return errCode; } public int extendedErrcode(){ return xerrCode; } public int errorOffset(){ return errOffset; } public int systemErrno(){ return sysErrno; } |
︙ | ︙ |
Changes to ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains a set of tests for the sqlite3 JNI bindings. */ package org.sqlite.jni.wrapper1; | < | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains a set of tests for the sqlite3 JNI bindings. */ package org.sqlite.jni.wrapper1; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.sqlite.jni.capi.CApi; /** An annotation for Tester2 tests which we do not want to run in reflection-driven test mode because either they are not suitable for multi-threaded threaded mode or we have to control their execution order. */ |
︙ | ︙ | |||
42 43 44 45 46 47 48 | //! True when running in multi-threaded mode. private static boolean mtMode = false; //! True to sleep briefly between tests. private static boolean takeNaps = false; //! True to shuffle the order of the tests. private static boolean shuffle = false; //! True to dump the list of to-run tests to stdout. | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | //! True when running in multi-threaded mode. private static boolean mtMode = false; //! True to sleep briefly between tests. private static boolean takeNaps = false; //! True to shuffle the order of the tests. private static boolean shuffle = false; //! True to dump the list of to-run tests to stdout. private static int listRunTests = 0; //! True to squelch all out() and outln() output. private static boolean quietMode = false; //! Total number of runTests() calls. private static int nTestRuns = 0; //! List of test*() methods to run. private static List<java.lang.reflect.Method> testMethods = null; //! List of exceptions collected by run() |
︙ | ︙ | |||
121 122 123 124 125 126 127 | } public static void affirm(Boolean v){ affirm(v, "Affirmation failed."); } | | > > > > > | | < < < < | < > | < < < | < < < < < < | < < | < < | > > | < < > | | > > > | < < < < | | < < < < < < < < | | | > > > > > > > > > > > > > < < | | < < | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | | | | > | > > > | > > | > > > > | > > > | < | > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | | 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 941 942 943 944 945 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 | } public static void affirm(Boolean v){ affirm(v, "Affirmation failed."); } public static void execSql(Sqlite db, String sql[]){ execSql(db, String.join("", sql)); } /** Executes all SQL statements in the given string. If throwOnError is true then it will throw for any prepare/step errors, else it will return the corresponding non-0 result code. */ public static int execSql(Sqlite dbw, boolean throwOnError, String sql){ final ValueHolder<Integer> rv = new ValueHolder<>(0); final Sqlite.PrepareMulti pm = new Sqlite.PrepareMulti(){ @Override public void call(Sqlite.Stmt stmt){ try{ while( Sqlite.ROW == (rv.value = stmt.step(throwOnError)) ){} } finally{ stmt.finalizeStmt(); } } }; try { dbw.prepareMulti(sql, pm); }catch(SqliteException se){ if( throwOnError ){ throw se; }else{ /* This error (likely) happened in the prepare() phase and we need to preempt it. */ rv.value = se.errcode(); } } return (rv.value==Sqlite.DONE) ? 0 : rv.value; } static void execSql(Sqlite db, String sql){ execSql(db, true, sql); } @SingleThreadOnly /* because it's thread-agnostic */ private void test1(){ affirm(Sqlite.libVersionNumber() == CApi.SQLITE_VERSION_NUMBER); } private void nap() throws InterruptedException { if( takeNaps ){ Thread.sleep(java.util.concurrent.ThreadLocalRandom.current().nextInt(3, 17), 0); } } Sqlite openDb(String name){ final Sqlite db = Sqlite.open(name, Sqlite.OPEN_READWRITE| Sqlite.OPEN_CREATE| Sqlite.OPEN_EXRESCODE); ++metrics.dbOpen; return db; } Sqlite openDb(){ return openDb(":memory:"); } void testOpenDb1(){ Sqlite db = openDb(); affirm( 0!=db.nativeHandle().getNativePointer() ); affirm( "main".equals( db.dbName(0) ) ); db.setMainDbName("foo"); affirm( "foo".equals( db.dbName(0) ) ); affirm( db.dbConfig(Sqlite.DBCONFIG_DEFENSIVE, true) /* The underlying function has different mangled names in jdk8 vs jdk19, and this call is here to ensure that the build fails if it cannot find both names. */ ); affirm( !db.dbConfig(Sqlite.DBCONFIG_DEFENSIVE, false) ); SqliteException ex = null; try{ db.dbConfig(0, false); } catch(SqliteException e){ ex = e; } affirm( null!=ex ); ex = null; db.close(); affirm( null==db.nativeHandle() ); try{ db = openDb("/no/such/dir/.../probably"); } catch(SqliteException e){ ex = e; } affirm( ex!=null ); affirm( ex.errcode() != 0 ); affirm( ex.extendedErrcode() != 0 ); affirm( ex.errorOffset() < 0 ); // there's no reliable way to predict what ex.systemErrno() might be } void testPrepare1(){ try (Sqlite db = openDb()) { Sqlite.Stmt stmt = db.prepare("SELECT ?1"); Exception e = null; affirm( null!=stmt.nativeHandle() ); affirm( db == stmt.getDb() ); affirm( 1==stmt.bindParameterCount() ); affirm( "?1".equals(stmt.bindParameterName(1)) ); affirm( null==stmt.bindParameterName(2) ); stmt.bindInt64(1, 1); stmt.bindDouble(1, 1.1); stmt.bindObject(1, db); stmt.bindNull(1); stmt.bindText(1, new byte[] {32,32,32}); stmt.bindText(1, "123"); stmt.bindText16(1, "123".getBytes(StandardCharsets.UTF_16)); stmt.bindText16(1, "123"); stmt.bindZeroBlob(1, 8); stmt.bindBlob(1, new byte[] {1,2,3,4}); stmt.bindInt(1, 17); try{ stmt.bindInt(2,1); } catch(Exception ex){ e = ex; } affirm( null!=e ); e = null; affirm( stmt.step() ); try{ stmt.columnInt(1); } catch(Exception ex){ e = ex; } affirm( null!=e ); e = null; affirm( 17 == stmt.columnInt(0) ); affirm( 17L == stmt.columnInt64(0) ); affirm( 17.0 == stmt.columnDouble(0) ); affirm( "17".equals(stmt.columnText16(0)) ); affirm( !stmt.step() ); stmt.reset(); affirm( Sqlite.ROW==stmt.step(false) ); affirm( !stmt.step() ); affirm( 0 == stmt.finalizeStmt() ); affirm( null==stmt.nativeHandle() ); stmt = db.prepare("SELECT ?"); stmt.bindObject(1, db); affirm( Sqlite.ROW == stmt.step(false) ); affirm( db==stmt.columnObject(0) ); affirm( db==stmt.columnObject(0, Sqlite.class ) ); affirm( null==stmt.columnObject(0, Sqlite.Stmt.class ) ); affirm( 0==stmt.finalizeStmt() ) /* getting a non-0 out of sqlite3_finalize() is tricky */; affirm( null==stmt.nativeHandle() ); } } void testUdfScalar(){ final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0); try (Sqlite db = openDb()) { execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)"); final ValueHolder<Integer> vh = new ValueHolder<>(0); final ScalarFunction f = new ScalarFunction(){ public void xFunc(SqlFunction.Arguments args){ affirm( db == args.getDb() ); for( SqlFunction.Arguments.Arg arg : args ){ vh.value += arg.getInt(); } args.resultInt(vh.value); } public void xDestroy(){ ++xDestroyCalled.value; } }; db.createFunction("myfunc", -1, f); Sqlite.Stmt q = db.prepare("select myfunc(1,2,3)"); affirm( q.step() ); affirm( 6 == vh.value ); affirm( 6 == q.columnInt(0) ); q.finalizeStmt(); affirm( 0 == xDestroyCalled.value ); vh.value = 0; q = db.prepare("select myfunc(-1,-2,-3)"); affirm( q.step() ); affirm( -6 == vh.value ); affirm( -6 == q.columnInt(0) ); affirm( 0 == xDestroyCalled.value ); q.finalizeStmt(); } affirm( 1 == xDestroyCalled.value ); } void testUdfAggregate(){ final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0); Sqlite.Stmt q = null; try (Sqlite db = openDb()) { execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)"); final AggregateFunction f = new AggregateFunction<Integer>(){ public void xStep(SqlFunction.Arguments args){ final ValueHolder<Integer> agg = this.getAggregateState(args, 0); for( SqlFunction.Arguments.Arg arg : args ){ agg.value += arg.getInt(); } } public void xFinal(SqlFunction.Arguments args){ final Integer v = this.takeAggregateState(args); if( null==v ) args.resultNull(); else args.resultInt(v); } public void xDestroy(){ ++xDestroyCalled.value; } }; db.createFunction("summer", 1, f); q = db.prepare( "with cte(v) as ("+ "select 3 union all select 5 union all select 7"+ ") select summer(v), summer(v+1) from cte" /* ------------------^^^^^^^^^^^ ensures that we're handling sqlite3_aggregate_context() properly. */ ); affirm( q.step() ); affirm( 15==q.columnInt(0) ); q.finalizeStmt(); q = null; affirm( 0 == xDestroyCalled.value ); db.createFunction("summerN", -1, f); q = db.prepare("select summerN(1,8,9), summerN(2,3,4)"); affirm( q.step() ); affirm( 18==q.columnInt(0) ); affirm( 9==q.columnInt(1) ); q.finalizeStmt(); q = null; }/*db*/ finally{ if( null!=q ) q.finalizeStmt(); } affirm( 2 == xDestroyCalled.value /* because we've bound the same instance twice */ ); } private void testUdfWindow(){ final Sqlite db = openDb(); /* Example window function, table, and results taken from: https://sqlite.org/windowfunctions.html#udfwinfunc */ final WindowFunction func = new WindowFunction<Integer>(){ //! Impl of xStep() and xInverse() private void xStepInverse(SqlFunction.Arguments args, int v){ this.getAggregateState(args,0).value += v; } @Override public void xStep(SqlFunction.Arguments args){ this.xStepInverse(args, args.getInt(0)); } @Override public void xInverse(SqlFunction.Arguments args){ this.xStepInverse(args, -args.getInt(0)); } //! Impl of xFinal() and xValue() private void xFinalValue(SqlFunction.Arguments args, Integer v){ if(null == v) args.resultNull(); else args.resultInt(v); } @Override public void xFinal(SqlFunction.Arguments args){ xFinalValue(args, this.takeAggregateState(args)); affirm( null == this.getAggregateState(args,null).value ); } @Override public void xValue(SqlFunction.Arguments args){ xFinalValue(args, this.getAggregateState(args,null).value); } }; db.createFunction("winsumint", 1, func); execSql(db, new String[] { "CREATE TEMP TABLE twin(x, y); INSERT INTO twin VALUES", "('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)" }); final Sqlite.Stmt stmt = db.prepare( "SELECT x, winsumint(y) OVER ("+ "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+ ") AS sum_y "+ "FROM twin ORDER BY x;" ); int n = 0; while( stmt.step() ){ final String s = stmt.columnText16(0); final int i = stmt.columnInt(1); switch(++n){ case 1: affirm( "a".equals(s) && 9==i ); break; case 2: affirm( "b".equals(s) && 12==i ); break; case 3: affirm( "c".equals(s) && 16==i ); break; case 4: affirm( "d".equals(s) && 12==i ); break; case 5: affirm( "e".equals(s) && 9==i ); break; default: affirm( false /* cannot happen */ ); } } stmt.close(); affirm( 5 == n ); db.close(); } private void testKeyword(){ final int n = Sqlite.keywordCount(); affirm( n>0 ); affirm( !Sqlite.keywordCheck("_nope_") ); affirm( Sqlite.keywordCheck("seLect") ); affirm( null!=Sqlite.keywordName(0) ); affirm( null!=Sqlite.keywordName(n-1) ); affirm( null==Sqlite.keywordName(n) ); } private void testExplain(){ final Sqlite db = openDb(); Sqlite.Stmt q = db.prepare("SELECT 1"); affirm( 0 == q.isExplain() ); q.explain(0); affirm( 0 == q.isExplain() ); q.explain(1); affirm( 1 == q.isExplain() ); q.explain(2); affirm( 2 == q.isExplain() ); Exception ex = null; try{ q.explain(-1); }catch(Exception e){ ex = e; } affirm( ex instanceof SqliteException ); q.finalizeStmt(); db.close(); } private void testTrace(){ final Sqlite db = openDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); /* Ensure that characters outside of the UTF BMP survive the trip from Java to sqlite3 and back to Java. (At no small efficiency penalty.) */ final String nonBmpChar = "😃"; db.trace( Sqlite.TRACE_ALL, new Sqlite.TraceCallback(){ @Override public void call(int traceFlag, Object pNative, Object x){ ++counter.value; //outln("TRACE "+traceFlag+" pNative = "+pNative.getClass().getName()); switch(traceFlag){ case Sqlite.TRACE_STMT: affirm(pNative instanceof Sqlite.Stmt); //outln("TRACE_STMT sql = "+x); affirm(x instanceof String); affirm( ((String)x).indexOf(nonBmpChar) > 0 ); break; case Sqlite.TRACE_PROFILE: affirm(pNative instanceof Sqlite.Stmt); affirm(x instanceof Long); //outln("TRACE_PROFILE time = "+x); break; case Sqlite.TRACE_ROW: affirm(pNative instanceof Sqlite.Stmt); affirm(null == x); //outln("TRACE_ROW = "+sqlite3_column_text16((sqlite3_stmt)pNative, 0)); break; case Sqlite.TRACE_CLOSE: affirm(pNative instanceof Sqlite); affirm(null == x); break; default: affirm(false /*cannot happen*/); break; } } }); execSql(db, "SELECT coalesce(null,null,'"+nonBmpChar+"'); "+ "SELECT 'w"+nonBmpChar+"orld'"); affirm( 6 == counter.value ); db.close(); affirm( 7 == counter.value ); } private void testStatus(){ final Sqlite db = openDb(); execSql(db, "create table t(a); insert into t values(1),(2),(3)"); Sqlite.Status s = Sqlite.libStatus(Sqlite.STATUS_MEMORY_USED, false); affirm( s.current > 0 ); affirm( s.peak >= s.current ); s = db.status(Sqlite.DBSTATUS_SCHEMA_USED, false); affirm( s.current > 0 ); affirm( s.peak == 0 /* always 0 for SCHEMA_USED */ ); db.close(); } @SingleThreadOnly /* because multiple threads legitimately make these results unpredictable */ private synchronized void testAutoExtension(){ final ValueHolder<Integer> val = new ValueHolder<>(0); final ValueHolder<String> toss = new ValueHolder<>(null); final Sqlite.AutoExtension ax = new Sqlite.AutoExtension(){ @Override public void call(Sqlite db){ ++val.value; if( null!=toss.value ){ throw new RuntimeException(toss.value); } } }; Sqlite.addAutoExtension(ax); openDb().close(); affirm( 1==val.value ); openDb().close(); affirm( 2==val.value ); Sqlite.clearAutoExtensions(); openDb().close(); affirm( 2==val.value ); Sqlite.addAutoExtension( ax ); Sqlite.addAutoExtension( ax ); // Must not add a second entry Sqlite.addAutoExtension( ax ); // or a third one openDb().close(); affirm( 3==val.value ); Sqlite db = openDb(); affirm( 4==val.value ); execSql(db, "ATTACH ':memory:' as foo"); affirm( 4==val.value, "ATTACH uses the same connection, not sub-connections." ); db.close(); db = null; Sqlite.removeAutoExtension(ax); openDb().close(); affirm( 4==val.value ); Sqlite.addAutoExtension(ax); Exception err = null; toss.value = "Throwing from auto_extension."; try{ openDb(); }catch(Exception e){ err = e; } affirm( err!=null ); affirm( err.getMessage().indexOf(toss.value)>=0 ); toss.value = null; val.value = 0; final Sqlite.AutoExtension ax2 = new Sqlite.AutoExtension(){ @Override public void call(Sqlite db){ ++val.value; } }; Sqlite.addAutoExtension(ax2); openDb().close(); affirm( 2 == val.value ); Sqlite.removeAutoExtension(ax); openDb().close(); affirm( 3 == val.value ); Sqlite.addAutoExtension(ax); openDb().close(); affirm( 5 == val.value ); Sqlite.removeAutoExtension(ax2); openDb().close(); affirm( 6 == val.value ); Sqlite.addAutoExtension(ax2); openDb().close(); affirm( 8 == val.value ); Sqlite.clearAutoExtensions(); openDb().close(); affirm( 8 == val.value ); } private void testBackup(){ final Sqlite dbDest = openDb(); try (Sqlite dbSrc = openDb()) { execSql(dbSrc, new String[]{ "pragma page_size=512; VACUUM;", "create table t(a);", "insert into t(a) values(1),(2),(3);" }); Exception e = null; try { dbSrc.initBackup("main",dbSrc,"main"); }catch(Exception x){ e = x; } affirm( e instanceof SqliteException ); e = null; try (Sqlite.Backup b = dbDest.initBackup("main",dbSrc,"main")) { affirm( null!=b ); int rc; while( Sqlite.DONE!=(rc = b.step(1)) ){ affirm( 0==rc ); } affirm( b.pageCount() > 0 ); b.finish(); } } try (Sqlite.Stmt q = dbDest.prepare("SELECT sum(a) from t")) { q.step(); affirm( q.columnInt(0) == 6 ); } dbDest.close(); } private void testCollation(){ final Sqlite db = openDb(); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); final Sqlite.Collation myCollation = new Sqlite.Collation() { private String myState = "this is local state. There is much like it, but this is mine."; @Override // Reverse-sorts its inputs... public int call(byte[] lhs, byte[] rhs){ int len = lhs.length > rhs.length ? rhs.length : lhs.length; int c = 0, i = 0; for(i = 0; i < len; ++i){ c = lhs[i] - rhs[i]; if(0 != c) break; } if(0==c){ if(i < lhs.length) c = 1; else if(i < rhs.length) c = -1; } return -c; } }; final Sqlite.CollationNeeded collLoader = new Sqlite.CollationNeeded(){ @Override public void call(Sqlite dbArg, int eTextRep, String collationName){ affirm(dbArg == db); db.createCollation("reversi", eTextRep, myCollation); } }; db.onCollationNeeded(collLoader); Sqlite.Stmt stmt = db.prepare("SELECT a FROM t ORDER BY a COLLATE reversi"); int counter = 0; while( stmt.step() ){ final String val = stmt.columnText16(0); ++counter; switch(counter){ case 1: affirm("c".equals(val)); break; case 2: affirm("b".equals(val)); break; case 3: affirm("a".equals(val)); break; } } affirm(3 == counter); stmt.finalizeStmt(); stmt = db.prepare("SELECT a FROM t ORDER BY a"); counter = 0; while( stmt.step() ){ final String val = stmt.columnText16(0); ++counter; //outln("Non-REVERSI'd row#"+counter+": "+val); switch(counter){ case 3: affirm("c".equals(val)); break; case 2: affirm("b".equals(val)); break; case 1: affirm("a".equals(val)); break; } } affirm(3 == counter); stmt.finalizeStmt(); db.onCollationNeeded(null); db.close(); } @SingleThreadOnly /* because threads inherently break this test */ private void testBusy(){ final String dbName = "_busy-handler.db"; try{ Sqlite db1 = openDb(dbName); ++metrics.dbOpen; execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)"); Sqlite db2 = openDb(dbName); ++metrics.dbOpen; final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0); Sqlite.BusyHandler handler = new Sqlite.BusyHandler(){ @Override public int call(int n){ return n > 2 ? 0 : ++xBusyCalled.value; } }; db2.setBusyHandler(handler); // Force a locked condition... execSql(db1, "BEGIN EXCLUSIVE"); int rc = 0; SqliteException ex = null; try{ db2.prepare("SELECT * from t"); }catch(SqliteException x){ ex = x; } affirm( null!=ex ); affirm( Sqlite.BUSY == ex.errcode() ); affirm( 3 == xBusyCalled.value ); db1.close(); db2.close(); }finally{ try{(new java.io.File(dbName)).delete();} catch(Exception e){/* ignore */} } } private void testCommitHook(){ final Sqlite db = openDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); final ValueHolder<Integer> hookResult = new ValueHolder<>(0); final Sqlite.CommitHook theHook = new Sqlite.CommitHook(){ @Override public int call(){ ++counter.value; return hookResult.value; } }; Sqlite.CommitHook oldHook = db.setCommitHook(theHook); affirm( null == oldHook ); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); affirm( 2 == counter.value ); execSql(db, "BEGIN; SELECT 1; SELECT 2; COMMIT;"); affirm( 2 == counter.value /* NOT invoked if no changes are made */ ); execSql(db, "BEGIN; update t set a='d' where a='c'; COMMIT;"); affirm( 3 == counter.value ); oldHook = db.setCommitHook(theHook); affirm( theHook == oldHook ); execSql(db, "BEGIN; update t set a='e' where a='d'; COMMIT;"); affirm( 4 == counter.value ); oldHook = db.setCommitHook(null); affirm( theHook == oldHook ); execSql(db, "BEGIN; update t set a='f' where a='e'; COMMIT;"); affirm( 4 == counter.value ); oldHook = db.setCommitHook(null); affirm( null == oldHook ); execSql(db, "BEGIN; update t set a='g' where a='f'; COMMIT;"); affirm( 4 == counter.value ); final Sqlite.CommitHook newHook = new Sqlite.CommitHook(){ @Override public int call(){return 0;} }; oldHook = db.setCommitHook(newHook); affirm( null == oldHook ); execSql(db, "BEGIN; update t set a='h' where a='g'; COMMIT;"); affirm( 4 == counter.value ); oldHook = db.setCommitHook(theHook); affirm( newHook == oldHook ); execSql(db, "BEGIN; update t set a='i' where a='h'; COMMIT;"); affirm( 5 == counter.value ); hookResult.value = Sqlite.ERROR; int rc = execSql(db, false, "BEGIN; update t set a='j' where a='i'; COMMIT;"); affirm( Sqlite.CONSTRAINT_COMMITHOOK == rc ); affirm( 6 == counter.value ); db.close(); } private void testRollbackHook(){ final Sqlite db = openDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); final Sqlite.RollbackHook theHook = new Sqlite.RollbackHook(){ @Override public void call(){ ++counter.value; } }; Sqlite.RollbackHook oldHook = db.setRollbackHook(theHook); affirm( null == oldHook ); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); affirm( 0 == counter.value ); execSql(db, false, "BEGIN; SELECT 1; SELECT 2; ROLLBACK;"); affirm( 1 == counter.value /* contra to commit hook, is invoked if no changes are made */ ); final Sqlite.RollbackHook newHook = new Sqlite.RollbackHook(){ @Override public void call(){} }; oldHook = db.setRollbackHook(newHook); affirm( theHook == oldHook ); execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); affirm( 1 == counter.value ); oldHook = db.setRollbackHook(theHook); affirm( newHook == oldHook ); execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); affirm( 2 == counter.value ); int rc = execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); affirm( 0 == rc ); affirm( 3 == counter.value ); db.close(); } private void testUpdateHook(){ final Sqlite db = openDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); final ValueHolder<Integer> expectedOp = new ValueHolder<>(0); final Sqlite.UpdateHook theHook = new Sqlite.UpdateHook(){ @Override public void call(int opId, String dbName, String tableName, long rowId){ ++counter.value; if( 0!=expectedOp.value ){ affirm( expectedOp.value == opId ); } } }; Sqlite.UpdateHook oldHook = db.setUpdateHook(theHook); affirm( null == oldHook ); expectedOp.value = Sqlite.INSERT; execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); affirm( 3 == counter.value ); expectedOp.value = Sqlite.UPDATE; execSql(db, "update t set a='d' where a='c';"); affirm( 4 == counter.value ); oldHook = db.setUpdateHook(theHook); affirm( theHook == oldHook ); expectedOp.value = Sqlite.DELETE; execSql(db, "DELETE FROM t where a='d'"); affirm( 5 == counter.value ); oldHook = db.setUpdateHook(null); affirm( theHook == oldHook ); execSql(db, "update t set a='e' where a='b';"); affirm( 5 == counter.value ); oldHook = db.setUpdateHook(null); affirm( null == oldHook ); final Sqlite.UpdateHook newHook = new Sqlite.UpdateHook(){ @Override public void call(int opId, String dbName, String tableName, long rowId){ } }; oldHook = db.setUpdateHook(newHook); affirm( null == oldHook ); execSql(db, "update t set a='h' where a='a'"); affirm( 5 == counter.value ); oldHook = db.setUpdateHook(theHook); affirm( newHook == oldHook ); expectedOp.value = Sqlite.UPDATE; execSql(db, "update t set a='i' where a='h'"); affirm( 6 == counter.value ); db.close(); } private void testProgress(){ final Sqlite db = openDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); db.setProgressHandler(1, new Sqlite.ProgressHandler(){ @Override public int call(){ ++counter.value; return 0; } }); execSql(db, "SELECT 1; SELECT 2;"); affirm( counter.value > 0 ); int nOld = counter.value; db.setProgressHandler(0, null); execSql(db, "SELECT 1; SELECT 2;"); affirm( nOld == counter.value ); db.close(); } private void testAuthorizer(){ final Sqlite db = openDb(); final ValueHolder<Integer> counter = new ValueHolder<>(0); final ValueHolder<Integer> authRc = new ValueHolder<>(0); final Sqlite.Authorizer auth = new Sqlite.Authorizer(){ public int call(int op, String s0, String s1, String s2, String s3){ ++counter.value; //outln("xAuth(): "+s0+" "+s1+" "+s2+" "+s3); return authRc.value; } }; execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); db.setAuthorizer(auth); execSql(db, "UPDATE t SET a=1"); affirm( 1 == counter.value ); authRc.value = Sqlite.DENY; int rc = execSql(db, false, "UPDATE t SET a=2"); affirm( Sqlite.AUTH==rc ); db.setAuthorizer(null); rc = execSql(db, false, "UPDATE t SET a=2"); affirm( 0==rc ); db.close(); } private void testBlobOpen(){ final Sqlite db = openDb(); execSql(db, "CREATE TABLE T(a BLOB);" +"INSERT INTO t(rowid,a) VALUES(1, 'def'),(2, 'XYZ');" ); Sqlite.Blob b = db.blobOpen("main", "t", "a", db.lastInsertRowId(), true); affirm( 3==b.bytes() ); b.write(new byte[] {100, 101, 102 /*"DEF"*/}, 0); b.close(); Sqlite.Stmt stmt = db.prepare("SELECT length(a), a FROM t ORDER BY a"); affirm( stmt.step() ); affirm( 3 == stmt.columnInt(0) ); affirm( "def".equals(stmt.columnText16(1)) ); stmt.finalizeStmt(); b = db.blobOpen("main", "t", "a", db.lastInsertRowId(), false); final byte[] tgt = new byte[3]; b.read( tgt, 0 ); affirm( 100==tgt[0] && 101==tgt[1] && 102==tgt[2], "DEF" ); execSql(db,"UPDATE t SET a=zeroblob(10) WHERE rowid=2"); b.close(); b = db.blobOpen("main", "t", "a", db.lastInsertRowId(), true); byte[] bw = new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; b.write(bw, 0); byte[] br = new byte[10]; b.read(br, 0); for( int i = 0; i < br.length; ++i ){ affirm(bw[i] == br[i]); } b.close(); db.close(); } void testPrepareMulti(){ final ValueHolder<Integer> fCount = new ValueHolder<>(0); final ValueHolder<Integer> mCount = new ValueHolder<>(0); try (Sqlite db = openDb()) { execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)"); db.createFunction("counter", -1, new ScalarFunction(){ @Override public void xFunc(SqlFunction.Arguments args){ ++fCount.value; args.resultNull(); } } ); final Sqlite.PrepareMulti pm = new Sqlite.PrepareMultiFinalize( new Sqlite.PrepareMulti() { @Override public void call(Sqlite.Stmt q){ ++mCount.value; while(q.step()){} } } ); final String sql = "select counter(*) from t;"+ "select counter(*) from t; /* comment */"+ "select counter(*) from t; -- comment\n" ; db.prepareMulti(sql, pm); } affirm( 3 == mCount.value ); affirm( 9 == fCount.value ); } /* Copy/paste/rename this to add new tests. */ private void _testTemplate(){ try (Sqlite db = openDb()) { Sqlite.Stmt stmt = db.prepare("SELECT 1"); stmt.finalizeStmt(); } } private void runTests(boolean fromThread) throws Exception { List<java.lang.reflect.Method> mlist = testMethods; affirm( null!=mlist ); if( shuffle ){ mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) ); java.util.Collections.shuffle(mlist); } if( (!fromThread && listRunTests>0) || listRunTests>1 ){ synchronized(this.getClass()){ if( !fromThread ){ out("Initial test"," list: "); for(java.lang.reflect.Method m : testMethods){ out(m.getName()+" "); } outln(); |
︙ | ︙ | |||
340 341 342 343 344 345 346 | try { runTests(0!=this.tId); }catch(Exception e){ synchronized( listErrors ){ listErrors.add(e); } }finally{ | | < | 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 | try { runTests(0!=this.tId); }catch(Exception e){ synchronized( listErrors ){ listErrors.add(e); } }finally{ Sqlite.uncacheThread(); } } /** Runs the basic sqlite3 JNI binding sanity-check suite. CLI flags: |
︙ | ︙ | |||
364 365 366 367 368 369 370 | -shuffle: randomizes the order of most of the test functions. -naps: sleep small random intervals between tests in order to add some chaos for cross-thread contention. -list-tests: outputs the list of tests being run, minus some | | > > | 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 | -shuffle: randomizes the order of most of the test functions. -naps: sleep small random intervals between tests in order to add some chaos for cross-thread contention. -list-tests: outputs the list of tests being run, minus some which are hard-coded. In multi-threaded mode, use this twice to to emit the list run by each thread (which may differ from the initial list, in particular if -shuffle is used). -fail: forces an exception to be thrown during the test run. Use with -shuffle to make its appearance unpredictable. -v: emit some developer-mode info at the end. */ public static void main(String[] args) throws Exception { |
︙ | ︙ | |||
393 394 395 396 397 398 399 | }else if(arg.equals("t") || arg.equals("thread")){ nThread = Integer.parseInt(args[i++]); }else if(arg.equals("r") || arg.equals("repeat")){ nRepeat = Integer.parseInt(args[i++]); }else if(arg.equals("shuffle")){ shuffle = true; }else if(arg.equals("list-tests")){ | | | | | < < > | < < < < | | < < > | < < < < | 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 | }else if(arg.equals("t") || arg.equals("thread")){ nThread = Integer.parseInt(args[i++]); }else if(arg.equals("r") || arg.equals("repeat")){ nRepeat = Integer.parseInt(args[i++]); }else if(arg.equals("shuffle")){ shuffle = true; }else if(arg.equals("list-tests")){ ++listRunTests; }else if(arg.equals("fail")){ forceFail = true; }else if(arg.equals("sqllog")){ sqlLog = true; }else if(arg.equals("configlog")){ configLog = true; }else if(arg.equals("naps")){ takeNaps = true; }else if(arg.equals("q") || arg.equals("quiet")){ squelchTestOutput = true; }else{ throw new IllegalArgumentException("Unhandled flag:"+arg); } } } if( sqlLog ){ if( Sqlite.compileOptionUsed("ENABLE_SQLLOG") ){ Sqlite.libConfigSqlLog( new Sqlite.ConfigSqlLog() { @Override public void call(Sqlite db, String msg, int op){ switch(op){ case 0: outln("Opening db: ",db); break; case 1: outln("SQL ",db,": ",msg); break; case 2: outln("Closing db: ",db); break; } } } ); }else{ outln("WARNING: -sqllog is not active because library was built ", "without SQLITE_ENABLE_SQLLOG."); } } if( configLog ){ Sqlite.libConfigLog( new Sqlite.ConfigLog() { @Override public void call(int code, String msg){ outln("ConfigLog: ",Sqlite.errstr(code),": ", msg); }; } ); } quietMode = squelchTestOutput; outln("If you just saw warning messages regarding CallStaticObjectMethod, ", "you are very likely seeing the side effects of a known openjdk8 ", "bug. It is unsightly but does not affect the library."); |
︙ | ︙ | |||
476 477 478 479 480 481 482 | } } } if( nSkipped>0 ) out("\n"); } final long timeStart = System.currentTimeMillis(); | < < < < < < < < < < < < < < < < < < < < < < < < | | > | 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | } } } if( nSkipped>0 ) out("\n"); } final long timeStart = System.currentTimeMillis(); outln("libversion_number: ", Sqlite.libVersionNumber(),"\n", Sqlite.libVersion(),"\n",Sqlite.libSourceId(),"\n", "SQLITE_THREADSAFE=",CApi.sqlite3_threadsafe()); final boolean showLoopCount = (nRepeat>1 && nThread>1); if( showLoopCount ){ outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each."); } if( takeNaps ) outln("Napping between tests is enabled."); int nLoop = 0; for( int n = 0; n < nRepeat; ++n ){ ++nLoop; if( showLoopCount ) out((1==nLoop ? "" : " ")+nLoop); if( nThread<=1 ){ new Tester2(0).runTests(false); continue; } |
︙ | ︙ | |||
550 551 552 553 554 555 556 | final long timeEnd = System.currentTimeMillis(); outln("Tests done. Metrics across ",nTestRuns," total iteration(s):"); outln("\tAssertions checked: ",affirmCount); outln("\tDatabases opened: ",metrics.dbOpen); if( doSomethingForDev ){ CApi.sqlite3_jni_internal_details(); } | | | 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 | final long timeEnd = System.currentTimeMillis(); outln("Tests done. Metrics across ",nTestRuns," total iteration(s):"); outln("\tAssertions checked: ",affirmCount); outln("\tDatabases opened: ",metrics.dbOpen); if( doSomethingForDev ){ CApi.sqlite3_jni_internal_details(); } affirm( 0==Sqlite.libReleaseMemory(1) ); CApi.sqlite3_shutdown(); int nMethods = 0; int nNatives = 0; int nCanonical = 0; final java.lang.reflect.Method[] declaredMethods = CApi.class.getDeclaredMethods(); for(java.lang.reflect.Method m : declaredMethods){ |
︙ | ︙ |
Changes to ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2023-10-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. ** ************************************************************************* | | | | 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 | /* ** 2023-10-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. ** ************************************************************************* ** This file contains the ValueHolder utility class. */ package org.sqlite.jni.wrapper1; /** A helper class which simply holds a single value. Its primary use is for communicating values out of anonymous callbacks, as doing so requires a "final" reference. */ public class ValueHolder<T> { public T value; public ValueHolder(){} public ValueHolder(T v){value = v;} } |
Added ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 2023-10-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. ** ************************************************************************* ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; /** A SqlFunction implementation for window functions. The T type represents the type of data accumulated by this function while it works. e.g. a SUM()-like UDF might use Integer or Long and a CONCAT()-like UDF might use a StringBuilder or a List<String>. */ public abstract class WindowFunction<T> extends AggregateFunction<T> { /** As for the xInverse() argument of the C API's sqlite3_create_window_function(). If this function throws, the exception is reported via sqlite3_result_error(). */ public abstract void xInverse(SqlFunction.Arguments args); /** As for the xValue() argument of the C API's sqlite3_create_window_function(). If this function throws, it is translated into sqlite3_result_error(). Note that the passed-in object will not actually contain any arguments for xValue() but will contain the context object needed for setting the call's result or error state. */ public abstract void xValue(SqlFunction.Arguments args); } |
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/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/randomjson.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 | ** ** gcc --shared -fPIC -o randomjson.so -I. ext/misc/randomjson.c ** ** USING FROM THE CLI: ** ** .load ./randomjson ** SELECT random_json(1); */ | > > > > | | > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ** ** gcc --shared -fPIC -o randomjson.so -I. ext/misc/randomjson.c ** ** USING FROM THE CLI: ** ** .load ./randomjson ** SELECT random_json(1); ** SELECT random_json5(1); */ #ifdef SQLITE_STATIC_RANDOMJSON # include "sqlite3.h" #else # include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #endif #include <assert.h> #include <string.h> #include <stdlib.h> /* Pseudo-random number generator */ typedef struct Prng { unsigned int x, y; |
︙ | ︙ | |||
47 48 49 50 51 52 53 | /* Extract a random number */ static unsigned int prngInt(Prng *p){ p->x = (p->x>>1) ^ ((1+~(p->x&1)) & 0xd0000001); p->y = p->y*1103515245 + 12345; return p->x ^ p->y; } | | | | | > | > > > > | | | | < < > | > > | < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | /* Extract a random number */ static unsigned int prngInt(Prng *p){ p->x = (p->x>>1) ^ ((1+~(p->x&1)) & 0xd0000001); p->y = p->y*1103515245 + 12345; return p->x ^ p->y; } static char *azJsonAtoms[] = { /* JSON JSON-5 */ "0", "0", "1", "1", "-1", "-1", "2", "+2", "3DDDD", "3DDDD", "2.5DD", "2.5DD", "0.75", ".75", "-4.0e2", "-4.e2", "5.0e-3", "+5e-3", "6.DDe+0DD", "6.DDe+0DD", "0", "0x0", "512", "0x200", "256", "+0x100", "-2748", "-0xabc", "true", "true", "false", "false", "null", "null", "9.0e999", "Infinity", "-9.0e999", "-Infinity", "9.0e999", "+Infinity", "null", "NaN", "-0.0005DD", "-0.0005DD", "4.35e-3", "+4.35e-3", "\"gem\\\"hay\"", "\"gem\\\"hay\"", "\"icy'joy\"", "'icy\\'joy\'", "\"keylog\"", "\"key\\\nlog\"", "\"mix\\\\\\tnet\"", "\"mix\\\\\\tnet\"", "\"oat\\r\\n\"", "\"oat\\r\\n\"", "\"\\fpan\\b\"", "\"\\fpan\\b\"", "{}", "{}", "[]", "[]", "[]", "[/*empty*/]", "{}", "{//empty\n}", "\"ask\"", "\"ask\"", "\"bag\"", "\"bag\"", "\"can\"", "\"can\"", "\"day\"", "\"day\"", "\"end\"", "'end'", "\"fly\"", "\"fly\"", "\"\\u00XX\\u00XX\"", "\"\\xXX\\xXX\"", "\"y\\uXXXXz\"", "\"y\\uXXXXz\"", "\"\"", "\"\"", }; static char *azJsonTemplate[] = { /* JSON JSON-5 */ "{\"a\":%,\"b\":%,\"cDD\":%}", "{a:%,b:%,cDD:%}", "{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"e\":%}", "{a:%,b:%,c:%,d:%,e:%}", "{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"\":%}", "{a:%,b:%,c:%,d:%,'':%}", "{\"d\":%}", "{d:%}", "{\"eeee\":%, \"ffff\":%}", "{eeee:% /*and*/, ffff:%}", "{\"$g\":%,\"_h_\":%,\"a b c d\":%}", "{$g:%,_h_:%,\"a b c d\":%}", "{\"x\":%,\n \"y\":%}", "{\"x\":%,\n \"y\":%}", "{\"\\u00XX\":%,\"\\uXXXX\":%}", "{\"\\xXX\":%,\"\\uXXXX\":%}", "{\"Z\":%}", "{Z:%,}", "[%]", "[%,]", "[%,%]", "[%,%]", "[%,%,%]", "[%,%,%,]", "[%,%,%,%]", "[%,%,%,%]", "[%,%,%,%,%]", "[%,%,%,%,%]", }; #define count(X) (sizeof(X)/sizeof(X[0])) #define STRSZ 10000 static void jsonExpand( const char *zSrc, char *zDest, Prng *p, int eType, /* 0 for JSON, 1 for JSON5 */ unsigned int r /* Growth probability 0..1000. 0 means no growth */ ){ unsigned int i, j, k; char *z; char *zX; size_t n; char zBuf[200]; j = 0; if( zSrc==0 ) zSrc = "%"; if( strlen(zSrc)>=STRSZ/10 ) r = 0; for(i=0; zSrc[i]; i++){ if( zSrc[i]!='%' ){ if( j<STRSZ ) zDest[j++] = zSrc[i]; continue; } if( r==0 || (r<1000 && (prngInt(p)%1000)<=r) ){ /* Fill in without values without any new % */ k = prngInt(p)%(count(azJsonAtoms)/2); k = k*2 + eType; z = azJsonAtoms[k]; }else{ /* Add new % terms */ k = prngInt(p)%(count(azJsonTemplate)/2); k = k*2 + eType; z = azJsonTemplate[k]; } n = strlen(z); if( (zX = strstr(z,"XX"))!=0 ){ unsigned int y = prngInt(p); if( (y&0xff)==((y>>8)&0xff) ) y += 0x100; while( (y&0xff)==((y>>16)&0xff) || ((y>>8)&0xff)==((y>>16)&0xff) ){ y += 0x10000; } memcpy(zBuf, z, n+1); z = zBuf; zX = strstr(z,"XX"); while( zX!=0 ){ zX[0] = "0123456789abcdef"[y%16]; y /= 16; zX[1] = "0123456789abcdef"[y%16]; y /= 16; zX = strstr(zX, "XX"); } }else if( (zX = strstr(z,"DD"))!=0 ){ unsigned int y = prngInt(p); memcpy(zBuf, z, n+1); z = zBuf; zX = strstr(z,"DD"); while( zX!=0 ){ zX[0] = "0123456789"[y%10]; y /= 10; zX[1] = "0123456789"[y%10]; y /= 10; zX = strstr(zX, "DD"); } } assert( strstr(z, "XX")==0 ); assert( strstr(z, "DD")==0 ); if( j+n<STRSZ ){ memcpy(&zDest[j], z, n); j += (int)n; } } zDest[STRSZ-1] = 0; if( j<STRSZ ) zDest[j] = 0; } static void randJsonFunc( |
︙ | ︙ | |||
174 175 176 177 178 179 180 | jsonExpand(z2, z1, &prng, eType, 1000); jsonExpand(z1, z2, &prng, eType, 100); jsonExpand(z2, z1, &prng, eType, 0); sqlite3_result_text(context, z1, -1, SQLITE_TRANSIENT); } #ifdef _WIN32 | | > > > > | 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 | jsonExpand(z2, z1, &prng, eType, 1000); jsonExpand(z1, z2, &prng, eType, 100); jsonExpand(z2, z1, &prng, eType, 0); sqlite3_result_text(context, z1, -1, SQLITE_TRANSIENT); } #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_randomjson_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ static int cOne = 1; static int cZero = 0; int rc = SQLITE_OK; #ifdef SQLITE_STATIC_RANDOMJSON (void)pApi; /* Unused parameter */ #else SQLITE_EXTENSION_INIT2(pApi); #endif (void)pzErrMsg; /* Unused parameter */ rc = sqlite3_create_function(db, "random_json", 1, SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, &cZero, randJsonFunc, 0, 0); if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "random_json5", 1, SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, &cOne, randJsonFunc, 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; |
︙ | ︙ |
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/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.
︙ | ︙ | |||
578 579 580 581 582 583 584 585 586 587 588 589 590 591 | iOff += nPointer; /* Load the "byte of payload including overflow" field */ if( bNextPage || iOff>pCsr->nPage ){ bNextPage = 1; }else{ iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload); } /* If this is a leaf intkey cell, load the rowid */ if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){ iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey); } | > | 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | iOff += nPointer; /* Load the "byte of payload including overflow" field */ if( bNextPage || iOff>pCsr->nPage ){ bNextPage = 1; }else{ iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload); if( nPayload>0x7fffff00 ) nPayload &= 0x3fff; } /* If this is a leaf intkey cell, load the rowid */ if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){ iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey); } |
︙ | ︙ |
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 |
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( |
︙ | ︙ |
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 | 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 # 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 | # 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 | > > > | 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | # 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. 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 |
︙ | ︙ | |||
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 | > > > > > > > > > > | > > > > > > > > | > > > > > | < < < | 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 | # 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 | | > > | > > > > > > | > > > > > | 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 | # -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 | > | | | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 | # # 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 $@..." @{ \ | > > > | | | | | > > > > > > > | | | > > > > | | > > > > > > > | > | | | | | | 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 | # -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 # | | | > > | | | > | 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 | # 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,\ | | < | | 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 | 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 | > | | > > | > > > > > > > > > > > > > > | > > > > | | 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 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 | # 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 speedtest1.sql := $(dir.sql)/speedtest1.sql speedtest1.cliflags := --size 10 --big-transactions $(speedtest1): $(MAKE) -C ../.. speedtest1 $(speedtest1.sql): $(speedtest1) $(MAKEFILE) $(speedtest1) $(speedtest1.cliflags) --script $@ batch-runner.list: $(MAKEFILE) $(speedtest1.sql) $(dir.sql)/000-mandelbrot.sql bash split-speedtest1-script.sh $(dir.sql)/speedtest1.sql ls -1 $(dir.sql)/*.sql | grep -v speedtest1.sql | sort > $@ |
︙ | ︙ | |||
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 | > > > | 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 | 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 |
︙ | ︙ | |||
1083 1084 1085 1086 1087 1088 1089 | # Create main client downloadable zip file: ifneq (,$(filter dist snapshot,$(MAKECMDGOALS))) include dist.make endif # Run local web server for the test/demo pages. httpd: | | | 1213 1214 1215 1216 1217 1218 1219 1220 | # Create main client downloadable zip file: ifneq (,$(filter dist snapshot,$(MAKECMDGOALS))) include dist.make endif # Run local web server for the test/demo pages. httpd: althttpd -max-age 1 -enable-sab 1 -page index.html |
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/EXPORTED_FUNCTIONS.sqlite3-api.
︙ | ︙ | |||
59 60 61 62 63 64 65 66 67 68 69 70 71 72 | _sqlite3_expanded_sql _sqlite3_extended_errcode _sqlite3_extended_result_codes _sqlite3_file_control _sqlite3_finalize _sqlite3_free _sqlite3_get_auxdata _sqlite3_initialize _sqlite3_keyword_count _sqlite3_keyword_name _sqlite3_keyword_check _sqlite3_last_insert_rowid _sqlite3_libversion _sqlite3_libversion_number | > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | _sqlite3_expanded_sql _sqlite3_extended_errcode _sqlite3_extended_result_codes _sqlite3_file_control _sqlite3_finalize _sqlite3_free _sqlite3_get_auxdata _sqlite3_get_autocommit _sqlite3_initialize _sqlite3_keyword_count _sqlite3_keyword_name _sqlite3_keyword_check _sqlite3_last_insert_rowid _sqlite3_libversion _sqlite3_libversion_number |
︙ | ︙ |
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); |
︙ | ︙ | |||
184 185 186 187 188 189 190 191 192 193 194 195 196 197 | ]], ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"], ["sqlite3_extended_errcode", "int", "sqlite3*"], ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"], ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"], ["sqlite3_finalize", "int", "sqlite3_stmt*"], ["sqlite3_free", undefined,"*"], ["sqlite3_get_auxdata", "*", "sqlite3_context*", "int"], ["sqlite3_initialize", undefined], /*["sqlite3_interrupt", undefined, "sqlite3*" ^^^ we cannot actually currently support this because JS is single-threaded and we don't have a portable way to access a DB from 2 SharedWorkers concurrently. ],*/ ["sqlite3_keyword_count", "int"], | > | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | ]], ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"], ["sqlite3_extended_errcode", "int", "sqlite3*"], ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"], ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"], ["sqlite3_finalize", "int", "sqlite3_stmt*"], ["sqlite3_free", undefined,"*"], ["sqlite3_get_autocommit", "int", "sqlite3*"], ["sqlite3_get_auxdata", "*", "sqlite3_context*", "int"], ["sqlite3_initialize", undefined], /*["sqlite3_interrupt", undefined, "sqlite3*" ^^^ we cannot actually currently support this because JS is single-threaded and we don't have a portable way to access a DB from 2 SharedWorkers concurrently. ],*/ ["sqlite3_keyword_count", "int"], |
︙ | ︙ | |||
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 | /* ^^^ "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(wasm.exports.sqlite3_activate_see instanceof Function){ wasm.bindingSignatures.push( ["sqlite3_key", "int", "sqlite3*", "string", "int"], ["sqlite3_key_v2","int","sqlite3*","string","*","int"], ["sqlite3_rekey", "int", "sqlite3*", "string", "int"], ["sqlite3_rekey_v2", "int", "sqlite3*", "string", "*", "int"], ["sqlite3_activate_see", undefined, "string"] ); } /** 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. 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 | > > > > > > > > > > | > > > | 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 | /* ^^^ "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(wasm.exports.sqlite3_activate_see instanceof Function){ /** This code is capable of using an SEE build but note that an SEE WASM build is generally incompatible with SEE's license conditions. It is permitted for use internally in organizations which have licensed SEE, but not for public sites because 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"] ); } /** 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*"], |
︙ | ︙ | |||
417 418 419 420 421 422 423 | ["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){ | < < | 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | ["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 = { |
︙ | ︙ | |||
596 597 598 599 600 601 602 | ]] ]); }/*session/changeset APIs*/ /** Functions which are intended solely for API-internal use by the WASM components, not client code. These get installed into | | | > > > > > | | | | | | | 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 | ]] ]); }/*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"], ["sqlite3__wasm_vfs_create_file", "int", "sqlite3_vfs*","string","*", "int"], ["sqlite3__wasm_posix_create_file", "int", "string","*", "int"], ["sqlite3__wasm_vfs_unlink", "int", "sqlite3_vfs*","string"] ]; /** Install JS<->C struct bindings for the non-opaque struct types we need... */ sqlite3.StructBinder = globalThis.Jaccwabyt({ heap: 0 ? wasm.memory : wasm.heap8u, |
︙ | ︙ | |||
647 648 649 650 651 652 653 | 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 | | | 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 | 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; |
︙ | ︙ | |||
669 670 671 672 673 674 675 | 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('*'); | | > > < | 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 | 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)) |
︙ | ︙ | |||
737 738 739 740 741 742 743 | 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); } | | | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 | 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. */ |
︙ | ︙ | |||
760 761 762 763 764 765 766 | : fI64Disabled(e[0]); } /* There's no need to expose bindingSignatures to clients, implicitly making it part of the public interface. */ delete wasm.bindingSignatures; | | | | | | | | | 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 | : 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', |
︙ | ︙ | |||
872 873 874 875 876 877 878 | 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( | | | | | 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 | 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 |
︙ | ︙ | |||
1123 1124 1125 1126 1127 1128 1129 | try{ const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); if(0===rc && xCompare instanceof Function){ __dbCleanupMap.addCollation(pDb, zName); } return rc; }catch(e){ | | | 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 | 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); |
︙ | ︙ | |||
1249 1250 1251 1252 1253 1254 1255 | || xFinal instanceof Function || xDestroy instanceof Function)){ __dbCleanupMap.addFunction(pDb, funcName, nArg); } return rc; }catch(e){ console.error("sqlite3_create_function_v2() setup threw:",e); | | | 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | || 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 |
︙ | ︙ | |||
1294 1295 1296 1297 1298 1299 1300 | || xInverse instanceof Function || xDestroy instanceof Function)){ __dbCleanupMap.addWindowFunc(pDb, funcName, nArg); } return rc; }catch(e){ console.error("sqlite3_create_window_function() setup threw:",e); | | | 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 | || 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 = |
︙ | ︙ | |||
1389 1390 1391 1392 1393 1394 1395 | 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: | | | 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 | 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. */ |
︙ | ︙ | |||
1433 1434 1435 1436 1437 1438 1439 | try{ if(util.isSQLableTypedArray(text)){ p = wasm.allocFromTypedArray(text); n = text.byteLength; }else if('string'===typeof text){ [p, n] = wasm.allocCString(text); }else{ | | | | 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 | 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){ |
︙ | ︙ | |||
1467 1468 1469 1470 1471 1472 1473 | 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{ | | | | 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 | 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()*/ |
︙ | ︙ | |||
1499 1500 1501 1502 1503 1504 1505 | 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 */ | | | | | 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 | 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* */ |
︙ | ︙ | |||
1569 1570 1571 1572 1573 1574 1575 | }; }/* auto-extension */ const pKvvfs = capi.sqlite3_vfs_find("kvvfs"); if( pKvvfs ){/* kvvfs-specific glue */ if(util.isUIThread()){ const kvvfsMethods = new capi.sqlite3_kvvfs_methods( | | | | | 1588 1589 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 | }; }/* 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(); |
︙ | ︙ | |||
1662 1663 1664 1665 1666 1667 1668 1669 1670 | /* 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; }); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 | /* 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. |
︙ | ︙ | |||
1936 1937 1938 1939 1940 1941 1942 | */ jdb.prototype.storageSize = function(){ return jdb.storageSize(affirmDbOpen(this).filename); }; }/*main-window-only bits*/ }); | | > > | 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 | */ 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 |
︙ | ︙ | |||
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: |
︙ | ︙ |
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. |
︙ | ︙ | |||
58 59 60 61 62 63 64 | Common message format... Each message posted to the worker has an operation-independent envelope and operation-dependent arguments: ``` { | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | Common message format... Each message posted to the worker has an operation-independent envelope and operation-dependent arguments: ``` { type: string, // one of: 'open', 'close', 'exec', 'export', 'config-get' messageId: OPTIONAL arbitrary value. The worker will copy it as-is into response messages to assist in client-side dispatching. dbId: a db identifier string (returned by 'open') which tells the operation which database instance to work on. If not provided, the first-opened db is used. This is an "opaque" value, with no |
︙ | ︙ | |||
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | call will tie up the Worker thread, causing any recursion attempt to wait until the first exec() is completed. The response is the input options object (or a synthesized one if passed only a string), noting that options.resultRows and options.columnNames may be populated by the call to db.exec(). */ 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."); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | call will tie up the Worker thread, causing any recursion attempt to wait until the first exec() is completed. The response is the input options object (or a synthesized one if passed only a string), noting that options.resultRows and options.columnNames may be populated by the call to db.exec(). ==================================================================== "export" the current db To export the underlying database as a byte array... Message format: ``` { type: "export", messageId: ...as above..., dbId: ...as above... } ``` Response: ``` { type: "export", messageId: ...as above..., dbId: ...as above... result: { byteArray: Uint8Array (as per sqlite3_js_db_export()), filename: the db filename, 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."); |
︙ | ︙ | |||
373 374 375 376 377 378 379 | 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 |
︙ | ︙ | |||
459 460 461 462 463 464 465 | }else{ oargs.filename = args.filename; byteArray = args.byteArray; if(byteArray) pVfs = guessVfs(args.filename); } if(pVfs){ /* 2022-11-02: this feature is as-yet untested except that | | | | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | }else{ oargs.filename = args.filename; byteArray = args.byteArray; if(byteArray) pVfs = guessVfs(args.filename); } if(pVfs){ /* 2022-11-02: this feature is as-yet untested except that sqlite3__wasm_vfs_create_file() has been tested from the browser dev console. */ let pMem; try{ pMem = sqlite3.wasm.allocFromTypedArray(byteArray); const rc = util.sqlite3__wasm_vfs_create_file( pVfs, oargs.filename, pMem, byteArray.byteLength ); if(rc) sqlite3.SQLite3Error.toss(rc); }catch(e){ throw new sqlite3.SQLite3Error( e.name+' creating '+args.filename+": "+e.message, { cause: e |
︙ | ︙ | |||
654 655 656 657 658 659 660 | //}, result: result }, wState.xfer); }; globalThis.postMessage({type:'sqlite3-api',result:'worker1-ready'}); }.bind({sqlite3}); }); | > > > | 687 688 689 690 691 692 693 694 695 696 | //}, 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 |
︙ | ︙ |
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.
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 | #endif #ifndef SQLITE_OMIT_UTF16 # define SQLITE_OMIT_UTF16 1 #endif #ifndef SQLITE_OS_KV_OPTIONAL # define SQLITE_OS_KV_OPTIONAL 1 #endif /**********************************************************************/ /* SQLITE_T... */ #ifndef SQLITE_TEMP_STORE # define SQLITE_TEMP_STORE 2 #endif #ifndef SQLITE_THREADSAFE | > > > > > > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | #endif #ifndef SQLITE_OMIT_UTF16 # define SQLITE_OMIT_UTF16 1 #endif #ifndef SQLITE_OS_KV_OPTIONAL # define SQLITE_OS_KV_OPTIONAL 1 #endif /**********************************************************************/ /* SQLITE_S... */ #ifndef SQLITE_STRICT_SUBTYPE # define SQLITE_STRICT_SUBTYPE 1 #endif /**********************************************************************/ /* SQLITE_T... */ #ifndef SQLITE_TEMP_STORE # define SQLITE_TEMP_STORE 2 #endif #ifndef SQLITE_THREADSAFE |
︙ | ︙ | |||
228 229 230 231 232 233 234 | ** 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 |
︙ | ︙ | |||
281 282 283 284 285 286 287 | &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{ |
︙ | ︙ | |||
370 371 372 373 374 375 376 | 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); } |
︙ | ︙ | |||
398 399 400 401 402 403 404 | ** 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 |
︙ | ︙ | |||
1093 1094 1095 1096 1097 1098 1099 | M(xSavepoint, "i(pi)"); M(xRelease, "i(pi)"); M(xRollbackTo, "i(pi)"); // ^^^ v2. v3+ follows... M(xShadowName, "i(s)"); } _StructBinder; #undef CurrentStruct | | | 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 | 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 { |
︙ | ︙ | |||
1210 1211 1212 1213 1214 1215 1216 | ** 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 | | | | 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 | ** 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; } /* |
︙ | ︙ | |||
1251 1252 1253 1254 1255 1256 1257 | ** 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 | | | 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 | ** 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); |
︙ | ︙ | |||
1278 1279 1280 1281 1282 1283 1284 | ** 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 | | | | 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 | ** 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 |
︙ | ︙ | |||
1333 1334 1335 1336 1337 1338 1339 | ** 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 | | | 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 | ** 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) ){ |
︙ | ︙ | |||
1356 1357 1358 1359 1360 1361 1362 | ** 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 | | | 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 | ** 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. ** |
︙ | ︙ | |||
1397 1398 1399 1400 1401 1402 1403 | ** 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 | | | 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 | ** 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 |
︙ | ︙ | |||
1487 1488 1489 1490 1491 1492 1493 | ** ** 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 | | | 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 | ** ** 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; |
︙ | ︙ | |||
1510 1511 1512 1513 1514 1515 1516 | } /* ** 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 | | | | | | | | | 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 | } /* ** 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: |
︙ | ︙ | |||
1603 1604 1605 1606 1607 1608 1609 | ** 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 | | | | | | | | | | 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 | ** 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); } #if 0 // Pending removal after verification of a workaround discussed in the // forum post linked to below. /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. ** ** Returns a pointer to sqlite3_free(). In compliant browsers the ** return value, when passed to sqlite3.wasm.exports.functionEntry(), ** must resolve to the same function as ** sqlite3.wasm.exports.sqlite3_free. i.e. from a dev console where ** sqlite3 is exported globally, the following must be true: ** ** ``` ** sqlite3.wasm.functionEntry( ** sqlite3.wasm.exports.sqlite3__wasm_ptr_to_sqlite3_free() ** ) === sqlite3.wasm.exports.sqlite3_free ** ``` ** ** Using a function to return this pointer, as opposed to exporting it ** via sqlite3__wasm_enum_json(), is an attempt to work around a ** Safari-specific quirk covered at ** https://sqlite.org/forum/info/e5b20e1feb37a19a. **/ SQLITE_WASM_EXPORT void * sqlite3__wasm_ptr_to_sqlite3_free(void){ return (void*)sqlite3_free; } #endif #if defined(__EMSCRIPTEN__) && defined(SQLITE_ENABLE_WASMFS) #include <emscripten/wasmfs.h> |
︙ | ︙ | |||
1719 1720 1721 1722 1723 1724 1725 | ** ** 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 | | | | | | | | | | | | | | | | | | 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 | ** ** 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; } |
︙ | ︙ | |||
1828 1829 1830 1831 1832 1833 1834 | ** ** [^...] 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... */ | | | | 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 | ** ** [^...] 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 ){ |
︙ | ︙ | |||
1908 1909 1910 1911 1912 1913 1914 | if( c!=(*(z++)) ) return 0; } } return *z==0; } SQLITE_WASM_EXPORT | | | < | 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 | 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 218 219 | 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 arugments for sqlite3Worker1Promiser()-created factory."); } if(!msg.dbId && msg.type!=='open') msg.dbId = dbId; msg.messageId = genMsgId(msg); msg.departureTime = performance.now(); const proxy = Object.create(null); proxy.message = msg; let rowCallbackId /* message handler ID for exec on-row callback proxy */; if('exec'===msg.type && msg.args){ if('function'===typeof msg.args.callback){ |
︙ | ︙ | |||
240 241 242 243 244 245 246 247 248 | 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() 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 |
Added ext/wasm/batch-runner-sahpool.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 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 | <!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/testing.css"/> <title>sqlite3-api batch SQL runner for the SAHPool VFS</title> </head> <body> <header id='titlebar'><span>sqlite3-api batch SQL runner for the SAHPool VFS</span></header> <div> <span class='input-wrapper'> <input type='checkbox' class='disable-during-eval' id='cb-reverse-log-order' checked></input> <label for='cb-reverse-log-order' id='lbl-reverse-log-order'>Reverse log order</label> </span> </div> <div id='test-output' class='reverse'></div> <script> (function(){ const E = (sel)=>document.querySelector(sel); const eOut = E('#test-output'); const log2 = function(cssClass,...args){ let ln; if(1 || cssClass){ ln = document.createElement('div'); if(cssClass) ln.classList.add(cssClass); ln.append(document.createTextNode(args.join(' '))); }else{ // This doesn't work with the "reverse order" option! ln = document.createTextNode(args.join(' ')+'\n'); } eOut.append(ln); }; const log = (...args)=>{ //console.log(...args); log2('', ...args); }; const logErr = function(...args){ console.error(...args); log2('error', ...args); }; const logWarn = function(...args){ console.warn(...args); log2('warning', ...args); }; const cbReverseLog = E('#cb-reverse-log-order'); const lblReverseLog = E('#lbl-reverse-log-order'); if(cbReverseLog.checked){ lblReverseLog.classList.add('warning'); eOut.classList.add('reverse'); } cbReverseLog.addEventListener('change', function(){ if(this.checked){ eOut.classList.add('reverse'); lblReverseLog.classList.add('warning'); }else{ eOut.classList.remove('reverse'); lblReverseLog.classList.remove('warning'); } }, false); const w = new Worker('batch-runner-sahpool.js?sqlite3.dir=jswasm'); w.onmessage = function(msg){ msg = msg.data; switch(msg.type){ case 'stdout': log(...msg.data); break; case 'warn': logWarn(...msg.data); break; case 'error': logErr(...msg.data); break; default: logErr("Unhandled worker message type:",msg); break; } }; })(); </script> <style> #test-output { white-space: break-spaces; overflow: auto; } </style> </body> </html> |
Added ext/wasm/batch-runner-sahpool.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 | /* 2023-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. *********************************************************************** A basic batch SQL runner for the SAHPool VFS. This file must be run in a worker thread. This is not a full-featured app, just a way to get some measurements for batch execution of SQL for the OPFS SAH Pool VFS. */ 'use strict'; const wMsg = function(msgType,...args){ postMessage({ type: msgType, data: args }); }; const toss = function(...args){throw new Error(args.join(' '))}; const warn = (...args)=>{ wMsg('warn',...args); }; const error = (...args)=>{ wMsg('error',...args); }; const log = (...args)=>{ wMsg('stdout',...args); } let sqlite3; const urlParams = new URL(globalThis.location.href).searchParams; const cacheSize = (()=>{ if(urlParams.has('cachesize')) return +urlParams.get('cachesize'); return 200; })(); /** Throws if the given sqlite3 result code is not 0. */ const checkSqliteRc = (dbh,rc)=>{ if(rc) toss("Prepare failed:",sqlite3.capi.sqlite3_errmsg(dbh)); }; const sqlToDrop = [ "SELECT type,name FROM sqlite_schema ", "WHERE name NOT LIKE 'sqlite\\_%' escape '\\' ", "AND name NOT LIKE '\\_%' escape '\\'" ].join(''); const clearDbSqlite = function(db){ // This would be SO much easier with the oo1 API, but we specifically want to // inject metrics we can't get via that API, and we cannot reliably (OPFS) // open the same DB twice to clear it using that API, so... const rc = sqlite3.wasm.exports.sqlite3_wasm_db_reset(db.handle); log("reset db rc =",rc,db.id, db.filename); }; const App = { db: undefined, cache:Object.create(null), log: log, warn: warn, error: error, metrics: { fileCount: 0, runTimeMs: 0, prepareTimeMs: 0, stepTimeMs: 0, stmtCount: 0, strcpyMs: 0, sqlBytes: 0 }, fileList: undefined, execSql: async function(name,sql){ const db = this.db; const banner = "========================================"; this.log(banner, "Running",name,'('+sql.length,'bytes)'); const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; let pStmt = 0, pSqlBegin; const metrics = db.metrics = Object.create(null); metrics.prepTotal = metrics.stepTotal = 0; metrics.stmtCount = 0; metrics.malloc = 0; metrics.strcpy = 0; if(this.gotErr){ this.error("Cannot run SQL: error cleanup is pending."); return; } // Run this async so that the UI can be updated for the above header... const endRun = ()=>{ metrics.evalSqlEnd = performance.now(); metrics.evalTimeTotal = (metrics.evalSqlEnd - metrics.evalSqlStart); this.log("metrics:",JSON.stringify(metrics, undefined, ' ')); this.log("prepare() count:",metrics.stmtCount); this.log("Time in prepare_v2():",metrics.prepTotal,"ms", "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())"); this.log("Time in step():",metrics.stepTotal,"ms", "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())"); this.log("Total runtime:",metrics.evalTimeTotal,"ms"); this.log("Overhead (time - prep - step):", (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); this.log(banner,"End of",name); this.metrics.prepareTimeMs += metrics.prepTotal; this.metrics.stepTimeMs += metrics.stepTotal; this.metrics.stmtCount += metrics.stmtCount; this.metrics.strcpyMs += metrics.strcpy; this.metrics.sqlBytes += sql.length; }; const runner = function(resolve, reject){ ++this.metrics.fileCount; metrics.evalSqlStart = performance.now(); const stack = wasm.scopedAllocPush(); try { let t, rc; let sqlByteLen = sql.byteLength; const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); t = performance.now(); pSqlBegin = wasm.scopedAlloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); metrics.malloc = performance.now() - t; metrics.byteLength = sqlByteLen; let pSql = pSqlBegin; const pSqlEnd = pSqlBegin + sqlByteLen; t = performance.now(); wasm.heap8().set(sql, pSql); wasm.poke(pSql + sqlByteLen, 0); //log("SQL:",wasm.cstrToJs(pSql)); metrics.strcpy = performance.now() - t; let breaker = 0; while(pSql && wasm.peek8(pSql)){ wasm.pokePtr(ppStmt, 0); wasm.pokePtr(pzTail, 0); t = performance.now(); rc = capi.sqlite3_prepare_v2( db.handle, pSql, sqlByteLen, ppStmt, pzTail ); metrics.prepTotal += performance.now() - t; checkSqliteRc(db.handle, rc); pStmt = wasm.peekPtr(ppStmt); pSql = wasm.peekPtr(pzTail); sqlByteLen = pSqlEnd - pSql; if(!pStmt) continue/*empty statement*/; ++metrics.stmtCount; t = performance.now(); rc = capi.sqlite3_step(pStmt); capi.sqlite3_finalize(pStmt); pStmt = 0; metrics.stepTotal += performance.now() - t; switch(rc){ case capi.SQLITE_ROW: case capi.SQLITE_DONE: break; default: checkSqliteRc(db.handle, rc); toss("Not reached."); } } resolve(this); }catch(e){ if(pStmt) capi.sqlite3_finalize(pStmt); this.gotErr = e; reject(e); }finally{ capi.sqlite3_exec(db.handle,"rollback;",0,0,0); wasm.scopedAllocPop(stack); } }.bind(this); const p = new Promise(runner); return p.catch( (e)=>this.error("Error via execSql("+name+",...):",e.message) ).finally(()=>{ endRun(); }); }, /** Loads batch-runner.list and populates the selection list from it. Returns a promise which resolves to nothing in particular when it completes. Only intended to be run once at the start of the app. */ loadSqlList: async function(){ const infile = 'batch-runner.list'; this.log("Loading list of SQL files:", infile); let txt; try{ const r = await fetch(infile); if(404 === r.status){ toss("Missing file '"+infile+"'."); } if(!r.ok) toss("Loading",infile,"failed:",r.statusText); txt = await r.text(); }catch(e){ this.error(e.message); throw e; } App.fileList = txt.split(/\n+/).filter(x=>!!x); this.log("Loaded",infile); }, /** Fetch ./fn and return its contents as a Uint8Array. */ fetchFile: async function(fn, cacheIt=false){ if(cacheIt && this.cache[fn]) return this.cache[fn]; this.log("Fetching",fn,"..."); let sql; try { const r = await fetch(fn); if(!r.ok) toss("Fetch failed:",r.statusText); sql = new Uint8Array(await r.arrayBuffer()); }catch(e){ this.error(e.message); throw e; } this.log("Fetched",sql.length,"bytes from",fn); if(cacheIt) this.cache[fn] = sql; return sql; }/*fetchFile()*/, /** Converts this.metrics() to a form which is suitable for easy conversion to CSV. It returns an array of arrays. The first sub-array is the column names. The 2nd and subsequent are the values, one per test file (only the most recent metrics are kept for any given file). */ metricsToArrays: function(){ const rc = []; Object.keys(this.dbs).sort().forEach((k)=>{ const d = this.dbs[k]; const m = d.metrics; delete m.evalSqlStart; delete m.evalSqlEnd; const mk = Object.keys(m).sort(); if(!rc.length){ rc.push(['db', ...mk]); } const row = [k.split('/').pop()/*remove dir prefix from filename*/]; rc.push(row); row.push(...mk.map((kk)=>m[kk])); }); return rc; }, metricsToBlob: function(colSeparator='\t'){ const ar = [], ma = this.metricsToArrays(); if(!ma.length){ this.error("Metrics are empty. Run something."); return; } ma.forEach(function(row){ ar.push(row.join(colSeparator),'\n'); }); return new Blob(ar); }, /** Fetch file fn and eval it as an SQL blob. This is an async operation and returns a Promise which resolves to this object on success. */ evalFile: async function(fn){ const sql = await this.fetchFile(fn); return this.execSql(fn,sql); }/*evalFile()*/, /** Fetches the handle of the db associated with this.e.selImpl.value, opening it if needed. */ initDb: function(){ const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; const stack = wasm.scopedAllocPush(); let pDb = 0; const d = Object.create(null); d.filename = "/batch.db"; try{ const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; const ppDb = wasm.scopedAllocPtr(); const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, this.PoolUtil.vfsName); pDb = wasm.peekPtr(ppDb) if(rc) toss("sqlite3_open_v2() failed with code",rc); capi.sqlite3_exec(pDb, "PRAGMA cache_size="+cacheSize, 0, 0, 0); this.log("cache_size =",cacheSize); }catch(e){ if(pDb) capi.sqlite3_close_v2(pDb); throw e; }finally{ wasm.scopedAllocPop(stack); } d.handle = pDb; this.log("Opened db:",d.filename,'@',d.handle); return d; }, closeDb: function(){ if(this.db.handle){ this.sqlite3.capi.sqlite3_close_v2(this.db.handle); this.db.handle = undefined; } }, run: async function(sqlite3){ delete this.run; this.sqlite3 = sqlite3; const capi = sqlite3.capi, wasm = sqlite3.wasm; this.log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); this.log("WASM heap size =",wasm.heap8().length); let timeStart; sqlite3.installOpfsSAHPoolVfs({ clearOnInit: true, initialCapacity: 4, name: 'batch-sahpool', verbosity: 2 }).then(PoolUtil=>{ App.PoolUtil = PoolUtil; App.db = App.initDb(); }) .then(async ()=>this.loadSqlList()) .then(async ()=>{ timeStart = performance.now(); for(let i = 0; i < App.fileList.length; ++i){ const fn = App.fileList[i]; await App.evalFile(fn); if(App.gotErr) throw App.gotErr; } }) .then(()=>{ App.metrics.runTimeMs = performance.now() - timeStart; App.log("total metrics:",JSON.stringify(App.metrics, undefined, ' ')); App.log("Reload the page to run this again."); App.closeDb(); App.PoolUtil.removeVfs(); }) .catch(e=>this.error("ERROR:",e)); }/*run()*/ }/*App*/; let sqlite3Js = 'sqlite3.js'; if(urlParams.has('sqlite3.dir')){ sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js; } importScripts(sqlite3Js); globalThis.sqlite3InitModule().then(async function(sqlite3_){ log("Done initializing. Running batch runner..."); sqlite3 = sqlite3_; App.run(sqlite3_); }); |
Changes to ext/wasm/batch-runner.js.
︙ | ︙ | |||
68 69 70 71 72 73 74 | // This would be SO much easier with the oo1 API, but we specifically want to // inject metrics we can't get via that API, and we cannot reliably (OPFS) // open the same DB twice to clear it using that API, so... const rc = sqlite3.wasm.exports.sqlite3_wasm_db_reset(db.handle); App.logHtml("reset db rc =",rc,db.id, db.filename); }; | < > > > > > > > > > | 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 | // This would be SO much easier with the oo1 API, but we specifically want to // inject metrics we can't get via that API, and we cannot reliably (OPFS) // open the same DB twice to clear it using that API, so... const rc = sqlite3.wasm.exports.sqlite3_wasm_db_reset(db.handle); App.logHtml("reset db rc =",rc,db.id, db.filename); }; const E = (s)=>document.querySelector(s); const App = { e: { output: E('#test-output'), selSql: E('#sql-select'), btnRun: E('#sql-run'), btnRunNext: E('#sql-run-next'), btnRunRemaining: E('#sql-run-remaining'), btnExportMetrics: E('#export-metrics'), btnClear: E('#output-clear'), btnReset: E('#db-reset'), cbReverseLog: E('#cb-reverse-log-order'), selImpl: E('#select-impl'), fsToolbar: E('#toolbar') }, db: Object.create(null), dbs: Object.create(null), cache:{}, metrics: { fileCount: 0, runTimeMs: 0, prepareTimeMs: 0, stepTimeMs: 0, stmtCount: 0, strcpyMs: 0, sqlBytes: 0 }, log: console.log.bind(console), warn: console.warn.bind(console), cls: function(){this.e.output.innerHTML = ''}, logHtml2: function(cssClass,...args){ const ln = document.createElement('div'); if(cssClass) ln.classList.add(cssClass); ln.append(document.createTextNode(args.join(' '))); |
︙ | ︙ | |||
113 114 115 116 117 118 119 | execSql: async function(name,sql){ const db = this.getSelectedDb(); const banner = "========================================"; this.logHtml(banner, "Running",name,'('+sql.length,'bytes) using',db.id); const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; let pStmt = 0, pSqlBegin; | < | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | execSql: async function(name,sql){ const db = this.getSelectedDb(); const banner = "========================================"; this.logHtml(banner, "Running",name,'('+sql.length,'bytes) using',db.id); const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; let pStmt = 0, pSqlBegin; const metrics = db.metrics = Object.create(null); metrics.prepTotal = metrics.stepTotal = 0; metrics.stmtCount = 0; metrics.malloc = 0; metrics.strcpy = 0; this.blockControls(true); if(this.gotErr){ |
︙ | ︙ | |||
138 139 140 141 142 143 144 145 146 147 148 149 150 151 | "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())"); this.logHtml("Time in step():",metrics.stepTotal,"ms", "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())"); this.logHtml("Total runtime:",metrics.evalTimeTotal,"ms"); this.logHtml("Overhead (time - prep - step):", (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); this.logHtml(banner,"End of",name); }; let runner; if('websql'===db.id){ const who = this; runner = function(resolve, reject){ /* WebSQL cannot execute multiple statements, nor can it execute SQL without | > > > > > | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())"); this.logHtml("Time in step():",metrics.stepTotal,"ms", "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())"); this.logHtml("Total runtime:",metrics.evalTimeTotal,"ms"); this.logHtml("Overhead (time - prep - step):", (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); this.logHtml(banner,"End of",name); this.metrics.prepareTimeMs += metrics.prepTotal; this.metrics.stepTimeMs += metrics.stepTotal; this.metrics.stmtCount += metrics.stmtCount; this.metrics.strcpyMs += metrics.strcpy; this.metrics.sqlBytes += sql.length; }; let runner; if('websql'===db.id){ const who = this; runner = function(resolve, reject){ /* WebSQL cannot execute multiple statements, nor can it execute SQL without |
︙ | ︙ | |||
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | console.error("websql error:",e); who.logErr(e.message); //reject(e); } }.bind(this); }else{/*sqlite3 db...*/ runner = function(resolve, reject){ metrics.evalSqlStart = performance.now(); try { let t; let sqlByteLen = sql.byteLength; const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); t = performance.now(); pSqlBegin = wasm.scopedAlloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); metrics.malloc = performance.now() - t; | > > | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | console.error("websql error:",e); who.logErr(e.message); //reject(e); } }.bind(this); }else{/*sqlite3 db...*/ runner = function(resolve, reject){ ++this.metrics.fileCount; metrics.evalSqlStart = performance.now(); const stack = wasm.scopedAllocPush(); try { let t; let sqlByteLen = sql.byteLength; const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); t = performance.now(); pSqlBegin = wasm.scopedAlloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); metrics.malloc = performance.now() - t; |
︙ | ︙ | |||
265 266 267 268 269 270 271 | wasm.scopedAllocPop(stack); } }.bind(this); } let p; if(1){ p = new Promise(function(res,rej){ | | | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | wasm.scopedAllocPop(stack); } }.bind(this); } let p; if(1){ p = new Promise(function(res,rej){ setTimeout(()=>runner(res, rej), 0)/*give UI a chance to output the "running" banner*/; }); }else{ p = new Promise(runner); } return p.catch( (e)=>this.logErr("Error via execSql("+name+",...):",e.message) ).finally(()=>{ |
︙ | ︙ | |||
397 398 399 400 401 402 403 | return; } ma.forEach(function(row){ ar.push(row.join(colSeparator),'\n'); }); return new Blob(ar); }, | | | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | return; } ma.forEach(function(row){ ar.push(row.join(colSeparator),'\n'); }); return new Blob(ar); }, downloadMetrics: function(){ const b = this.metricsToBlob(); if(!b) return; const url = URL.createObjectURL(b); const a = document.createElement('a'); a.href = url; a.download = 'batch-runner-js-'+((new Date().getTime()/1000) | 0)+'.csv'; |
︙ | ︙ | |||
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 | } ++who.e.selSql.selectedIndex; v = who.e.selSql.value; } const timeTotal = performance.now() - timeStart; who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))"); who.clearStorage(); }, false); }/*run()*/ }/*App*/; self.sqlite3TestModule.initSqlite3().then(function(sqlite3_){ sqlite3 = sqlite3_; self.App = App /* only to facilitate dev console access */; App.run(sqlite3); }); })(); | > > | 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | } ++who.e.selSql.selectedIndex; v = who.e.selSql.value; } const timeTotal = performance.now() - timeStart; who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))"); who.clearStorage(); App.metrics.runTimeMs = timeTotal; who.logHtml("Total metrics:",JSON.stringify(App.metrics,undefined,' ')); }, false); }/*run()*/ }/*App*/; self.sqlite3TestModule.initSqlite3().then(function(sqlite3_){ sqlite3 = sqlite3_; self.App = App /* only to facilitate dev console access */; App.run(sqlite3); }); })(); |
Changes to ext/wasm/demo-123.js.
︙ | ︙ | |||
16 17 18 19 20 21 22 | (function(){ /** Set up our output channel differently depending on whether we are running in a worker thread or the main (UI) thread. */ let logHtml; | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | (function(){ /** Set up our output channel differently depending on whether we are running in a worker thread or the main (UI) thread. */ let logHtml; if(globalThis.window === globalThis /* UI thread */){ console.log("Running demo from main UI thread."); logHtml = function(cssClass,...args){ const ln = document.createElement('div'); if(cssClass) ln.classList.add(cssClass); ln.append(document.createTextNode(args.join(' '))); document.body.append(ln); }; |
︙ | ︙ | |||
246 247 248 249 250 251 252 | e.g. getInt(), getFloat(), getBlob(), getJSON() - getColumnName(ndx), getColumnNames() - getParamIndex(name) */ }/*demo1()*/; log("Loading and initializing sqlite3 module..."); | | | | | | > | | 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 | e.g. getInt(), getFloat(), getBlob(), getJSON() - getColumnName(ndx), getColumnNames() - getParamIndex(name) */ }/*demo1()*/; log("Loading and initializing sqlite3 module..."); if(globalThis.window!==globalThis) /*worker thread*/{ /* If sqlite3.js is in a directory other than this script, in order to get sqlite3.js to resolve sqlite3.wasm properly, we have to explicitly tell it where sqlite3.js is being loaded from. We do that by passing the `sqlite3.dir=theDirName` URL argument to _this_ script. That URL argument will be seen by the JS/WASM loader and it will adjust the sqlite3.wasm path accordingly. If sqlite3.js/.wasm are in the same directory as this script then that's not needed. URL arguments passed as part of the filename via importScripts() are simply lost, and such scripts see the globalThis.location of _this_ script. */ let sqlite3Js = 'sqlite3.js'; const urlParams = new URL(globalThis.location.href).searchParams; if(urlParams.has('sqlite3.dir')){ sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js; } importScripts(sqlite3Js); } globalThis.sqlite3InitModule({ /* We can redirect any stdout/stderr from the module like so, but note that doing so makes use of Emscripten-isms, not well-defined sqlite APIs. */ print: log, printErr: error }).then(function(sqlite3){ //console.log('sqlite3 =',sqlite3); log("Done initializing. Running demo..."); try { demo1(sqlite3); }catch(e){ error("Exception:",e.message); } }); })(); |
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.
︙ | ︙ | |||
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 \ | | > | > > > > > | > > | 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 | 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.
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | Warnings and Caveats: <ul class='warning'> <li>All of these pages must be served via an HTTP server. Browsers do not support loading WASM files via file:// URLs.</li> <li>Any OPFS-related pages or tests require: <ul> <li>That the web server emit the so-called <a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy'>COOP</a> and <a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy'>COEP</a> headers. <a href='https://sqlite.org/althttpd'>althttpd</a> requires the <code>-enable-sab</code> flag for that. </li> | > > > < < < < < < | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | Warnings and Caveats: <ul class='warning'> <li>All of these pages must be served via an HTTP server. Browsers do not support loading WASM files via file:// URLs.</li> <li>Any OPFS-related pages or tests require: <ul> <li>An OPFS-capable browser released after February 2023. Some tests will work with Chromium-based browsers going back to around v102.</li> <li>That the web server emit the so-called <a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy'>COOP</a> and <a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy'>COEP</a> headers. <a href='https://sqlite.org/althttpd'>althttpd</a> requires the <code>-enable-sab</code> flag for that. </li> </ul> </li> </ul> </div> <div>The tests and demos... <ul id='test-list'> <li>Core-most tests |
︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 | 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.
︙ | ︙ | |||
27 28 29 30 31 32 33 34 35 36 37 38 39 40 | Warnings and Caveats: <ul class='warning'> <li>All of these pages must be served via an HTTP server. Browsers do not support loading WASM files via file:// URLs.</li> <li>Any OPFS-related pages or tests require: <ul> <li>That the web server emit the so-called <a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy'>COOP</a> and <a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy'>COEP</a> headers. <a href='https://sqlite.org/althttpd'>althttpd</a> requires the <code>-enable-sab</code> flag for that. </li> | > > > < < < < < < | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | Warnings and Caveats: <ul class='warning'> <li>All of these pages must be served via an HTTP server. Browsers do not support loading WASM files via file:// URLs.</li> <li>Any OPFS-related pages or tests require: <ul> <li>An OPFS-capable browser released after February 2023. Some tests will work with Chromium-based browsers going back to around v102.</li> <li>That the web server emit the so-called <a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy'>COOP</a> and <a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy'>COEP</a> headers. <a href='https://sqlite.org/althttpd'>althttpd</a> requires the <code>-enable-sab</code> flag for that. </li> </ul> </li> </ul> </div> <div>The tests and demos... <ul id='test-list'> <li>Core-most tests |
︙ | ︙ | |||
83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 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.html.
︙ | ︙ | |||
346 347 348 349 350 351 352 | msg = msg.data; switch(msg.type){ case 'ready': log("Worker is ready."); eControls.classList.remove('hidden'); break; case 'stdout': log(msg.data); break; | | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | msg = msg.data; switch(msg.type){ case 'ready': log("Worker is ready."); eControls.classList.remove('hidden'); break; case 'stdout': log(msg.data); break; case 'stderr': logErr(msg.data); break; case 'run-start': eControls.disabled = true; log("Running speedtest1 with argv =",msg.data.join(' ')); break; case 'run-end': log("speedtest1 finished."); eControls.disabled = false; |
︙ | ︙ |
Changes to ext/wasm/speedtest1-worker.js.
1 2 3 4 5 6 7 8 9 | 'use strict'; (function(){ let speedtestJs = 'speedtest1.js'; const urlParams = new URL(self.location.href).searchParams; if(urlParams.has('sqlite3.dir')){ speedtestJs = urlParams.get('sqlite3.dir') + '/' + speedtestJs; } importScripts(speedtestJs); /** | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 'use strict'; (function(){ let speedtestJs = 'speedtest1.js'; const urlParams = new URL(self.location.href).searchParams; if(urlParams.has('sqlite3.dir')){ speedtestJs = urlParams.get('sqlite3.dir') + '/' + speedtestJs; } importScripts(speedtestJs); /** If this build includes WASMFS, this function initializes it and returns the name of the dir on which OPFS is mounted, else it returns an empty string. */ const wasmfsDir = function f(wasmUtil){ if(undefined !== f._) return f._; const pdir = '/opfs'; if( !self.FileSystemHandle || !self.FileSystemDirectoryHandle || !self.FileSystemFileHandle){ |
︙ | ︙ | |||
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | const logMsg = (type,msgArgs)=>{ const msg = msgArgs.join(' '); App.logBuffer.push(msg); mPost(type,msg); }; const log = (...args)=>logMsg('stdout',args); const logErr = (...args)=>logMsg('stderr',args); const runSpeedtest = async function(cliFlagsArray){ const scope = App.wasm.scopedAllocPush(); const dbFile = App.pDir+"/speedtest1.sqlite3"; try{ const argv = [ "speedtest1.wasm", ...cliFlagsArray, dbFile ]; App.logBuffer.length = 0; const ndxSahPool = argv.indexOf('opfs-sahpool'); | > < | | 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 | const logMsg = (type,msgArgs)=>{ const msg = msgArgs.join(' '); App.logBuffer.push(msg); mPost(type,msg); }; const log = (...args)=>logMsg('stdout',args); const logErr = (...args)=>logMsg('stderr',args); const realSahName = 'opfs-sahpool-speedtest1'; const runSpeedtest = async function(cliFlagsArray){ const scope = App.wasm.scopedAllocPush(); const dbFile = App.pDir+"/speedtest1.sqlite3"; try{ const argv = [ "speedtest1.wasm", ...cliFlagsArray, dbFile ]; App.logBuffer.length = 0; const ndxSahPool = argv.indexOf('opfs-sahpool'); if(ndxSahPool>0){ argv[ndxSahPool] = realSahName; log("Updated argv for opfs-sahpool: --vfs",realSahName); } mPost('run-start', [...argv]); if(App.sqlite3.installOpfsSAHPoolVfs && !App.sqlite3.$SAHPoolUtil && ndxSahPool>0){ log("Installing opfs-sahpool as",realSahName,"..."); await App.sqlite3.installOpfsSAHPoolVfs({ name: realSahName, initialCapacity: 3, clearOnInit: true, verbosity: 2 }).then(PoolUtil=>{ log("opfs-sahpool successfully installed as",PoolUtil.vfsName); App.sqlite3.$SAHPoolUtil = PoolUtil; //console.log("sqlite3.oo1.OpfsSAHPoolDb =", App.sqlite3.oo1.OpfsSAHPoolDb); }); } App.wasm.xCall('wasm_main', argv.length, App.wasm.scopedAllocMainArgv(argv)); }catch(e){ |
︙ | ︙ | |||
98 99 100 101 102 103 104 | break; default: logErr("Unhandled worker message type:",msg.type); break; } }; | < < < < < < < < < < < < < < < < < | 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 | break; default: logErr("Unhandled worker message type:",msg.type); break; } }; const EmscriptenModule = { print: log, 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=>{ logErr(e); }); })(); |
Changes to ext/wasm/test-opfs-vfs.js.
︙ | ︙ | |||
18 19 20 21 22 23 24 | const logPrefix = "OPFS tester:"; const log = (...args)=>console.log(logPrefix,...args); const warn = (...args)=>console.warn(logPrefix,...args); const error = (...args)=>console.error(logPrefix,...args); const opfs = sqlite3.opfs; log("tryOpfsVfs()"); if(!sqlite3.opfs){ | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | const logPrefix = "OPFS tester:"; const log = (...args)=>console.log(logPrefix,...args); const warn = (...args)=>console.warn(logPrefix,...args); const error = (...args)=>console.error(logPrefix,...args); const opfs = sqlite3.opfs; log("tryOpfsVfs()"); if(!sqlite3.opfs){ const e = new Error("OPFS is not available."); error(e); throw e; } const capi = sqlite3.capi; const pVfs = capi.sqlite3_vfs_find("opfs") || toss("Missing 'opfs' VFS."); const oVfs = new capi.sqlite3_vfs(pVfs); log("OPFS VFS:",pVfs, oVfs); |
︙ | ︙ |
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: |
︙ | ︙ | |||
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 1696 1697 1698 | 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', test: function(sqlite3){ const db = this.db; db.createFunction("foo",(pCx,a,b)=>a+b); |
︙ | ︙ | |||
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') |
︙ | ︙ | |||
2601 2602 2603 2604 2605 2606 2607 | 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*/) | < < < < < < < < < < < < < < < < < < < < < < > | > > > | 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 | 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*/) ;/* end kvvfs tests */ //////////////////////////////////////////////////////////////////////// T.g('Hook APIs') .t({ name: "sqlite3_commit/rollback/update_hook()", predicate: ()=>wasm.bigIntEnabled || "Update hook requires int64", test: function(sqlite3){ let countCommit = 0, countRollback = 0;; const db = new sqlite3.oo1.DB(':memory:',1 ? 'c' : 'ct'); let rc = capi.sqlite3_commit_hook(db, (p)=>{ ++countCommit; return (1 === p) ? 0 : capi.SQLITE_ERROR; }, 1); T.assert( 0 === rc /*void pointer*/ ); // Commit hook... T.assert( 0!=capi.sqlite3_get_autocommit(db) ); db.exec("BEGIN; SELECT 1; COMMIT"); T.assert(0 === countCommit, "No-op transactions (mostly) do not trigger commit hook."); db.exec("BEGIN EXCLUSIVE; SELECT 1; COMMIT"); T.assert(1 === countCommit, "But EXCLUSIVE transactions do."); db.transaction((d)=>{ T.assert( 0==capi.sqlite3_get_autocommit(db) ); d.exec("create table t(a)"); }); T.assert(2 === countCommit); // Rollback hook: rc = capi.sqlite3_rollback_hook(db, (p)=>{ ++countRollback; T.assert( 2 === p ); }, 2); |
︙ | ︙ | |||
2902 2903 2904 2905 2906 2907 2908 2909 | //////////////////////////////////////////////////////////////////////// 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'; | > > | < | < | > > | | < < < > > > > > > | > > < > > | 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 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 | //////////////////////////////////////////////////////////////////////// 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){ |
︙ | ︙ | |||
2968 2969 2970 2971 2972 2973 2974 | } } }/*OPFS export/import*/) .t({ name: '(Internal-use) OPFS utility APIs', test: async function(sqlite3){ const filename = this.opfsDbFile; | < | < | 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 | } } }/*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. ***************************************************************/ |
︙ | ︙ | |||
3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 | 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()){ | > | | | 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 | 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 |
︙ | ︙ | |||
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/mmapwarm.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/normalize.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/prefixes.c \ $(TOP)/ext/misc/qpvtab.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/remember.c \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/unionvtab.c \ $(TOP)/ext/misc/wholenumber.c \ $(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 \ | > | > > | 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 | $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/mmapwarm.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/normalize.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/prefixes.c \ $(TOP)/ext/misc/qpvtab.c \ $(TOP)/ext/misc/randomjson.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/remember.c \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/unionvtab.c \ $(TOP)/ext/misc/wholenumber.c \ $(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 \ |
︙ | ︙ | |||
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 | FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_OPT += -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS4 FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB FUZZSRC += $(TOP)/test/fuzzcheck.c FUZZSRC += $(TOP)/test/ossfuzz.c FUZZSRC += $(TOP)/test/vt02.c FUZZSRC += $(TOP)/test/fuzzinvariants.c FUZZSRC += $(TOP)/ext/recover/dbdata.c FUZZSRC += $(TOP)/ext/recover/sqlite3recover.c DBFUZZ_OPT = KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ ST_OPT = -DSQLITE_THREADSAFE=0 # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # | > > > | 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 | FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_OPT += -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS4 FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB FUZZCHECK_OPT += -DSQLITE_STRICT_SUBTYPE=1 FUZZCHECK_OPT += -DSQLITE_STATIC_RANDOMJSON FUZZSRC += $(TOP)/test/fuzzcheck.c FUZZSRC += $(TOP)/test/ossfuzz.c FUZZSRC += $(TOP)/test/vt02.c FUZZSRC += $(TOP)/test/fuzzinvariants.c FUZZSRC += $(TOP)/ext/recover/dbdata.c FUZZSRC += $(TOP)/ext/recover/sqlite3recover.c FUZZSRC += $(TOP)/ext/misc/randomjson.c DBFUZZ_OPT = KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ ST_OPT = -DSQLITE_THREADSAFE=0 # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # |
︙ | ︙ | |||
651 652 653 654 655 656 657 | 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 |
︙ | ︙ | |||
709 710 711 712 713 714 715 | # 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 . |
︙ | ︙ | |||
733 734 735 736 737 738 739 | 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) |
︙ | ︙ | |||
894 895 896 897 898 899 900 901 902 903 904 905 906 907 | TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit TESTFIXTURE_FLAGS += -DSQLITE_CKSUMVFS_STATIC testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ -o testfixture$(EXE) $(LIBTCL) libsqlite3.a $(THREADLIB) amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c \ | > > | 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 | TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit TESTFIXTURE_FLAGS += -DSQLITE_CKSUMVFS_STATIC TESTFIXTURE_FLAGS += -DSQLITE_STATIC_RANDOMJSON TESTFIXTURE_FLAGS += -DSQLITE_STRICT_SUBTYPE=1 testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ -o testfixture$(EXE) $(LIBTCL) libsqlite3.a $(THREADLIB) amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c \ |
︙ | ︙ |
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 | ** 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)); |
︙ | ︙ | |||
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)); | < | 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 | ){ 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; |
︙ | ︙ | |||
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) ); | | > | < | | 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 | 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; | > | > > | 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 | 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 ); | | > > > | 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 | 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] ){ | | | 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 | 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]) ){ | | | 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 | ** 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 ){ | | | 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 | 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. */ | | | 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 | }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 ){ | | | 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 | 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 ){ | | | 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 | 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. */ | | | 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 | 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 */ | | | > > > | 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469 | 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 ){ | | | 9477 9478 9479 9480 9481 9482 9483 9484 9485 9486 9487 9488 9489 9490 9491 | 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 ){ | | | | 9504 9505 9506 9507 9508 9509 9510 9511 9512 9513 9514 9515 9516 9517 9518 9519 9520 9521 | ** 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 ){ |
︙ | ︙ | |||
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 ){ | | | 9609 9610 9611 9612 9613 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 | }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 ){ | | | 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 9644 9645 9646 9647 9648 | 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{ | | | | | | 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 | 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); | | | 9837 9838 9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 | } 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) ){ | | | 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 | /* 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 ){ | | | 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 | /* 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) ){ | | | | 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108 10109 10110 10111 10112 | 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) ){ | | | 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 | 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)]; | > > > | 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 10810 10811 10812 | 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]); | > | 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 | 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; } | > > > | 11010 11011 11012 11013 11014 11015 11016 11017 11018 11019 11020 11021 11022 11023 11024 11025 11026 11027 11028 11029 11030 11031 11032 11033 11034 11035 11036 11037 11038 11039 11040 | ** 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++){ | > > | < | | | | | > > | 11099 11100 11101 11102 11103 11104 11105 11106 11107 11108 11109 11110 11111 11112 11113 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124 | ); } } #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.
︙ | ︙ | |||
60 61 62 63 64 65 66 | ** 18 1 File format write version ** 19 1 File format read version ** 20 1 Bytes of unused space at the end of each page ** 21 1 Max embedded payload fraction (must be 64) ** 22 1 Min embedded payload fraction (must be 32) ** 23 1 Min leaf payload fraction (must be 32) ** 24 4 File change counter | | | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | ** 18 1 File format write version ** 19 1 File format read version ** 20 1 Bytes of unused space at the end of each page ** 21 1 Max embedded payload fraction (must be 64) ** 22 1 Min embedded payload fraction (must be 32) ** 23 1 Min leaf payload fraction (must be 32) ** 24 4 File change counter ** 28 4 The size of the database in pages ** 32 4 First freelist page ** 36 4 Number of freelist pages in the file ** 40 60 15 4-byte meta values passed to higher layers ** ** 40 4 Schema cookie ** 44 4 File format of schema layer ** 48 4 Size of page cache |
︙ | ︙ | |||
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; } |
︙ | ︙ | |||
716 717 718 719 720 721 722 | /* ** Return the expression associated with a column. The expression might be ** the DEFAULT clause or the AS clause of a generated column. ** Return NULL if the column has no associated expression. */ Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){ if( pCol->iDflt==0 ) return 0; | | | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 | /* ** Return the expression associated with a column. The expression might be ** the DEFAULT clause or the AS clause of a generated column. ** Return NULL if the column has no associated expression. */ Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){ if( pCol->iDflt==0 ) return 0; if( !IsOrdinaryTable(pTab) ) return 0; if( NEVER(pTab->u.tab.pDfltList==0) ) return 0; if( NEVER(pTab->u.tab.pDfltList->nExpr<pCol->iDflt) ) return 0; return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; } /* ** Set the collating sequence name for a column. |
︙ | ︙ | |||
868 869 870 871 872 873 874 875 876 877 878 879 880 881 | } void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ /* Do not delete the table until the reference count reaches zero. */ assert( db!=0 ); if( !pTable ) return; if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return; deleteTable(db, pTable); } /* ** Unlink the given table from the hash tables and the delete the ** table structure with all its indices and foreign keys. */ | > > > | 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 | } void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ /* Do not delete the table until the reference count reaches zero. */ assert( db!=0 ); if( !pTable ) return; if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return; deleteTable(db, pTable); } void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){ sqlite3DeleteTable(db, (Table*)pTable); } /* ** Unlink the given table from the hash tables and the delete the ** table structure with all its indices and foreign keys. */ |
︙ | ︙ | |||
1406 1407 1408 1409 1410 1411 1412 | } } #endif /* ** Clean up the data structures associated with the RETURNING clause. */ | | > | 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 | } } #endif /* ** Clean up the data structures associated with the RETURNING clause. */ static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){ Returning *pRet = (Returning*)pArg; Hash *pHash; pHash = &(db->aDb[1].pSchema->trigHash); sqlite3HashInsert(pHash, pRet->zName, 0); sqlite3ExprListDelete(db, pRet->pReturnEL); sqlite3DbFree(db, pRet); } |
︙ | ︙ | |||
1448 1449 1450 1451 1452 1453 1454 | if( pRet==0 ){ sqlite3ExprListDelete(db, pList); return; } pParse->u1.pReturning = pRet; pRet->pParse = pParse; pRet->pReturnEL = pList; | | < | 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 | if( pRet==0 ){ sqlite3ExprListDelete(db, pList); return; } pParse->u1.pReturning = pRet; pRet->pParse = pParse; pRet->pReturnEL = pList; sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet); testcase( pParse->earlyCleanup ); if( db->mallocFailed ) return; sqlite3_snprintf(sizeof(pRet->zName), pRet->zName, "sqlite_returning_%p", pParse); pRet->retTrig.zName = pRet->zName; pRet->retTrig.op = TK_RETURNING; pRet->retTrig.tr_tm = TRIGGER_AFTER; |
︙ | ︙ | |||
1648 1649 1650 1651 1652 1653 1654 | 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 */ |
︙ | ︙ | |||
2915 2916 2917 2918 2919 2920 2921 | /* 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 2937 | /* 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); } sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)", db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } /* Add the table to the in-memory representation of the database. */ if( db->init.busy ){ |
︙ | ︙ | |||
2998 2999 3000 3001 3002 3003 3004 | 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 | | > | > > | | 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 | 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 SQLITE_TESTCTRL_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; |
︙ | ︙ | |||
5513 5514 5515 5516 5517 5518 5519 | } sqlite3DbFree(db, zColl); } iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); if( iDb<0 ) return; z = sqlite3NameFromToken(db, pObjName); if( z==0 ) return; | | > | 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 | } 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 |
︙ | ︙ | |||
5687 5688 5689 5690 5691 5692 5693 5694 5695 | if( pWith ){ int i; for(i=0; i<pWith->nCte; i++){ cteClear(db, &pWith->a[i]); } sqlite3DbFree(db, pWith); } } #endif /* !defined(SQLITE_OMIT_CTE) */ | > > > | 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 | if( pWith ){ int i; for(i=0; i<pWith->nCte; i++){ cteClear(db, &pWith->a[i]); } sqlite3DbFree(db, pWith); } } void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){ sqlite3WithDelete(db, (With*)pWith); } #endif /* !defined(SQLITE_OMIT_CTE) */ |
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); |
︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 | for(i=1; i<argc; i++){ z = sqlite3_value_text(argv[i]); n = sqlite3_value_bytes(argv[i]); if( z==0 || parseModifier(context, (char*)z, n, p, i) ) return 1; } computeJD(p); if( p->isError || !validJulianDay(p->iJD) ) return 1; return 0; } /* ** The following routines implement the various date and time functions ** of SQLite. | > > > > > > | 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 | for(i=1; i<argc; i++){ z = sqlite3_value_text(argv[i]); n = sqlite3_value_bytes(argv[i]); if( z==0 || parseModifier(context, (char*)z, n, p, i) ) return 1; } computeJD(p); if( p->isError || !validJulianDay(p->iJD) ) return 1; if( argc==1 && p->validYMD && p->D>28 ){ /* Make sure a YYYY-MM-DD is normalized. ** Example: 2023-02-31 -> 2023-03-03 */ assert( p->validJD ); p->validYMD = 0; } return 0; } /* ** The following routines implement the various date and time functions ** of SQLite. |
︙ | ︙ | |||
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 | 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 |
︙ | ︙ | |||
1278 1279 1280 1281 1282 1283 1284 | 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; } |
︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 | 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, '%'); |
︙ | ︙ | |||
1515 1516 1517 1518 1519 1520 1521 | } 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); } |
︙ | ︙ | |||
1585 1586 1587 1588 1589 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 | #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; |
︙ | ︙ | |||
1219 1220 1221 1222 1223 1224 1225 | return; } assert( pExpr->op==TK_FUNCTION ); assert( pExpr->pLeft==0 ); assert( ExprUseXList(pExpr) ); if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){ /* Ignore ORDER BY on zero-argument aggregates */ | | < < | 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 | return; } assert( pExpr->op==TK_FUNCTION ); assert( pExpr->pLeft==0 ); assert( ExprUseXList(pExpr) ); if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){ /* Ignore ORDER BY on zero-argument aggregates */ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy); return; } if( IsWindowFunc(pExpr) ){ sqlite3ExprOrderByAggregateError(pParse, pExpr); sqlite3ExprListDelete(db, pOrderBy); return; } |
︙ | ︙ | |||
1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 | if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbNNFreeNN(db, p); } } void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p ) sqlite3ExprDeleteNN(db, p); } /* ** Clear both elements of an OnOrUsing object */ void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ if( p==0 ){ /* Nothing to clear */ | > > > | 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 | if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbNNFreeNN(db, p); } } void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p ) sqlite3ExprDeleteNN(db, p); } void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){ if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p); } /* ** Clear both elements of an OnOrUsing object */ void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ if( p==0 ){ /* Nothing to clear */ |
︙ | ︙ | |||
1427 1428 1429 1430 1431 1432 1433 | ** ** The pExpr might be deleted immediately on an OOM error. ** ** The deferred delete is (currently) implemented by adding the ** pExpr to the pParse->pConstExpr list with a register number of 0. */ void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ | | < < | 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 | ** ** The pExpr might be deleted immediately on an OOM error. ** ** The deferred delete is (currently) implemented by adding the ** pExpr to the pParse->pConstExpr list with a register number of 0. */ void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); } /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the ** expression. */ void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){ if( p ){ |
︙ | ︙ | |||
1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 | 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++; } | > | 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 | 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); } pNewItem->u2 = pOldItem->u2; if( pNewItem->fg.isCte ){ pNewItem->u2.pCteUse->nUse++; } |
︙ | ︙ | |||
2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 | pItem++; }while( --i>0 ); sqlite3DbNNFreeNN(db, pList); } void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ if( pList ) exprListDeleteNN(db, pList); } /* ** Return the bitwise-OR of all Expr.flags fields in the given ** ExprList. */ u32 sqlite3ExprListFlags(const ExprList *pList){ int i; | > > > | 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 | pItem++; }while( --i>0 ); sqlite3DbNNFreeNN(db, pList); } void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ if( pList ) exprListDeleteNN(db, pList); } void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){ if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList); } /* ** Return the bitwise-OR of all Expr.flags fields in the given ** ExprList. */ u32 sqlite3ExprListFlags(const ExprList *pList){ int i; |
︙ | ︙ | |||
2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 | }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. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | }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_Continue; } /* ** 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. ** |
︙ | ︙ | |||
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 | ** 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 */ | > > > | 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 2450 2451 2452 2453 2454 | ** 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 */ |
︙ | ︙ | |||
2437 2438 2439 2440 2441 2442 2443 | /* no break */ deliberate_fall_through default: testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */ testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */ return WRC_Continue; } } | | > > > > > > > | | | | | | 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 | /* 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, int iCur){ Walker w; w.eCode = initFlag; w.pParse = pParse; w.xExprCallback = exprNodeIsConstant; w.xSelectCallback = sqlite3SelectWalkFail; #ifdef SQLITE_DEBUG w.xSelectCallback2 = sqlite3SelectWalkAssert2; #endif w.u.iCur = iCur; 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, 0); } /* ** 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, 0); } /* ** 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. */ int sqlite3ExprIsTableConstant(Expr *p, int iCur){ return exprIsConst(0, p, 3, iCur); } /* ** 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 |
︙ | ︙ | |||
2643 2644 2645 2646 2647 2648 2649 | ** ** 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 ); | | | 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 | ** ** 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, 0); } #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. */ |
︙ | ︙ | |||
2734 2735 2736 2737 2738 2739 2740 | case TK_STRING: case TK_FLOAT: case TK_BLOB: return 0; case TK_COLUMN: assert( ExprUseYTab(p) ); return ExprHasProperty(p, EP_CanBeNull) || | | > | 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 | 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 */ (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; } } /* |
︙ | ︙ | |||
2887 2888 2889 2890 2891 2892 2893 | #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. */ | | | | 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 | #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. |
︙ | ︙ | |||
3162 3163 3164 3165 3166 3167 3168 | ** 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) | | | 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 | ** 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 ){ |
︙ | ︙ | |||
3445 3446 3447 3448 3449 3450 3451 | 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. */ | | | 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 | 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 */ |
︙ | ︙ | |||
4609 4610 4611 4612 4613 4614 4615 | } #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); | < < < < < < | 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 | } #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: { |
︙ | ︙ | |||
4788 4789 4790 4791 4792 4793 4794 | #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ return pExpr->y.pWin->regResult; } #endif | | > > | 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 | #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; |
︙ | ︙ | |||
4819 4820 4821 4822 4823 4824 4825 | 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++){ | | | 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 | 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); } } |
︙ | ︙ | |||
5286 5287 5288 5289 5290 5291 5292 | */ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ int r2; pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); if( ConstFactorOk(pParse) && ALWAYS(pExpr!=0) && pExpr->op!=TK_REGISTER | | | 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 | */ 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 ){ |
︙ | ︙ | |||
5318 5319 5320 5321 5322 5323 5324 | assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) ); assert( target>0 && target<=pParse->nMem ); assert( pParse->pVdbe!=0 || pParse->db->mallocFailed ); if( pParse->pVdbe==0 ) return; inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); if( inReg!=target ){ u8 op; | > > | | | 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 | assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) ); assert( target>0 && target<=pParse->nMem ); assert( pParse->pVdbe!=0 || pParse->db->mallocFailed ); if( pParse->pVdbe==0 ) return; inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); if( inReg!=target ){ u8 op; Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr); testcase( pX!=pExpr ); if( ALWAYS(pX) && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER) ){ op = OP_Copy; }else{ op = OP_SCopy; } sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target); } |
︙ | ︙ | |||
5348 5349 5350 5351 5352 5353 5354 | /* ** 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){ | | | 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 | /* ** 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); } } /* |
︙ | ︙ | |||
5407 5408 5409 5410 5411 5412 5413 | if( flags & SQLITE_ECEL_OMITREF ){ i--; n--; }else{ sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i); } }else if( (flags & SQLITE_ECEL_FACTOR)!=0 | | | 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 | 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 |
︙ | ︙ | |||
6039 6040 6041 6042 6043 6044 6045 | /* ** Like sqlite3ExprCompare() except COLLATE operators at the top-level ** are ignored. */ int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ return sqlite3ExprCompare(0, | | | | 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 | /* ** Like sqlite3ExprCompare() except COLLATE operators at the top-level ** are ignored. */ int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ return sqlite3ExprCompare(0, sqlite3ExprSkipCollate(pA), sqlite3ExprSkipCollate(pB), iTab); } /* ** Return non-zero if Expr p can only be true if pNN is not NULL. ** ** Or if seenNot is true, return non-zero if Expr p can only be |
︙ | ︙ | |||
6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 | } /* end loop over pSrcList */ } return WRC_Continue; } case TK_AGG_FUNCTION: { if( (pNC->ncFlags & NC_InAggFunc)==0 && pWalker->walkerDepth==pExpr->op2 ){ /* Check to see if pExpr is a duplicate of another aggregate ** function that is already in the pAggInfo structure */ struct AggInfo_func *pItem = pAggInfo->aFunc; for(i=0; i<pAggInfo->nFunc; i++, pItem++){ | > | | 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 | } /* end loop over pSrcList */ } return WRC_Continue; } case TK_AGG_FUNCTION: { if( (pNC->ncFlags & NC_InAggFunc)==0 && pWalker->walkerDepth==pExpr->op2 && pExpr->pAggInfo==0 ){ /* Check to see if pExpr is a duplicate of another aggregate ** function that is already in the pAggInfo structure */ struct AggInfo_func *pItem = pAggInfo->aFunc; for(i=0; i<pAggInfo->nFunc; i++, pItem++){ if( NEVER(pItem->pFExpr==pExpr) ) break; if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){ break; } } if( i>=pAggInfo->nFunc ){ /* pExpr is original. Make a new entry in pAggInfo->aFunc[] */ |
︙ | ︙ | |||
6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 | pExpr->x.pList->a[0].pExpr,0)==0 ){ pItem->bOBPayload = 0; pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct); }else{ pItem->bOBPayload = 1; } }else{ pItem->iOBTab = -1; } if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){ pItem->iDistinct = pParse->nTab++; }else{ pItem->iDistinct = -1; | > > | 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 | pExpr->x.pList->a[0].pExpr,0)==0 ){ pItem->bOBPayload = 0; pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct); }else{ pItem->bOBPayload = 1; } pItem->bUseSubtype = (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0; }else{ pItem->iOBTab = -1; } if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){ pItem->iDistinct = pParse->nTab++; }else{ pItem->iDistinct = -1; |
︙ | ︙ |
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]); |
︙ | ︙ |
Changes to src/global.c.
︙ | ︙ | |||
240 241 242 243 244 245 246 247 248 249 250 251 252 253 | 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ SQLITE_USE_URI, /* bOpenUri */ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */ 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ {0,0,0,0,0,0,0,0}, /* m */ {0,0,0,0,0,0,0,0,0}, /* mutex */ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ | > > > | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ SQLITE_USE_URI, /* bOpenUri */ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */ #ifdef SQLITE_DEBUG 0, /* bJsonSelfcheck */ #endif 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ {0,0,0,0,0,0,0,0}, /* m */ {0,0,0,0,0,0,0,0,0}, /* mutex */ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ |
︙ | ︙ | |||
282 283 284 285 286 287 288 289 290 291 292 293 294 295 | #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 | ** 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++){ assert( pRow->a[ii].pExpr->affExpr==0 ); if( 0!=sqlite3ExprAffinity(pRow->a[ii].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. */ | < < > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | > | 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 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 | ** 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 ){ | | | 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 | 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 |
︙ | ︙ |
Changes to src/json.c.
1 2 3 4 5 6 7 8 9 10 11 12 | /* ** 2015-08-12 ** ** 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. ** ****************************************************************************** ** | | | > > > > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > | | > > | 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 | /* ** 2015-08-12 ** ** 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. ** ****************************************************************************** ** ** SQLite JSON functions. ** ** This file began as an extension in ext/misc/json1.c in 2015. That ** extension proved so useful that it has now been moved into the core. ** ** The original design stored all JSON as pure text, canonical RFC-8259. ** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16). ** All generated JSON text still conforms strictly to RFC-8259, but text ** with JSON-5 extensions is accepted as input. ** ** Beginning with version 3.45.0 (circa 2024-01-01), these routines also ** accept BLOB values that have JSON encoded using a binary representation ** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk ** format SQLite JSONB is completely different and incompatible with ** PostgreSQL JSONB. ** ** Decoding and interpreting JSONB is still O(N) where N is the size of ** the input, the same as text JSON. However, the constant of proportionality ** for JSONB is much smaller due to faster parsing. The size of each ** element in JSONB is encoded in its header, so there is no need to search ** for delimiters using persnickety syntax rules. JSONB seems to be about ** 3x faster than text JSON as a result. JSONB is also tends to be slightly ** smaller than text JSON, by 5% or 10%, but there are corner cases where ** JSONB can be slightly larger. So you are not far mistaken to say that ** a JSONB blob is the same size as the equivalent RFC-8259 text. ** ** ** THE JSONB ENCODING: ** ** Every JSON element is encoded in JSONB as a header and a payload. ** The header is between 1 and 9 bytes in size. The payload is zero ** or more bytes. ** ** The lower 4 bits of the first byte of the header determines the ** element type: ** ** 0: NULL ** 1: TRUE ** 2: FALSE ** 3: INT -- RFC-8259 integer literal ** 4: INT5 -- JSON5 integer literal ** 5: FLOAT -- RFC-8259 floating point literal ** 6: FLOAT5 -- JSON5 floating point literal ** 7: TEXT -- Text literal acceptable to both SQL and JSON ** 8: TEXTJ -- Text containing RFC-8259 escapes ** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes ** 10: TEXTRAW -- Text containing unescaped syntax characters ** 11: ARRAY ** 12: OBJECT ** ** The other three possible values (13-15) are reserved for future ** enhancements. ** ** The upper 4 bits of the first byte determine the size of the header ** and sometimes also the size of the payload. If X is the first byte ** of the element and if X>>4 is between 0 and 11, then the payload ** will be that many bytes in size and the header is exactly one byte ** in size. Other four values for X>>4 (12-15) indicate that the header ** is more than one byte in size and that the payload size is determined ** by the remainder of the header, interpreted as a unsigned big-endian ** integer. ** ** Value of X>>4 Size integer Total header size ** ------------- -------------------- ----------------- ** 12 1 byte (0-255) 2 ** 13 2 byte (0-65535) 3 ** 14 4 byte (0-4294967295) 5 ** 15 8 byte (0-1.8e19) 9 ** ** The payload size need not be expressed in its minimal form. For example, ** if the payload size is 10, the size can be expressed in any of 5 different ** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte, ** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by ** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and ** a single byte of 0x0a. The shorter forms are preferred, of course, but ** sometimes when generating JSONB, the payload size is not known in advance ** and it is convenient to reserve sufficient header space to cover the ** largest possible payload size and then come back later and patch up ** the size when it becomes known, resulting in a non-minimal encoding. ** ** The value (X>>4)==15 is not actually used in the current implementation ** (as SQLite is currently unable handle BLOBs larger than about 2GB) ** but is included in the design to allow for future enhancements. ** ** The payload follows the header. NULL, TRUE, and FALSE have no payload and ** their payload size must always be zero. The payload for INT, INT5, ** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the ** "..." or '...' delimiters are omitted from the various text encodings. ** The payload for ARRAY and OBJECT is a list of additional elements that ** are the content for the array or object. The payload for an OBJECT ** must be an even number of elements. The first element of each pair is ** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW. ** ** A valid JSONB blob consists of a single element, as described above. ** Usually this will be an ARRAY or OBJECT element which has many more ** elements as its content. But the overall blob is just a single element. ** ** Input validation for JSONB blobs simply checks that the element type ** code is between 0 and 12 and that the total size of the element ** (header plus payload) is the same as the size of the BLOB. If those ** checks are true, the BLOB is assumed to be JSONB and processing continues. ** Errors are only raised if some other miscoding is discovered during ** processing. ** ** Additional information can be found in the doc/jsonb.md file of the ** canonical SQLite source tree. */ #ifndef SQLITE_OMIT_JSON #include "sqliteInt.h" /* JSONB element types */ #define JSONB_NULL 0 /* "null" */ #define JSONB_TRUE 1 /* "true" */ #define JSONB_FALSE 2 /* "false" */ #define JSONB_INT 3 /* integer acceptable to JSON and SQL */ #define JSONB_INT5 4 /* integer in 0x000 notation */ #define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */ #define JSONB_FLOAT5 6 /* float with JSON5 extensions */ #define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */ #define JSONB_TEXTJ 8 /* Text with JSON escapes */ #define JSONB_TEXT5 9 /* Text with JSON-5 escape */ #define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */ #define JSONB_ARRAY 11 /* An array */ #define JSONB_OBJECT 12 /* An object */ /* Human-readable names for the JSONB values. The index for each ** string must correspond to the JSONB_* integer above. */ static const char * const jsonbType[] = { "null", "true", "false", "integer", "integer", "real", "real", "text", "text", "text", "text", "array", "object", "", "", "", "" }; /* ** Growing our own isspace() routine this way is twice as fast as ** the library isspace() function, resulting in a 7% overall performance ** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). */ static const char jsonIsSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #define jsonIsspace(x) (jsonIsSpace[(unsigned char)x]) /* ** The set of all space characters recognized by jsonIsspace(). ** Useful as the second argument to strspn(). */ static const char jsonSpaces[] = "\011\012\015\040"; /* ** Characters that are special to JSON. Control characters, ** '"' and '\\' and '\''. Actually, '\'' is not special to ** canonical JSON, but it is special in JSON-5, so we include ** it in the set of special characters. */ static const char jsonIsOk[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
︙ | ︙ | |||
70 71 72 73 74 75 76 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; | < < < < < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | < < < < < < < < | < < < | | | < < < < < | > > < < < < < < | | < < | | < | | | < < < < < < < < < < < < < < < < < < < < < | < | | > < | | | | < < < < < < < < < > > | | | | | | | | > | | | | < | | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | | | | | | | | | | | | | | | | < | | > > > > > > > > | > > | < < < < < | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | > > > | | | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | < | | < < < < < < < < < < < < | | < | < < < < < < > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | 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 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; /* Objects */ typedef struct JsonCache JsonCache; typedef struct JsonString JsonString; typedef struct JsonParse JsonParse; /* ** Magic number used for the JSON parse cache in sqlite3_get_auxdata() */ #define JSON_CACHE_ID (-429938) /* Cache entry */ #define JSON_CACHE_SIZE 4 /* Max number of cache entries */ /* ** jsonUnescapeOneChar() returns this invalid code point if it encounters ** a syntax error. */ #define JSON_INVALID_CHAR 0x99999 /* A cache mapping JSON text into JSONB blobs. ** ** Each cache entry is a JsonParse object with the following restrictions: ** ** * The bReadOnly flag must be set ** ** * The aBlob[] array must be owned by the JsonParse object. In other ** words, nBlobAlloc must be non-zero. ** ** * eEdit and delta must be zero. ** ** * zJson must be an RCStr. In other words bJsonIsRCStr must be true. */ struct JsonCache { sqlite3 *db; /* Database connection */ int nUsed; /* Number of active entries in the cache */ JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */ }; /* An instance of this object represents a JSON string ** under construction. Really, this is a generic string accumulator ** that can be and is used to create strings other than JSON. ** ** If the generated string is longer than will fit into the zSpace[] buffer, ** then it will be an RCStr string. This aids with caching of large ** JSON strings. */ struct JsonString { sqlite3_context *pCtx; /* Function context - put error messages here */ char *zBuf; /* Append JSON content here */ u64 nAlloc; /* Bytes of storage available in zBuf[] */ u64 nUsed; /* Bytes of zBuf[] currently used */ u8 bStatic; /* True if zBuf is static space */ u8 eErr; /* True if an error has been encountered */ char zSpace[100]; /* Initial static space */ }; /* Allowed values for JsonString.eErr */ #define JSTRING_OOM 0x01 /* Out of memory */ #define JSTRING_MALFORMED 0x02 /* Malformed JSONB */ #define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */ /* The "subtype" set for text JSON values passed through using ** sqlite3_result_subtype() and sqlite3_value_subtype(). */ #define JSON_SUBTYPE 74 /* Ascii for "J" */ /* ** Bit values for the flags passed into various SQL function implementations ** via the sqlite3_user_data() value. */ #define JSON_JSON 0x01 /* Result is always JSON */ #define JSON_SQL 0x02 /* Result is always SQL */ #define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ #define JSON_ISSET 0x04 /* json_set(), not json_insert() */ #define JSON_BLOB 0x08 /* Use the BLOB output format */ /* A parsed JSON value. Lifecycle: ** ** 1. JSON comes in and is parsed into a JSONB value in aBlob. The ** original text is stored in zJson. This step is skipped if the ** input is JSONB instead of text JSON. ** ** 2. The aBlob[] array is searched using the JSON path notation, if needed. ** ** 3. Zero or more changes are made to aBlob[] (via json_remove() or ** json_replace() or json_patch() or similar). ** ** 4. New JSON text is generated from the aBlob[] for output. This step ** is skipped if the function is one of the jsonb_* functions that ** 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 */ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ u8 bReadOnly; /* Do not modify. */ /* Search and edit information. See jsonLookupStep() */ u8 eEdit; /* Edit operation to apply */ int delta; /* Size change due to the edit */ u32 nIns; /* Number of bytes to insert */ u32 iLabel; /* Location of label if search landed on an object value */ u8 *aIns; /* Content to be inserted */ }; /* Allowed values for JsonParse.eEdit */ #define JEDIT_DEL 1 /* Delete if exists */ #define JEDIT_REPL 2 /* Overwrite if exists */ #define JEDIT_INS 3 /* Insert if not exists */ #define JEDIT_SET 4 /* Insert or overwrite */ /* ** Maximum nesting depth of JSON for this implementation. ** ** This limit is needed to avoid a stack overflow in the recursive ** descent parser. A depth of 1000 is far deeper than any sane JSON ** should go. Historical note: This limit was 2000 prior to version 3.42.0 */ #ifndef SQLITE_JSON_MAX_DEPTH # define JSON_MAX_DEPTH 1000 #else # define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH #endif /* ** Allowed values for the flgs argument to jsonParseFuncArg(); */ #define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */ #define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */ /************************************************************************** ** Forward references **************************************************************************/ static void jsonReturnStringAsBlob(JsonString*); static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*); static void jsonReturnParse(sqlite3_context*,JsonParse*); static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); static void jsonParseFree(JsonParse*); static u32 jsonbPayloadSize(const JsonParse*, u32, u32*); static u32 jsonUnescapeOneChar(const char*, u32, u32*); /************************************************************************** ** Utility routines for dealing with JsonCache objects **************************************************************************/ /* ** Free a JsonCache object. */ static void jsonCacheDelete(JsonCache *p){ int i; for(i=0; i<p->nUsed; i++){ jsonParseFree(p->a[i]); } sqlite3DbFree(p->db, p); } static void jsonCacheDeleteGeneric(void *p){ jsonCacheDelete((JsonCache*)p); } /* ** Insert a new entry into the cache. If the cache is full, expel ** the least recently used entry. Return SQLITE_OK on success or a ** result code otherwise. ** ** Cache entries are stored in age order, oldest first. */ static int jsonCacheInsert( sqlite3_context *ctx, /* The SQL statement context holding the cache */ JsonParse *pParse /* The parse object to be added to the cache */ ){ JsonCache *p; assert( pParse->zJson!=0 ); assert( pParse->bJsonIsRCStr ); assert( pParse->delta==0 ); p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); if( p==0 ){ sqlite3 *db = sqlite3_context_db_handle(ctx); p = sqlite3DbMallocZero(db, sizeof(*p)); if( p==0 ) return SQLITE_NOMEM; p->db = db; sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric); p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); if( p==0 ) return SQLITE_NOMEM; } if( p->nUsed >= JSON_CACHE_SIZE ){ jsonParseFree(p->a[0]); memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0])); p->nUsed = JSON_CACHE_SIZE-1; } assert( pParse->nBlobAlloc>0 ); pParse->eEdit = 0; pParse->nJPRef++; pParse->bReadOnly = 1; p->a[p->nUsed] = pParse; p->nUsed++; return SQLITE_OK; } /* ** Search for a cached translation the json text supplied by pArg. Return ** the JsonParse object if found. Return NULL if not found. ** ** When a match if found, the matching entry is moved to become the ** most-recently used entry if it isn't so already. ** ** The JsonParse object returned still belongs to the Cache and might ** be deleted at any moment. If the caller whants the JsonParse to ** linger, it needs to increment the nPJRef reference counter. */ static JsonParse *jsonCacheSearch( sqlite3_context *ctx, /* The SQL statement context holding the cache */ sqlite3_value *pArg /* Function argument containing SQL text */ ){ JsonCache *p; int i; const char *zJson; int nJson; if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){ return 0; } zJson = (const char*)sqlite3_value_text(pArg); if( zJson==0 ) return 0; nJson = sqlite3_value_bytes(pArg); p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); if( p==0 ){ return 0; } for(i=0; i<p->nUsed; i++){ if( p->a[i]->zJson==zJson ) break; } if( i>=p->nUsed ){ for(i=0; i<p->nUsed; i++){ if( p->a[i]->nJson!=nJson ) continue; if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break; } } if( i<p->nUsed ){ if( i<p->nUsed-1 ){ /* Make the matching entry the most recently used entry */ JsonParse *tmp = p->a[i]; memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp)); p->a[p->nUsed-1] = tmp; i = p->nUsed - 1; } assert( p->a[i]->delta==0 ); return p->a[i]; }else{ return 0; } } /************************************************************************** ** Utility routines for dealing with JsonString objects **************************************************************************/ /* Turn uninitialized bulk memory into a valid JsonString object ** holding a zero-length string. */ static void jsonStringZero(JsonString *p){ p->zBuf = p->zSpace; p->nAlloc = sizeof(p->zSpace); p->nUsed = 0; p->bStatic = 1; } /* Initialize the JsonString object */ static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){ p->pCtx = pCtx; p->eErr = 0; jsonStringZero(p); } /* Free all allocated memory and reset the JsonString object back to its ** initial state. */ static void jsonStringReset(JsonString *p){ if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf); jsonStringZero(p); } /* Report an out-of-memory (OOM) condition */ static void jsonStringOom(JsonString *p){ p->eErr |= JSTRING_OOM; if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx); jsonStringReset(p); } /* Enlarge pJson->zBuf so that it can hold at least N more bytes. ** Return zero on success. Return non-zero on an OOM error */ static int jsonStringGrow(JsonString *p, u32 N){ u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10; char *zNew; if( p->bStatic ){ if( p->eErr ) return 1; zNew = sqlite3RCStrNew(nTotal); if( zNew==0 ){ jsonStringOom(p); return SQLITE_NOMEM; } memcpy(zNew, p->zBuf, (size_t)p->nUsed); p->zBuf = zNew; p->bStatic = 0; }else{ p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal); if( p->zBuf==0 ){ p->eErr |= JSTRING_OOM; jsonStringZero(p); return SQLITE_NOMEM; } } p->nAlloc = nTotal; return SQLITE_OK; } /* Append N bytes from zIn onto the end of the JsonString string. */ static SQLITE_NOINLINE void jsonStringExpandAndAppend( JsonString *p, const char *zIn, u32 N ){ assert( N>0 ); if( jsonStringGrow(p,N) ) return; memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; } static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ if( N==0 ) return; if( N+p->nUsed >= p->nAlloc ){ jsonStringExpandAndAppend(p,zIn,N); }else{ memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; } } static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ assert( N>0 ); if( N+p->nUsed >= p->nAlloc ){ 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); va_end(ap); p->nUsed += (int)strlen(p->zBuf+p->nUsed); } /* Append a single character */ static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){ if( jsonStringGrow(p,1) ) return; p->zBuf[p->nUsed++] = c; } 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 ** increase associated with unwinding the jsonIsOk[] loop. */ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ u32 k; u8 c; const u8 *z = (const u8*)zIn; if( z==0 ) return; if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return; p->zBuf[p->nUsed++] = '"'; while( 1 /*exit-by-break*/ ){ k = 0; /* The following while() is the 4-way unwound equivalent of ** ** while( k<N && jsonIsOk[z[k]] ){ k++; } */ while( 1 /* Exit by break */ ){ if( k+3>=N ){ while( k<N && jsonIsOk[z[k]] ){ k++; } break; } if( !jsonIsOk[z[k]] ){ break; } if( !jsonIsOk[z[k+1]] ){ k += 1; break; } if( !jsonIsOk[z[k+2]] ){ k += 2; break; } if( !jsonIsOk[z[k+3]] ){ k += 3; break; }else{ k += 4; } } if( k>=N ){ if( k>0 ){ memcpy(&p->zBuf[p->nUsed], z, k); p->nUsed += k; } break; } if( k>0 ){ 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 ); } /* ** Append an sqlite3_value (such as a function parameter) to the JSON ** string under construction in p. */ static void jsonAppendSqlValue( JsonString *p, /* Append to this JSON string */ sqlite3_value *pValue /* Value to append */ ){ switch( sqlite3_value_type(pValue) ){ case SQLITE_NULL: { jsonAppendRawNZ(p, "null", 4); break; |
︙ | ︙ | |||
581 582 583 584 585 586 587 | jsonAppendRaw(p, z, n); }else{ jsonAppendString(p, z, n); } break; } default: { | > > > > > > | | | | | | > > > > | > > > > > > | > > > | | > > | > > > > > > > > > | | > | | > > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | > | | > | | < < < < > | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < | < < < < < < < < < < < < < < > | | > < | | | > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | jsonAppendRaw(p, z, n); }else{ jsonAppendString(p, z, n); } break; } default: { if( jsonFuncArgMightBeBinary(pValue) ){ JsonParse px; memset(&px, 0, sizeof(px)); px.aBlob = (u8*)sqlite3_value_blob(pValue); px.nBlob = sqlite3_value_bytes(pValue); jsonTranslateBlobToText(&px, 0, p); }else if( p->eErr==0 ){ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); p->eErr = JSTRING_ERR; jsonStringReset(p); } break; } } } /* Make the text in p (which is probably a generated JSON text string) ** the result of the SQL function. ** ** The JsonString is reset. ** ** If pParse and ctx are both non-NULL, then the SQL string in p is ** loaded into the zJson field of the pParse object as a RCStr and the ** pParse is added to the cache. */ static void jsonReturnString( JsonString *p, /* String to return */ JsonParse *pParse, /* JSONB source or NULL */ sqlite3_context *ctx /* Where to cache */ ){ assert( (pParse!=0)==(ctx!=0) ); assert( ctx==0 || ctx==p->pCtx ); if( p->eErr==0 ){ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx)); if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(p); }else if( p->bStatic ){ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, SQLITE_TRANSIENT, SQLITE_UTF8); }else if( jsonStringTerminate(p) ){ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){ int rc; pParse->zJson = sqlite3RCStrRef(p->zBuf); pParse->nJson = p->nUsed; pParse->bJsonIsRCStr = 1; rc = jsonCacheInsert(ctx, pParse); if( rc==SQLITE_NOMEM ){ sqlite3_result_error_nomem(ctx); jsonStringReset(p); return; } } sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed, sqlite3RCStrUnref, SQLITE_UTF8); }else{ sqlite3_result_error_nomem(p->pCtx); } }else if( p->eErr & JSTRING_OOM ){ sqlite3_result_error_nomem(p->pCtx); }else if( p->eErr & JSTRING_MALFORMED ){ sqlite3_result_error(p->pCtx, "malformed JSON", -1); } jsonStringReset(p); } /************************************************************************** ** Utility routines for dealing with JsonParse objects **************************************************************************/ /* ** Reclaim all memory allocated by a JsonParse object. But do not ** delete the JsonParse object itself. */ static void jsonParseReset(JsonParse *pParse){ assert( pParse->nJPRef<=1 ); 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 **************************************************************************/ /* ** Translate a single byte of Hex into an integer. ** This routine only gives a correct answer if h really is a valid hexadecimal ** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not ** assert() if the digit is not hex. */ static u8 jsonHexToInt(int h){ #ifdef SQLITE_ASCII h += 9*(1&(h>>6)); #endif #ifdef SQLITE_EBCDIC h += 9*(1&~(h>>4)); #endif return (u8)(h & 0xf); } /* ** Convert a 4-byte hex string into an integer */ static u32 jsonHexToInt4(const char *z){ u32 v; v = (jsonHexToInt(z[0])<<12) + (jsonHexToInt(z[1])<<8) + (jsonHexToInt(z[2])<<4) + jsonHexToInt(z[3]); return v; } /* ** Return true if z[] begins with 2 (or more) hexadecimal digits */ static int jsonIs2Hex(const char *z){ return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]); } |
︙ | ︙ | |||
1315 1316 1317 1318 1319 1320 1321 | char c2; char n; char eType; char nRepl; char *zMatch; char *zRepl; } aNanInfName[] = { | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | | | | | | | | | > | < > | | > | > | | | > | | | < | > | | | | | | | > > | > | < | | | | | | | | | | > | > > > > > > > > > | | | | | | | > > > > > > | > > | | | | | | < | < | | | | | | > | | | | | > > > > | | 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 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 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 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 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 1496 1497 1498 1499 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 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 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 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 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 | char c2; char n; char eType; char nRepl; char *zMatch; char *zRepl; } aNanInfName[] = { { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" }, { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" }, { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" }, { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" }, { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" }, }; /* ** Report the wrong number of arguments for json_insert(), json_replace() ** or json_set(). */ static void jsonWrongNumArgs( sqlite3_context *pCtx, const char *zFuncName ){ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", zFuncName); sqlite3_result_error(pCtx, zMsg, -1); sqlite3_free(zMsg); } /**************************************************************************** ** Utility routines for dealing with the binary BLOB representation of JSON ****************************************************************************/ /* ** Expand pParse->aBlob so that it holds at least N bytes. ** ** Return the number of errors. */ static int jsonBlobExpand(JsonParse *pParse, u32 N){ u8 *aNew; u32 t; 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; } /* ** If pParse->aBlob is not previously editable (because it is taken ** from sqlite3_value_blob(), as indicated by the fact that ** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable ** by making a copy into space obtained from malloc. ** ** Return true on success. Return false on OOM. */ static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ u8 *aOld; u32 nSize; assert( !pParse->bReadOnly ); if( pParse->oom ) return 0; if( pParse->nBlobAlloc>0 ) return 1; aOld = pParse->aBlob; nSize = pParse->nBlob + nExtra; pParse->aBlob = 0; if( jsonBlobExpand(pParse, nSize) ){ return 0; } assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra ); memcpy(pParse->aBlob, aOld, pParse->nBlob); return 1; } /* Expand pParse->aBlob and append one bytes. */ static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte( JsonParse *pParse, u8 c ){ jsonBlobExpand(pParse, pParse->nBlob+1); if( pParse->oom==0 ){ assert( pParse->nBlob+1<=pParse->nBlobAlloc ); pParse->aBlob[pParse->nBlob++] = c; } } /* Append a single character. */ static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ if( pParse->nBlob >= pParse->nBlobAlloc ){ jsonBlobExpandAndAppendOneByte(pParse, c); }else{ pParse->aBlob[pParse->nBlob++] = c; } } /* Slow version of jsonBlobAppendNode() that first resizes the ** pParse->aBlob structure. */ static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*); static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode( JsonParse *pParse, u8 eType, u32 szPayload, const void *aPayload ){ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return; jsonBlobAppendNode(pParse, eType, szPayload, aPayload); } /* Append an node type byte together with the payload size and ** possibly also the payload. ** ** If aPayload is not NULL, then it is a pointer to the payload which ** is also appended. If aPayload is NULL, the pParse->aBlob[] array ** is resized (if necessary) so that it is big enough to hold the ** payload, but the payload is not appended and pParse->nBlob is left ** pointing to where the first byte of payload will eventually be. */ static void jsonBlobAppendNode( JsonParse *pParse, /* The JsonParse object under construction */ u8 eType, /* Node type. One of JSONB_* */ u32 szPayload, /* Number of bytes of payload */ const void *aPayload /* The payload. Might be NULL */ ){ u8 *a; if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){ jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload); return; } assert( pParse->aBlob!=0 ); a = &pParse->aBlob[pParse->nBlob]; if( szPayload<=11 ){ a[0] = eType | (szPayload<<4); pParse->nBlob += 1; }else if( szPayload<=0xff ){ a[0] = eType | 0xc0; a[1] = szPayload & 0xff; pParse->nBlob += 2; }else if( szPayload<=0xffff ){ a[0] = eType | 0xd0; a[1] = (szPayload >> 8) & 0xff; a[2] = szPayload & 0xff; pParse->nBlob += 3; }else{ a[0] = eType | 0xe0; a[1] = (szPayload >> 24) & 0xff; a[2] = (szPayload >> 16) & 0xff; a[3] = (szPayload >> 8) & 0xff; a[4] = szPayload & 0xff; pParse->nBlob += 5; } if( aPayload ){ pParse->nBlob += szPayload; memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload); } } /* Change the payload size for the node at index i to be szPayload. */ static int jsonBlobChangePayloadSize( JsonParse *pParse, u32 i, u32 szPayload ){ u8 *a; u8 szType; u8 nExtra; u8 nNeeded; int delta; if( pParse->oom ) return 0; a = &pParse->aBlob[i]; szType = a[0]>>4; if( szType<=11 ){ nExtra = 0; }else if( szType==12 ){ nExtra = 1; }else if( szType==13 ){ nExtra = 2; }else{ nExtra = 4; } if( szPayload<=11 ){ nNeeded = 0; }else if( szPayload<=0xff ){ nNeeded = 1; }else if( szPayload<=0xffff ){ nNeeded = 2; }else{ nNeeded = 4; } delta = nNeeded - nExtra; if( delta ){ u32 newSize = pParse->nBlob + delta; if( delta>0 ){ if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){ return 0; /* OOM error. Error state recorded in pParse->oom. */ } a = &pParse->aBlob[i]; memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1)); }else{ memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta)); } pParse->nBlob = newSize; } if( nNeeded==0 ){ a[0] = (a[0] & 0x0f) | (szPayload<<4); }else if( nNeeded==1 ){ a[0] = (a[0] & 0x0f) | 0xc0; a[1] = szPayload & 0xff; }else if( nNeeded==2 ){ a[0] = (a[0] & 0x0f) | 0xd0; a[1] = (szPayload >> 8) & 0xff; a[2] = szPayload & 0xff; }else{ a[0] = (a[0] & 0x0f) | 0xe0; a[1] = (szPayload >> 24) & 0xff; a[2] = (szPayload >> 16) & 0xff; a[3] = (szPayload >> 8) & 0xff; a[4] = szPayload & 0xff; } return delta; } /* ** If z[0] is 'u' and is followed by exactly 4 hexadecimal character, ** then set *pOp to JSONB_TEXTJ and return true. If not, do not make ** any changes to *pOp and return false. */ static int jsonIs4HexB(const char *z, int *pOp){ if( z[0]!='u' ) return 0; if( !jsonIs4Hex(&z[1]) ) return 0; *pOp = JSONB_TEXTJ; return 1; } /* ** Check a single element of the JSONB in pParse for validity. ** ** The element to be checked starts at offset i and must end at on the ** last byte before iEnd. ** ** Return 0 if everything is correct. Return the 1-based byte offset of the ** error if a problem is detected. (In other words, if the error is at offset ** 0, return 1). */ static u32 jsonbValidityCheck( const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */ u32 i, /* Start of element as pParse->aBlob[i] */ u32 iEnd, /* One more than the last byte of the element */ u32 iDepth /* Current nesting depth */ ){ u32 n, sz, j, k; const u8 *z; u8 x; if( iDepth>JSON_MAX_DEPTH ) return i+1; sz = 0; n = jsonbPayloadSize(pParse, i, &sz); if( NEVER(n==0) ) return i+1; /* Checked by caller */ if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */ z = pParse->aBlob; x = z[i] & 0x0f; switch( x ){ case JSONB_NULL: case JSONB_TRUE: case JSONB_FALSE: { return n+sz==1 ? 0 : i+1; } case JSONB_INT: { if( sz<1 ) return i+1; j = i+n; if( z[j]=='-' ){ j++; if( sz<2 ) return i+1; } k = i+n+sz; while( j<k ){ if( sqlite3Isdigit(z[j]) ){ j++; }else{ return j+1; } } return 0; } case JSONB_INT5: { if( sz<3 ) return i+1; j = i+n; if( z[j]=='-' ){ if( sz<4 ) return i+1; j++; } if( z[j]!='0' ) return i+1; if( z[j+1]!='x' && z[j+1]!='X' ) return j+2; j += 2; k = i+n+sz; while( j<k ){ if( sqlite3Isxdigit(z[j]) ){ j++; }else{ return j+1; } } return 0; } case JSONB_FLOAT: case JSONB_FLOAT5: { u8 seen = 0; /* 0: initial. 1: '.' seen 2: 'e' seen */ if( sz<2 ) return i+1; j = i+n; k = j+sz; if( z[j]=='-' ){ j++; if( sz<3 ) return i+1; } if( z[j]=='.' ){ if( x==JSONB_FLOAT ) return j+1; if( !sqlite3Isdigit(z[j+1]) ) return j+1; j += 2; seen = 1; }else if( z[j]=='0' && x==JSONB_FLOAT ){ if( j+3>k ) return j+1; if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1; j++; } for(; j<k; j++){ if( sqlite3Isdigit(z[j]) ) continue; if( z[j]=='.' ){ if( seen>0 ) return j+1; if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){ return j+1; } seen = 1; continue; } if( z[j]=='e' || z[j]=='E' ){ if( seen==2 ) return j+1; if( j==k-1 ) return j+1; if( z[j+1]=='+' || z[j+1]=='-' ){ j++; if( j==k-1 ) return j+1; } seen = 2; continue; } return j+1; } if( seen==0 ) return i+1; return 0; } case JSONB_TEXT: { j = i+n; k = j+sz; while( j<k ){ if( !jsonIsOk[z[j]] && z[j]!='\'' ) return j+1; j++; } return 0; } case JSONB_TEXTJ: 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++; }else if( x!=JSONB_TEXT5 ){ return j+1; }else{ u32 c = 0; u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c); if( c==JSON_INVALID_CHAR ) return j+1; j += szC - 1; } } j++; } return 0; } case JSONB_TEXTRAW: { return 0; } case JSONB_ARRAY: { u32 sub; j = i+n; k = j+sz; while( j<k ){ sz = 0; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 ) return j+1; if( j+n+sz>k ) return j+1; sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1); if( sub ) return sub; j += n + sz; } assert( j==k ); return 0; } case JSONB_OBJECT: { u32 cnt = 0; u32 sub; j = i+n; k = j+sz; while( j<k ){ sz = 0; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 ) return j+1; if( j+n+sz>k ) return j+1; if( (cnt & 1)==0 ){ x = z[j] & 0x0f; if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return j+1; } sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1); if( sub ) return sub; cnt++; j += n + sz; } assert( j==k ); if( (cnt & 1)!=0 ) return j+1; return 0; } default: { return i+1; } } } /* ** Translate a single element of JSON text at pParse->zJson[i] into ** its equivalent binary JSONB representation. Append the translation into ** pParse->aBlob[] beginning at pParse->nBlob. The size of ** pParse->aBlob[] is increased as necessary. ** ** Return the index of the first character past the end of the element parsed, ** or one of the following special result codes: ** ** 0 End of input ** -1 Syntax error or OOM ** -2 '}' seen \ ** -3 ']' seen \___ For these returns, pParse->iErr is set to ** -4 ',' seen / the index in zJson[] of the seen character ** -5 ':' seen / */ static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){ char c; u32 j; u32 iThis, iStart; int x; u8 t; const char *z = pParse->zJson; json_parse_restart: switch( (u8)z[i] ){ case '{': { /* Parse object */ iThis = pParse->nBlob; jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0); if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; return -1; } iStart = pParse->nBlob; for(j=i+1;;j++){ u32 iBlob = pParse->nBlob; x = jsonTranslateTextToBlob(pParse, j); if( x<=0 ){ int op; if( x==(-2) ){ j = pParse->iErr; if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1; break; } j += json5Whitespace(&z[j]); op = JSONB_TEXT; if( sqlite3JsonId1(z[j]) || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op)) ){ int k = j+1; while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op)) ){ k++; } assert( iBlob==pParse->nBlob ); jsonBlobAppendNode(pParse, op, k-j, &z[j]); pParse->hasNonstd = 1; x = k; }else{ if( x!=-1 ) pParse->iErr = j; return -1; } } if( pParse->oom ) return -1; t = pParse->aBlob[iBlob] & 0x0f; if( t<JSONB_TEXT || t>JSONB_TEXTRAW ){ pParse->iErr = j; return -1; } j = x; if( z[j]==':' ){ j++; }else{ if( jsonIsspace(z[j]) ){ /* strspn() is not helpful here */ do{ j++; }while( jsonIsspace(z[j]) ); if( z[j]==':' ){ j++; goto parse_object_value; } } x = jsonTranslateTextToBlob(pParse, j); if( x!=(-5) ){ if( x!=(-1) ) pParse->iErr = j; return -1; } j = pParse->iErr+1; } parse_object_value: x = jsonTranslateTextToBlob(pParse, j); if( x<=0 ){ if( x!=(-1) ) pParse->iErr = j; return -1; } j = x; if( z[j]==',' ){ continue; }else if( z[j]=='}' ){ break; }else{ if( jsonIsspace(z[j]) ){ j += 1 + (u32)strspn(&z[j+1], jsonSpaces); if( z[j]==',' ){ continue; }else if( z[j]=='}' ){ break; } } x = jsonTranslateTextToBlob(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; } if( x==(-2) ){ j = pParse->iErr; break; } } pParse->iErr = j; return -1; } 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; } for(j=i+1;;j++){ x = jsonTranslateTextToBlob(pParse, j); if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1; break; } if( x!=(-1) ) pParse->iErr = j; return -1; } j = x; if( z[j]==',' ){ continue; }else if( z[j]==']' ){ break; }else{ if( jsonIsspace(z[j]) ){ j += 1 + (u32)strspn(&z[j+1], jsonSpaces); if( z[j]==',' ){ continue; }else if( z[j]==']' ){ break; } } x = jsonTranslateTextToBlob(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; } if( x==(-3) ){ j = pParse->iErr; break; } } pParse->iErr = j; return -1; } jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); pParse->iDepth--; return j+1; } case '\'': { u8 opcode; char cDelim; pParse->hasNonstd = 1; opcode = JSONB_TEXT; goto parse_string; case '"': /* Parse string */ opcode = JSONB_TEXT; parse_string: cDelim = z[i]; j = i+1; while( 1 /*exit-by-break*/ ){ if( jsonIsOk[(u8)z[j]] ){ if( !jsonIsOk[(u8)z[j+1]] ){ j += 1; }else if( !jsonIsOk[(u8)z[j+2]] ){ j += 2; }else{ j += 3; continue; } } c = z[j]; if( c==cDelim ){ break; }else if( c=='\\' ){ c = z[++j]; if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' || c=='n' || c=='r' || c=='t' || (c=='u' && jsonIs4Hex(&z[j+1])) ){ if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ; }else if( c=='\'' || c=='0' || c=='v' || c=='\n' || (0xe2==(u8)c && 0x80==(u8)z[j+1] && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) || (c=='x' && jsonIs2Hex(&z[j+1])) ){ opcode = JSONB_TEXT5; pParse->hasNonstd = 1; }else if( c=='\r' ){ if( z[j+1]=='\n' ) j++; 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; } case 't': { if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ jsonBlobAppendOneByte(pParse, JSONB_TRUE); return i+4; } pParse->iErr = i; return -1; } case 'f': { if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ jsonBlobAppendOneByte(pParse, JSONB_FALSE); return i+5; } pParse->iErr = i; return -1; } case '+': { u8 seenE; pParse->hasNonstd = 1; t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ goto parse_number; case '.': if( sqlite3Isdigit(z[i+1]) ){ pParse->hasNonstd = 1; t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ seenE = 0; goto parse_number_2; } pParse->iErr = i; return -1; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* Parse number */ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ parse_number: seenE = 0; assert( '-' < '0' ); assert( '+' < '0' ); assert( '.' < '0' ); c = z[i]; if( c<='0' ){ if( c=='0' ){ if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ assert( t==0x00 ); pParse->hasNonstd = 1; t = 0x01; for(j=i+3; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; }else if( sqlite3Isdigit(z[i+1]) ){ pParse->iErr = i+1; return -1; } }else{ if( !sqlite3Isdigit(z[i+1]) ){ /* JSON5 allows for "+Infinity" and "-Infinity" using exactly ** that case. SQLite also allows these in any case and it allows ** "+inf" and "-inf". */ if( (z[i+1]=='I' || z[i+1]=='i') && sqlite3StrNICmp(&z[i+1], "inf",3)==0 ){ pParse->hasNonstd = 1; if( z[i]=='-' ){ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); }else{ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); } return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); } if( z[i+1]=='.' ){ pParse->hasNonstd = 1; t |= 0x01; goto parse_number_2; } pParse->iErr = i; return -1; } if( z[i+1]=='0' ){ if( sqlite3Isdigit(z[i+2]) ){ pParse->iErr = i+1; return -1; }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ pParse->hasNonstd = 1; t |= 0x01; for(j=i+4; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; } } } } parse_number_2: for(j=i+1;; j++){ c = z[j]; if( sqlite3Isdigit(c) ) continue; if( c=='.' ){ if( (t & 0x02)!=0 ){ pParse->iErr = j; return -1; } t |= 0x02; continue; } if( c=='e' || c=='E' ){ if( z[j-1]<'0' ){ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ pParse->hasNonstd = 1; t |= 0x01; }else{ pParse->iErr = j; return -1; } } if( seenE ){ pParse->iErr = j; return -1; } t |= 0x02; seenE = 1; c = z[j+1]; if( c=='+' || c=='-' ){ j++; c = z[j+1]; } if( c<'0' || c>'9' ){ pParse->iErr = j; return -1; } continue; } break; } if( z[j-1]<'0' ){ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ pParse->hasNonstd = 1; t |= 0x01; }else{ pParse->iErr = j; return -1; } } parse_number_finish: assert( JSONB_INT+0x01==JSONB_INT5 ); assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 ); assert( JSONB_INT+0x02==JSONB_FLOAT ); if( z[i]=='+' ) i++; jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]); return j; } case '}': { pParse->iErr = i; return -2; /* End of {...} */ } case ']': { |
︙ | ︙ | |||
1715 1716 1717 1718 1719 1720 1721 | case 0: { return 0; /* End of file */ } case 0x09: case 0x0a: case 0x0d: case 0x20: { | | < < | 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 | case 0: { return 0; /* End of file */ } case 0x09: case 0x0a: case 0x0d: case 0x20: { i += 1 + (u32)strspn(&z[i+1], jsonSpaces); goto json_parse_restart; } case 0x0b: case 0x0c: case '/': case 0xc2: case 0xe1: |
︙ | ︙ | |||
1739 1740 1741 1742 1743 1744 1745 | goto json_parse_restart; } pParse->iErr = i; return -1; } case 'n': { if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ | | > | | > > > > | | > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > > > | < > > > > | < > | > | < < < < > > | > > > > > > > | > > | < | > | | > > > > > | > > | > > > > > > > > | > > > > > > > > | > > | > > | > > > > > > > > > > > | > > > | | | | > | > > > > | | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > > > > | < | > > > > > | > > > > > > > > > > > > > > > > > > > > | < | < > | | | | < < > > | > > | < < > > > > > | > > | < > > | | < > | < > | | | < > | | < | > | | < > > > > > | > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > > > | > | > > > > > > > > > > | > > | < > > | > | > > > > > > > > | > > > > > > > > > > > > > | > > | > > > > > | > > > > > > > > | > | < | < < < | > > > | | > | > > > | > | | | < > | | | > > | | > > > | > > > | > | | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > | < > > > | | < < > > > | < < < | | > > > > > > > > > | < < < < < < < | < > > > > > > > > > > > > > | > > > > > > > | | < > | > > > | < > < < < < | < | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > | > | > > > > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | | < < < < < | > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | < | < < < < | | | > | > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > < > | < > | > | < < | | < > > > | < > | | | > | < > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > | > | > > | | > | > > | < | > < | > > > | < | > > > > > > > > > > | > > > > > | > > | | > > > | | | | | < > > | > > | < < < | | | | < | > | > > | | | > | < > | | > | > > > | > > > | | | | > > > > > > > > > > > | > > > | < | > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | | > | > > > > | > > | | | > > > > > > | < > > | > > > > > > | | | | < < < < < | | < | > > | > > > > > > > > > > > > > > > > | | > > > > > > > > | > > > > > > > > > > < < > > > | < > | | > | | < | < < > > | < > | < < < < < | > > > > > > > > > > > > > > > > | | > > > | | < < > > | > > > > | > | | > > > > > > > > > | > > > > > > > > | < > > > > > > > > > | | > > | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > | < < > > > > > | < > | < < < < < | | > > | | | > > > > > > > > > > > > > | > > > > | > > > > > > > | | > > > > > > < < > > > | < | > > > | > > > > > > | > > > > | | < | > | < < < < | < < < | > > > > > | > | > | > | > | > > > | < | > > | | > > | | | < < < < | < < > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | | < < < < < | | > > > > > | > | > > > > | < | | > > > > > > > > > > > > > > > > > | < < | > > > > > > > > > > > | > > > > | > > > > > > > > > > > > | > | > > > > | > > > | | > > > > | > > > > | > | > > > > > | > > > > > > | | > > > > > > > | > > > > > > > | | > | > | < < > > > > > > > | > > > > > > > > > > > | > > > > > | > > | | > > > | | | < < < < < < < < < | > > | > > > > > > > > > > > > > > | | > | > > > | < > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < | | < > | > | < < | < < < < < < < < < | < > | > | < < < < < | | < < < < < | | | | | | | < | | | < > > > > | > > > > > > > > > > > | | | | < < < < | | < < < < | > > > > > | < | > > > > > | < < < < < < < < | 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 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 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 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 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 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 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 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 2203 2204 2205 2206 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 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 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 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 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 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 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 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 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 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 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 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 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 3067 3068 3069 3070 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 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 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 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 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 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 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 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 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 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 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 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 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 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 | goto json_parse_restart; } pParse->iErr = i; return -1; } 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; nn = aNanInfName[k].n; if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){ continue; } if( sqlite3Isalnum(z[i+nn]) ) continue; if( aNanInfName[k].eType==JSONB_FLOAT ){ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); }else{ jsonBlobAppendOneByte(pParse, JSONB_NULL); } pParse->hasNonstd = 1; return i + nn; } pParse->iErr = i; return -1; /* Syntax error */ } } /* End switch(z[i]) */ } /* ** Parse a complete JSON string. Return 0 on success or non-zero if there ** are any errors. If an error occurs, free all memory held by pParse, ** but not pParse itself. ** ** pParse must be initialized to an empty parse object prior to calling ** this routine. */ static int jsonConvertTextToBlob( JsonParse *pParse, /* Initialize and fill this JsonParse object */ sqlite3_context *pCtx /* Report errors here */ ){ int i; const char *zJson = pParse->zJson; i = jsonTranslateTextToBlob(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ #ifdef SQLITE_DEBUG assert( pParse->iDepth==0 ); if( sqlite3Config.bJsonSelfcheck ){ assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 ); } #endif while( jsonIsspace(zJson[i]) ) i++; if( zJson[i] ){ i += json5Whitespace(&zJson[i]); if( zJson[i] ){ if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1); jsonParseReset(pParse); return 1; } pParse->hasNonstd = 1; } } if( i<=0 ){ if( pCtx!=0 ){ if( pParse->oom ){ sqlite3_result_error_nomem(pCtx); }else{ sqlite3_result_error(pCtx, "malformed JSON", -1); } } jsonParseReset(pParse); return 1; } return 0; } /* ** The input string pStr is a well-formed JSON text string. Convert ** 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. */ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ u8 x; u32 sz; u32 n; if( NEVER(i>pParse->nBlob) ){ *pSz = 0; return 0; } x = pParse->aBlob[i]>>4; if( x<=11 ){ sz = x; n = 1; }else if( x==12 ){ if( i+1>=pParse->nBlob ){ *pSz = 0; return 0; } sz = pParse->aBlob[i+1]; n = 2; }else if( x==13 ){ if( i+2>=pParse->nBlob ){ *pSz = 0; return 0; } sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; n = 3; }else if( x==14 ){ if( i+4>=pParse->nBlob ){ *pSz = 0; return 0; } sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) + (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; n = 5; }else{ if( i+8>=pParse->nBlob || pParse->aBlob[i+1]!=0 || pParse->aBlob[i+2]!=0 || pParse->aBlob[i+3]!=0 || pParse->aBlob[i+4]!=0 ){ *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; } /* ** 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. ** ** 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 jsonTranslateBlobToText( const JsonParse *pParse, /* the complete parse of the JSON */ u32 i, /* Start rendering at this index */ JsonString *pOut /* Write JSON here */ ){ u32 sz, n, j, iEnd; n = jsonbPayloadSize(pParse, i, &sz); if( n==0 ){ pOut->eErr |= JSTRING_MALFORMED; return pParse->nBlob+1; } switch( pParse->aBlob[i] & 0x0f ){ case JSONB_NULL: { jsonAppendRawNZ(pOut, "null", 4); return i+1; } case JSONB_TRUE: { jsonAppendRawNZ(pOut, "true", 4); return i+1; } 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++){ if( !sqlite3Isxdigit(zIn[k]) ){ pOut->eErr |= JSTRING_MALFORMED; break; }else if( (u>>60)!=0 ){ bOverflow = 1; }else{ u = u*16 + sqlite3HexToInt(zIn[k]); } } 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'); } for(; k<sz; k++){ jsonAppendChar(pOut, zIn[k]); if( zIn[k]=='.' && (k+1==sz || !sqlite3Isdigit(zIn[k+1])) ){ jsonAppendChar(pOut, '0'); } } break; } case JSONB_TEXT: case JSONB_TEXTJ: { jsonAppendChar(pOut, '"'); jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); jsonAppendChar(pOut, '"'); break; } 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; break; } switch( (u8)zIn[1] ){ case '\'': jsonAppendChar(pOut, '\''); break; case 'v': jsonAppendRawNZ(pOut, "\\u0009", 6); break; case 'x': if( sz2<4 ){ pOut->eErr |= JSTRING_MALFORMED; sz2 = 2; break; } jsonAppendRawNZ(pOut, "\\u00", 4); jsonAppendRawNZ(pOut, &zIn[2], 2); zIn += 2; sz2 -= 2; break; case '0': jsonAppendRawNZ(pOut, "\\u0000", 6); break; case '\r': if( sz2>2 && zIn[2]=='\n' ){ zIn++; sz2--; } break; case '\n': break; case 0xe2: /* '\' followed by either U+2028 or U+2029 is ignored as ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29. ** U+2029 is the same except for the last byte */ if( sz2<4 || 0x80!=(u8)zIn[2] || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) ){ pOut->eErr |= JSTRING_MALFORMED; sz2 = 2; break; } zIn += 2; sz2 -= 2; break; default: jsonAppendRawNZ(pOut, zIn, 2); break; } assert( sz2>=2 ); zIn += 2; sz2 -= 2; } jsonAppendChar(pOut, '"'); break; } case JSONB_TEXTRAW: { 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. */ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ u32 sz, n; const u8 *aBlob; int nBlob; JsonParse s; if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0; aBlob = sqlite3_value_blob(pJson); nBlob = sqlite3_value_bytes(pJson); if( nBlob<1 ) return 0; if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0; memset(&s, 0, sizeof(s)); s.aBlob = (u8*)aBlob; s.nBlob = nBlob; n = jsonbPayloadSize(&s, 0, &sz); if( n==0 ) return 0; if( sz+n!=(u32)nBlob ) return 0; if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0; return sz+n==(u32)nBlob; } /* ** Given that a JSONB_ARRAY object starts at offset i, return ** the number of entries in that array. */ static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ u32 n, sz, i, iEnd; u32 k = 0; n = jsonbPayloadSize(pParse, iRoot, &sz); iEnd = iRoot+n+sz; for(i=iRoot+n; n>0 && i<iEnd; i+=sz+n, k++){ n = jsonbPayloadSize(pParse, i, &sz); } return k; } /* ** Edit the payload size of the element at iRoot by the amount in ** pParse->delta. */ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ u32 sz = 0; u32 nBlob; assert( pParse->delta!=0 ); assert( pParse->nBlobAlloc >= pParse->nBlob ); nBlob = pParse->nBlob; pParse->nBlob = pParse->nBlobAlloc; (void)jsonbPayloadSize(pParse, iRoot, &sz); pParse->nBlob = nBlob; sz += pParse->delta; pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz); } /* ** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of ** content beginning at iDel, and replacing them with nIns bytes of ** content given by aIns. ** ** nDel may be zero, in which case no bytes are removed. But iDel is ** still important as new bytes will be insert beginning at iDel. ** ** aIns may be zero, in which case space is created to hold nIns bytes ** beginning at iDel, but that space is uninitialized. ** ** Set pParse->oom if an OOM occurs. */ static void jsonBlobEdit( JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */ u32 iDel, /* First byte to be removed */ u32 nDel, /* Number of bytes to remove */ const u8 *aIns, /* Content to insert */ u32 nIns /* Bytes of content to insert */ ){ i64 d = (i64)nIns - (i64)nDel; if( d!=0 ){ if( pParse->nBlob + d > pParse->nBlobAlloc ){ jsonBlobExpand(pParse, pParse->nBlob+d); if( pParse->oom ) return; } memmove(&pParse->aBlob[iDel+nIns], &pParse->aBlob[iDel+nDel], pParse->nBlob - (iDel+nDel)); pParse->nBlob += d; pParse->delta += d; } if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns); } /* ** Return the number of escaped newlines to be ignored. ** An escaped newline is a one of the following byte sequences: ** ** 0x5c 0x0a ** 0x5c 0x0d ** 0x5c 0x0d 0x0a ** 0x5c 0xe2 0x80 0xa8 ** 0x5c 0xe2 0x80 0xa9 */ static u32 jsonBytesToBypass(const char *z, u32 n){ u32 i = 0; while( i+1<n ){ if( z[i]!='\\' ) return i; if( z[i+1]=='\n' ){ i += 2; continue; } if( z[i+1]=='\r' ){ if( i+2<n && z[i+2]=='\n' ){ i += 3; }else{ i += 2; } continue; } if( 0xe2==(u8)z[i+1] && i+3<n && 0x80==(u8)z[i+2] && (0xa8==(u8)z[i+3] || 0xa9==(u8)z[i+3]) ){ i += 4; continue; } break; } return i; } /* ** Input z[0..n] defines JSON escape sequence including the leading '\\'. ** Decode that escape sequence into a single character. Write that ** character into *piOut. Return the number of bytes in the escape sequence. ** ** If there is a syntax error of some kind (for example too few characters ** after the '\\' to complete the encoding) then *piOut is set to ** JSON_INVALID_CHAR. */ static u32 jsonUnescapeOneChar(const char *z, u32 n, u32 *piOut){ assert( n>0 ); assert( z[0]=='\\' ); if( n<2 ){ *piOut = JSON_INVALID_CHAR; return n; } switch( (u8)z[1] ){ case 'u': { u32 v, vlo; if( n<6 ){ *piOut = JSON_INVALID_CHAR; return n; } v = jsonHexToInt4(&z[2]); if( (v & 0xfc00)==0xd800 && n>=12 && z[6]=='\\' && z[7]=='u' && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00 ){ *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000; return 12; }else{ *piOut = v; return 6; } } case 'b': { *piOut = '\b'; return 2; } case 'f': { *piOut = '\f'; return 2; } case 'n': { *piOut = '\n'; return 2; } case 'r': { *piOut = '\r'; return 2; } case 't': { *piOut = '\t'; return 2; } case 'v': { *piOut = '\v'; return 2; } case '0': { *piOut = 0; return 2; } case '\'': case '"': case '/': case '\\':{ *piOut = z[1]; return 2; } case 'x': { if( n<4 ){ *piOut = JSON_INVALID_CHAR; return n; } *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]); return 4; } case 0xe2: case '\r': case '\n': { u32 nSkip = jsonBytesToBypass(z, n); if( nSkip==0 ){ *piOut = JSON_INVALID_CHAR; return n; }else if( nSkip==n ){ *piOut = 0; return n; }else if( z[nSkip]=='\\' ){ return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut); }else{ int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut); return nSkip + sz; } } default: { *piOut = JSON_INVALID_CHAR; return 2; } } } /* ** Compare two object labels. Return 1 if they are equal and ** 0 if they differ. ** ** In this version, we know that one or the other or both of the ** two comparands contains an escape sequence. */ static SQLITE_NOINLINE int jsonLabelCompareEscaped( const char *zLeft, /* The left label */ u32 nLeft, /* Size of the left label in bytes */ int rawLeft, /* True if zLeft contains no escapes */ const char *zRight, /* The right label */ u32 nRight, /* Size of the right label in bytes */ int rawRight /* True if zRight is escape-free */ ){ u32 cLeft, cRight; assert( rawLeft==0 || rawRight==0 ); while( 1 /*exit-by-return*/ ){ if( nLeft==0 ){ cLeft = 0; }else if( rawLeft || zLeft[0]!='\\' ){ cLeft = ((u8*)zLeft)[0]; if( cLeft>=0xc0 ){ int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft); zLeft += sz; nLeft -= sz; }else{ zLeft++; nLeft--; } }else{ u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft); zLeft += n; assert( n<=nLeft ); nLeft -= n; } if( nRight==0 ){ cRight = 0; }else if( rawRight || zRight[0]!='\\' ){ cRight = ((u8*)zRight)[0]; if( cRight>=0xc0 ){ int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight); zRight += sz; nRight -= sz; }else{ zRight++; nRight--; } }else{ u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight); zRight += n; assert( n<=nRight ); nRight -= n; } if( cLeft!=cRight ) return 0; if( cLeft==0 ) return 1; } } /* ** Compare two object labels. Return 1 if they are equal and ** 0 if they differ. Return -1 if an OOM occurs. */ static int jsonLabelCompare( const char *zLeft, /* The left label */ u32 nLeft, /* Size of the left label in bytes */ int rawLeft, /* True if zLeft contains no escapes */ const char *zRight, /* The right label */ u32 nRight, /* Size of the right label in bytes */ int rawRight /* True if zRight is escape-free */ ){ if( rawLeft && rawRight ){ /* Simpliest case: Neither label contains escapes. A simple ** memcmp() is sufficient. */ if( nLeft!=nRight ) return 0; return memcmp(zLeft, zRight, nLeft)==0; }else{ return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft, zRight, nRight, rawRight); } } /* ** Error returns from jsonLookupStep() */ #define JSON_LOOKUP_ERROR 0xffffffff #define JSON_LOOKUP_NOTFOUND 0xfffffffe #define JSON_LOOKUP_PATHERROR 0xfffffffd #define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) /* Forward declaration */ static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); /* This helper routine for jsonLookupStep() populates pIns with ** binary data that is to be inserted into pParse. ** ** In the common case, pIns just points to pParse->aIns and pParse->nIns. ** But if the zPath of the original edit operation includes path elements ** that go deeper, additional substructure must be created. ** ** For example: ** ** json_insert('{}', '$.a.b.c', 123); ** ** The search stops at '$.a' But additional substructure must be ** created for the ".b.c" part of the patch so that the final result ** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with ** the binary equivalent of {"b":{"c":123}} so that it can be inserted. ** ** The caller is responsible for resetting pIns when it has finished ** using the substructure. */ static u32 jsonCreateEditSubstructure( 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 */ pIns->nBlob = 1; pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.']; pIns->eEdit = pParse->eEdit; pIns->nIns = pParse->nIns; pIns->aIns = pParse->aIns; rc = jsonLookupStep(pIns, 0, zTail, 0); pParse->oom |= pIns->oom; } return rc; /* Error code only */ } /* ** Search along zPath to find the Json element specified. Return an ** index into pParse->aBlob[] for the start of that element's value. ** ** If the value found by this routine is the value half of label/value pair ** within an object, then set pPath->iLabel to the start of the corresponding ** label, before returning. ** ** Return one of the JSON_LOOKUP error codes if problems are seen. ** ** This routine will also modify the blob. If pParse->eEdit is one of ** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be ** made to the selected value. If an edit is performed, then the return ** value does not necessarily point to the select element. If an edit ** is performed, the return value is only useful for detecting error ** conditions. */ static u32 jsonLookupStep( JsonParse *pParse, /* The JSON to search */ u32 iRoot, /* Begin the search at this element of aBlob[] */ const char *zPath, /* The path to search */ u32 iLabel /* Label if iRoot is a value of in an object */ ){ u32 i, j, k, nKey, sz, n, iEnd, rc; const char *zKey; u8 x; if( zPath[0]==0 ){ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){ n = jsonbPayloadSize(pParse, iRoot, &sz); sz += n; if( pParse->eEdit==JEDIT_DEL ){ if( iLabel>0 ){ sz += iRoot - iLabel; iRoot = iLabel; } jsonBlobEdit(pParse, iRoot, sz, 0, 0); }else if( pParse->eEdit==JEDIT_INS ){ /* Already exists, so json_insert() is a no-op */ }else{ /* json_set() or json_replace() */ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); } } pParse->iLabel = iLabel; return iRoot; } if( zPath[0]=='.' ){ int rawKey = 1; x = pParse->aBlob[iRoot]; zPath++; if( zPath[0]=='"' ){ zKey = zPath + 1; for(i=1; zPath[i] && zPath[i]!='"'; i++){} nKey = i-1; if( zPath[i] ){ i++; }else{ return JSON_LOOKUP_PATHERROR; } testcase( nKey==0 ); rawKey = memchr(zKey, '\\', nKey)==0; }else{ zKey = zPath; for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} nKey = i; if( nKey==0 ){ return JSON_LOOKUP_PATHERROR; } } if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND; n = jsonbPayloadSize(pParse, iRoot, &sz); j = iRoot + n; /* j is the index of a label */ iEnd = j+sz; while( j<iEnd ){ int rawLabel; const char *zLabel; x = pParse->aBlob[j] & 0x0f; if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return JSON_LOOKUP_ERROR; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 ) return JSON_LOOKUP_ERROR; k = j+n; /* k is the index of the label text */ if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR; zLabel = (const char*)&pParse->aBlob[k]; rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW; if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){ u32 v = k+sz; /* v is the index of the value */ if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; n = jsonbPayloadSize(pParse, v, &sz); if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR; assert( j>0 ); rc = jsonLookupStep(pParse, v, &zPath[i], j); if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } j = k+sz; if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 ) return JSON_LOOKUP_ERROR; j += n+sz; } if( j>iEnd ) return JSON_LOOKUP_ERROR; 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 ); nIns = ix.nBlob + nKey + v.nBlob; jsonBlobEdit(pParse, j, 0, 0, nIns); if( !pParse->oom ){ assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */ assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); k = j + ix.nBlob; memcpy(&pParse->aBlob[k], zKey, nKey); k += nKey; memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob); if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot); } } jsonParseReset(&v); jsonParseReset(&ix); return rc; } }else if( zPath[0]=='[' ){ x = pParse->aBlob[iRoot] & 0x0f; if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; n = jsonbPayloadSize(pParse, iRoot, &sz); k = 0; i = 1; while( sqlite3Isdigit(zPath[i]) ){ k = k*10 + zPath[i] - '0'; i++; } if( i<2 || zPath[i]!=']' ){ if( zPath[1]=='#' ){ k = jsonbArrayCount(pParse, iRoot); i = 2; if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ unsigned int nn = 0; i = 3; do{ nn = nn*10 + zPath[i] - '0'; i++; }while( sqlite3Isdigit(zPath[i]) ); if( nn>k ) return JSON_LOOKUP_NOTFOUND; k -= nn; } if( zPath[i]!=']' ){ return JSON_LOOKUP_PATHERROR; } }else{ return JSON_LOOKUP_PATHERROR; } } j = iRoot+n; iEnd = j+sz; while( j<iEnd ){ if( k==0 ){ rc = jsonLookupStep(pParse, j, &zPath[i+1], 0); if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } k--; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 ) return JSON_LOOKUP_ERROR; j += n+sz; } if( j>iEnd ) return JSON_LOOKUP_ERROR; if( k>0 ) return JSON_LOOKUP_NOTFOUND; if( pParse->eEdit>=JEDIT_INS ){ JsonParse v; testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); if( !JSON_LOOKUP_ISERROR(rc) && jsonBlobMakeEditable(pParse, v.nBlob) ){ assert( !pParse->oom ); jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob); } jsonParseReset(&v); if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } }else{ return JSON_LOOKUP_PATHERROR; } return JSON_LOOKUP_NOTFOUND; } /* ** Convert a JSON BLOB into text and make that text the return value ** of an SQL function. */ static void jsonReturnTextJsonFromBlob( sqlite3_context *ctx, const u8 *aBlob, u32 nBlob ){ JsonParse x; JsonString s; if( NEVER(aBlob==0) ) return; memset(&x, 0, sizeof(x)); x.aBlob = (u8*)aBlob; x.nBlob = nBlob; jsonStringInit(&s, ctx); jsonTranslateBlobToText(&x, 0, &s); jsonReturnString(&s, 0, 0); } /* ** Return the value of the BLOB node at index i. ** ** If the value is a primitive, return it as an SQL value. ** If the value is an array or object, return it as either ** JSON text or the BLOB encoding, depending on the JSON_B flag ** on the userdata. */ static void jsonReturnFromBlob( JsonParse *pParse, /* Complete JSON parse tree */ u32 i, /* Index of the node */ sqlite3_context *pCtx, /* Return value for this function */ int textOnly /* return text JSON. Disregard user-data */ ){ u32 n, sz; int rc; sqlite3 *db = sqlite3_context_db_handle(pCtx); n = jsonbPayloadSize(pParse, i, &sz); if( n==0 ){ sqlite3_result_error(pCtx, "malformed JSON", -1); return; } switch( pParse->aBlob[i] & 0x0f ){ case JSONB_NULL: { if( sz ) goto returnfromblob_malformed; sqlite3_result_null(pCtx); break; } case JSONB_TRUE: { if( sz ) goto returnfromblob_malformed; sqlite3_result_int(pCtx, 1); break; } case JSONB_FALSE: { if( sz ) goto returnfromblob_malformed; sqlite3_result_int(pCtx, 0); break; } case JSONB_INT5: case JSONB_INT: { sqlite3_int64 iRes = 0; char *z; int bNeg = 0; char x; if( sz==0 ) goto returnfromblob_malformed; x = (char)pParse->aBlob[i+n]; if( x=='-' ){ if( sz<2 ) goto returnfromblob_malformed; n++; sz--; bNeg = 1; } z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); if( z==0 ) goto returnfromblob_oom; rc = sqlite3DecOrHexToI64(z, &iRes); sqlite3DbFree(db, z); if( rc==0 ){ sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes); }else if( rc==3 && bNeg ){ sqlite3_result_int64(pCtx, SMALLEST_INT64); }else if( rc==1 ){ goto returnfromblob_malformed; }else{ if( bNeg ){ n--; sz++; } goto to_double; } break; } case JSONB_FLOAT5: case JSONB_FLOAT: { double r; char *z; if( sz==0 ) goto returnfromblob_malformed; to_double: z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); if( z==0 ) goto returnfromblob_oom; rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); sqlite3DbFree(db, z); if( rc<=0 ) goto returnfromblob_malformed; sqlite3_result_double(pCtx, r); break; } case JSONB_TEXTRAW: case JSONB_TEXT: { sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz, SQLITE_TRANSIENT); break; } case JSONB_TEXT5: 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 ){ zOut[iOut++] = (char)v; }else if( v<=0x7ff ){ assert( szEscape>=2 ); zOut[iOut++] = (char)(0xc0 | (v>>6)); zOut[iOut++] = 0x80 | (v&0x3f); }else if( v<0x10000 ){ assert( szEscape>=3 ); zOut[iOut++] = 0xe0 | (v>>12); zOut[iOut++] = 0x80 | ((v>>6)&0x3f); zOut[iOut++] = 0x80 | (v&0x3f); }else if( v==JSON_INVALID_CHAR ){ /* Silently ignore illegal unicode */ }else{ assert( szEscape>=4 ); zOut[iOut++] = 0xf0 | (v>>18); zOut[iOut++] = 0x80 | ((v>>12)&0x3f); zOut[iOut++] = 0x80 | ((v>>6)&0x3f); zOut[iOut++] = 0x80 | (v&0x3f); } 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); }else{ jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n); } break; } default: { goto returnfromblob_malformed; } } return; returnfromblob_oom: sqlite3_result_error_nomem(pCtx); return; returnfromblob_malformed: sqlite3_result_error(pCtx, "malformed JSON", -1); return; } /* ** pArg is a function argument that might be an SQL value or a JSON ** value. Figure out what it is and encode it as a JSONB blob. ** Return the results in pParse. ** ** pParse is uninitialized upon entry. This routine will handle the ** initialization of pParse. The result will be contained in ** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically ** allocated (if pParse->nBlobAlloc is greater than zero) in which case ** the caller is responsible for freeing the space allocated to pParse->aBlob ** when it has finished with it. Or pParse->aBlob might be a static string ** or a value obtained from sqlite3_value_blob(pArg). ** ** If the argument is a BLOB that is clearly not a JSONB, then this ** function might set an error message in ctx and return non-zero. ** It might also set an error message and return non-zero on an OOM error. */ static int jsonFunctionArgToBlob( 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: { if( jsonFuncArgMightBeBinary(pArg) ){ pParse->aBlob = (u8*)sqlite3_value_blob(pArg); pParse->nBlob = sqlite3_value_bytes(pArg); }else{ sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1); return 1; } break; } case SQLITE_TEXT: { const char *zJson = (const char*)sqlite3_value_text(pArg); 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; } case SQLITE_FLOAT: { double r = sqlite3_value_double(pArg); if( NEVER(sqlite3IsNaN(r)) ){ jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0); }else{ int n = sqlite3_value_bytes(pArg); const char *z = (const char*)sqlite3_value_text(pArg); if( z==0 ) return 1; if( z[0]=='I' ){ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); }else if( z[0]=='-' && z[1]=='I' ){ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); }else{ jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z); } } break; } case SQLITE_INTEGER: { int n = sqlite3_value_bytes(pArg); const char *z = (const char*)sqlite3_value_text(pArg); if( z==0 ) return 1; jsonBlobAppendNode(pParse, JSONB_INT, n, z); break; } } if( pParse->oom ){ sqlite3_result_error_nomem(ctx); return 1; }else{ return 0; } } /* ** Generate a bad path error. ** ** If ctx is not NULL then push the error message into ctx and return NULL. ** If ctx is NULL, then return the text of the error message. */ static char *jsonBadPathError( sqlite3_context *ctx, /* The function call containing the error */ const char *zPath /* The path with the problem */ ){ char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); if( ctx==0 ) return zMsg; if( zMsg ){ sqlite3_result_error(ctx, zMsg, -1); sqlite3_free(zMsg); }else{ sqlite3_result_error_nomem(ctx); } return 0; } /* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent ** arguments come in parse where each pair contains a JSON path and ** content to insert or set at that patch. Do the updates ** and return the result. ** ** The specific operation is determined by eEdit, which can be one ** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET. */ static void jsonInsertIntoBlob( sqlite3_context *ctx, int argc, sqlite3_value **argv, int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ ){ int i; u32 rc = 0; const char *zPath = 0; int flgs; JsonParse *p; JsonParse ax; assert( (argc&1)==1 ); flgs = argc==1 ? 0 : JSON_EDITABLE; p = jsonParseFuncArg(ctx, argv[0], flgs); if( p==0 ) return; for(i=1; i<argc-1; i+=2){ if( sqlite3_value_type(argv[i])==SQLITE_NULL ) continue; zPath = (const char*)sqlite3_value_text(argv[i]); if( zPath==0 ){ sqlite3_result_error_nomem(ctx); jsonParseFree(p); return; } if( zPath[0]!='$' ) goto jsonInsertIntoBlob_patherror; if( jsonFunctionArgToBlob(ctx, argv[i+1], &ax) ){ jsonParseReset(&ax); jsonParseFree(p); return; } if( zPath[1]==0 ){ if( eEdit==JEDIT_REPL || eEdit==JEDIT_SET ){ jsonBlobEdit(p, 0, p->nBlob, ax.aBlob, ax.nBlob); } rc = 0; }else{ p->eEdit = eEdit; p->nIns = ax.nBlob; p->aIns = ax.aBlob; p->delta = 0; rc = jsonLookupStep(p, 0, zPath+1, 0); } jsonParseReset(&ax); if( rc==JSON_LOOKUP_NOTFOUND ) continue; if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror; } jsonReturnParse(ctx, p); jsonParseFree(p); return; jsonInsertIntoBlob_patherror: jsonParseFree(p); 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 ** caller should invoke jsonParseFree() on the return value when it ** has finished using it. ** ** If any errors are detected, an appropriate error messages is set ** using sqlite3_result_error() or the equivalent and this routine ** returns NULL. This routine also returns NULL if the pArg argument ** is an SQL NULL value, but no error message is set in that case. This ** is so that SQL functions that are given NULL arguments will return ** a NULL value. */ static JsonParse *jsonParseFuncArg( 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; } }else{ int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref); int rc; if( !isRCStr ){ char *zNew = sqlite3RCStrNew( p->nJson ); if( zNew==0 ) goto json_pfa_oom; memcpy(zNew, p->zJson, p->nJson); p->zJson = zNew; p->zJson[p->nJson] = 0; }else{ sqlite3RCStrRef(p->zJson); } p->bJsonIsRCStr = 1; rc = jsonCacheInsert(ctx, p); if( rc==SQLITE_NOMEM ) goto json_pfa_oom; if( flgs & JSON_EDITABLE ){ pFromCache = p; p = 0; goto rebuild_from_cache; } } return p; json_pfa_malformed: if( flgs & JSON_KEEPERROR ){ p->nErr = 1; return p; }else{ jsonParseFree(p); sqlite3_result_error(ctx, "malformed JSON", -1); return 0; } json_pfa_oom: jsonParseFree(pFromCache); jsonParseFree(p); sqlite3_result_error_nomem(ctx); return 0; } /* ** Make the return value of a JSON function either the raw JSONB blob ** or make it JSON text, depending on whether the JSON_BLOB flag is ** set on the function. */ static void jsonReturnParse( sqlite3_context *ctx, JsonParse *p ){ int flgs; if( p->oom ){ sqlite3_result_error_nomem(ctx); return; } flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( flgs & JSON_BLOB ){ if( p->nBlobAlloc>0 && !p->bReadOnly ){ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC); p->nBlobAlloc = 0; }else{ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT); } }else{ JsonString s; jsonStringInit(&s, ctx); p->delta = 0; jsonTranslateBlobToText(p, 0, &s); jsonReturnString(&s, p, ctx); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } } /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ #if SQLITE_DEBUG /* ** Decode JSONB bytes in aBlob[] starting at iStart through but not ** including iEnd. Indent the ** content by nIndent spaces. */ static void jsonDebugPrintBlob( JsonParse *pParse, /* JSON content */ u32 iStart, /* Start rendering here */ u32 iEnd, /* Do not render this byte or any byte after this one */ int nIndent, /* Indent by this many spaces */ sqlite3_str *pOut /* Generate output into this sqlite3_str object */ ){ while( iStart<iEnd ){ u32 i, n, nn, sz = 0; int showContent = 1; u8 x = pParse->aBlob[iStart] & 0x0f; u32 savedNBlob = pParse->nBlob; sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, ""); if( pParse->nBlobAlloc>pParse->nBlob ){ pParse->nBlob = pParse->nBlobAlloc; } nn = n = jsonbPayloadSize(pParse, iStart, &sz); if( nn==0 ) nn = 1; if( sz>0 && x<JSONB_ARRAY ){ nn += sz; } for(i=0; i<nn; i++){ sqlite3_str_appendf(pOut, " %02x", pParse->aBlob[iStart+i]); } if( n==0 ){ sqlite3_str_appendf(pOut, " ERROR invalid node size\n"); iStart = n==0 ? iStart+1 : iEnd; continue; } pParse->nBlob = savedNBlob; if( iStart+n+sz>iEnd ){ iEnd = iStart+n+sz; if( iEnd>pParse->nBlob ){ if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){ iEnd = pParse->nBlobAlloc; }else{ iEnd = pParse->nBlob; } } } sqlite3_str_appendall(pOut," <-- "); switch( x ){ case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break; case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break; case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break; case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break; case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break; case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break; case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break; case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break; case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break; case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break; case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break; case JSONB_ARRAY: { sqlite3_str_appendf(pOut,"array, %u bytes\n", sz); jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut); showContent = 0; break; } case JSONB_OBJECT: { sqlite3_str_appendf(pOut, "object, %u bytes\n", sz); jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut); showContent = 0; break; } default: { sqlite3_str_appendall(pOut, "ERROR: unknown node type\n"); showContent = 0; 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; } } static void jsonShowParse(JsonParse *pParse){ sqlite3_str out; char zBuf[1000]; if( pParse==0 ){ printf("NULL pointer\n"); return; }else{ printf("nBlobAlloc = %u\n", pParse->nBlobAlloc); printf("nBlob = %u\n", pParse->nBlob); printf("delta = %d\n", pParse->delta); if( pParse->nBlob==0 ) return; printf("content (bytes 0..%u):\n", pParse->nBlob-1); } sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000); jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out); printf("%s", sqlite3_str_value(&out)); sqlite3_str_reset(&out); } #endif /* SQLITE_DEBUG */ #ifdef SQLITE_DEBUG /* ** SQL function: json_parse(JSON) ** ** Parse JSON using jsonParseFuncArg(). Return text that is a ** human-readable dump of the binary JSONB for the input parameter. */ static void jsonParseFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonParse *p; /* The parse */ sqlite3_str out; 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 ****************************************************************************/ /* ** Implementation of the json_quote(VALUE) function. Return a JSON value ** corresponding to the SQL value input. Mostly this means putting ** double-quotes around strings and returning the unquoted string "null" ** when given a NULL input. */ static void jsonQuoteFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonString jx; UNUSED_PARAMETER(argc); jsonStringInit(&jx, ctx); jsonAppendSqlValue(&jx, argv[0]); jsonReturnString(&jx, 0, 0); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } /* ** Implementation of the json_array(VALUE,...) function. Return a JSON ** array that contains all values given in arguments. Or if any argument ** is a BLOB, throw an error. */ static void jsonArrayFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ int i; JsonString jx; jsonStringInit(&jx, ctx); jsonAppendChar(&jx, '['); for(i=0; i<argc; i++){ jsonAppendSeparator(&jx); jsonAppendSqlValue(&jx, argv[i]); } jsonAppendChar(&jx, ']'); jsonReturnString(&jx, 0, 0); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } /* ** json_array_length(JSON) ** json_array_length(JSON, PATH) ** ** Return the number of elements in the top-level JSON array. ** Return 0 if the input is not a well-formed JSON array. */ static void jsonArrayLengthFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonParse *p; /* The parse */ sqlite3_int64 cnt = 0; u32 i; u8 eErr = 0; p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; if( argc==2 ){ const char *zPath = (const char*)sqlite3_value_text(argv[1]); if( zPath==0 ){ jsonParseFree(p); return; } i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); if( JSON_LOOKUP_ISERROR(i) ){ if( i==JSON_LOOKUP_NOTFOUND ){ /* no-op */ }else if( i==JSON_LOOKUP_PATHERROR ){ jsonBadPathError(ctx, zPath); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); } eErr = 1; i = 0; } }else{ i = 0; } 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 digits */ static int jsonAllDigits(const char *z, int n){ int i; for(i=0; i<n && sqlite3Isdigit(z[i]); i++){} return i==n; } /* 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; } /* ** json_extract(JSON, PATH, ...) ** "->"(JSON,PATH) ** "->>"(JSON,PATH) ** ** Return the element described by PATH. Return NULL if that PATH element |
︙ | ︙ | |||
2537 2538 2539 2540 2541 2542 2543 | ** compatibility with PG. */ static void jsonExtractFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ | | < < | > | | > > | > > > | > > | > > > | < | | | | | | | | | | | | | > > > > > | | | | | > | | | | > | > | > > | > > > | > > > > | | | | < > | > > > | < < < < < < < < < | > > | > | | > | | | | | | > > > | > > | | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > | > > > | > > | > > | < > > > > > > > > > | | < > | | > > | | < > > | < < < < < < | < | > | < < < | | < | < | | < | > > > > > > | > | > > > > | | > > > > > > > > | < | | | > > > > | > > | | > > | > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | < < > > > > > | | > > | | | > > | > | | | > | | < < | | < < | < | | > | < | | | > > > | 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 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 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 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 | ** compatibility with PG. */ static void jsonExtractFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonParse *p = 0; /* The parse */ int flags; /* Flags associated with the function */ int i; /* Loop counter */ JsonString jx; /* String for array result */ if( argc<2 ) return; p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); jsonStringInit(&jx, ctx); if( argc>2 ){ jsonAppendChar(&jx, '['); } for(i=1; i<argc; i++){ /* With a single PATH argument */ const char *zPath = (const char*)sqlite3_value_text(argv[i]); int nPath; u32 j; if( zPath==0 ) goto json_extract_error; nPath = sqlite3Strlen30(zPath); if( zPath[0]=='$' ){ j = jsonLookupStep(p, 0, zPath+1, 0); }else if( (flags & JSON_ABPATH) ){ /* The -> and ->> operators accept abbreviated PATH arguments. This ** is mostly for compatibility with PostgreSQL, but also for ** convenience. ** ** NUMBER ==> $[NUMBER] // PG compatible ** LABEL ==> $.LABEL // PG compatible ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience */ jsonStringInit(&jx, ctx); if( jsonAllDigits(zPath, nPath) ){ 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]==']' ){ jsonAppendRaw(&jx, zPath, nPath); }else{ jsonAppendRawNZ(&jx, ".\"", 2); jsonAppendRaw(&jx, zPath, nPath); jsonAppendRawNZ(&jx, "\"", 1); } jsonStringTerminate(&jx); j = jsonLookupStep(p, 0, jx.zBuf, 0); jsonStringReset(&jx); }else{ jsonBadPathError(ctx, zPath); goto json_extract_error; } if( j<p->nBlob ){ if( argc==2 ){ if( flags & JSON_JSON ){ jsonStringInit(&jx, ctx); jsonTranslateBlobToText(p, j, &jx); jsonReturnString(&jx, 0, 0); jsonStringReset(&jx); assert( (flags & JSON_BLOB)==0 ); sqlite3_result_subtype(ctx, JSON_SUBTYPE); }else{ jsonReturnFromBlob(p, j, ctx, 0); if( (flags & (JSON_SQL|JSON_BLOB))==0 && (p->aBlob[j]&0x0f)>=JSONB_ARRAY ){ sqlite3_result_subtype(ctx, JSON_SUBTYPE); } } }else{ jsonAppendSeparator(&jx); jsonTranslateBlobToText(p, j, &jx); } }else if( j==JSON_LOOKUP_NOTFOUND ){ if( argc==2 ){ goto json_extract_error; /* Return NULL if not found */ }else{ jsonAppendSeparator(&jx); jsonAppendRawNZ(&jx, "null", 4); } }else if( j==JSON_LOOKUP_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); goto json_extract_error; }else{ jsonBadPathError(ctx, zPath); goto json_extract_error; } } if( argc>2 ){ jsonAppendChar(&jx, ']'); jsonReturnString(&jx, 0, 0); if( (flags & JSON_BLOB)==0 ){ sqlite3_result_subtype(ctx, JSON_SUBTYPE); } } json_extract_error: jsonStringReset(&jx); jsonParseFree(p); return; } /* ** Return codes for jsonMergePatch() */ #define JSON_MERGE_OK 0 /* Success */ #define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */ #define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */ #define JSON_MERGE_OOM 3 /* Out-of-memory condition */ /* ** RFC-7396 MergePatch for two JSONB blobs. ** ** pTarget is the target. pPatch is the patch. The target is updated ** in place. The patch is read-only. ** ** The original RFC-7396 algorithm is this: ** ** define MergePatch(Target, Patch): ** if Patch is an Object: ** if Target is not an Object: ** Target = {} # Ignore the contents and set it to an empty Object ** for each Name/Value pair in Patch: ** if Value is null: ** if Name exists in Target: ** remove the Name/Value pair from Target ** else: ** Target[Name] = MergePatch(Target[Name], Value) ** return Target ** else: ** return Patch ** ** Here is an equivalent algorithm restructured to show the actual ** implementation: ** ** 01 define MergePatch(Target, Patch): ** 02 if Patch is not an Object: ** 03 return Patch ** 04 else: // if Patch is an Object ** 05 if Target is not an Object: ** 06 Target = {} ** 07 for each Name/Value pair in Patch: ** 08 if Name exists in Target: ** 09 if Value is null: ** 10 remove the Name/Value pair from Target ** 11 else ** 12 Target[name] = MergePatch(Target[Name], Value) ** 13 else if Value is not NULL: ** 14 if Value is not an Object: ** 15 Target[name] = Value ** 16 else: ** 17 Target[name] = MergePatch('{}',value) ** 18 return Target ** | ** ^---- Line numbers referenced in comments in the implementation */ static int jsonMergePatch( JsonParse *pTarget, /* The JSON parser that contains the TARGET */ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */ const JsonParse *pPatch, /* The PATCH */ u32 iPatch /* Index of PATCH in pPatch->aBlob[] */ ){ u8 x; /* Type of a single node */ u32 n, sz=0; /* Return values from jsonbPayloadSize() */ u32 iTCursor; /* Cursor position while scanning the target object */ u32 iTStart; /* First label in the target object */ u32 iTEndBE; /* Original first byte past end of target, before edit */ u32 iTEnd; /* Current first byte past end of target */ u8 eTLabel; /* Node type of the target label */ u32 iTLabel = 0; /* Index of the label */ u32 nTLabel = 0; /* Header size in bytes for the target label */ u32 szTLabel = 0; /* Size of the target label payload */ u32 iTValue = 0; /* Index of the target value */ u32 nTValue = 0; /* Header size of the target value */ u32 szTValue = 0; /* Payload size for the target value */ u32 iPCursor; /* Cursor position while scanning the patch */ u32 iPEnd; /* First byte past the end of the patch */ u8 ePLabel; /* Node type of the patch label */ u32 iPLabel; /* Start of patch label */ u32 nPLabel; /* Size of header on the patch label */ u32 szPLabel; /* Payload size of the patch label */ u32 iPValue; /* Start of patch value */ u32 nPValue; /* Header size for the patch value */ u32 szPValue; /* Payload size of the patch value */ assert( iTarget>=0 && iTarget<pTarget->nBlob ); assert( iPatch>=0 && iPatch<pPatch->nBlob ); x = pPatch->aBlob[iPatch] & 0x0f; if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */ u32 szPatch; /* Total size of the patch, header+payload */ u32 szTarget; /* Total size of the target, header+payload */ n = jsonbPayloadSize(pPatch, iPatch, &sz); szPatch = n+sz; sz = 0; n = jsonbPayloadSize(pTarget, iTarget, &sz); szTarget = n+sz; jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch); return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */ } x = pTarget->aBlob[iTarget] & 0x0f; if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */ n = jsonbPayloadSize(pTarget, iTarget, &sz); jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0); x = pTarget->aBlob[iTarget]; pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT; } n = jsonbPayloadSize(pPatch, iPatch, &sz); if( NEVER(n==0) ) return JSON_MERGE_BADPATCH; iPCursor = iPatch+n; iPEnd = iPCursor+sz; n = jsonbPayloadSize(pTarget, iTarget, &sz); if( NEVER(n==0) ) return JSON_MERGE_BADTARGET; iTStart = iTarget+n; iTEndBE = iTStart+sz; while( iPCursor<iPEnd ){ /* Algorithm line 07 */ iPLabel = iPCursor; ePLabel = pPatch->aBlob[iPCursor] & 0x0f; if( ePLabel<JSONB_TEXT || ePLabel>JSONB_TEXTRAW ){ return JSON_MERGE_BADPATCH; } nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel); if( nPLabel==0 ) return JSON_MERGE_BADPATCH; iPValue = iPCursor + nPLabel + szPLabel; if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH; nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue); if( nPValue==0 ) return JSON_MERGE_BADPATCH; iPCursor = iPValue + nPValue + szPValue; if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH; iTCursor = iTStart; iTEnd = iTEndBE + pTarget->delta; while( iTCursor<iTEnd ){ int isEqual; /* true if the patch and target labels match */ iTLabel = iTCursor; eTLabel = pTarget->aBlob[iTCursor] & 0x0f; if( eTLabel<JSONB_TEXT || eTLabel>JSONB_TEXTRAW ){ return JSON_MERGE_BADTARGET; } nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel); if( nTLabel==0 ) return JSON_MERGE_BADTARGET; iTValue = iTLabel + nTLabel + szTLabel; if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET; nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue); if( nTValue==0 ) return JSON_MERGE_BADTARGET; if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET; isEqual = jsonLabelCompare( (const char*)&pPatch->aBlob[iPLabel+nPLabel], szPLabel, (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW), (const char*)&pTarget->aBlob[iTLabel+nTLabel], szTLabel, (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW)); if( isEqual ) break; iTCursor = iTValue + nTValue + szTValue; } x = pPatch->aBlob[iPValue] & 0x0f; if( iTCursor<iTEnd ){ /* A match was found. Algorithm line 08 */ if( x==0 ){ /* Patch value is NULL. Algorithm line 09 */ jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, 0,0); /* vvvvvv----- No OOM on a delete-only edit */ if( NEVER(pTarget->oom) ) return JSON_MERGE_OOM; }else{ /* Algorithm line 12 */ int rc, savedDelta = pTarget->delta; pTarget->delta = 0; rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue); if( rc ) return rc; pTarget->delta += savedDelta; } }else if( x>0 ){ /* Algorithm line 13 */ /* No match and patch value is not NULL */ u32 szNew = szPLabel+nPLabel; if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */ jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew); if( pTarget->oom ) return JSON_MERGE_OOM; memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); memcpy(&pTarget->aBlob[iTEnd+szNew], &pPatch->aBlob[iPValue], szPValue+nPValue); }else{ int rc, savedDelta; jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1); if( pTarget->oom ) return JSON_MERGE_OOM; memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); pTarget->aBlob[iTEnd+szNew] = 0x00; savedDelta = pTarget->delta; pTarget->delta = 0; rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue); if( rc ) return rc; pTarget->delta += savedDelta; } } } if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget); return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; } /* ** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON ** object that is the result of running the RFC 7396 MergePatch() algorithm ** on the two arguments. */ static void jsonPatchFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonParse *pTarget; /* The TARGET */ JsonParse *pPatch; /* The PATCH */ int rc; /* Result code */ UNUSED_PARAMETER(argc); assert( argc==2 ); pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); if( pTarget==0 ) return; pPatch = jsonParseFuncArg(ctx, argv[1], 0); if( pPatch ){ rc = jsonMergePatch(pTarget, 0, pPatch, 0); if( rc==JSON_MERGE_OK ){ jsonReturnParse(ctx, pTarget); }else if( rc==JSON_MERGE_OOM ){ sqlite3_result_error_nomem(ctx); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); } jsonParseFree(pPatch); } jsonParseFree(pTarget); } /* ** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON ** object that contains all name/value given in arguments. Or if any name ** is not a string or if any value is a BLOB, throw an error. |
︙ | ︙ | |||
2737 2738 2739 2740 2741 2742 2743 | u32 n; if( argc&1 ){ sqlite3_result_error(ctx, "json_object() requires an even number " "of arguments", -1); return; } | | | | | | | < | > | | | | | < | < < < < < | | | > > | < | | < < < < < < | < < | | < < < < < | < < < < | | < < < < | < < < < < < < < < | < < < | < | < < < < < | | < < < < < < < < < < | | < < < | < < | | | < < < < | < | | < < | < < | < < < < < < < | < < < < < < < < < < < < < < < | < < < | | | < < < < < < < < < < < < < < < < < < < < | | | > > > > > | > > > > > | > > > > > | > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > | | | > | | > > | < < < < > | < | < > | < | < < < < < | < < < < > | > > > | < | | | > | > > | > > > | < > | < > | | | > > > > > > | < | > | | | | > > | > > > > | | > > > > | | 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 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 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 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 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 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 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 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 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 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 4609 4610 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 4636 4637 4638 4639 4640 4641 4642 | u32 n; if( argc&1 ){ sqlite3_result_error(ctx, "json_object() requires an even number " "of arguments", -1); return; } jsonStringInit(&jx, ctx); jsonAppendChar(&jx, '{'); for(i=0; i<argc; i+=2){ if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){ sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1); jsonStringReset(&jx); return; } jsonAppendSeparator(&jx); z = (const char*)sqlite3_value_text(argv[i]); n = sqlite3_value_bytes(argv[i]); jsonAppendString(&jx, z, n); jsonAppendChar(&jx, ':'); jsonAppendSqlValue(&jx, argv[i+1]); } jsonAppendChar(&jx, '}'); jsonReturnString(&jx, 0, 0); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } /* ** json_remove(JSON, PATH, ...) ** ** Remove the named elements from JSON and return the result. malformed ** JSON or PATH arguments result in an error. */ static void jsonRemoveFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonParse *p; /* The parse */ const char *zPath = 0; /* Path of element to be removed */ int i; /* Loop counter */ u32 rc; /* Subroutine return code */ if( argc<1 ) return; p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0); if( p==0 ) return; for(i=1; i<argc; i++){ zPath = (const char*)sqlite3_value_text(argv[i]); if( zPath==0 ){ goto json_remove_done; } if( zPath[0]!='$' ){ goto json_remove_patherror; } if( zPath[1]==0 ){ /* json_remove(j,'$') returns NULL */ goto json_remove_done; } p->eEdit = JEDIT_DEL; p->delta = 0; rc = jsonLookupStep(p, 0, zPath+1, 0); if( JSON_LOOKUP_ISERROR(rc) ){ if( rc==JSON_LOOKUP_NOTFOUND ){ continue; /* No-op */ }else if( rc==JSON_LOOKUP_PATHERROR ){ jsonBadPathError(ctx, zPath); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); } goto json_remove_done; } } jsonReturnParse(ctx, p); jsonParseFree(p); return; json_remove_patherror: jsonBadPathError(ctx, zPath); json_remove_done: jsonParseFree(p); return; } /* ** json_replace(JSON, PATH, VALUE, ...) ** ** Replace the value at PATH with VALUE. If PATH does not already exist, ** this routine is a no-op. If JSON or PATH is malformed, throw an error. */ static void jsonReplaceFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ if( argc<1 ) return; if( (argc&1)==0 ) { jsonWrongNumArgs(ctx, "replace"); return; } jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL); } /* ** json_set(JSON, PATH, VALUE, ...) ** ** Set the value at PATH to VALUE. Create the PATH if it does not already ** exist. Overwrite existing values that do exist. ** If JSON or PATH is malformed, throw an error. ** ** json_insert(JSON, PATH, VALUE, ...) ** ** Create PATH and initialize it to VALUE. If PATH already exists, this ** routine is a no-op. If JSON or PATH is malformed, throw an error. */ static void jsonSetFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); int bIsSet = (flags&JSON_ISSET)!=0; if( argc<1 ) return; if( (argc&1)==0 ) { jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); return; } jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); } /* ** json_type(JSON) ** json_type(JSON, PATH) ** ** Return the top-level "type" of a JSON string. json_type() raises an ** error if either the JSON or PATH inputs are not well-formed. */ static void jsonTypeFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonParse *p; /* The parse */ const char *zPath = 0; u32 i; p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; if( argc==2 ){ zPath = (const char*)sqlite3_value_text(argv[1]); if( zPath==0 ) goto json_type_done; if( zPath[0]!='$' ){ jsonBadPathError(ctx, zPath); goto json_type_done; } i = jsonLookupStep(p, 0, zPath+1, 0); if( JSON_LOOKUP_ISERROR(i) ){ if( i==JSON_LOOKUP_NOTFOUND ){ /* no-op */ }else if( i==JSON_LOOKUP_PATHERROR ){ jsonBadPathError(ctx, zPath); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); } goto json_type_done; } }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": ** ** 0x01 Canonical RFC-8259 JSON text ** 0x02 JSON text with optional JSON-5 extensions ** 0x04 Superficially appears to be JSONB ** 0x08 Strictly well-formed JSONB ** ** If the FLAGS argument is omitted, it defaults to 1. Useful values for ** FLAGS include: ** ** 1 Strict canonical JSON text ** 2 JSON text perhaps with JSON-5 extensions ** 4 Superficially appears to be JSONB ** 5 Canonical JSON text or superficial JSONB ** 6 JSON-5 text or superficial JSONB ** 8 Strict JSONB ** 9 Canonical JSON text or strict JSONB ** 10 JSON-5 text or strict JSONB ** ** Other flag combinations are redundant. For example, every canonical ** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3 ** are the same. Similarly, any input that passes a strict JSONB validation ** will also pass the superficial validation so 12 through 15 are the same ** as 8 through 11 respectively. ** ** This routine runs in linear time to validate text and when doing strict ** JSONB validation. Superficial JSONB validation is constant time, ** assuming the BLOB is already in memory. The performance advantage ** of superficial JSONB validation is why that option is provided. ** Application developers can choose to do fast superficial validation or ** slower strict validation, according to their specific needs. ** ** Only the lower four bits of the FLAGS argument are currently used. ** Higher bits are reserved for future expansion. To facilitate ** compatibility, the current implementation raises an error if any bit ** in FLAGS is set other than the lower four bits. ** ** The original circa 2015 implementation of the JSON routines in ** SQLite only supported canonical RFC-8259 JSON text and the json_valid() ** function only accepted one argument. That is why the default value ** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only ** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS ** argument was added when the JSON routines were extended to support ** JSON5-like extensions and binary JSONB stored in BLOBs. ** ** Return Values: ** ** * Raise an error if FLAGS is outside the range of 1 to 15. ** * Return NULL if the input is NULL ** * Return 1 if the input is well-formed. ** * Return 0 if the input is not well-formed. */ static void jsonValidFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonParse *p; /* The parse */ u8 flags = 1; u8 res = 0; if( argc==2 ){ i64 f = sqlite3_value_int64(argv[1]); if( f<1 || f>15 ){ sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be" " between 1 and 15", -1); return; } flags = f & 0x0f; } switch( sqlite3_value_type(argv[0]) ){ case SQLITE_NULL: { #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); if( p ){ if( p->oom ){ sqlite3_result_error_nomem(ctx); }else if( p->nErr ){ /* no-op */ }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){ res = 1; } jsonParseFree(p); }else{ sqlite3_result_error_nomem(ctx); } break; } } sqlite3_result_int(ctx, res); } /* ** json_error_position(JSON) ** ** If the argument is NULL, return NULL ** ** If the argument is BLOB, do a full validity check and return non-zero ** if the check fails. The return value is the approximate 1-based offset ** to the byte of the element that contains the first error. ** ** Otherwise interpret the argument is TEXT (even if it is numeric) and ** return the 1-based character position for where the parser first recognized ** that the input was not valid JSON, or return 0 if the input text looks ** ok. JSON-5 extensions are accepted. */ static void jsonErrorFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ 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 */ s.nJson = sqlite3_value_bytes(argv[0]); if( jsonConvertTextToBlob(&s,0) ){ if( s.oom ){ iErrPos = -1; }else{ /* Convert byte-offset s.iErr into a character offset */ u32 k; assert( s.zJson!=0 ); /* Because s.oom is false */ for(k=0; k<s.iErr && ALWAYS(s.zJson[k]); k++){ if( (s.zJson[k] & 0xc0)!=0x80 ) iErrPos++; } iErrPos++; } } } jsonParseReset(&s); if( iErrPos<0 ){ sqlite3_result_error_nomem(ctx); }else{ sqlite3_result_int64(ctx, iErrPos); } } /**************************************************************************** ** Aggregate SQL function implementations ****************************************************************************/ /* ** json_group_array(VALUE) ** ** Return a JSON array composed of all values in the aggregate. */ static void jsonArrayStep( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonString *pStr; UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ jsonStringInit(pStr, ctx); jsonAppendChar(pStr, '['); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); } pStr->pCtx = ctx; jsonAppendSqlValue(pStr, argv[0]); } } static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ JsonString *pStr; pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ int flags; pStr->pCtx = ctx; jsonAppendChar(pStr, ']'); flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( pStr->eErr ){ 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){ |
︙ | ︙ | |||
3215 3216 3217 3218 3219 3220 3221 | JsonString *pStr; const char *z; u32 n; UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ | | | | > > > | > > > > | | > > > > | > > > > > > > > > < | > | | > > | > > > > > > > | | 4709 4710 4711 4712 4713 4714 4715 4716 4717 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 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 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 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 | JsonString *pStr; const char *z; u32 n; UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ jsonStringInit(pStr, ctx); jsonAppendChar(pStr, '{'); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); } pStr->pCtx = ctx; z = (const char*)sqlite3_value_text(argv[0]); n = sqlite3Strlen30(z); jsonAppendString(pStr, z, n); jsonAppendChar(pStr, ':'); jsonAppendSqlValue(pStr, argv[1]); } } static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ JsonString *pStr; pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ int flags; jsonAppendChar(pStr, '}'); pStr->pCtx = ctx; flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( pStr->eErr ){ 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){ jsonObjectCompute(ctx, 0); } static void jsonObjectFinal(sqlite3_context *ctx){ jsonObjectCompute(ctx, 1); } #ifndef SQLITE_OMIT_VIRTUALTABLE /**************************************************************************** ** The json_each virtual table ****************************************************************************/ typedef struct JsonParent JsonParent; struct JsonParent { u32 iHead; /* Start of object or array */ u32 iValue; /* Start of the value */ u32 iEnd; /* First byte past the end */ u32 nPath; /* Length of path */ i64 iKey; /* Key for JSONB_ARRAY */ }; typedef struct JsonEachCursor JsonEachCursor; struct JsonEachCursor { sqlite3_vtab_cursor base; /* Base class - must be first */ u32 iRowid; /* The rowid */ u32 i; /* Index in sParse.aBlob[] of current row */ u32 iEnd; /* EOF when i equals or exceeds this value */ u32 nRoot; /* Size of the root path in bytes */ u8 eType; /* Type of the container for element i */ u8 bRecursive; /* True for json_tree(). False for json_each() */ u32 nParent; /* Current nesting depth */ u32 nParentAlloc; /* Space allocated for aParent[] */ JsonParent *aParent; /* Parent elements of i */ sqlite3 *db; /* Database connection */ JsonString path; /* Current path */ JsonParse sParse; /* Parse of the input JSON */ }; typedef struct JsonEachConnection JsonEachConnection; struct JsonEachConnection { sqlite3_vtab base; /* Base class - must be first */ sqlite3 *db; /* Database connection */ }; /* Constructor for the json_each virtual table */ static int jsonEachConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ JsonEachConnection *pNew; int rc; /* Column numbers */ #define JEACH_KEY 0 #define JEACH_VALUE 1 #define JEACH_TYPE 2 #define JEACH_ATOM 3 |
︙ | ︙ | |||
3311 3312 3313 3314 3315 3316 3317 | 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 ){ | > | < > > | > | | > < > > > > > < < | > | | > > > | | > | | > | > > | > > > > | < | | > | > | | > | > > | | | > > > > > | < < < | < | < < < < | < < < | | < > > > | < > > > > > > > > > > > > > > > > > | < < < < < | < | | < < | | | > | < < < < < > | < < > > > > > > > > | < > > > > > | > | < < < < < < < < < < < < | < < < < < < < < > > > > > | > > > > > | > > > | > > > > > > > > > > > > > | > > > | < | | > | | | < | | | > | > > > < > > > > > > > | | | > > > | > | | | | > | < | | < < < < < | < < | < < | > | < < < < < < < | | > | | < < < < < < | > > > | | > | 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 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 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 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 | 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(). */ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ int rc = jsonEachOpenEach(p, ppCursor); if( rc==SQLITE_OK ){ JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor; pCur->bRecursive = 1; } return rc; } /* Reset a JsonEachCursor back to its original state. Free any memory ** held. */ static void jsonEachCursorReset(JsonEachCursor *p){ jsonParseReset(&p->sParse); jsonStringReset(&p->path); sqlite3DbFree(p->db, p->aParent); p->iRowid = 0; p->i = 0; p->aParent = 0; p->nParent = 0; p->nParentAlloc = 0; p->iEnd = 0; p->eType = 0; } /* 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; return p->i >= p->iEnd; } /* ** If the cursor is currently pointing at the label of a object entry, ** then return the index of the value. For all other cases, return the ** current pointer position, which is the value. */ static int jsonSkipLabel(JsonEachCursor *p){ if( p->eType==JSONB_OBJECT ){ u32 sz = 0; u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz); return p->i + n + sz; }else{ return p->i; } } /* ** Append the path name for the current element. */ static void jsonAppendPathName(JsonEachCursor *p){ assert( p->nParent>0 ); assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT ); if( p->eType==JSONB_ARRAY ){ jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey); }else{ u32 n, sz = 0, k, i; const char *z; int needQuote = 0; n = jsonbPayloadSize(&p->sParse, p->i, &sz); k = p->i + n; z = (const char*)&p->sParse.aBlob[k]; if( sz==0 || !sqlite3Isalpha(z[0]) ){ needQuote = 1; }else{ for(i=0; i<sz; i++){ if( !sqlite3Isalnum(z[i]) ){ needQuote = 1; break; } } } if( needQuote ){ jsonPrintf(sz+4,&p->path,".\"%.*s\"", sz, z); }else{ jsonPrintf(sz+2,&p->path,".%.*s", sz, z); } } } /* Advance the cursor to the next element for json_tree() */ static int jsonEachNext(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; int rc = SQLITE_OK; if( p->bRecursive ){ u8 x; u8 levelChange = 0; u32 n, sz = 0; u32 i = jsonSkipLabel(p); x = p->sParse.aBlob[i] & 0x0f; n = jsonbPayloadSize(&p->sParse, i, &sz); if( x==JSONB_OBJECT || x==JSONB_ARRAY ){ JsonParent *pParent; if( p->nParent>=p->nParentAlloc ){ JsonParent *pNew; u64 nNew; nNew = p->nParentAlloc*2 + 3; pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew); if( pNew==0 ) return SQLITE_NOMEM; p->nParentAlloc = (u32)nNew; p->aParent = pNew; } levelChange = 1; pParent = &p->aParent[p->nParent]; pParent->iHead = p->i; pParent->iValue = i; pParent->iEnd = i + n + sz; pParent->iKey = -1; pParent->nPath = (u32)p->path.nUsed; if( p->eType && p->nParent ){ jsonAppendPathName(p); if( p->path.eErr ) rc = SQLITE_NOMEM; } p->nParent++; p->i = i + n; }else{ p->i = i + n + sz; } while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ p->nParent--; p->path.nUsed = p->aParent[p->nParent].nPath; levelChange = 1; } if( levelChange ){ if( p->nParent>0 ){ JsonParent *pParent = &p->aParent[p->nParent-1]; u32 iVal = pParent->iValue; p->eType = p->sParse.aBlob[iVal] & 0x0f; }else{ p->eType = 0; } } }else{ u32 n, sz = 0; u32 i = jsonSkipLabel(p); n = jsonbPayloadSize(&p->sParse, i, &sz); p->i = i + n + sz; } if( p->eType==JSONB_ARRAY && p->nParent ){ p->aParent[p->nParent-1].iKey++; } p->iRowid++; return rc; } /* Length of the path for rowid==0 in bRecursive mode. */ static int jsonEachPathLength(JsonEachCursor *p){ u32 n = p->path.nUsed; char *z = p->path.zBuf; if( p->iRowid==0 && p->bRecursive && n>=2 ){ while( n>1 ){ n--; if( z[n]=='[' || z[n]=='.' ){ u32 x, sz = 0; char cSaved = z[n]; z[n] = 0; assert( p->sParse.eEdit==0 ); x = jsonLookupStep(&p->sParse, 0, z+1, 0); z[n] = cSaved; if( JSON_LOOKUP_ISERROR(x) ) continue; if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break; } } } return n; } /* Return the value of a column */ static int jsonEachColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int iColumn /* Which column to return */ ){ JsonEachCursor *p = (JsonEachCursor*)cur; switch( iColumn ){ case JEACH_KEY: { if( p->nParent==0 ){ u32 n, j; if( p->nRoot==1 ) break; j = jsonEachPathLength(p); n = p->nRoot - j; if( n==0 ){ break; }else if( p->path.zBuf[j]=='[' ){ i64 x; sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8); sqlite3_result_int64(ctx, x); }else if( p->path.zBuf[j+1]=='"' ){ sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT); }else{ sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT); } break; } if( p->eType==JSONB_OBJECT ){ jsonReturnFromBlob(&p->sParse, p->i, ctx, 1); }else{ assert( p->eType==JSONB_ARRAY ); 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; } case JEACH_ATOM: { u32 i = jsonSkipLabel(p); if( (p->sParse.aBlob[i] & 0x0f)<JSONB_ARRAY ){ jsonReturnFromBlob(&p->sParse, i, ctx, 1); } break; } case JEACH_ID: { sqlite3_result_int64(ctx, (sqlite3_int64)p->i); break; } case JEACH_PARENT: { if( p->nParent>0 && p->bRecursive ){ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead); } break; } case JEACH_FULLKEY: { u64 nBase = p->path.nUsed; if( p->nParent ) jsonAppendPathName(p); sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, SQLITE_TRANSIENT, SQLITE_UTF8); p->path.nUsed = nBase; break; } case JEACH_PATH: { u32 n = jsonEachPathLength(p); sqlite3_result_text64(ctx, p->path.zBuf, n, SQLITE_TRANSIENT, SQLITE_UTF8); break; } 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; } /* Return the current rowid value */ |
︙ | ︙ | |||
3659 3660 3661 3662 3663 3664 3665 | /* Start a search on a new JSON string */ static int jsonEachFilter( sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ JsonEachCursor *p = (JsonEachCursor*)cur; | < | < < > > | | > | < | | > | < < | < | < < | | | < < < < < < > > | < | | < < < < | > > > > > > | > > | | > > > > > > | < | < < > > > | | > | < < < < < < < < < < < | > | | > > > | | > | > > > > > > > > > > > | > | > > > > | | 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 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 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 | /* Start a search on a new JSON string */ static int jsonEachFilter( sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ JsonEachCursor *p = (JsonEachCursor*)cur; const char *zRoot = 0; u32 i, n, sz; 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; } if( jsonConvertTextToBlob(&p->sParse, 0) ){ if( p->sParse.oom ){ return SQLITE_NOMEM; } goto json_each_malformed_input; } } if( idxNum==3 ){ zRoot = (const char*)sqlite3_value_text(argv[1]); if( zRoot==0 ) return SQLITE_OK; if( zRoot[0]!='$' ){ sqlite3_free(cur->pVtab->zErrMsg); cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } p->nRoot = sqlite3Strlen30(zRoot); if( zRoot[1]==0 ){ i = p->i = 0; p->eType = 0; }else{ i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0); if( JSON_LOOKUP_ISERROR(i) ){ if( i==JSON_LOOKUP_NOTFOUND ){ p->i = 0; p->eType = 0; p->iEnd = 0; return SQLITE_OK; } sqlite3_free(cur->pVtab->zErrMsg); cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } if( p->sParse.iLabel ){ p->i = p->sParse.iLabel; p->eType = JSONB_OBJECT; }else{ p->i = i; p->eType = JSONB_ARRAY; } } jsonAppendRaw(&p->path, zRoot, p->nRoot); }else{ i = p->i = 0; p->eType = 0; p->nRoot = 1; jsonAppendRaw(&p->path, "$", 1); } p->nParent = 0; n = jsonbPayloadSize(&p->sParse, i, &sz); p->iEnd = i+n+sz; if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ p->i = i + n; p->eType = p->sParse.aBlob[i] & 0x0f; p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent)); if( p->aParent==0 ) return SQLITE_NOMEM; p->nParent = 1; p->nParentAlloc = 1; p->aParent[0].iKey = 0; p->aParent[0].iEnd = p->iEnd; p->aParent[0].iHead = p->i; p->aParent[0].iValue = i; } return SQLITE_OK; json_each_malformed_input: sqlite3_free(cur->pVtab->zErrMsg); cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } /* The methods of the json_each virtual table */ static sqlite3_module jsonEachModule = { 0, /* iVersion */ 0, /* xCreate */ jsonEachConnect, /* xConnect */ |
︙ | ︙ | |||
3807 3808 3809 3810 3811 3812 3813 | /* ** Register JSON functions. */ void sqlite3RegisterJsonFunctions(void){ #ifndef SQLITE_OMIT_JSON static FuncDef aJsonFunc[] = { | | | | | | | < | > | > | | | | > | | | > | > | > > > | | > | > | > | | | > | | < > > > > > > | 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 | /* ** Register JSON functions. */ void sqlite3RegisterJsonFunctions(void){ #ifndef SQLITE_OMIT_JSON static FuncDef aJsonFunc[] = { /* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */ /* | | */ /* Uses cache ------, | | ,---- Returns JSONB */ /* | | | | */ /* Number of arguments ---, | | | | ,--- Flags */ /* | | | | | | */ JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc), JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc), JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc), JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc), JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc), JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc), 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), JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc), JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc), JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc), JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc), #if SQLITE_DEBUG JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc), #endif WAGGREGATE(json_group_array, 1, 0, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| SQLITE_DETERMINISTIC), WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), WAGGREGATE(json_group_object, 2, 0, 0, jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0, jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| SQLITE_DETERMINISTIC) }; sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); #endif } |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 | typedef int(*sqlite3LocaltimeType)(const void*,void*); sqlite3GlobalConfig.xAltLocaltime = va_arg(ap, sqlite3LocaltimeType); }else{ sqlite3GlobalConfig.xAltLocaltime = 0; } break; } /* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, sqlite3*); ** ** Toggle the ability to use internal functions on or off for ** the database connection given in the argument. */ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 | typedef int(*sqlite3LocaltimeType)(const void*,void*); sqlite3GlobalConfig.xAltLocaltime = va_arg(ap, sqlite3LocaltimeType); }else{ sqlite3GlobalConfig.xAltLocaltime = 0; } break; } /* sqlite3_test_control(SQLITE_TESTCTRL_ROWID_IN_VIEW, int *pVal); ** ** Query or set the sqlite3Config.mNoVisibleRowid flag. Cases: ** ** *pVal==1 Allow ROWID in VIEWs ** *pVal==0 Disallow ROWID in VIEWs ** *pVal<0 No change ** ** In every case *pVal is written with 1 if ROWID is allowd in VIEWs and ** 0 if not. Changes to the setting only occur if SQLite is compiled ** with -DSQLITE_ALLOW_ROWID_IN_VIEW (hereafter: "SARIV"). With the ** "SARIV" compile-time option the default value for this setting is 1. ** Otherwise this setting defaults to 0. This setting may only be changed ** if SQLite is compiled with "SARIV". Hence, in the normal case when ** SQLite is compiled without "SARIV", this test-control is a no-op ** that always leaves *pVal set to 0. ** ** IMPORTANT: If you change this setting while a database connection ** is option, it is very important to run "PRAGMA writable_schema=RESET" ** afterwards in order to reparse all VIEW definitions in the schema. */ case SQLITE_TESTCTRL_ROWID_IN_VIEW: { int *pVal = va_arg(ap, int*); #ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( *pVal==0 ) sqlite3Config.mNoVisibleRowid = TF_NoVisibleRowid; if( *pVal==1 ) sqlite3Config.mNoVisibleRowid = 0; *pVal = (sqlite3Config.mNoVisibleRowid==0); #else *pVal = 0; #endif break; } /* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, sqlite3*); ** ** Toggle the ability to use internal functions on or off for ** the database connection given in the argument. */ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: { |
︙ | ︙ | |||
4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 | *piValue = Tuning(-id); }else{ rc = SQLITE_NOTFOUND; } break; } #endif } va_end(ap); #endif /* SQLITE_UNTESTABLE */ return rc; } /* | > > > > > > > > > > > > > > > > > > > > > > | 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 | *piValue = Tuning(-id); }else{ rc = SQLITE_NOTFOUND; } break; } #endif /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff); ** ** Activate or deactivate validation of JSONB that is generated from ** text. Off by default, as the validation is slow. Validation is ** 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 break; } } va_end(ap); #endif /* SQLITE_UNTESTABLE */ return rc; } /* |
︙ | ︙ |
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 = ""; |
︙ | ︙ | |||
4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 | case SQLITE_FCNTL_HAS_MOVED: { *(int*)pArg = fileHasMoved(pFile); return SQLITE_OK; } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT case SQLITE_FCNTL_LOCK_TIMEOUT: { int iOld = pFile->iBusyTimeout; pFile->iBusyTimeout = *(int*)pArg; *(int*)pArg = iOld; return SQLITE_OK; } #endif #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; | > > > > > > | 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 | case SQLITE_FCNTL_HAS_MOVED: { *(int*)pArg = fileHasMoved(pFile); return SQLITE_OK; } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT case SQLITE_FCNTL_LOCK_TIMEOUT: { int iOld = pFile->iBusyTimeout; #if SQLITE_ENABLE_SETLK_TIMEOUT==1 pFile->iBusyTimeout = *(int*)pArg; #elif SQLITE_ENABLE_SETLK_TIMEOUT==2 pFile->iBusyTimeout = !!(*(int*)pArg); #else # error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" #endif *(int*)pArg = iOld; return SQLITE_OK; } #endif #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; |
︙ | ︙ | |||
4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 | ** ** hShm ** zFilename ** ** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and ** unixMutexHeld() is true when reading or writing any other field ** in this structure. */ struct unixShmNode { unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ sqlite3_mutex *pShmMutex; /* Mutex to access this object */ char *zFilename; /* Name of the mmapped file */ int hShm; /* Open file descriptor */ int szRegion; /* Size of shared-memory regions */ u16 nRegion; /* Size of array apRegion */ u8 isReadonly; /* True if read-only */ u8 isUnlocked; /* True if no DMS lock held */ char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ #ifdef SQLITE_DEBUG | > > > > > > > > > > > > > > > > > > > > > > < < | 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 | ** ** hShm ** zFilename ** ** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and ** unixMutexHeld() is true when reading or writing any other field ** in this structure. ** ** aLock[SQLITE_SHM_NLOCK]: ** This array records the various locks held by clients on each of the ** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no ** locks are held by the process on this slot. If it is set to -1, then ** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[] ** value is set to a positive value, then it is the number of shared ** locks currently held on the slot. ** ** aMutex[SQLITE_SHM_NLOCK]: ** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex ** pShmMutex is used to protect the aLock[] array and the right to ** call fcntl() on unixShmNode.hShm to obtain or release locks. ** ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array ** of mutexes - one for each locking slot. To read or write locking ** slot aLock[iSlot], the caller must hold the corresponding mutex ** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a ** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held. */ struct unixShmNode { unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ sqlite3_mutex *pShmMutex; /* Mutex to access this object */ char *zFilename; /* Name of the mmapped file */ int hShm; /* Open file descriptor */ int szRegion; /* Size of shared-memory regions */ u16 nRegion; /* Size of array apRegion */ u8 isReadonly; /* True if read-only */ u8 isUnlocked; /* True if no DMS lock held */ char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK]; #endif int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ #ifdef SQLITE_DEBUG u8 nextShmId; /* Next available unixShm.id value */ #endif }; /* ** Structure used internally by this VFS to record the state of an ** open shared memory connection. |
︙ | ︙ | |||
4402 4403 4404 4405 4406 4407 4408 | int ofst, /* First byte of the locking range */ int n /* Number of bytes to lock */ ){ unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */ struct flock f; /* The posix advisory locking structure */ int rc = SQLITE_OK; /* Result code form fcntl() */ | < > > > > > > > > > | > > > > > > > > | > > > | | < < | < < | < < | < < | | | < < < | 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 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 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 | int ofst, /* First byte of the locking range */ int n /* Number of bytes to lock */ ){ unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */ struct flock f; /* The posix advisory locking structure */ int rc = SQLITE_OK; /* Result code form fcntl() */ pShmNode = pFile->pInode->pShmNode; /* Assert that the parameters are within expected range and that the ** correct mutex or mutexes are held. */ assert( pShmNode->nRef>=0 ); assert( (ofst==UNIX_SHM_DMS && n==1) || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK)) ); if( ofst==UNIX_SHM_DMS ){ assert( pShmNode->nRef>0 || unixMutexHeld() ); assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); }else{ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT int ii; for(ii=ofst-UNIX_SHM_BASE; ii<ofst-UNIX_SHM_BASE+n; ii++){ assert( sqlite3_mutex_held(pShmNode->aMutex[ii]) ); } #else assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); assert( pShmNode->nRef>0 ); #endif } /* Shared locks never span more than one byte */ assert( n==1 || lockType!=F_RDLCK ); /* Locks are within range */ assert( n>=1 && n<=SQLITE_SHM_NLOCK ); assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) ); if( pShmNode->hShm>=0 ){ int res; /* Initialize the locking parameters */ f.l_type = lockType; f.l_whence = SEEK_SET; f.l_start = ofst; f.l_len = n; res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); if( res==-1 ){ #if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1 rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); #else rc = SQLITE_BUSY; #endif } } /* Do debug tracing */ #ifdef SQLITE_DEBUG OSTRACE(("SHM-LOCK ")); if( rc==SQLITE_OK ){ if( lockType==F_UNLCK ){ OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1)); }else if( lockType==F_RDLCK ){ OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1)); }else{ assert( lockType==F_WRLCK ); OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1)); } }else{ if( lockType==F_UNLCK ){ OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1)); }else if( lockType==F_RDLCK ){ OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1)); }else{ assert( lockType==F_WRLCK ); OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1)); } } #endif return rc; } /* |
︙ | ︙ | |||
4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 | unixShmNode *p = pFd->pInode->pShmNode; assert( unixMutexHeld() ); if( p && ALWAYS(p->nRef==0) ){ int nShmPerMap = unixShmRegionPerMap(); int i; assert( p->pInode==pFd->pInode ); sqlite3_mutex_free(p->pShmMutex); for(i=0; i<p->nRegion; i+=nShmPerMap){ if( p->hShm>=0 ){ osMunmap(p->apRegion[i], p->szRegion); }else{ sqlite3_free(p->apRegion[i]); } } | > > > > > | 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 | unixShmNode *p = pFd->pInode->pShmNode; assert( unixMutexHeld() ); if( p && ALWAYS(p->nRef==0) ){ int nShmPerMap = unixShmRegionPerMap(); int i; assert( p->pInode==pFd->pInode ); sqlite3_mutex_free(p->pShmMutex); #ifdef SQLITE_ENABLE_SETLK_TIMEOUT for(i=0; i<SQLITE_SHM_NLOCK; i++){ sqlite3_mutex_free(p->aMutex[i]); } #endif for(i=0; i<p->nRegion; i+=nShmPerMap){ if( p->hShm>=0 ){ osMunmap(p->apRegion[i], p->szRegion); }else{ sqlite3_free(p->apRegion[i]); } } |
︙ | ︙ | |||
4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 | if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) { rc = SQLITE_IOERR_LOCK; }else if( lock.l_type==F_UNLCK ){ if( pShmNode->isReadonly ){ pShmNode->isUnlocked = 1; rc = SQLITE_READONLY_CANTINIT; }else{ rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); /* The first connection to attach must truncate the -shm file. We ** truncate to 3 bytes (an arbitrary small number, less than the ** -shm header size) rather than 0 as a system debugging aid, to ** help detect if a -shm file truncation is legitimate or is the work ** or a rogue process. */ if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){ rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename); | > > > > > > > > > > > > > | 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 | if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) { rc = SQLITE_IOERR_LOCK; }else if( lock.l_type==F_UNLCK ){ if( pShmNode->isReadonly ){ pShmNode->isUnlocked = 1; rc = SQLITE_READONLY_CANTINIT; }else{ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT /* Do not use a blocking lock here. If the lock cannot be obtained ** immediately, it means some other connection is truncating the ** *-shm file. And after it has done so, it will not release its ** lock, but only downgrade it to a shared lock. So no point in ** blocking here. The call below to obtain the shared DMS lock may ** use a blocking lock. */ int iSaveTimeout = pDbFd->iBusyTimeout; pDbFd->iBusyTimeout = 0; #endif rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); #ifdef SQLITE_ENABLE_SETLK_TIMEOUT pDbFd->iBusyTimeout = iSaveTimeout; #endif /* The first connection to attach must truncate the -shm file. We ** truncate to 3 bytes (an arbitrary small number, less than the ** -shm header size) rather than 0 as a system debugging aid, to ** help detect if a -shm file truncation is legitimate or is the work ** or a rogue process. */ if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){ rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename); |
︙ | ︙ | |||
4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 | pShmNode->pInode = pDbFd->pInode; if( sqlite3GlobalConfig.bCoreMutex ){ pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->pShmMutex==0 ){ rc = SQLITE_NOMEM_BKPT; goto shm_open_err; } } if( pInode->bProcessLock==0 ){ if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW, (sStat.st_mode&0777)); } | > > > > > > > > > > > > | 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 | pShmNode->pInode = pDbFd->pInode; if( sqlite3GlobalConfig.bCoreMutex ){ pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->pShmMutex==0 ){ rc = SQLITE_NOMEM_BKPT; goto shm_open_err; } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT { int ii; for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){ pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->aMutex[ii]==0 ){ rc = SQLITE_NOMEM_BKPT; goto shm_open_err; } } } #endif } if( pInode->bProcessLock==0 ){ if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW, (sStat.st_mode&0777)); } |
︙ | ︙ | |||
4900 4901 4902 4903 4904 4905 4906 4907 4908 | ** held by each client. Return true if it does, or false otherwise. This ** is to be used in an assert(). e.g. ** ** assert( assertLockingArrayOk(pShmNode) ); */ #ifdef SQLITE_DEBUG static int assertLockingArrayOk(unixShmNode *pShmNode){ unixShm *pX; int aLock[SQLITE_SHM_NLOCK]; | > > > < > | | | 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 | ** held by each client. Return true if it does, or false otherwise. This ** is to be used in an assert(). e.g. ** ** assert( assertLockingArrayOk(pShmNode) ); */ #ifdef SQLITE_DEBUG static int assertLockingArrayOk(unixShmNode *pShmNode){ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT return 1; #else unixShm *pX; int aLock[SQLITE_SHM_NLOCK]; memset(aLock, 0, sizeof(aLock)); for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ int i; for(i=0; i<SQLITE_SHM_NLOCK; i++){ if( pX->exclMask & (1<<i) ){ assert( aLock[i]==0 ); aLock[i] = -1; }else if( pX->sharedMask & (1<<i) ){ assert( aLock[i]>=0 ); aLock[i]++; } } } assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); #endif } #endif /* ** Change the lock state for a shared-memory segment. ** ** Note that the relationship between SHARED and EXCLUSIVE locks is a little ** different here than in posix. In xShmLock(), one can go from unlocked ** to shared and back or from unlocked to exclusive and back. But one may ** not go from shared to exclusive or from exclusive to shared. */ static int unixShmLock( sqlite3_file *fd, /* Database file holding the shared memory */ int ofst, /* First lock to acquire or release */ int n, /* Number of locks to acquire or release */ int flags /* What to do with the lock */ ){ unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ unixShm *p; /* The shared memory being locked */ unixShmNode *pShmNode; /* The underlying file iNode */ int rc = SQLITE_OK; /* Result code */ u16 mask = (1<<(ofst+n)) - (1<<ofst); /* Mask of locks to take or release */ int *aLock; p = pDbFd->pShm; if( p==0 ) return SQLITE_IOERR_SHMLOCK; pShmNode = p->pShmNode; if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; aLock = pShmNode->aLock; |
︙ | ︙ | |||
4976 4977 4978 4979 4980 4981 4982 | ** In other words, if this is a blocking lock, none of the locks that ** occur later in the above list than the lock being obtained may be ** held. ** ** It is not permitted to block on the RECOVER lock. */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT | > > | | | | | | > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | < < > | > > > | > > > > > > | | | > > | | | | | | | < | < < < < < < | < | | | > | < < | > | | | | | | | | | | > > > | > > > | | | < | < | | | | | | | | | | | < | | | | | | | | > > > > > > > > > | > > > | 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 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 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 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 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 | ** In other words, if this is a blocking lock, none of the locks that ** occur later in the above list than the lock being obtained may be ** held. ** ** It is not permitted to block on the RECOVER lock. */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT { u16 lockMask = (p->exclMask|p->sharedMask); assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( (ofst!=2) /* not RECOVER */ && (ofst!=1 || lockMask==0 || lockMask==2) && (ofst!=0 || lockMask<3) && (ofst<3 || lockMask<(1<<ofst)) )); } #endif /* Check if there is any work to do. There are three cases: ** ** a) An unlock operation where there are locks to unlock, ** b) An shared lock where the requested lock is not already held ** c) An exclusive lock where the requested lock is not already held ** ** The SQLite core never requests an exclusive lock that it already holds. ** This is assert()ed below. */ assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK) || 0==(p->exclMask & mask) ); if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) ){ /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any ** other thread is holding this mutex, then it is either holding or about ** to hold a lock exclusive to the one being requested, and we may ** therefore return SQLITE_BUSY to the caller. ** ** Doing this prevents some deadlock scenarios. For example, thread 1 may ** be a checkpointer blocked waiting on the WRITER lock. And thread 2 ** may be a normal SQL client upgrading to a write transaction. In this ** case thread 2 does a non-blocking request for the WRITER lock. But - ** if it were to use sqlite3_mutex_enter() then it would effectively ** become a (doomed) blocking request, as thread 2 would block until thread ** 1 obtained WRITER and released the mutex. Since thread 2 already holds ** 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 ); /* If this is a SHARED lock being unlocked, it is possible that other ** clients within this process are holding the same SHARED lock. In ** this case, set bUnlock to 0 so that the posix lock is not removed ** from the file-descriptor below. */ if( flags & SQLITE_SHM_SHARED ){ assert( n==1 ); assert( aLock[ofst]>=1 ); if( aLock[ofst]>1 ){ bUnlock = 0; aLock[ofst]--; p->sharedMask &= ~mask; } } if( bUnlock ){ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); if( rc==SQLITE_OK ){ memset(&aLock[ofst], 0, sizeof(int)*n); p->sharedMask &= ~mask; p->exclMask &= ~mask; } } }else if( flags & SQLITE_SHM_SHARED ){ /* Case (b) - a shared lock. */ if( aLock[ofst]<0 ){ /* An exclusive lock is held by some other connection. BUSY. */ rc = SQLITE_BUSY; }else if( aLock[ofst]==0 ){ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); } /* Get the local shared locks */ if( rc==SQLITE_OK ){ p->sharedMask |= mask; aLock[ofst]++; } }else{ /* Case (c) - an exclusive lock. */ int ii; assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ); assert( (p->sharedMask & mask)==0 ); assert( (p->exclMask & mask)==0 ); /* Make sure no sibling connections hold locks that will block this ** lock. If any do, return SQLITE_BUSY right away. */ for(ii=ofst; ii<ofst+n; ii++){ if( aLock[ii] ){ rc = SQLITE_BUSY; break; } } /* Get the exclusive locks at the system level. Then if successful ** also update the in-memory values. */ if( rc==SQLITE_OK ){ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n); if( rc==SQLITE_OK ){ p->exclMask |= mask; for(ii=ofst; ii<ofst+n; ii++){ aLock[ii] = -1; } } } } 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 } OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", p->id, osGetpid(0), p->sharedMask, p->exclMask)); return rc; } /* ** Implement a memory barrier or memory fence on shared memory. |
︙ | ︙ | |||
5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 | #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; } |
︙ | ︙ |
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.
︙ | ︙ | |||
684 685 686 687 688 689 690 | Pgno lckPgno; /* Page number for the locking page */ i64 pageSize; /* Number of bytes in a page */ i64 journalSizeLimit; /* Size limit for persistent journal files */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ | | | 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 | Pgno lckPgno; /* Page number for the locking page */ i64 pageSize; /* Number of bytes in a page */ i64 journalSizeLimit; /* Size limit for persistent journal files */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ u32 aStat[4]; /* Total cache hits, misses, writes, spills */ #ifdef SQLITE_TEST int nRead; /* Database pages read */ #endif void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */ char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ |
︙ | ︙ | |||
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 |
︙ | ︙ | |||
6828 6829 6830 6831 6832 6833 6834 | static int a[11]; a[0] = sqlite3PcacheRefCount(pPager->pPCache); a[1] = sqlite3PcachePagecount(pPager->pPCache); a[2] = sqlite3PcacheGetCachesize(pPager->pPCache); a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; a[4] = pPager->eState; a[5] = pPager->errCode; | | | | | | 6827 6828 6829 6830 6831 6832 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 | static int a[11]; a[0] = sqlite3PcacheRefCount(pPager->pPCache); a[1] = sqlite3PcachePagecount(pPager->pPCache); a[2] = sqlite3PcacheGetCachesize(pPager->pPCache); a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; a[4] = pPager->eState; a[5] = pPager->errCode; a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff; a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff; a[8] = 0; /* Used to be pPager->nOvfl */ a[9] = pPager->nRead; a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff; return a; } #endif /* ** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE, ** or _WRITE+1. The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation ** of SQLITE_DBSTATUS_CACHE_SPILL. The _SPILL case is not contiguous because ** it was added later. ** ** Before returning, *pnVal is incremented by the ** current cache hit or miss count, according to the value of eStat. If the ** reset parameter is non-zero, the cache hit or miss count is zeroed before ** returning. */ void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){ assert( eStat==SQLITE_DBSTATUS_CACHE_HIT || eStat==SQLITE_DBSTATUS_CACHE_MISS || eStat==SQLITE_DBSTATUS_CACHE_WRITE || eStat==SQLITE_DBSTATUS_CACHE_WRITE+1 ); |
︙ | ︙ | |||
7788 7789 7790 7791 7792 7793 7794 | */ int sqlite3PagerWalFramesize(Pager *pPager){ assert( pPager->eState>=PAGER_READER ); return sqlite3WalFramesize(pPager->pWal); } #endif | | | 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 | */ int sqlite3PagerWalFramesize(Pager *pPager){ assert( pPager->eState>=PAGER_READER ); return sqlite3WalFramesize(pPager->pWal); } #endif #if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) int sqlite3PagerWalSystemErrno(Pager *pPager){ return sqlite3WalSystemErrno(pPager->pWal); } #endif #endif /* SQLITE_OMIT_DISKIO */ |
Changes to src/pager.h.
︙ | ︙ | |||
212 213 214 215 216 217 218 | const char *sqlite3PagerFilename(const Pager*, int); sqlite3_vfs *sqlite3PagerVfs(Pager*); sqlite3_file *sqlite3PagerFile(Pager*); sqlite3_file *sqlite3PagerJrnlFile(Pager*); const char *sqlite3PagerJournalname(Pager*); void *sqlite3PagerTempSpace(Pager*); int sqlite3PagerIsMemdb(Pager*); | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | const char *sqlite3PagerFilename(const Pager*, int); sqlite3_vfs *sqlite3PagerVfs(Pager*); sqlite3_file *sqlite3PagerFile(Pager*); sqlite3_file *sqlite3PagerJrnlFile(Pager*); const char *sqlite3PagerJournalname(Pager*); void *sqlite3PagerTempSpace(Pager*); int sqlite3PagerIsMemdb(Pager*); void sqlite3PagerCacheStat(Pager *, int, int, u64*); void sqlite3PagerClearCache(Pager*); int sqlite3SectorSize(sqlite3_file *); /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno); void sqlite3PagerRekey(DbPage*, Pgno, u16); |
︙ | ︙ |
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;} |
︙ | ︙ | |||
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; | | | 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 | ** 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;} | | > | 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 | 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 |
︙ | ︙ | |||
1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 | 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 */ . /* 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 | > > > > > > | 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 | 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 |
︙ | ︙ | |||
1688 1689 1690 1691 1692 1693 1694 | /* 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[] */ | < | 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 | /* 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); |
︙ | ︙ | |||
1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 | }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 ){ |
︙ | ︙ | |||
2089 2090 2091 2092 2093 2094 2095 | } 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 */ |
︙ | ︙ | |||
2369 2370 2371 2372 2373 2374 2375 | ** ** 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 ** |
︙ | ︙ |
Changes to src/prepare.c.
︙ | ︙ | |||
864 865 866 867 868 869 870 871 872 873 874 875 876 877 | }while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY) || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) ); sqlite3BtreeLeaveAll(db); rc = sqlite3ApiExit(db, rc); assert( (rc&db->errMask)==rc ); db->busyHandler.nBusy = 0; sqlite3_mutex_leave(db->mutex); return rc; } /* ** Rerun the compilation of a statement after a schema change. ** | > | 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 | }while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY) || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) ); sqlite3BtreeLeaveAll(db); rc = sqlite3ApiExit(db, rc); assert( (rc&db->errMask)==rc ); db->busyHandler.nBusy = 0; sqlite3_mutex_leave(db->mutex); assert( rc==SQLITE_OK || (*ppStmt)==0 ); return rc; } /* ** Rerun the compilation of a statement after a schema change. ** |
︙ | ︙ |
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; } |
︙ | ︙ | |||
854 855 856 857 858 859 860 861 862 863 864 865 866 867 | }else if( pItem->zAlias ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else{ Select *pSel = pItem->pSelect; assert( pSel!=0 ); if( pSel->selFlags & SF_NestedFrom ){ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); }else{ sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId); } } length = width = 0; break; } | > > > > | 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 | }else if( pItem->zAlias ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else{ Select *pSel = pItem->pSelect; assert( pSel!=0 ); 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; } |
︙ | ︙ | |||
1365 1366 1367 1368 1369 1370 1371 | va_start(ap,zFormat); sqlite3_str_vappendf(p, zFormat, ap); va_end(ap); } /***************************************************************************** | | | 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 | va_start(ap,zFormat); sqlite3_str_vappendf(p, zFormat, ap); va_end(ap); } /***************************************************************************** ** Reference counted string/blob storage *****************************************************************************/ /* ** Increase the reference count of the string by one. ** ** The input parameter is returned. */ |
︙ | ︙ |
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; |
︙ | ︙ | |||
178 179 180 181 182 183 184 185 186 187 188 189 190 191 | int n; Table *pExTab; n = pExpr->iColumn; assert( ExprUseYTab(pExpr) ); pExTab = pExpr->y.pTab; assert( pExTab!=0 ); if( (pExTab->tabFlags & TF_HasGenerated)!=0 && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0 ){ testcase( pExTab->nCol==BMS-1 ); testcase( pExTab->nCol==BMS ); return pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1; }else{ | > | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | int n; Table *pExTab; n = pExpr->iColumn; assert( ExprUseYTab(pExpr) ); pExTab = pExpr->y.pTab; assert( pExTab!=0 ); assert( n < pExTab->nCol ); if( (pExTab->tabFlags & TF_HasGenerated)!=0 && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0 ){ testcase( pExTab->nCol==BMS-1 ); testcase( pExTab->nCol==BMS ); return pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1; }else{ |
︙ | ︙ | |||
272 273 274 275 276 277 278 | ** 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 */ |
︙ | ︙ | |||
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | } } 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++; } assert( pFJMatch==0 ); /* Remove all substructure from pExpr */ if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ sqlite3ExprDelete(db, pExpr->pLeft); pExpr->pLeft = 0; | > > > > > | 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 | } } 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; } assert( pFJMatch==0 ); /* Remove all substructure from pExpr */ if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ sqlite3ExprDelete(db, pExpr->pLeft); pExpr->pLeft = 0; |
︙ | ︙ | |||
780 781 782 783 784 785 786 | ** 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. */ | | | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 | ** 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( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){ pMatch->colUsed |= sqlite3ExprColUsed(pExpr); } pExpr->op = eNewExprOp; lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); |
︙ | ︙ | |||
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 | ** "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); | > > > > > > > > > > > > > > | > > > > > > > > > | | > > > > > > > > > | | | < | | | | | < < | < | | 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 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 | ** "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 */ |
︙ | ︙ | |||
1245 1246 1247 1248 1249 1250 1251 | sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); } #endif pNC2 = pNC; while( pNC2 && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 ){ | | > | 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 | sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); } #endif pNC2 = pNC; while( pNC2 && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 ){ pExpr->op2 += (1 + pNC2->nNestedSelect); pNC2 = pNC2->pNext; } assert( pDef!=0 || IN_RENAME_OBJECT ); if( pNC2 && pDef ){ pExpr->op2 += pNC2->nNestedSelect; assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg ); testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 ); pNC2->ncFlags |= NC_HasAgg | ((pDef->funcFlags^SQLITE_FUNC_ANYORDER) & (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER)); |
︙ | ︙ | |||
1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 | assert( pSub->pPrior && pSub->pOrderBy==0 ); pSub->pOrderBy = p->pOrderBy; p->pOrderBy = 0; } /* Recursively resolve names in all subqueries in the FROM clause */ 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; | > | 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 | assert( pSub->pPrior && pSub->pOrderBy==0 ); pSub->pOrderBy = p->pOrderBy; p->pOrderBy = 0; } /* 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; |
︙ | ︙ | |||
1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 | ** the refcount on all contexts between the current one and the ** context containing the column when it resolves a name. */ if( pOuterNC ){ assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef ); pItem->fg.isCorrelated = (pOuterNC->nRef>nRef); } } } /* Set up the local name-context to pass to sqlite3ResolveExprNames() to ** resolve the result-set expression list. */ sNC.ncFlags = NC_AllowAgg|NC_AllowWin; sNC.pSrcList = p->pSrc; | > > > | 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 | ** the refcount on all contexts between the current one and the ** context containing the column when it resolves a name. */ if( pOuterNC ){ assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef ); pItem->fg.isCorrelated = (pOuterNC->nRef>nRef); } } } if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){ pOuterNC->nNestedSelect--; } /* Set up the local name-context to pass to sqlite3ResolveExprNames() to ** resolve the result-set expression list. */ sNC.ncFlags = NC_AllowAgg|NC_AllowWin; sNC.pSrcList = p->pSrc; |
︙ | ︙ | |||
1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 | 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) ){ | > > | 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 | 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.
︙ | ︙ | |||
180 181 182 183 184 185 186 187 188 189 190 191 192 193 | /* ** Delete the given Select structure and all of its substructures. */ void sqlite3SelectDelete(sqlite3 *db, Select *p){ if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1); } /* ** Return a pointer to the right-most SELECT statement in a compound. */ static Select *findRightmost(Select *p){ while( p->pNext ) p = p->pNext; return p; | > > > | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | /* ** Delete the given Select structure and all of its substructures. */ void sqlite3SelectDelete(sqlite3 *db, Select *p){ if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1); } void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){ if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1); } /* ** Return a pointer to the right-most SELECT statement in a compound. */ static Select *findRightmost(Select *p){ while( p->pNext ) p = p->pNext; return p; |
︙ | ︙ | |||
1946 1947 1948 1949 1950 1951 1952 | 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 | < | < < < | 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 | 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; |
︙ | ︙ | |||
2315 2316 2317 2318 2319 2320 2321 | CollSeq *pColl; int i,j; Expr *p; struct ExprList_item *a; NameContext sNC; assert( pSelect!=0 ); | > | | 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 | CollSeq *pColl; int i,j; Expr *p; struct ExprList_item *a; NameContext sNC; assert( pSelect!=0 ); testcase( (pSelect->selFlags & SF_Resolved)==0 ); assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT ); 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; |
︙ | ︙ | |||
3199 3200 3201 3202 3203 3204 3205 | sqlite3KeyInfoUnref(pKeyInfo); } multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; if( pDelete ){ | | < < | 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 | sqlite3KeyInfoUnref(pKeyInfo); } multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; if( pDelete ){ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete); } return rc; } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ /* ** Error message for when two or more terms of a compound select have different |
︙ | ︙ | |||
3752 3753 3754 3755 3756 3757 3758 | /* Jump to the this point in order to terminate the query. */ sqlite3VdbeResolveLabel(v, labelEnd); /* Make arrangements to free the 2nd and subsequent arms of the compound ** after the parse has finished */ if( pSplit->pPrior ){ | | < | 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 | /* Jump to the this point in order to terminate the query. */ sqlite3VdbeResolveLabel(v, labelEnd); /* Make arrangements to free the 2nd and subsequent arms of the compound ** after the parse has finished */ if( pSplit->pPrior ){ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior); } pSplit->pPrior = pPrior; pPrior->pNext = pSplit; sqlite3ExprListDelete(db, pPrior->pOrderBy); pPrior->pOrderBy = 0; /*** TBD: Insert subroutine calls to close cursors on incomplete |
︙ | ︙ | |||
4574 4575 4576 4577 4578 4579 4580 | ** ** pSubitem->pTab is always non-NULL by test restrictions and tests above. */ if( ALWAYS(pSubitem->pTab!=0) ){ Table *pTabToDel = pSubitem->pTab; if( pTabToDel->nTabRef==1 ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); | | < < | 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 | ** ** pSubitem->pTab is always non-NULL by test restrictions and tests above. */ if( ALWAYS(pSubitem->pTab!=0) ){ Table *pTabToDel = pSubitem->pTab; if( pTabToDel->nTabRef==1 ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel); testcase( pToplevel->earlyCleanup ); }else{ pTabToDel->nTabRef--; } pSubitem->pTab = 0; } |
︙ | ︙ | |||
4773 4774 4775 4776 4777 4778 4779 | 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 ); | | | 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 | 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; } |
︙ | ︙ | |||
4831 4832 4833 4834 4835 4836 4837 | return; } if( pExpr->op!=TK_EQ ) return; pRight = pExpr->pRight; pLeft = pExpr->pLeft; assert( pRight!=0 ); assert( pLeft!=0 ); | | | | 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 | 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(). ** |
︙ | ︙ | |||
5623 5624 5625 5626 5627 5628 5629 | ** ** If bFree is true, do not continue to use the pWith pointer after ** calling this routine, Instead, use only the return value. */ With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ if( pWith ){ if( bFree ){ | | < | 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 | ** ** If bFree is true, do not continue to use the pWith pointer after ** calling this routine, Instead, use only the return value. */ With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ if( pWith ){ if( bFree ){ pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric, pWith); if( pWith==0 ) return 0; } if( pParse->nErr==0 ){ assert( pParse->pWith!=pWith ); pWith->pOuter = pParse->pWith; pParse->pWith = pWith; |
︙ | ︙ | |||
5872 5873 5874 5875 5876 5877 5878 | 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 | | > | 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 | 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 /* 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!) |
︙ | ︙ | |||
6140 6141 6142 6143 6144 6145 6146 | 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 ); | | | 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 | 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 : "*"; |
︙ | ︙ | |||
6172 6173 6174 6175 6176 6177 6178 | pX->fg.bUsingTerm = 1; } } }else{ pUsing = 0; } | | > > > > > > | 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 | pX->fg.bUsingTerm = 1; } } }else{ pUsing = 0; } nAdd = pTab->nCol; if( VisibleRowid(pTab) && !ViewCanHaveRowid && (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; |
︙ | ︙ | |||
6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 | #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, | > > | 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 | #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, |
︙ | ︙ | |||
6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 | if( pFunc->iOBTab>=0 ){ ExprList *pOBList; KeyInfo *pKeyInfo; int nExtra = 0; assert( pFunc->pFExpr->pLeft!=0 ); assert( pFunc->pFExpr->pLeft->op==TK_ORDER ); assert( ExprUseXList(pFunc->pFExpr->pLeft) ); pOBList = pFunc->pFExpr->pLeft->x.pList; if( !pFunc->bOBUnique ){ nExtra++; /* One extra column for the OP_Sequence */ } if( pFunc->bOBPayload ){ /* extra columns for the function arguments */ assert( ExprUseXList(pFunc->pFExpr) ); nExtra += pFunc->pFExpr->x.pList->nExpr; } pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra); if( !pFunc->bOBUnique && pParse->nErr==0 ){ pKeyInfo->nKeyField++; } sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iOBTab, pOBList->nExpr+nExtra, 0, (char*)pKeyInfo, P4_KEYINFO); | > > > > | 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 | if( pFunc->iOBTab>=0 ){ ExprList *pOBList; KeyInfo *pKeyInfo; int nExtra = 0; assert( pFunc->pFExpr->pLeft!=0 ); assert( pFunc->pFExpr->pLeft->op==TK_ORDER ); assert( ExprUseXList(pFunc->pFExpr->pLeft) ); assert( pFunc->pFunc!=0 ); pOBList = pFunc->pFExpr->pLeft->x.pList; if( !pFunc->bOBUnique ){ nExtra++; /* One extra column for the OP_Sequence */ } if( pFunc->bOBPayload ){ /* extra columns for the function arguments */ assert( ExprUseXList(pFunc->pFExpr) ); nExtra += pFunc->pFExpr->x.pList->nExpr; } if( pFunc->bUseSubtype ){ nExtra += pFunc->pFExpr->x.pList->nExpr; } pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra); if( !pFunc->bOBUnique && pParse->nErr==0 ){ pKeyInfo->nKeyField++; } sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iOBTab, pOBList->nExpr+nExtra, 0, (char*)pKeyInfo, P4_KEYINFO); |
︙ | ︙ | |||
6692 6693 6694 6695 6696 6697 6698 | int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); pList = pF->pFExpr->x.pList; if( pF->iOBTab>=0 ){ | | | | | > > > > > > > > > > | 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 | int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); pList = pF->pFExpr->x.pList; if( pF->iOBTab>=0 ){ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs ** were stored in emphermal table pF->iOBTab. Here, we extract those ** inputs (in ORDER BY order) and make all calls to OP_AggStep ** before doing the OP_AggFinal call. */ int iTop; /* Start of loop for extracting columns */ int nArg; /* Number of columns to extract */ int nKey; /* Key columns to be skipped */ int regAgg; /* Extract into this array */ int j; /* Loop counter */ assert( pF->pFunc!=0 ); nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); if( pF->bOBPayload==0 ){ nKey = 0; }else{ assert( pF->pFExpr->pLeft!=0 ); assert( ExprUseXList(pF->pFExpr->pLeft) ); assert( pF->pFExpr->pLeft->x.pList!=0 ); nKey = pF->pFExpr->pLeft->x.pList->nExpr; if( ALWAYS(!pF->bOBUnique) ) nKey++; } iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v); for(j=nArg-1; j>=0; j--){ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j); } if( pF->bUseSubtype ){ int regSubtype = sqlite3GetTempReg(pParse); int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0); for(j=nArg-1; j>=0; j--){ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype); sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j); } sqlite3ReleaseTempReg(pParse, regSubtype); } sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, iTop); sqlite3ReleaseTempRange(pParse, regAgg, nArg); } |
︙ | ︙ | |||
6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 | int addrNext = 0; int regAgg; int regAggSz = 0; int regDistinct = 0; ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); assert( !IsWindowFunc(pF->pFExpr) ); pList = pF->pFExpr->x.pList; if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ Expr *pFilter = pF->pFExpr->y.pWin->pFilter; if( pAggInfo->nAccumulator && (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) && regAcc ){ | > | 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 | int addrNext = 0; int regAgg; int regAggSz = 0; int regDistinct = 0; ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); assert( !IsWindowFunc(pF->pFExpr) ); assert( pF->pFunc!=0 ); pList = pF->pFExpr->x.pList; if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ Expr *pFilter = pF->pFExpr->y.pWin->pFilter; if( pAggInfo->nAccumulator && (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) && regAcc ){ |
︙ | ︙ | |||
6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 | assert( pOBList->nExpr>0 ); regAggSz = pOBList->nExpr; if( !pF->bOBUnique ){ regAggSz++; /* One register for OP_Sequence */ } if( pF->bOBPayload ){ regAggSz += nArg; } regAggSz++; /* One extra register to hold result of MakeRecord */ regAgg = sqlite3GetTempRange(pParse, regAggSz); regDistinct = regAgg; sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP); jj = pOBList->nExpr; if( !pF->bOBUnique ){ sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj); jj++; } if( pF->bOBPayload ){ regDistinct = regAgg+jj; sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP); } }else if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); regDistinct = regAgg; sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP); }else{ | > > > > > > > > > > > | 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 6867 6868 6869 6870 | assert( pOBList->nExpr>0 ); regAggSz = pOBList->nExpr; if( !pF->bOBUnique ){ regAggSz++; /* One register for OP_Sequence */ } if( pF->bOBPayload ){ regAggSz += nArg; } if( pF->bUseSubtype ){ regAggSz += nArg; } regAggSz++; /* One extra register to hold result of MakeRecord */ regAgg = sqlite3GetTempRange(pParse, regAggSz); regDistinct = regAgg; sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP); jj = pOBList->nExpr; if( !pF->bOBUnique ){ sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj); jj++; } if( pF->bOBPayload ){ regDistinct = regAgg+jj; sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP); jj += nArg; } if( pF->bUseSubtype ){ int kk; int regBase = pF->bOBPayload ? regDistinct : regAgg; for(kk=0; kk<nArg; kk++, jj++){ sqlite3VdbeAddOp2(v, OP_GetSubtype, regBase+kk, regAgg+jj); } } }else if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); regDistinct = regAgg; sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP); }else{ |
︙ | ︙ | |||
7032 7033 7034 7035 7036 7037 7038 | } return 0; } /* ** Deallocate a single AggInfo object */ | | > | 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 | } return 0; } /* ** Deallocate a single AggInfo object */ static void agginfoFree(sqlite3 *db, void *pArg){ AggInfo *p = (AggInfo*)pArg; sqlite3DbFree(db, p->aCol); sqlite3DbFree(db, p->aFunc); sqlite3DbFreeNN(db, p); } /* ** Attempt to transform a query of the form |
︙ | ︙ | |||
7106 7107 7108 7109 7110 7111 7112 | Expr *pTerm; pPrior = pSub->pPrior; pSub->pPrior = 0; pSub->pNext = 0; pSub->selFlags |= SF_Aggregate; pSub->selFlags &= ~SF_Compound; pSub->nSelectRow = 0; | | | 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 | Expr *pTerm; pPrior = pSub->pPrior; pSub->pPrior = 0; pSub->pNext = 0; pSub->selFlags |= SF_Aggregate; pSub->selFlags &= ~SF_Compound; pSub->nSelectRow = 0; sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList); pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount; pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm); pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0); sqlite3PExprAddSelect(pParse, pTerm, pSub); if( pExpr==0 ){ pExpr = pTerm; }else{ |
︙ | ︙ | |||
7286 7287 7288 7289 7290 7291 7292 | if( p->pOrderBy ){ #if TREETRACE_ENABLED TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n")); if( sqlite3TreeTrace & 0x800 ){ sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); } #endif | | < | | 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 | if( p->pOrderBy ){ #if TREETRACE_ENABLED TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n")); if( sqlite3TreeTrace & 0x800 ){ sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); } #endif sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, p->pOrderBy); testcase( pParse->earlyCleanup ); p->pOrderBy = 0; } p->selFlags &= ~SF_Distinct; p->selFlags |= SF_NoopOrderBy; } sqlite3SelectPrep(pParse, p, 0); |
︙ | ︙ | |||
7480 7481 7482 7483 7484 7485 7486 | && pSub->pLimit==0 /* Condition (1) */ && (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */ && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */ && OptimizationEnabled(db, SQLITE_OmitOrderBy) ){ TREETRACE(0x800,pParse,p, ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); | | < | | 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 | && pSub->pLimit==0 /* Condition (1) */ && (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */ && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */ && OptimizationEnabled(db, SQLITE_OmitOrderBy) ){ TREETRACE(0x800,pParse,p, ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pOrderBy); pSub->pOrderBy = 0; } /* If the outer query contains a "complex" result set (that is, ** if the result set of the outer query uses functions or subqueries) ** and if the subquery contains an ORDER BY clause and if ** it will be implemented as a co-routine, then do not flatten. This |
︙ | ︙ | |||
7607 7608 7609 7610 7611 7612 7613 | 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; | | | 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 | 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 |
︙ | ︙ | |||
8011 8012 8013 8014 8015 8016 8017 | /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the ** SELECT statement. */ pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); if( pAggInfo ){ | | < | 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 | /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the ** SELECT statement. */ pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); if( pAggInfo ){ sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo); testcase( pParse->earlyCleanup ); } if( db->mallocFailed ){ goto select_end; } pAggInfo->selId = p->selId; #ifdef SQLITE_DEBUG |
︙ | ︙ | |||
8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 | */ 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++){ | > > > > > > | 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 | */ 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.
︙ | ︙ | |||
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 | # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE #endif INCLUDE ../ext/consio/console_io.h INCLUDE ../ext/consio/console_io.c #ifndef SQLITE_SHELL_FIDDLE /* From here onward, fgets() is redirected to the console_io library. */ # define fgets(b,n,f) fGetsUtf8(b,n,f) /* * Define macros for emitting output text in various ways: * sputz(s, z) => emit 0-terminated string z to given stream s * sputf(s, f, ...) => emit varargs per format f to given stream s * oputz(z) => emit 0-terminated string z to default stream * oputf(f, ...) => emit varargs per format f to default stream * eputz(z) => emit 0-terminated string z to error stream * 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. */ | > > > > > > > > > > | > > < | | 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 | # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE #endif INCLUDE ../ext/consio/console_io.h INCLUDE ../ext/consio/console_io.c #ifndef SQLITE_SHELL_FIDDLE /* From here onward, fgets() is redirected to the console_io library. */ # define fgets(b,n,f) fGetsUtf8(b,n,f) /* * Define macros for emitting output text in various ways: * sputz(s, z) => emit 0-terminated string z to given stream s * sputf(s, f, ...) => emit varargs per format f to given stream s * oputz(z) => emit 0-terminated string z to default stream * oputf(f, ...) => emit varargs per format f to default stream * eputz(z) => emit 0-terminated string z to error stream * 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 */ |
︙ | ︙ | |||
354 355 356 357 358 359 360 | ** Print the timing results. */ static void endTimer(void){ if( enableTimer ){ sqlite3_int64 iEnd = timeOfDay(); struct rusage sEnd; getrusage(RUSAGE_SELF, &sEnd); | | | 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | ** Print the timing results. */ static void endTimer(void){ if( enableTimer ){ sqlite3_int64 iEnd = timeOfDay(); struct rusage sEnd; getrusage(RUSAGE_SELF, &sEnd); sputf(stdout, "Run Time: real %.3f user %f sys %f\n", (iEnd - iBegin)*0.001, timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); } } #define BEGIN_TIMER beginTimer() |
︙ | ︙ | |||
433 434 435 436 437 438 439 | ** Print the timing results. */ static void endTimer(void){ if( enableTimer && getProcessTimesAddr){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; sqlite3_int64 ftWallEnd = timeOfDay(); getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); | | | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | ** Print the timing results. */ static void endTimer(void){ if( enableTimer && getProcessTimesAddr){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; sqlite3_int64 ftWallEnd = timeOfDay(); getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); sputf(stdout, "Run Time: real %.3f user %f sys %f\n", (ftWallEnd - ftWallBegin)*0.001, timeDiff(&ftUserBegin, &ftUserEnd), timeDiff(&ftKernelBegin, &ftKernelEnd)); } } #define BEGIN_TIMER beginTimer() |
︙ | ︙ | |||
473 474 475 476 477 478 479 | /* ** Treat stdin as an interactive input if the following variable ** is true. Otherwise, assume stdin is connected to a file or pipe. */ static int stdin_is_interactive = 1; /* | | | | | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | /* ** Treat stdin as an interactive input if the following variable ** is true. Otherwise, assume stdin is connected to a file or pipe. */ static int stdin_is_interactive = 1; /* ** On Windows systems we need to know if standard output is a console ** in order to show that UTF-16 translation is done in the sign-on ** banner. The following variable is true if it is the console. */ static int stdout_is_console = 1; /* ** The following is the open SQLite database. We make a pointer ** to this database a static variable so that it can be accessed ** by the SIGINT handler to interrupt database processing. |
︙ | ︙ | |||
730 731 732 733 734 735 736 | /* ** Return open FILE * if zFile exists, can be opened for read ** and is an ordinary file or a character stream source. ** Otherwise return 0. */ static FILE * openChrSource(const char *zFile){ #if defined(_WIN32) || defined(WIN32) | | | | 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 | /* ** Return open FILE * if zFile exists, can be opened for read ** and is an ordinary file or a character stream source. ** Otherwise return 0. */ static FILE * openChrSource(const char *zFile){ #if defined(_WIN32) || defined(WIN32) struct __stat64 x = {0}; # define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) /* On Windows, open first, then check the stream nature. This order ** is necessary because _stat() and sibs, when checking a named pipe, ** effectively break the pipe as its supplier sees it. */ FILE *rv = fopen(zFile, "rb"); if( rv==0 ) return 0; if( _fstat64(_fileno(rv), &x) != 0 || !STAT_CHR_SRC(x.st_mode)){ fclose(rv); rv = 0; } return rv; #else struct stat x = {0}; |
︙ | ︙ | |||
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 | #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 |
︙ | ︙ | |||
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 | 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 */ |
︙ | ︙ | |||
3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 | 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*) ); |
︙ | ︙ | |||
3842 3843 3844 3845 3846 3847 3848 | 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]; |
︙ | ︙ | |||
4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 | #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", |
︙ | ︙ | |||
5313 5314 5315 5316 5317 5318 5319 | case SHELL_OPEN_UNSPEC: case SHELL_OPEN_NORMAL: { sqlite3_open_v2(zDbFilename, &p->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0); break; } } | < > | 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 | case SHELL_OPEN_UNSPEC: case SHELL_OPEN_NORMAL: { sqlite3_open_v2(zDbFilename, &p->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0); break; } } if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ eputf("Error: unable to open database \"%s\": %s\n", zDbFilename, sqlite3_errmsg(p->db)); if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ exit(1); } sqlite3_close(p->db); sqlite3_open(":memory:", &p->db); if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ eputz("Also: unable to open substitute in-memory database.\n"); exit(1); }else{ eputf("Notice: using substitute in-memory database instead of \"%s\"\n", zDbFilename); } } globalDb = p->db; sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0); /* Reflect the use or absence of --unsafe-testing invocation. */ { int testmode_on = ShellHasFlag(p,SHFLG_TestingMode); sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0); sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0); |
︙ | ︙ | |||
6725 6726 6727 6728 6729 6730 6731 | 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; |
︙ | ︙ | |||
6772 6773 6774 6775 6776 6777 6778 | }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( |
︙ | ︙ | |||
7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 | 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: |
︙ | ︙ | |||
7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 | } 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 | } 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 */ } faultsim_state = {-1, 0, 0, 0, 0}; /* ** This is the fault-sim callback */ static int faultsim_callback(int iArg){ if( faultsim_state.iId>0 && faultsim_state.iId!=iArg ){ return SQLITE_OK; } if( faultsim_state.iCnt>0 ){ faultsim_state.iCnt--; if( faultsim_state.eVerbose>=2 ){ oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); } return SQLITE_OK; } if( faultsim_state.eVerbose>=1 ){ oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); } faultsim_state.iCnt = faultsim_state.iInterval; return faultsim_state.iErr; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ |
︙ | ︙ | |||
8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 | 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"); } | > | 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 | 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"); } |
︙ | ︙ | |||
8350 8351 8352 8353 8354 8355 8356 | ); 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" | | > | 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 | ); 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 ){ |
︙ | ︙ | |||
8673 8674 8675 8676 8677 8678 8679 | 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 */ | | | | < | 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 | 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 */ |
︙ | ︙ | |||
8816 8817 8818 8819 8820 8821 8822 | import_cleanup(&sCtx); shell_out_of_memory(); } /* Below, resources must be freed before exit. */ while( (nSkip--)>0 ){ while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} } | < < < < < < < < < < < < | > | > < < < < > > > > > > | > > > > > > > > > > > | | < < | > > | | > > > > | > > > > > > | > > < < | 8916 8917 8918 8919 8920 8921 8922 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 | 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); /* |
︙ | ︙ | |||
9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 | }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 ){ | > > > > > > > > > > > > > > > | 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 | }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 ){ |
︙ | ︙ | |||
10071 10072 10073 10074 10075 10076 10077 | } if( rc ){ sputf(stdout, "Error: error code %d\n", rc); rc = 0; } if( pChng && fwrite(pChng, szChng, 1, out)!=1 ){ | | < | 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 | } if( rc ){ sputf(stdout, "Error: error code %d\n", rc); rc = 0; } if( pChng && fwrite(pChng, szChng, 1, out)!=1 ){ eputf("ERROR: Failed to write entire %d-byte output\n", szChng); } sqlite3_free(pChng); fclose(out); } }else /* .session close |
︙ | ︙ | |||
10747 10748 10749 10750 10751 10752 10753 | } 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" }, | | > > | 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 10903 | } 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" }, #ifdef YYCOVERAGE {"parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" }, #endif {"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " }, {"prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" }, {"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, {"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, {"rowid_in_view", SQLITE_TESTCTRL_ROWID_IN_VIEW,0,"?BOOLEAN?" }, {"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, {"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, {"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"}, }; int testctrl = -1; int iCtrl = -1; |
︙ | ︙ | |||
10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 | if( nArg==2 ){ sqlite3_test_control(testctrl, p->out); isOk = 3; } break; } #endif #ifdef SQLITE_DEBUG case SQLITE_TESTCTRL_TUNE: { if( nArg==4 ){ int id = (int)integerValue(azArg[2]); int val = (int)integerValue(azArg[3]); sqlite3_test_control(testctrl, id, &val); isOk = 3; | > > > > > > > > > > > > > > | 11064 11065 11066 11067 11068 11069 11070 11071 11072 11073 11074 11075 11076 11077 11078 11079 11080 11081 11082 11083 11084 11085 11086 11087 11088 11089 11090 11091 | if( nArg==2 ){ sqlite3_test_control(testctrl, p->out); isOk = 3; } break; } #endif case SQLITE_TESTCTRL_ROWID_IN_VIEW: { rc2 = -1; if( nArg>=3 ){ if( !ShellHasFlag(p,SHFLG_TestingMode) ){ eputz("The --unsafe-testing option is required to change " "this setting\n"); }else{ rc2 = booleanValue(azArg[2]); } } sqlite3_test_control(testctrl, &rc2); isOk = 1; break; } #ifdef SQLITE_DEBUG case SQLITE_TESTCTRL_TUNE: { if( nArg==4 ){ int id = (int)integerValue(azArg[2]); int val = (int)integerValue(azArg[3]); sqlite3_test_control(testctrl, id, &val); isOk = 3; |
︙ | ︙ | |||
10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 10980 10981 10982 | case SQLITE_TESTCTRL_SORTER_MMAP: if( nArg==3 ){ int opt = (unsigned int)integerValue(azArg[2]); rc2 = sqlite3_test_control(testctrl, p->db, opt); isOk = 3; } 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); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 | case SQLITE_TESTCTRL_SORTER_MMAP: if( nArg==3 ){ int opt = (unsigned int)integerValue(azArg[2]); rc2 = sqlite3_test_control(testctrl, p->db, opt); isOk = 3; } break; case SQLITE_TESTCTRL_JSON_SELFCHECK: if( nArg==2 ){ rc2 = -1; 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.iInterval; if( faultsim_state.iErr==0 ) faultsim_state.iErr = 1; sqlite3_test_control(testctrl, faultsim_callback); }else if( cli_strcmp(z,"reset")==0 ){ faultsim_state.iCnt = faultsim_state.iInterval; }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.iInterval: %d\n", faultsim_state.iInterval); oputf("faultsim.eVerbose: %d\n", faultsim_state.eVerbose); }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,"-?")==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" ); } 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); |
︙ | ︙ | |||
11374 11375 11376 11377 11378 11379 11380 11381 11382 11383 11384 11385 11386 11387 | 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; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11585 11586 11587 11588 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 | 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; |
︙ | ︙ | |||
11422 11423 11424 11425 11426 11427 11428 11429 11430 11431 11432 11433 11434 11435 | }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); } | > > | 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 | }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); } |
︙ | ︙ | |||
11806 11807 11808 11809 11810 11811 11812 | "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"); } | | | 12101 12102 12103 12104 12105 12106 12107 12108 12109 12110 12111 12112 12113 12114 12115 | "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){ |
︙ | ︙ | |||
11855 11856 11857 11858 11859 11860 11861 | HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; GetConsoleScreenBufferInfo(out, &defaultScreenInfo); SetConsoleTextAttribute(out, FOREGROUND_RED|FOREGROUND_INTENSITY ); #endif | | | | < | 12150 12151 12152 12153 12154 12155 12156 12157 12158 12159 12160 12161 12162 12163 12164 12165 12166 12167 12168 12169 12170 12171 12172 12173 12174 12175 12176 12177 12178 12179 12180 12181 | HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; GetConsoleScreenBufferInfo(out, &defaultScreenInfo); SetConsoleTextAttribute(out, FOREGROUND_RED|FOREGROUND_INTENSITY ); #endif sputz(stdout, zText); #if !SQLITE_OS_WINRT SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); #endif } #else static void printBold(const char *zText){ sputf(stdout, "\033[1m%s\033[0m", zText); } #endif /* ** Get the argument to an --option. Throw an error and die if no argument ** is available. */ static char *cmdline_option_value(int argc, char **argv, int i){ if( i==argc ){ eputf("%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); exit(1); } return argv[i]; } static void sayAbnormalExit(void){ if( seenInterrupt ) eputz("Program interrupted.\n"); |
︙ | ︙ | |||
11965 11966 11967 11968 11969 11970 11971 | } #endif /* Register a valid signal handler early, before much else is done. */ #ifdef SIGINT signal(SIGINT, interrupt_handler); #elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){ | | | 12259 12260 12261 12262 12263 12264 12265 12266 12267 12268 12269 12270 12271 12272 12273 | } #endif /* Register a valid signal handler early, before much else is done. */ #ifdef SIGINT signal(SIGINT, interrupt_handler); #elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){ eputz("No ^C handler.\n"); } #endif #if USE_SYSTEM_SQLITE+0!=1 if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ eputf("SQLite header and source version mismatch\n%s\n%s\n", sqlite3_sourceid(), SQLITE_SOURCE_ID); |
︙ | ︙ | |||
12057 12058 12059 12060 12061 12062 12063 | || cli_strcmp(z,"-newline")==0 || cli_strcmp(z,"-cmd")==0 ){ (void)cmdline_option_value(argc, argv, ++i); }else if( cli_strcmp(z,"-init")==0 ){ zInitFile = cmdline_option_value(argc, argv, ++i); }else if( cli_strcmp(z,"-interactive")==0 ){ | < < < < | 12351 12352 12353 12354 12355 12356 12357 12358 12359 12360 12361 12362 12363 12364 | || cli_strcmp(z,"-newline")==0 || cli_strcmp(z,"-cmd")==0 ){ (void)cmdline_option_value(argc, argv, ++i); }else if( cli_strcmp(z,"-init")==0 ){ zInitFile = cmdline_option_value(argc, argv, ++i); }else if( cli_strcmp(z,"-interactive")==0 ){ }else if( cli_strcmp(z,"-batch")==0 ){ /* 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 ){ |
︙ | ︙ | |||
12330 12331 12332 12333 12334 12335 12336 | ** prior to sending the SQL into SQLite. Useful for injecting ** crazy bytes in the middle of SQL statements for testing and debugging. */ ShellSetFlag(&data, SHFLG_Backslash); }else if( cli_strcmp(z,"-bail")==0 ){ /* No-op. The bail_on_error flag should already be set. */ }else if( cli_strcmp(z,"-version")==0 ){ | | | | > > > | 12620 12621 12622 12623 12624 12625 12626 12627 12628 12629 12630 12631 12632 12633 12634 12635 12636 12637 12638 12639 12640 12641 | ** prior to sending the SQL into SQLite. Useful for injecting ** crazy bytes in the middle of SQL statements for testing and debugging. */ ShellSetFlag(&data, SHFLG_Backslash); }else if( cli_strcmp(z,"-bail")==0 ){ /* No-op. The bail_on_error flag should already be set. */ }else if( cli_strcmp(z,"-version")==0 ){ sputf(stdout, "%s %s (%d-bit)\n", sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); return 0; }else if( cli_strcmp(z,"-interactive")==0 ){ /* Need to check for interactive override here to so that it can ** affect console setup (for Windows only) and testing thereof. */ 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 ){ |
︙ | ︙ | |||
12457 12458 12459 12460 12461 12462 12463 | } }else{ /* Run commands received from standard input */ if( stdin_is_interactive ){ char *zHome; char *zHistory; | < | | > | < | | | | | 12750 12751 12752 12753 12754 12755 12756 12757 12758 12759 12760 12761 12762 12763 12764 12765 12766 12767 12768 12769 12770 12771 12772 12773 12774 12775 12776 | } }else{ /* Run commands received from standard input */ if( stdin_is_interactive ){ char *zHome; char *zHistory; int nHistory; #if CIO_WIN_WC_XLATE # define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "") #else # define SHELL_CIO_CHAR_SET "" #endif sputf(stdout, "SQLite version %s %.19s%s\n" /*extra-version-info*/ "Enter \".help\" for usage hints.\n", sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET); if( warnInmemoryDb ){ sputz(stdout, "Connected to a "); printBold("transient in-memory database"); sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a" " persistent database.\n"); } zHistory = getenv("SQLITE_HISTORY"); if( zHistory ){ zHistory = strdup(zHistory); }else if( (zHome = find_home_dir(0))!=0 ){ nHistory = strlen30(zHome) + 20; |
︙ | ︙ | |||
12503 12504 12505 12506 12507 12508 12509 12510 12511 12512 12513 12514 12515 12516 | 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++){ | > > > > > | 12795 12796 12797 12798 12799 12800 12801 12802 12803 12804 12805 12806 12807 12808 12809 12810 12811 12812 12813 | 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++){ |
︙ | ︙ | |||
12569 12570 12571 12572 12573 12574 12575 | SQLITE_FCNTL_VFS_POINTER, &pVfs); } return pVfs; } /* Only for emcc experimentation purposes. */ sqlite3 * fiddle_db_arg(sqlite3 *arg){ | | | 12866 12867 12868 12869 12870 12871 12872 12873 12874 12875 12876 12877 12878 12879 12880 | 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 |
︙ | ︙ | |||
12595 12596 12597 12598 12599 12600 12601 | return globalDb ? sqlite3_db_filename(globalDb, zDbName ? zDbName : "main") : NULL; } /* ** Completely wipes out the contents of the currently-opened database | | > > > > > > > > > > | | | 12892 12893 12894 12895 12896 12897 12898 12899 12900 12901 12902 12903 12904 12905 12906 12907 12908 12909 12910 12911 12912 12913 12914 12915 12916 12917 12918 12919 12920 12921 | 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 |
︙ | ︙ | |||
3950 3951 3952 3953 3954 3955 3956 | ** <li> sqlite3_extended_errcode() ** <li> sqlite3_errmsg() ** <li> sqlite3_errmsg16() ** <li> sqlite3_error_offset() ** </ul> ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language | | > | | > | 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 | ** <li> sqlite3_extended_errcode() ** <li> sqlite3_errmsg() ** <li> sqlite3_errmsg16() ** <li> sqlite3_error_offset() ** </ul> ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively, ** or NULL if no error message is available. ** (See how SQLite handles [invalid UTF] for exceptions to this rule.) ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** ** ^The sqlite3_errstr(E) interface returns the English-language text ** that describes the [result code] E, as UTF-8, or NULL if E is not an ** result code for which a text error message is available. ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** ** ^If the most recent error references a specific token in the input ** SQL, the sqlite3_error_offset() interface returns the byte offset ** of the start of that token. ^The byte offset returned by ** sqlite3_error_offset() assumes that the input SQL is UTF8. |
︙ | ︙ | |||
8033 8034 8035 8036 8037 8038 8039 | ** In such cases, the ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() | | | | > > | 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 | ** In such cases, the ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() ** will always return SQLITE_BUSY. In most cases the SQLite core only uses ** sqlite3_mutex_try() as an optimization, so this is acceptable ** behavior. The exceptions are unix builds that set the ** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working ** sqlite3_mutex_try() is required.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), |
︙ | ︙ | |||
8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 | > > | 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_ROWID_IN_VIEW 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 |
︙ | ︙ |
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) \ |
︙ | ︙ | |||
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 { |
︙ | ︙ | |||
2110 2111 2112 2113 2114 2115 2116 | SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define MFUNCTION(zName, nArg, xPtr, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } | | | | 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 | SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define MFUNCTION(zName, nArg, xPtr, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } #define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\ SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\ ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \ SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ {nArg, SQLITE_FUNC_BUILTIN|\ SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } #define TEST_FUNC(zName, nArg, iArg, mFlags) \ {nArg, SQLITE_FUNC_BUILTIN|\ SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ |
︙ | ︙ | |||
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[] */ |
︙ | ︙ | |||
2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 | Expr *pFExpr; /* Expression encoding the function */ FuncDef *pFunc; /* The aggregate function implementation */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ int iDistAddr; /* Address of OP_OpenEphemeral */ int iOBTab; /* Ephemeral table to implement ORDER BY */ u8 bOBPayload; /* iOBTab has payload columns separate from key */ u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ #ifdef SQLITE_DEBUG Select *pSelect; /* SELECT statement that this AggInfo supports */ #endif }; | > | 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 | Expr *pFExpr; /* Expression encoding the function */ FuncDef *pFunc; /* The aggregate function implementation */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ int iDistAddr; /* Address of OP_OpenEphemeral */ int iOBTab; /* Ephemeral table to implement ORDER BY */ u8 bOBPayload; /* iOBTab has payload columns separate from key */ u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */ u8 bUseSubtype; /* Transfer subtype info through sorter */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ #ifdef SQLITE_DEBUG Select *pSelect; /* SELECT statement that this AggInfo supports */ #endif }; |
︙ | ︙ | |||
3245 3246 3247 3248 3249 3250 3251 | ** 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 */ |
︙ | ︙ | |||
3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 | 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; }; | > | 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 | 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; }; |
︙ | ︙ | |||
3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 | Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */ int iBaseReg; /* For TK_REGISTER when parsing RETURNING */ } uNC; NameContext *pNext; /* Next outer name context. NULL for outermost */ int nRef; /* Number of names resolved by this context */ int nNcErr; /* Number of errors encountered while resolving names */ int ncFlags; /* Zero or more NC_* flags defined below */ Select *pWinSelect; /* SELECT statement for any window functions */ }; /* ** Allowed values for the NameContext, ncFlags field. ** ** Value constraints (all checked via assert()): | > | 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 | Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */ int iBaseReg; /* For TK_REGISTER when parsing RETURNING */ } uNC; NameContext *pNext; /* Next outer name context. NULL for outermost */ int nRef; /* Number of names resolved by this context */ int nNcErr; /* Number of errors encountered while resolving names */ int ncFlags; /* Zero or more NC_* flags defined below */ u32 nNestedSelect; /* Number of nested selects using this NC */ Select *pWinSelect; /* SELECT statement for any window functions */ }; /* ** Allowed values for the NameContext, ncFlags field. ** ** Value constraints (all checked via assert()): |
︙ | ︙ | |||
3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 | #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 | > | 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 | #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 |
︙ | ︙ | |||
3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 | 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 | > | 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 | 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 |
︙ | ︙ | |||
3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 | 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 */ | > | 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 | 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 */ |
︙ | ︙ | |||
4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 | ** when the reference count reaches zero. ** ** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than ** sqlite3_free() ** ** 3. Make a (read-only) copy of a read-only RCStr string using ** sqlite3RCStrRef(). */ struct RCStr { u64 nRCRef; /* Number of references */ /* Total structure size should be a multiple of 8 bytes for alignment */ }; /* | > > > | 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 | ** when the reference count reaches zero. ** ** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than ** sqlite3_free() ** ** 3. Make a (read-only) copy of a read-only RCStr string using ** sqlite3RCStrRef(). ** ** "String" is in the name, but an RCStr object can also be used to hold ** binary data. */ struct RCStr { u64 nRCRef; /* Number of references */ /* Total structure size should be a multiple of 8 bytes for alignment */ }; /* |
︙ | ︙ | |||
4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 | u8 bCoreMutex; /* True to enable core mutexing */ u8 bFullMutex; /* True to enable full mutexing */ u8 bOpenUri; /* True to interpret filenames as URIs */ u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ u8 bUseLongDouble; /* Make use of long double */ int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ int nStmtSpill; /* Stmt-journal spill-to-disk threshold */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ sqlite3_mutex_methods mutex; /* Low-level mutex interface */ | > > > | 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 | u8 bCoreMutex; /* True to enable core mutexing */ u8 bFullMutex; /* True to enable full mutexing */ u8 bOpenUri; /* True to interpret filenames as URIs */ u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ u8 bUseLongDouble; /* Make use of long double */ #ifdef SQLITE_DEBUG u8 bJsonSelfcheck; /* Double-check JSON parsing */ #endif int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ int nStmtSpill; /* Stmt-journal spill-to-disk threshold */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ sqlite3_mutex_methods mutex; /* Low-level mutex interface */ |
︙ | ︙ | |||
4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 | #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 | > > > > > | 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 | #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 |
︙ | ︙ | |||
4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 | int iArgCol; /* Offset of first argument for this function */ 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*); | > > > | 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 | int iArgCol; /* Offset of first argument for this function */ 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*); |
︙ | ︙ | |||
4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 | 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); | > | 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 | 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); |
︙ | ︙ | |||
4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 | Expr *sqlite3ExprSimplifiedAndOr(Expr*); 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 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*); void sqlite3ExprListDelete(sqlite3*, ExprList*); u32 sqlite3ExprListFlags(const ExprList*); int sqlite3IndexHasDuplicateRootPage(Index*); int sqlite3Init(sqlite3*, char**); int sqlite3InitCallback(void*, int, char**, char**); int sqlite3InitOne(sqlite3*, int, char**, u32); void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); #ifndef SQLITE_OMIT_VIRTUALTABLE | > > | 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 | Expr *sqlite3ExprSimplifiedAndOr(Expr*); 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*); void 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*); void sqlite3ExprListDelete(sqlite3*, ExprList*); void sqlite3ExprListDeleteGeneric(sqlite3*,void*); u32 sqlite3ExprListFlags(const ExprList*); int sqlite3IndexHasDuplicateRootPage(Index*); int sqlite3Init(sqlite3*, char**); int sqlite3InitCallback(void*, int, char**, char**); int sqlite3InitOne(sqlite3*, int, char**, u32); void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); #ifndef SQLITE_OMIT_VIRTUALTABLE |
︙ | ︙ | |||
4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 | #if SQLITE_MAX_ATTACHED>30 int sqlite3DbMaskAllZero(yDbMask); #endif void sqlite3DropTable(Parse*, SrcList*, int, int); void sqlite3CodeDropTable(Parse*, Table*, int, int); void sqlite3DeleteTable(sqlite3*, Table*); void sqlite3FreeIndex(sqlite3*, Index*); #ifndef SQLITE_OMIT_AUTOINCREMENT void sqlite3AutoincrementBegin(Parse *pParse); void sqlite3AutoincrementEnd(Parse *pParse); #else # define sqlite3AutoincrementBegin(X) # define sqlite3AutoincrementEnd(X) | > | 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 | #if SQLITE_MAX_ATTACHED>30 int sqlite3DbMaskAllZero(yDbMask); #endif void sqlite3DropTable(Parse*, SrcList*, int, int); void sqlite3CodeDropTable(Parse*, Table*, int, int); void sqlite3DeleteTable(sqlite3*, Table*); void sqlite3DeleteTableGeneric(sqlite3*, void*); void sqlite3FreeIndex(sqlite3*, Index*); #ifndef SQLITE_OMIT_AUTOINCREMENT void sqlite3AutoincrementBegin(Parse *pParse); void sqlite3AutoincrementEnd(Parse *pParse); #else # define sqlite3AutoincrementBegin(X) # define sqlite3AutoincrementEnd(X) |
︙ | ︙ | |||
4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 | void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, Expr*, int, int, u8); void sqlite3DropIndex(Parse*, SrcList*, int); int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,u32,Expr*); void sqlite3SelectDelete(sqlite3*, Select*); Table *sqlite3SrcListLookup(Parse*, SrcList*); int sqlite3IsReadOnly(Parse*, Table*, Trigger*); void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*); #endif void sqlite3CodeChangeCount(Vdbe*,int,const char*); | > | 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 | void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, Expr*, int, int, u8); void sqlite3DropIndex(Parse*, SrcList*, int); int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,u32,Expr*); void sqlite3SelectDelete(sqlite3*, Select*); void sqlite3SelectDeleteGeneric(sqlite3*,void*); Table *sqlite3SrcListLookup(Parse*, SrcList*); int sqlite3IsReadOnly(Parse*, Table*, Trigger*); void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*); #endif void sqlite3CodeChangeCount(Vdbe*,int,const char*); |
︙ | ︙ | |||
5015 5016 5017 5018 5019 5020 5021 | 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*); | | < | 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 | 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 sqlite3ExprIsTableConstant(Expr*,int); int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS int sqlite3ExprContainsSubquery(Expr*); #endif |
︙ | ︙ | |||
5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 | int sqlite3GetUInt32(const char*, u32*); int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 int sqlite3Utf16ByteLen(const void *pData, int nChar); #endif int sqlite3Utf8CharLen(const char *pData, int nByte); u32 sqlite3Utf8Read(const u8**); LogEst sqlite3LogEst(u64); LogEst sqlite3LogEstAdd(LogEst,LogEst); LogEst sqlite3LogEstFromDouble(double); u64 sqlite3LogEstToInt(LogEst); VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int); const char *sqlite3VListNumToName(VList*,int); int sqlite3VListNameToNum(VList*,const char*,int); | > | 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 | int sqlite3GetUInt32(const char*, u32*); int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 int sqlite3Utf16ByteLen(const void *pData, int nChar); #endif int sqlite3Utf8CharLen(const char *pData, int nByte); u32 sqlite3Utf8Read(const u8**); int sqlite3Utf8ReadLimited(const u8*, int, u32*); LogEst sqlite3LogEst(u64); LogEst sqlite3LogEstAdd(LogEst,LogEst); LogEst sqlite3LogEstFromDouble(double); u64 sqlite3LogEstToInt(LogEst); VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int); const char *sqlite3VListNumToName(VList*,int); int sqlite3VListNameToNum(VList*,const char*,int); |
︙ | ︙ | |||
5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 | int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); #endif #ifndef SQLITE_OMIT_CTE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); void sqlite3CteDelete(sqlite3*,Cte*); With *sqlite3WithAdd(Parse*,With*,Cte*); void sqlite3WithDelete(sqlite3*,With*); With *sqlite3WithPush(Parse*, With*, u8); #else # define sqlite3CteNew(P,T,E,S) ((void*)0) # define sqlite3CteDelete(D,C) # define sqlite3CteWithAdd(P,W,C) ((void*)0) # 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*); | > | | 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 | int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); #endif #ifndef SQLITE_OMIT_CTE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); void sqlite3CteDelete(sqlite3*,Cte*); With *sqlite3WithAdd(Parse*,With*,Cte*); void sqlite3WithDelete(sqlite3*,With*); void sqlite3WithDeleteGeneric(sqlite3*,void*); With *sqlite3WithPush(Parse*, With*, u8); #else # define sqlite3CteNew(P,T,E,S) ((void*)0) # define sqlite3CteDelete(D,C) # define sqlite3CteWithAdd(P,W,C) ((void*)0) # 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/status.c.
︙ | ︙ | |||
358 359 360 361 362 363 364 | case SQLITE_DBSTATUS_CACHE_SPILL: op = SQLITE_DBSTATUS_CACHE_WRITE+1; /* no break */ deliberate_fall_through case SQLITE_DBSTATUS_CACHE_HIT: case SQLITE_DBSTATUS_CACHE_MISS: case SQLITE_DBSTATUS_CACHE_WRITE:{ int i; | | | | 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 | case SQLITE_DBSTATUS_CACHE_SPILL: op = SQLITE_DBSTATUS_CACHE_WRITE+1; /* no break */ deliberate_fall_through case SQLITE_DBSTATUS_CACHE_HIT: case SQLITE_DBSTATUS_CACHE_MISS: case SQLITE_DBSTATUS_CACHE_WRITE:{ int i; u64 nRet = 0; assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 ); for(i=0; i<db->nDb; i++){ if( db->aDb[i].pBt ){ Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt); sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet); } } *pHighwater = 0; /* IMP: R-42420-56072 */ /* IMP: R-54100-20147 */ /* IMP: R-29431-39229 */ *pCurrent = (int)nRet & 0x7fffffff; break; } /* Set *pCurrent to non-zero if there are unresolved deferred foreign ** key constraints. Set *pCurrent to zero if all foreign key constraints ** have been satisfied. The *pHighwater is always set to zero. */ |
︙ | ︙ |
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 |
︙ | ︙ | |||
8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 | extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*); #ifndef SQLITE_OMIT_VIRTUALTABLE extern int sqlite3_prefixes_init(sqlite3*,char**,const sqlite3_api_routines*); #endif extern int sqlite3_qpvtab_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_remember_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_unionvtab_init(sqlite3*,char**,const sqlite3_api_routines*); | > | 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 | extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*); #ifndef SQLITE_OMIT_VIRTUALTABLE extern int sqlite3_prefixes_init(sqlite3*,char**,const sqlite3_api_routines*); #endif extern int sqlite3_qpvtab_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_randomjson_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_remember_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_unionvtab_init(sqlite3*,char**,const sqlite3_api_routines*); |
︙ | ︙ | |||
8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 | { "ieee754", sqlite3_ieee_init }, { "nextchar", sqlite3_nextchar_init }, { "percentile", sqlite3_percentile_init }, #ifndef SQLITE_OMIT_VIRTUALTABLE { "prefixes", sqlite3_prefixes_init }, #endif { "qpvtab", sqlite3_qpvtab_init }, { "regexp", sqlite3_regexp_init }, { "remember", sqlite3_remember_init }, { "series", sqlite3_series_init }, { "spellfix", sqlite3_spellfix_init }, { "totype", sqlite3_totype_init }, { "unionvtab", sqlite3_unionvtab_init }, { "wholenumber", sqlite3_wholenumber_init }, | > | 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 | { "ieee754", sqlite3_ieee_init }, { "nextchar", sqlite3_nextchar_init }, { "percentile", sqlite3_percentile_init }, #ifndef SQLITE_OMIT_VIRTUALTABLE { "prefixes", sqlite3_prefixes_init }, #endif { "qpvtab", sqlite3_qpvtab_init }, { "randomjson", sqlite3_randomjson_init }, { "regexp", sqlite3_regexp_init }, { "remember", sqlite3_remember_init }, { "series", sqlite3_series_init }, { "spellfix", sqlite3_spellfix_init }, { "totype", sqlite3_totype_init }, { "unionvtab", sqlite3_unionvtab_init }, { "wholenumber", sqlite3_wholenumber_init }, |
︙ | ︙ |
Changes to src/test_func.c.
︙ | ︙ | |||
690 691 692 693 694 695 696 | { "test_isolation", 2, SQLITE_UTF8, test_isolation}, { "test_counter", 1, SQLITE_UTF8, counterFunc}, { "real2hex", 1, SQLITE_UTF8, real2hex}, { "test_decode", 1, SQLITE_UTF8, test_decode}, { "test_extract", 2, SQLITE_UTF8, test_extract}, { "test_zeroblob", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, test_zeroblob}, { "test_getsubtype", 1, SQLITE_UTF8, test_getsubtype}, | | > | 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | { "test_isolation", 2, SQLITE_UTF8, test_isolation}, { "test_counter", 1, SQLITE_UTF8, counterFunc}, { "real2hex", 1, SQLITE_UTF8, real2hex}, { "test_decode", 1, SQLITE_UTF8, test_decode}, { "test_extract", 2, SQLITE_UTF8, test_extract}, { "test_zeroblob", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, test_zeroblob}, { "test_getsubtype", 1, SQLITE_UTF8, test_getsubtype}, { "test_setsubtype", 2, SQLITE_UTF8|SQLITE_RESULT_SUBTYPE, test_setsubtype}, { "test_frombind", -1, SQLITE_UTF8, test_frombind}, }; int i; for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){ sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg, aFuncs[i].eTextRep, 0, aFuncs[i].xFunc, 0, 0); |
︙ | ︙ |
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.
︙ | ︙ | |||
777 778 779 780 781 782 783 | case TK_BETWEEN: { const Expr *pX, *pY, *pZ; pX = pExpr->pLeft; assert( ExprUseXList(pExpr) ); assert( pExpr->x.pList->nExpr==2 ); pY = pExpr->x.pList->a[0].pExpr; pZ = pExpr->x.pList->a[1].pExpr; | | | 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 | case TK_BETWEEN: { const Expr *pX, *pY, *pZ; pX = pExpr->pLeft; assert( ExprUseXList(pExpr) ); assert( pExpr->x.pList->nExpr==2 ); pY = pExpr->x.pList->a[0].pExpr; pZ = pExpr->x.pList->a[1].pExpr; sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs); sqlite3TreeViewExpr(pView, pX, 1); sqlite3TreeViewExpr(pView, pY, 1); sqlite3TreeViewExpr(pView, pZ, 0); break; } case TK_TRIGGER: { /* If the opcode is TK_TRIGGER, then the expression is a reference |
︙ | ︙ |
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/utf.c.
︙ | ︙ | |||
160 161 162 163 164 165 166 | if( c<0x80 || (c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } } return c; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | 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 | if( c<0x80 || (c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } } return c; } /* ** Read a single UTF8 character out of buffer z[], but reading no ** more than n characters from the buffer. z[] is not zero-terminated. ** ** Return the number of bytes used to construct the character. ** ** Invalid UTF8 might generate a strange result. No effort is made ** to detect invalid UTF8. ** ** At most 4 bytes will be read out of z[]. The return value will always ** be between 1 and 4. */ int sqlite3Utf8ReadLimited( const u8 *z, int n, u32 *piOut ){ u32 c; int i = 1; assert( n>0 ); c = z[0]; if( c>=0xc0 ){ c = sqlite3Utf8Trans1[c-0xc0]; if( n>4 ) n = 4; while( i<n && (z[i] & 0xc0)==0x80 ){ c = (c<<6) + (0x3f & z[i]); i++; } } *piOut = c; return i; } /* ** If the TRANSLATE_TRACE macro is defined, the value of each Mem is ** printed on stderr on the way into and out of sqlite3VdbeMemTranslate(). */ /* #define TRANSLATE_TRACE 1 */ |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
137 138 139 140 141 142 143 | /* ** Load the sqlite3.iSysErrno field if that is an appropriate thing ** to do based on the SQLite error code in rc. */ void sqlite3SystemError(sqlite3 *db, int rc){ if( rc==SQLITE_IOERR_NOMEM ) return; | | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /* ** Load the sqlite3.iSysErrno field if that is an appropriate thing ** to do based on the SQLite error code in rc. */ void sqlite3SystemError(sqlite3 *db, int rc){ if( rc==SQLITE_IOERR_NOMEM ) return; #if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) if( rc==SQLITE_IOERR_IN_PAGE ){ int ii; int iErr; sqlite3BtreeEnterAll(db); for(ii=0; ii<db->nDb; ii++){ if( db->aDb[ii].pBt ){ iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt)); |
︙ | ︙ | |||
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 "") | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } 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 ){ | > > > | 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | *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; | | | 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | 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.
︙ | ︙ | |||
128 129 130 131 132 133 134 | ** Other useful labels for breakpoints include: ** test_addop_breakpoint(pc,pOp) ** sqlite3CorruptError(lineno) ** sqlite3MisuseError(lineno) ** sqlite3CantopenError(lineno) */ static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ | | > | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | ** Other useful labels for breakpoints include: ** test_addop_breakpoint(pc,pOp) ** sqlite3CorruptError(lineno) ** sqlite3MisuseError(lineno) ** sqlite3CantopenError(lineno) */ static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ static u64 n = 0; (void)pc; (void)pOp; (void)v; n++; if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */ } #endif /* ** Invoke the VDBE coverage callback, if that callback is defined. This ** feature is used for test suite validation only and does not appear an ** production builds. |
︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 | ** ** 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; |
︙ | ︙ | |||
1177 1178 1179 1180 1181 1182 1183 | ** 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 */ | | | 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 | ** 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); |
︙ | ︙ | |||
1507 1508 1509 1510 1511 1512 1513 | sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); } pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } | | | < < < < | 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 | 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); |
︙ | ︙ | |||
2029 2030 2031 2032 2033 2034 2035 | ** ** To force any register to be an integer, just add 0. */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); sqlite3VdbeMemIntegerify(pIn1); | | | | 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 | ** ** To force any register to be an integer, just add 0. */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); sqlite3VdbeMemIntegerify(pIn1); *(u64*)&pIn1->u.i += (u64)pOp->p2; break; } /* 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; |
︙ | ︙ | |||
2081 2082 2083 2084 2085 2086 2087 | sqlite3VdbeMemRealify(pIn1); REGISTER_TRACE(pOp->p1, pIn1); } break; } #endif | | | 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 | 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 |
︙ | ︙ | |||
2296 2297 2298 2299 2300 2301 2302 | 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 ){ | > > | > > | | 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 2320 | 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); } |
︙ | ︙ | |||
3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 | 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 ){ | > > > > > | 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 | 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 ){ |
︙ | ︙ | |||
4715 4716 4717 4718 4719 4720 4721 | ** 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 */ | | | | | | 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 | ** 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 */ |
︙ | ︙ | |||
5385 5386 5387 5388 5389 5390 5391 | ** ** 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 */ | | | 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 | ** ** 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 ); |
︙ | ︙ | |||
6144 6145 6146 6147 6148 6149 6150 | ** 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 */ | | | 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 | ** 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 ); |
︙ | ︙ | |||
6178 6179 6180 6181 6182 6183 6184 | if( pOp->p2>0 ){ VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; } break; } | | | | > > | > > > | > > > | > | 6184 6185 6186 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 | 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. Thus X will be within the range of -1 to 640, inclusive ** 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 ); assert( pOp->p4.i>=-1 && pOp->p4.i<=640 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); if( rc ) goto abort_due_to_error; if( res!=0 ){ sz = -1; /* -Infinity encoding */ }else{ 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 * * * |
︙ | ︙ | |||
6252 6253 6254 6255 6256 6257 6258 | ** 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. */ | | | 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 | ** 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 ); |
︙ | ︙ | |||
6899 6900 6901 6902 6903 6904 6905 | assert( pDb->pBt!=0 ); rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, pOp->p3); if( rc ) goto abort_due_to_error; pOut->u.i = pgno; break; } | | > > > | > | > > > > > | > > > > | 6914 6915 6916 6917 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 | 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; |
︙ | ︙ | |||
7082 7083 7084 7085 7086 7087 7088 | } #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* Opcode: IntegrityCk P1 P2 P3 P4 P5 ** ** Do an analysis of the currently open database. Store in | | | | | > > | | | | | | 7110 7111 7112 7113 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 | } #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{ |
︙ | ︙ | |||
7245 7246 7247 7248 7249 7250 7251 | /* 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 | | > > | | 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 | /* 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 */ |
︙ | ︙ | |||
8175 8176 8177 8178 8179 8180 8181 | Table *pTab; sqlite3_vtab *pVtab; const sqlite3_module *pModule; char *zErr = 0; pOut = &aMem[pOp->p2]; sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */ | | > < < | 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 | Table *pTab; sqlite3_vtab *pVtab; const sqlite3_module *pModule; char *zErr = 0; pOut = &aMem[pOp->p2]; sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */ assert( pOp->p4type==P4_TABLEREF ); pTab = pOp->p4.pTab; assert( pTab!=0 ); assert( pTab->nTabRef>0 ); assert( IsVirtual(pTab) ); if( pTab->u.vtab.p==0 ) break; pVtab = pTab->u.vtab.p->pVtab; assert( pVtab!=0 ); pModule = pVtab->pModule; assert( pModule!=0 ); assert( pModule->iVersion>=4 ); assert( pModule->xIntegrity!=0 ); sqlite3VtabLock(pTab->u.vtab.p); assert( pOp->p1>=0 && pOp->p1<db->nDb ); rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName, pOp->p3, &zErr); sqlite3VtabUnlock(pTab->u.vtab.p); if( rc ){ sqlite3_free(zErr); goto abort_due_to_error; } if( zErr ){ sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free); } |
︙ | ︙ | |||
8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 | ** unused by OP_VColumn. */ case OP_VColumn: { /* ncycle */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur!=0 ); assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); if( pCur->nullRow ){ sqlite3VdbeMemSetNull(pDest); break; } assert( pCur->eCurType==CURTYPE_VTAB ); pVtab = pCur->uc.pVCur->pVtab; pModule = pVtab->pModule; assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; sContext.enc = encoding; assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); if( pOp->p5 & OPFLAG_NOCHNG ){ sqlite3VdbeMemSetNull(pDest); pDest->flags = MEM_Null|MEM_Zero; pDest->u.nZero = 0; }else{ MemSetTypeFlag(pDest, MEM_Null); | > > > > | 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 | ** unused by OP_VColumn. */ case OP_VColumn: { /* ncycle */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; FuncDef nullFunc; VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur!=0 ); assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); if( pCur->nullRow ){ sqlite3VdbeMemSetNull(pDest); break; } assert( pCur->eCurType==CURTYPE_VTAB ); pVtab = pCur->uc.pVCur->pVtab; pModule = pVtab->pModule; assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; sContext.enc = encoding; nullFunc.pUserData = 0; nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE; sContext.pFunc = &nullFunc; assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); if( pOp->p5 & OPFLAG_NOCHNG ){ sqlite3VdbeMemSetNull(pDest); pDest->flags = MEM_Null|MEM_Zero; pDest->u.nZero = 0; }else{ MemSetTypeFlag(pDest, MEM_Null); |
︙ | ︙ | |||
8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 | ** Clear the subtype from register P1. */ case OP_ClrSubtype: { /* in1 */ pIn1 = &aMem[pOp->p1]; pIn1->flags &= ~MEM_Subtype; break; } /* Opcode: FilterAdd P1 * P3 P4 * ** Synopsis: filter(P1) += key(P3@P4) ** ** Compute a hash on the P4 registers starting with r[P3] and ** add that hash to the bloom filter contained in r[P1]. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 8700 8701 8702 8703 8704 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 | ** Clear the subtype from register P1. */ case OP_ClrSubtype: { /* in1 */ pIn1 = &aMem[pOp->p1]; pIn1->flags &= ~MEM_Subtype; break; } /* Opcode: GetSubtype P1 P2 * * * ** Synopsis: r[P2] = r[P1].subtype ** ** Extract the subtype value from register P1 and write that subtype ** into register P2. If P1 has no subtype, then P1 gets a NULL. */ case OP_GetSubtype: { /* in1 out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; if( pIn1->flags & MEM_Subtype ){ sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype); }else{ sqlite3VdbeMemSetNull(pOut); } break; } /* Opcode: SetSubtype P1 P2 * * * ** Synopsis: r[P2].subtype = r[P1] ** ** Set the subtype value of register P2 to the integer from register P1. ** If P1 is NULL, clear the subtype from p2. */ case OP_SetSubtype: { /* in1 out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; if( pIn1->flags & MEM_Null ){ pOut->flags &= ~MEM_Subtype; }else{ assert( pIn1->flags & MEM_Int ); pOut->flags |= MEM_Subtype; pOut->eSubtype = (u8)(pIn1->u.i & 0xff); } break; } /* Opcode: FilterAdd P1 * P3 P4 * ** Synopsis: filter(P1) += key(P3@P4) ** ** Compute a hash on the P4 registers starting with r[P3] and ** add that hash to the bloom filter contained in r[P1]. */ |
︙ | ︙ | |||
8763 8764 8765 8766 8767 8768 8769 | ** 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: | | | 8834 8835 8836 8837 8838 8839 8840 8841 8842 8843 8844 8845 8846 8847 8848 | ** 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.
︙ | ︙ | |||
122 123 124 125 126 127 128 129 130 131 132 133 134 135 | #define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ #define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ #define P4_VTAB (-11) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 #define P5_ConstraintUnique 2 #define P5_ConstraintCheck 3 #define P5_ConstraintFK 4 | > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | #define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ #define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ #define P4_VTAB (-11) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ #define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 #define P5_ConstraintUnique 2 #define P5_ConstraintCheck 3 #define P5_ConstraintFK 4 |
︙ | ︙ | |||
290 291 292 293 294 295 296 297 298 299 300 301 302 303 | 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.
︙ | ︙ | |||
148 149 150 151 152 153 154 | ** Set all the parameters in the compiled SQL statement to NULL. */ int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ int i; int rc = SQLITE_OK; Vdbe *p = (Vdbe*)pStmt; #if SQLITE_THREADSAFE | | > > > > > > > > | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | ** Set all the parameters in the compiled SQL statement to NULL. */ int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ int i; int rc = SQLITE_OK; Vdbe *p = (Vdbe*)pStmt; #if SQLITE_THREADSAFE sqlite3_mutex *mutex; #endif #ifdef SQLITE_ENABLE_API_ARMOR if( pStmt==0 ){ return SQLITE_MISUSE_BKPT; } #endif #if SQLITE_THREADSAFE mutex = p->db->mutex; #endif sqlite3_mutex_enter(mutex); for(i=0; i<p->nVar; i++){ sqlite3VdbeMemRelease(&p->aVar[i]); p->aVar[i].flags = MEM_Null; } assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 ); |
︙ | ︙ | |||
938 939 940 941 942 943 944 | /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ void *sqlite3_user_data(sqlite3_context *p){ #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ) return 0; | | < | 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 | /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ void *sqlite3_user_data(sqlite3_context *p){ #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ) return 0; #endif assert( p && p->pFunc ); return p->pFunc->pUserData; } /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. ** |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
207 208 209 210 211 212 213 | ** Other useful labels for breakpoints include: ** test_trace_breakpoint(pc,pOp) ** sqlite3CorruptError(lineno) ** sqlite3MisuseError(lineno) ** sqlite3CantopenError(lineno) */ static void test_addop_breakpoint(int pc, Op *pOp){ | | > | 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | ** Other useful labels for breakpoints include: ** test_trace_breakpoint(pc,pOp) ** sqlite3CorruptError(lineno) ** sqlite3MisuseError(lineno) ** sqlite3CantopenError(lineno) */ static void test_addop_breakpoint(int pc, Op *pOp){ static u64 n = 0; (void)pc; (void)pOp; n++; if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */ } #endif /* ** Slow paths for sqlite3VdbeAddOp3() and sqlite3VdbeAddOp4Int() for the ** unusual case when we need to increase the size of the Vdbe.aOp[] array ** before adding the new opcode. |
︙ | ︙ | |||
934 935 936 937 938 939 940 941 942 943 944 945 946 947 | ** 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); |
︙ | ︙ | |||
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 | } break; } case P4_VTAB : { if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4); break; } } } /* ** Free the space allocated for aOp and any p4 values allocated for the ** opcodes contained within. If aOp is not NULL it is assumed to contain ** nOp entries. | > > > > | 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 | } break; } case P4_VTAB : { if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4); break; } case P4_TABLEREF: { if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4); break; } } } /* ** Free the space allocated for aOp and any p4 values allocated for the ** opcodes contained within. If aOp is not NULL it is assumed to contain ** nOp entries. |
︙ | ︙ | |||
1522 1523 1524 1525 1526 1527 1528 | static void SQLITE_NOINLINE vdbeChangeP4Full( Vdbe *p, Op *pOp, const char *zP4, int n ){ if( pOp->p4type ){ | | | 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 | static void SQLITE_NOINLINE vdbeChangeP4Full( Vdbe *p, Op *pOp, const char *zP4, int n ){ if( pOp->p4type ){ assert( pOp->p4type > P4_FREE_IF_LE ); pOp->p4type = 0; pOp->p4.p = 0; } if( n<0 ){ sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n); }else{ if( n==0 ) n = sqlite3Strlen30(zP4); |
︙ | ︙ | |||
4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 | 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 ){ |
︙ | ︙ | |||
4490 4491 4492 4493 4494 4495 4496 | 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 |
︙ | ︙ | |||
4730 4731 4732 4733 4734 4735 4736 | 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 ){ |
︙ | ︙ | |||
4755 4756 4757 4758 4759 4760 4761 | ** 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 ){ |
︙ | ︙ | |||
4832 4833 4834 4835 4836 4837 4838 | } } } /* 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 ); |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
311 312 313 314 315 316 317 | VTable *p = db->pDisconnect; assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3_mutex_held(db->mutex) ); if( p ){ db->pDisconnect = 0; | < | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | VTable *p = db->pDisconnect; assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3_mutex_held(db->mutex) ); if( p ){ db->pDisconnect = 0; do { VTable *pNext = p->pNext; sqlite3VtabUnlock(p); p = pNext; }while( p ); } } |
︙ | ︙ | |||
608 609 610 611 612 613 614 615 616 617 618 619 620 621 | 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 ){ |
︙ | ︙ | |||
630 631 632 633 634 635 636 | ** 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 |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 | p = 0; } *pp = p; return rc; } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT /* ** Attempt to enable blocking locks. Blocking locks are enabled only if (a) ** they are supported by the VFS, and (b) the database handle is configured ** with a busy-timeout. Return 1 if blocking locks are successfully enabled, ** or 0 otherwise. */ static int walEnableBlocking(Wal *pWal){ int res = 0; if( pWal->db ){ int tmout = pWal->db->busyTimeout; if( tmout ){ | > > > > > > > > > > > > > < < < < | | 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 | p = 0; } *pp = p; return rc; } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT /* ** Attempt to enable blocking locks that block for nMs ms. Return 1 if ** blocking locks are successfully enabled, or 0 otherwise. */ static int walEnableBlockingMs(Wal *pWal, int nMs){ int rc = sqlite3OsFileControl( pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs ); return (rc==SQLITE_OK); } /* ** Attempt to enable blocking locks. Blocking locks are enabled only if (a) ** they are supported by the VFS, and (b) the database handle is configured ** with a busy-timeout. Return 1 if blocking locks are successfully enabled, ** or 0 otherwise. */ static int walEnableBlocking(Wal *pWal){ int res = 0; if( pWal->db ){ int tmout = pWal->db->busyTimeout; if( tmout ){ res = walEnableBlockingMs(pWal, tmout); } } return res; } /* ** Disable blocking locks. |
︙ | ︙ | |||
2064 2065 2066 2067 2068 2069 2070 | /* ** Set the database handle used to determine if blocking locks are required. */ void sqlite3WalDb(Wal *pWal, sqlite3 *db){ pWal->db = db; } | < < < < < < < < < < | | 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 | /* ** Set the database handle used to determine if blocking locks are required. */ void sqlite3WalDb(Wal *pWal, sqlite3 *db){ pWal->db = db; } #else # define walEnableBlocking(x) 0 # define walDisableBlocking(x) # define walEnableBlockingMs(pWal, ms) 0 # define sqlite3WalDb(pWal, db) #endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ /* ** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and ** n. If the attempt fails and parameter xBusy is not NULL, then it is a |
︙ | ︙ | |||
2678 2679 2680 2681 2682 2683 2684 | if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){ if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; } }else{ int bWriteLock = pWal->writeLock; | | > > | > | 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 | if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){ if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; } }else{ int bWriteLock = pWal->writeLock; if( bWriteLock || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ pWal->writeLock = 1; if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ badHdr = walIndexTryHdr(pWal, pChanged); if( badHdr ){ /* If the wal-index header is still malformed even while holding ** a WRITE lock, it can only mean that the header is corrupted and ** needs to be reconstructed. So run recovery to do exactly that. ** Disable blocking locks first. */ walDisableBlocking(pWal); rc = walIndexRecover(pWal); *pChanged = 1; } } if( bWriteLock==0 ){ pWal->writeLock = 0; walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); |
︙ | ︙ | |||
2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 | 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 |
︙ | ︙ | |||
2946 2947 2948 2949 2950 2951 2952 | ** 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 2994 2995 2996 2997 2998 2999 3000 3001 3002 | ** 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 int nBlockTmout = 0; #endif assert( pWal->readLock<0 ); /* Not currently locked */ /* useWal may only be set for read/write connections */ assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 ); /* Take steps to avoid spinning forever if there is a protocol error. |
︙ | ︙ | |||
2976 2977 2978 2979 2980 2981 2982 | ** 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 3067 | ** 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 ** WAL_RETRY this routine will be called again and will probably be |
︙ | ︙ | |||
3105 3106 3107 3108 3109 3110 3111 3112 3113 | } } if( mxI==0 ){ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; } rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); if( rc ){ | > > > > > > > > > > | | 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 | } } if( mxI==0 ){ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); 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. ** ** It is necessary to check that the wal-index header did not change ** between the time it was read and when the shared-lock was obtained |
︙ | ︙ | |||
3295 3296 3297 3298 3299 3300 3301 | 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 |
︙ | ︙ | |||
3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 | 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; } |
︙ | ︙ | |||
3779 3780 3781 3782 3783 3784 3785 | } } 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; |
︙ | ︙ | |||
4200 4201 4202 4203 4204 4205 4206 | /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); if( pWal->readOnly ) return SQLITE_READONLY; WALTRACE(("WAL%p: checkpoint begins\n", pWal)); | | < | | 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 | /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); if( pWal->readOnly ) return SQLITE_READONLY; WALTRACE(("WAL%p: checkpoint begins\n", pWal)); /* Enable blocking locks, if possible. */ sqlite3WalDb(pWal, db); if( xBusy2 ) (void)walEnableBlocking(pWal); /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive ** "checkpoint" lock on the database file. ** EVIDENCE-OF: R-10421-19736 If any other process is running a ** checkpoint operation at the same time, the lock cannot be obtained and ** SQLITE_BUSY is returned. ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, |
︙ | ︙ | |||
4244 4245 4246 4247 4248 4249 4250 4251 4252 | } } /* Read the wal-index header. */ SEH_TRY { if( rc==SQLITE_OK ){ walDisableBlocking(pWal); rc = walIndexReadHdr(pWal, &isChanged); | > > > > > | | 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 | } } /* Read the wal-index header. */ SEH_TRY { if( rc==SQLITE_OK ){ /* For a passive checkpoint, do not re-enable blocking locks after ** reading the wal-index header. A passive checkpoint should not block ** or invoke the busy handler. The only lock such a checkpoint may ** attempt to obtain is a lock on a read-slot, and it should give up ** immediately and do a partial checkpoint if it cannot obtain it. */ walDisableBlocking(pWal); rc = walIndexReadHdr(pWal, &isChanged); if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal); if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ sqlite3OsUnfetch(pWal->pDbFd, 0, 0); } } /* Copy data from the log to the database file. */ if( rc==SQLITE_OK ){ |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 | Vdbe *v = pParse->pVdbe; VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart); int iEnd = sqlite3VdbeCurrentAddr(v); if( pParse->db->mallocFailed ) return; for(; iStart<iEnd; iStart++, pOp++){ if( pOp->p1!=iTabCur ) continue; if( pOp->opcode==OP_Column ){ pOp->opcode = OP_Copy; pOp->p1 = pOp->p2 + iRegister; pOp->p2 = pOp->p3; pOp->p3 = 0; pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */ }else if( pOp->opcode==OP_Rowid ){ pOp->opcode = OP_Sequence; pOp->p1 = iAutoidxCur; #ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( iAutoidxCur==0 ){ pOp->opcode = OP_Null; pOp->p3 = 0; } | > > > > > > > > > > | 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 | Vdbe *v = pParse->pVdbe; VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart); int iEnd = sqlite3VdbeCurrentAddr(v); if( pParse->db->mallocFailed ) return; for(; iStart<iEnd; iStart++, pOp++){ if( pOp->p1!=iTabCur ) continue; if( pOp->opcode==OP_Column ){ #ifdef SQLITE_DEBUG if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart); } #endif pOp->opcode = OP_Copy; pOp->p1 = pOp->p2 + iRegister; pOp->p2 = pOp->p3; pOp->p3 = 0; pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */ }else if( pOp->opcode==OP_Rowid ){ #ifdef SQLITE_DEBUG if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart); } #endif pOp->opcode = OP_Sequence; pOp->p1 = iAutoidxCur; #ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( iAutoidxCur==0 ){ pOp->opcode = OP_Null; pOp->p3 = 0; } |
︙ | ︙ | |||
1315 1316 1317 1318 1319 1320 1321 | 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 */ | | | 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 | 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 */ |
︙ | ︙ | |||
1427 1428 1429 1430 1431 1432 1433 | j++; } assert( j==nTerm ); pIdxInfo->nConstraint = j; for(i=j=0; i<nOrderBy; i++){ Expr *pExpr = pOrderBy->a[i].pExpr; | | | 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 | 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++; } |
︙ | ︙ | |||
2006 2007 2008 2009 2010 2011 2012 | if( rc==SQLITE_OK ){ if( iUpper>iLower ){ nNew = sqlite3LogEst(iUpper - iLower); /* TUNING: If both iUpper and iLower are derived from the same ** sample, then assume they are 4x more selective. This brings ** the estimated selectivity more in line with what it would be ** if estimated without the use of STAT4 tables. */ | | > | 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 | if( rc==SQLITE_OK ){ if( iUpper>iLower ){ nNew = sqlite3LogEst(iUpper - iLower); /* TUNING: If both iUpper and iLower are derived from the same ** sample, then assume they are 4x more selective. This brings ** the estimated selectivity more in line with what it would be ** if estimated without the use of STAT4 tables. */ if( iLwrIdx==iUprIdx ){ nNew -= 20; } assert( 20==sqlite3LogEst(4) ); }else{ nNew = 10; assert( 10==sqlite3LogEst(2) ); } if( nNew<nOut ){ nOut = nNew; } WHERETRACE(0x20, ("STAT4 range scan: %u..%u est=%d\n", |
︙ | ︙ | |||
2230 2231 2232 2233 2234 2235 2236 2237 | } } #endif #ifdef WHERETRACE_ENABLED /* ** Print a WhereLoop object for debugging purposes */ | > > > > > > > > > > > > | > | | | | | | | | | > > > > | 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 | } } #endif #ifdef WHERETRACE_ENABLED /* ** Print a WhereLoop object for debugging purposes ** ** Format example: ** ** .--- Position in WHERE clause rSetup, rRun, nOut ---. ** | | ** | .--- selfMask nTerm ------. | ** | | | | ** | | .-- prereq Idx wsFlags----. | | ** | | | Name | | | ** | | | __|__ nEq ---. ___|__ | __|__ ** | / \ / \ / \ | / \ / \ / \ ** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31 */ void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ if( pWC ){ WhereInfo *pWInfo = pWC->pWInfo; int nb = 1+(pWInfo->pTabList->nSrc+3)/4; SrcItem *pItem = pWInfo->pTabList->a + p->iTab; Table *pTab = pItem->pTab; Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); sqlite3DebugPrintf(" %12s", pItem->zAlias ? pItem->zAlias : pTab->zName); }else{ sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d", p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab); } if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ const char *zName; if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){ if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){ int i = sqlite3Strlen30(zName) - 1; while( zName[i]!='_' ) i--; zName += i; |
︙ | ︙ | |||
2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 | sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){ int i; for(i=0; i<p->nLTerm; i++){ sqlite3WhereTermPrint(p->aLTerm[i], i); } } } #endif /* ** Convert bulk memory into a valid WhereLoop that can be passed ** to whereLoopClear harmlessly. */ | > > > > > > > > > | 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 | sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){ int i; for(i=0; i<p->nLTerm; i++){ sqlite3WhereTermPrint(p->aLTerm[i], i); } } } void sqlite3ShowWhereLoop(const WhereLoop *p){ if( p ) sqlite3WhereLoopPrint(p, 0); } void sqlite3ShowWhereLoopList(const WhereLoop *p){ while( p ){ sqlite3ShowWhereLoop(p); p = p->pNextLoop; } } #endif /* ** Convert bulk memory into a valid WhereLoop that can be passed ** to whereLoopClear harmlessly. */ |
︙ | ︙ | |||
2389 2390 2391 2392 2393 2394 2395 | sqlite3DbNNFreeNN(db, pWInfo->pMemToFree); pWInfo->pMemToFree = pNext; } sqlite3DbNNFreeNN(db, pWInfo); } /* | | > > > > > > > > > > > > > > > | | | | | | < < < < < < < < < > > > > > > > > > | < | | | | | 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 | sqlite3DbNNFreeNN(db, pWInfo->pMemToFree); pWInfo->pMemToFree = pNext; } sqlite3DbNNFreeNN(db, pWInfo); } /* ** Return TRUE if X is a proper subset of Y but is of equal or less cost. ** In other words, return true if all constraints of X are also part of Y ** and Y has additional constraints that might speed the search that X lacks ** but the cost of running X is not more than the cost of running Y. ** ** In other words, return true if the cost relationwship between X and Y ** is inverted and needs to be adjusted. ** ** Case 1: ** ** (1a) X and Y use the same index. ** (1b) X has fewer == terms than Y ** (1c) Neither X nor Y use skip-scan ** (1d) X does not have a a greater cost than Y ** ** Case 2: ** ** (2a) X has the same or lower cost, or returns the same or fewer rows, ** than Y. ** (2b) X uses fewer WHERE clause terms than Y ** (2c) Every WHERE clause term used by X is also used by Y ** (2d) X skips at least as many columns as Y ** (2e) If X is a covering index, than Y is too */ static int whereLoopCheaperProperSubset( const WhereLoop *pX, /* First WhereLoop to compare */ const WhereLoop *pY /* Compare against this WhereLoop */ ){ int i, j; if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */ assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 ); assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 ); if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */ && pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */ && pX->nSkip==0 && pY->nSkip==0 /* (1c) */ ){ return 1; /* Case 1 is true */ } if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ return 0; /* (2b) */ } if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */ for(i=pX->nLTerm-1; i>=0; i--){ if( pX->aLTerm[i]==0 ) continue; for(j=pY->nLTerm-1; j>=0; j--){ if( pY->aLTerm[j]==pX->aLTerm[i] ) break; } if( j<0 ) return 0; /* (2c) */ } if( (pX->wsFlags&WHERE_IDX_ONLY)!=0 && (pY->wsFlags&WHERE_IDX_ONLY)==0 ){ return 0; /* (2e) */ } return 1; /* Case 2 is true */ } /* ** Try to adjust the cost and number of output rows of WhereLoop pTemplate ** upwards or downwards so that: ** ** (1) pTemplate costs less than any other WhereLoops that are a proper |
︙ | ︙ | |||
2918 2919 2920 2921 2922 2923 2924 | 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; } | > | > > > > | 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 | 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; |
︙ | ︙ | |||
3304 3305 3306 3307 3308 3309 3310 | 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; | > | > | 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 | 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; |
︙ | ︙ | |||
3561 3562 3563 3564 3565 3566 3567 | 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; | | | 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 | 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)); |
︙ | ︙ | |||
3910 3911 3912 3913 3914 3915 3916 | 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 */ | | | 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 | 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 } |
︙ | ︙ | |||
4935 4936 4937 4938 4939 4940 4941 | 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); | | | 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 | 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; |
︙ | ︙ | |||
5404 5405 5406 5407 5408 5409 5410 | 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; } | > | < | < | 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 | 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 |
︙ | ︙ | |||
5746 5747 5748 5749 5750 5751 5752 | 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; | | | 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 | 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 ); |
︙ | ︙ | |||
5805 5806 5807 5808 5809 5810 5811 | bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0; }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); bMaybeNullRow = 0; }else{ continue; } | | | 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 | bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0; }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); bMaybeNullRow = 0; }else{ continue; } if( sqlite3ExprIsConstant(0,pExpr) ) continue; if( pExpr->op==TK_FUNCTION ){ /* 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 */ int n; FuncDef *pDef; sqlite3 *db = pParse->db; |
︙ | ︙ | |||
5998 5999 6000 6001 6002 6003 6004 | /* 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 ); | | > > > | 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 | /* 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); |
︙ | ︙ | |||
6023 6024 6025 6026 6027 6028 6029 | /* Allocate and initialize the WhereInfo structure that will become the ** return value. A single allocation is used to store the WhereInfo ** struct, the contents of WhereInfo.a[], the WhereClause structure ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte ** field (type Bitmask) it must be aligned on an 8-byte boundary on ** some architectures. Hence the ROUND8() below. */ | | > > > | 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 | /* Allocate and initialize the WhereInfo structure that will become the ** return value. A single allocation is used to store the WhereInfo ** struct, the contents of WhereInfo.a[], the WhereClause structure ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte ** field (type Bitmask) it must be aligned on an 8-byte boundary on ** some architectures. Hence the ROUND8() below. */ nByteWInfo = ROUND8P(sizeof(WhereInfo)); if( nTabList>1 ){ nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel)); } pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); if( db->mallocFailed ){ sqlite3DbFree(db, pWInfo); pWInfo = 0; goto whereBeginError; } pWInfo->pParse = pParse; |
︙ | ︙ | |||
6077 6078 6079 6080 6081 6082 6083 | if( nTabList==0 ){ if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr; if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0 && OptimizationEnabled(db, SQLITE_DistinctOpt) ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } | > > > | > | 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 | 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. |
︙ | ︙ | |||
6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 | /* Jump here if malloc fails */ whereBeginError: if( pWInfo ){ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); } return 0; } /* ** Part of sqlite3WhereEnd() will rewrite opcodes to reference the ** index rather than the main table. In SQLITE_DEBUG mode, we want ** to trace those changes if PRAGMA vdbe_addoptrace=on. This routine | > > > > > | 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 | /* Jump here if malloc fails */ whereBeginError: if( pWInfo ){ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); } #ifdef WHERETRACE_ENABLED /* Prevent harmless compiler warnings about debugging routines ** being declared but never used */ sqlite3ShowWhereLoopList(0); #endif /* WHERETRACE_ENABLED */ return 0; } /* ** Part of sqlite3WhereEnd() will rewrite opcodes to reference the ** index rather than the main table. In SQLITE_DEBUG mode, we want ** to trace those changes if PRAGMA vdbe_addoptrace=on. This routine |
︙ | ︙ | |||
6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 | /* 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 | > | 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 | /* 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/whereInt.h.
︙ | ︙ | |||
498 499 500 501 502 503 504 | ** ** where.c: */ Bitmask sqlite3WhereGetMask(WhereMaskSet*,int); #ifdef WHERETRACE_ENABLED void sqlite3WhereClausePrint(WhereClause *pWC); void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm); | | | 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 | ** ** where.c: */ Bitmask sqlite3WhereGetMask(WhereMaskSet*,int); #ifdef WHERETRACE_ENABLED void sqlite3WhereClausePrint(WhereClause *pWC); void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm); void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC); #endif WhereTerm *sqlite3WhereFindTerm( WhereClause *pWC, /* The WHERE clause to be searched */ int iCur, /* Cursor number of LHS */ int iColumn, /* Column number of LHS */ Bitmask notReady, /* RHS must not overlap with this mask */ u32 op, /* Mask of WO_xx values describing operator */ |
︙ | ︙ |
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; } } } |
︙ | ︙ |
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/aggnested.test.
︙ | ︙ | |||
354 355 356 357 358 359 360 361 362 | do_execsql_test 6.2.2 { SELECT ( SELECT c FROM (SELECT t2.b AS c FROM t1) GROUP BY c HAVING t2.b ) FROM t2 GROUP BY 'constant_string'; } {{}} | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_execsql_test 6.2.2 { SELECT ( SELECT c FROM (SELECT t2.b AS c FROM t1) GROUP BY c HAVING t2.b ) FROM t2 GROUP BY 'constant_string'; } {{}} #------------------------------------------------------------------------- reset_db do_execsql_test 7.0 { CREATE TABLE invoice ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, amount DOUBLE PRECISION DEFAULT NULL, name VARCHAR(100) DEFAULT NULL ); INSERT INTO invoice (amount, name) VALUES (4.0, 'Michael'), (15.0, 'Bara'), (4.0, 'Michael'), (6.0, 'John'); } do_execsql_test 7.1 { SELECT sum(amount), name from invoice group by name having (select v > 6 from (select sum(amount) v) t) } { 15.0 Bara 8.0 Michael } do_execsql_test 7.2 { SELECT (select 1 from (select sum(amount))) FROM invoice } {1} do_execsql_test 8.0 { CREATE TABLE t1(x INT); INSERT INTO t1 VALUES(100); INSERT INTO t1 VALUES(20); INSERT INTO t1 VALUES(3); SELECT (SELECT y FROM (SELECT sum(x) AS y) AS t2 ) FROM t1; } {123} do_execsql_test 8.1 { SELECT ( SELECT y FROM ( SELECT z AS y FROM (SELECT sum(x) AS z) AS t2 ) ) FROM t1; } {123} do_execsql_test 8.2 { SELECT ( SELECT a FROM ( SELECT y AS a FROM ( SELECT z AS y FROM (SELECT sum(x) AS z) AS t2 ) ) ) FROM t1; } {123} #------------------------------------------------------------------------- # dbsqlfuzz 04408efc51ae46897c4c122b407412045ed221b4 # reset_db do_execsql_test 9.1 { WITH out(i, j, k) AS ( VALUES(1234, 5678, 9012) ) SELECT ( SELECT ( SELECT min(abc) = ( SELECT ( SELECT 1234 fROM (SELECT abc) ) ) FROM ( SELECT sum( out.i ) + ( SELECT sum( out.i ) ) AS abc FROM (SELECT out.j) ) ) ) FROM out; } {0} do_execsql_test 9.2 { CREATE TABLE t1(a); CREATE TABLE t2(b); INSERT INTO t1 VALUES(1), (2), (3); INSERT INTO t2 VALUES(4), (5), (6); SELECT ( SELECT min(y) + (SELECT x) FROM ( SELECT sum(a) AS x, b AS y FROM t2 ) ) FROM t1; } {10} do_execsql_test 9.3 { SELECT ( SELECT min(y) + (SELECT (SELECT x)) FROM ( SELECT sum(a) AS x, b AS y FROM t2 ) ) FROM t1; } {10} do_execsql_test 9.4 { SELECT ( SELECT (SELECT x) FROM ( SELECT sum(a) AS x, b AS y FROM t2 ) GROUP BY y ) FROM t1; } {6} do_execsql_test 9.5 { SELECT ( SELECT (SELECT (SELECT x)) FROM ( SELECT sum(a) AS x, b AS y FROM t2 ) GROUP BY y ) FROM t1; } {6} # 2023-12-16 # New test case for check-in [4470f657d2069972] from 2023-11-02 # https://bugs.chromium.org/p/chromium/issues/detail?id=1511689 # do_execsql_test 10.1 { DROP TABLE IF EXISTS t0; DROP TABLE IF EXISTS t1; CREATE TABLE t0(c1, c2); INSERT INTO t0 VALUES(1,2); CREATE TABLE t1(c3, c4); INSERT INTO t1 VALUES(3,4); SELECT * FROM t0 WHERE EXISTS (SELECT 1 FROM t1 GROUP BY c3 HAVING ( SELECT count(*) FROM (SELECT 1 UNION ALL SELECT sum(DISTINCT c1) ) ) ) BETWEEN 1 AND 1; } {1 2} finish_test |
Changes to test/aggorderby.test.
︙ | ︙ | |||
113 114 115 116 117 118 119 120 121 122 | WITH c(x,y,z) AS (VALUES('a',4,5),('b',3,6),('b',2,7),('c',1,8)) SELECT group_concat(DISTINCT x ORDER BY y, z) FROM c; } {c,b,a} do_execsql_test aggorderby-8.2 { WITH c(x,y) AS (VALUES(1,1),(2,2),(3,3),(3,4),(3,5),(3,6)) SELECT sum(DISTINCT x ORDER BY y) FROM c; } 6 finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | WITH c(x,y,z) AS (VALUES('a',4,5),('b',3,6),('b',2,7),('c',1,8)) SELECT group_concat(DISTINCT x ORDER BY y, z) FROM c; } {c,b,a} do_execsql_test aggorderby-8.2 { WITH c(x,y) AS (VALUES(1,1),(2,2),(3,3),(3,4),(3,5),(3,6)) SELECT sum(DISTINCT x ORDER BY y) FROM c; } 6 # Subtype information is transfered through the sorter for aggregates # that make use of subtype info. # do_execsql_test aggorderby-9.0 { WITH c(x,y) AS (VALUES ('{a:3}', 3), ('[1,1]', 1), ('[4,4]', 4), ('{x:2}', 2)) SELECT json_group_array(json(x) ORDER BY y) FROM c; } {{[[1,1],{"x":2},{"a":3},[4,4]]}} do_execsql_test aggorderby-9.1 { WITH c(x,y) AS (VALUES ('[4,4]', 4), ('{a:3}', 3), ('[4,4]', 4), ('[1,1]', 1), ('[4,4]', 4), ('{x:2}', 2)) SELECT json_group_array(DISTINCT json(x) ORDER BY y) FROM c; } {{[[1,1],{"x":2},{"a":3},[4,4]]}} do_execsql_test aggorderby-9.2 { WITH c(x,y) AS (VALUES ('{a:3}', 3), ('[1,1]', 1), ('[4,4]', 4), ('{x:2}', 2)) SELECT json_group_array(json(x) ORDER BY json(x)) FROM c; } {{[[1,1],[4,4],{"a":3},{"x":2}]}} do_execsql_test aggorderby-9.3 { WITH c(x,y) AS (VALUES ('[4,4]', 4), ('{a:3}', 3), ('[4,4]', 4), ('[1,1]', 1), ('[4,4]', 4), ('{x:2}', 2)) SELECT json_group_array(DISTINCT json(x) ORDER BY json(x)) FROM c; } {{[[1,1],[4,4],{"a":3},{"x":2}]}} finish_test |
Changes to test/alter.test.
︙ | ︙ | |||
966 967 968 969 970 971 972 973 974 | SELECT unknown_function(a ORDER BY (SELECT group_concat(DISTINCT a ORDER BY a) FROM t1)) FROM t1; END; ALTER TABLE t2 RENAME TO e; } {} do_execsql_test alter-21.2 { SELECT name, type FROM sqlite_schema ORDER BY name; } {e table r1 trigger t1 table} finish_test | > > > > > > > > > > > > | 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 | SELECT unknown_function(a ORDER BY (SELECT group_concat(DISTINCT a ORDER BY a) FROM t1)) FROM t1; END; ALTER TABLE t2 RENAME TO e; } {} do_execsql_test alter-21.2 { SELECT name, type FROM sqlite_schema ORDER BY name; } {e table r1 trigger t1 table} do_execsql_test alter-21.3 { DROP TRIGGER r1; CREATE TRIGGER r2 AFTER INSERT ON e BEGIN SELECT unknown_function(a ORDER BY (SELECT group_concat(a ORDER BY a) FROM (SELECT b FROM t1))) FROM t1; END; ALTER TABLE e RENAME TO t99; } do_execsql_test alter-21.4 { SELECT name, type FROM sqlite_schema ORDER BY name; } {r2 trigger t1 table t99 table} finish_test |
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/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 | 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" )} } 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/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 ) |
︙ | ︙ |
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/date.test.
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 | datetest 2.46 {datetime('2003-10-22 12:24','70 second')} {2003-10-22 12:25:10} datetest 2.47 {datetime('2003-10-22 12:24','8.6 seconds')} {2003-10-22 12:24:08} datetest 2.48 {datetime('2003-10-22 12:24','9.4 second')} {2003-10-22 12:24:09} datetest 2.49 {datetime('2003-10-22 12:24','0000 second')} {2003-10-22 12:24:00} datetest 2.50 {datetime('2003-10-22 12:24','0001 second')} {2003-10-22 12:24:01} datetest 2.51 {datetime('2003-10-22 12:24','nonsense')} NULL datetest 3.1 {strftime('%d','2003-10-31 12:34:56.432')} 31 datetest 3.2.1 {strftime('pre%fpost','2003-10-31 12:34:56.432')} pre56.432post datetest 3.2.2 {strftime('%f','2003-10-31 12:34:59.9999999')} 59.999 datetest 3.3 {strftime('%H','2003-10-31 12:34:56.432')} 12 datetest 3.4 {strftime('%j','2003-10-31 12:34:56.432')} 304 datetest 3.5 {strftime('%J','2003-10-31 12:34:56.432')} 2452944.024264259 datetest 3.6 {strftime('%m','2003-10-31 12:34:56.432')} 10 | > > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | datetest 2.46 {datetime('2003-10-22 12:24','70 second')} {2003-10-22 12:25:10} datetest 2.47 {datetime('2003-10-22 12:24','8.6 seconds')} {2003-10-22 12:24:08} datetest 2.48 {datetime('2003-10-22 12:24','9.4 second')} {2003-10-22 12:24:09} datetest 2.49 {datetime('2003-10-22 12:24','0000 second')} {2003-10-22 12:24:00} datetest 2.50 {datetime('2003-10-22 12:24','0001 second')} {2003-10-22 12:24:01} datetest 2.51 {datetime('2003-10-22 12:24','nonsense')} NULL datetest 2.60 {datetime('2023-02-31')} {2023-03-03 00:00:00} datetest 3.1 {strftime('%d','2003-10-31 12:34:56.432')} 31 datetest 3.2.1 {strftime('pre%fpost','2003-10-31 12:34:56.432')} pre56.432post datetest 3.2.2 {strftime('%f','2003-10-31 12:34:59.9999999')} 59.999 datetest 3.3 {strftime('%H','2003-10-31 12:34:56.432')} 12 datetest 3.4 {strftime('%j','2003-10-31 12:34:56.432')} 304 datetest 3.5 {strftime('%J','2003-10-31 12:34:56.432')} 2452944.024264259 datetest 3.6 {strftime('%m','2003-10-31 12:34:56.432')} 10 |
︙ | ︙ | |||
203 204 205 206 207 208 209 | } 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} |
︙ | ︙ | |||
256 257 258 259 260 261 262 | 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. # |
︙ | ︙ | |||
314 315 316 317 318 319 320 321 322 323 324 325 326 327 | # 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. # |
︙ | ︙ | |||
448 449 450 451 452 453 454 455 456 457 458 459 460 461 | datetest 13.24 {julianday(2454832.5,'+1.5 years')} {2455380.0} datetest 13.30 {date('2000-01-01','+1.5 years')} {2001-07-02} datetest 13.31 {date('2001-01-01','+1.5 years')} {2002-07-02} datetest 13.32 {date('2002-01-01','+1.5 years')} {2003-07-02} datetest 13.33 {date('2002-01-01','-1.5 years')} {2000-07-02} datetest 13.34 {date('2001-01-01','-1.5 years')} {1999-07-02} # Test for issues reported by BareFeet (list.sql at tandb.com.au) # on mailing list on 2008-06-12. # # Put a floating point number in the database so that we can manipulate # raw bits using the hexio interface. # | > > > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | datetest 13.24 {julianday(2454832.5,'+1.5 years')} {2455380.0} datetest 13.30 {date('2000-01-01','+1.5 years')} {2001-07-02} datetest 13.31 {date('2001-01-01','+1.5 years')} {2002-07-02} datetest 13.32 {date('2002-01-01','+1.5 years')} {2003-07-02} datetest 13.33 {date('2002-01-01','-1.5 years')} {2000-07-02} datetest 13.34 {date('2001-01-01','-1.5 years')} {1999-07-02} datetest 13.35 {date('2023-02-28')} {2023-02-28} datetest 13.36 {date('2023-02-29')} {2023-03-01} datetest 13.37 {date('2023-04-31')} {2023-05-01} # Test for issues reported by BareFeet (list.sql at tandb.com.au) # on mailing list on 2008-06-12. # # Put a floating point number in the database so that we can manipulate # raw bits using the hexio interface. # |
︙ | ︙ | |||
564 565 566 567 568 569 570 571 | # 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/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/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 |
Added test/fts3integrity.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 | # 2023 December 16 # # 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 runs all tests. # # $Id: fts3.test,v 1.2 2008/07/23 18:17:32 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix fts3integrity # If SQLITE_ENABLE_FTS3 is defined, omit this file. ifcapable !fts3 { finish_test return } do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts3(x); INSERT INTO t1 VALUES('first row'); INSERT INTO t1 VALUES('second row'); CREATE TABLE t2(x PRIMARY KEY); INSERT INTO t2 VALUES('first row'); INSERT INTO t2 VALUES('second row'); } sqlite3 db2 test.db do_execsql_test -db db2 1.1 { CREATE TABLE t3(x, y); } do_execsql_test 1.2 { PRAGMA integrity_check; } {ok} 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] |
︙ | ︙ |
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.
︙ | ︙ | |||
155 156 157 158 159 160 161 | Blob *pFirstSql; /* First SQL script */ unsigned int uRandom; /* Seed for the SQLite PRNG */ unsigned int nInvariant; /* Number of invariant checks run */ char zTestName[100]; /* Name of current test */ } g; /* | | | > | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | Blob *pFirstSql; /* First SQL script */ unsigned int uRandom; /* Seed for the SQLite PRNG */ 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; |
︙ | ︙ | |||
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 | sqlite3_limit(cx.db, SQLITE_LIMIT_LENGTH, lengthLimit); } if( depthLimit>0 ){ sqlite3_limit(cx.db, SQLITE_LIMIT_EXPR_DEPTH, depthLimit); } sqlite3_limit(cx.db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 100); sqlite3_hard_heap_limit64(heapLimit); if( nDb>=20 && aDb[18]==2 && aDb[19]==2 ){ aDb[18] = aDb[19] = 1; } rc = sqlite3_deserialize(cx.db, "main", aDb, nDb, nDb, SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE); | > > | 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 | sqlite3_limit(cx.db, SQLITE_LIMIT_LENGTH, lengthLimit); } if( depthLimit>0 ){ sqlite3_limit(cx.db, SQLITE_LIMIT_EXPR_DEPTH, depthLimit); } sqlite3_limit(cx.db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 100); sqlite3_hard_heap_limit64(heapLimit); rc = 1; sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &rc); if( nDb>=20 && aDb[18]==2 && aDb[19]==2 ){ aDb[18] = aDb[19] = 1; } rc = sqlite3_deserialize(cx.db, "main", aDb, nDb, nDb, SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE); |
︙ | ︙ | |||
1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 | /* Block debug pragmas and ATTACH/DETACH. But wait until after ** deserialize to do this because deserialize depends on ATTACH */ sqlite3_set_authorizer(cx.db, block_troublesome_sql, &btsFlags); /* Add the vt02 virtual table */ sqlite3_vt02_init(cx.db, 0, 0); /* Add support for sqlite_dbdata and sqlite_dbptr virtual tables used ** by the recovery API */ sqlite3_dbdata_init(cx.db, 0, 0); /* Consistent PRNG seed */ #ifdef SQLITE_TESTCTRL_PRNG_SEED | > > > | 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 | /* Block debug pragmas and ATTACH/DETACH. But wait until after ** deserialize to do this because deserialize depends on ATTACH */ sqlite3_set_authorizer(cx.db, block_troublesome_sql, &btsFlags); /* Add the vt02 virtual table */ sqlite3_vt02_init(cx.db, 0, 0); /* Add the random_json() and random_json5() functions */ sqlite3_randomjson_init(cx.db, 0, 0); /* Add support for sqlite_dbdata and sqlite_dbptr virtual tables used ** by the recovery API */ sqlite3_dbdata_init(cx.db, 0, 0); /* Consistent PRNG seed */ #ifdef SQLITE_TESTCTRL_PRNG_SEED |
︙ | ︙ |
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/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; |
Changes to test/json/json-speed-check.sh.
︙ | ︙ | |||
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 | 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 while test "$1" != ""; do case $1 in --nodiff) doDiff=0 ;; --lean) CC_OPTS="$CC_OPTS $LEAN_OPTS" ;; --clang) CC=clang ;; --gcc7) CC=gcc-7 ;; -*) 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,/[^/]*$,,'` | > > > > > > > | | | | | | | | | 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 | 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 case $1 in --nodiff) doDiff=0 ;; --lean) CC_OPTS="$CC_OPTS $LEAN_OPTS" ;; --clang) 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.
︙ | ︙ | |||
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 | } {[1,{"abc":2.5,"def":null,"ghi":"hello"},99]} do_execsql_test json101-1.2 { SELECT hex(json_array('String "\ Test')); } {5B22537472696E67205C225C5C2054657374225D} do_catchsql_test json101-1.3 { SELECT json_array(1,printf('%.1000c','x'),x'abcd',3); } {1 {JSON cannot hold BLOB values}} do_execsql_test json101-1.4 { SELECT json_array(-9223372036854775808,9223372036854775807,0,1,-1, 0.0, 1.0, -1.0, -1e99, +2e100, 'one','two','three', 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, NULL, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 99); } {[-9223372036854775808,9223372036854775807,0,1,-1,0.0,1.0,-1.0,-1.0e+99,2.0e+100,"one","two","three",4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,21,22,23,24,25,26,27,28,29,30,31,"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ",99]} do_execsql_test json101-2.1 { SELECT json_object('a',1,'b',2.5,'c',null,'d','String Test'); } {{{"a":1,"b":2.5,"c":null,"d":"String Test"}}} do_catchsql_test json101-2.2 { SELECT json_object('a',printf('%.1000c','x'),2,2.5); } {1 {json_object() labels must be TEXT}} do_catchsql_test json101-2.3 { SELECT json_object('a',1,'b'); } {1 {json_object() requires an even number of arguments}} do_catchsql_test json101-2.4 { SELECT json_object('a',printf('%.1000c','x'),'b',x'abcd'); } {1 {JSON cannot hold BLOB values}} do_execsql_test json101-3.1 { SELECT json_replace('{"a":1,"b":2}','$.a','[3,4,5]'); } {{{"a":"[3,4,5]","b":2}}} do_execsql_test json101-3.2 { SELECT json_replace('{"a":1,"b":2}','$.a',json('[3,4,5]')); } {{{"a":[3,4,5],"b":2}}} do_execsql_test json101-3.3 { SELECT json_type(json_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b'); } {text} do_execsql_test json101-3.4 { SELECT json_type(json_set('{"a":1,"b":2}','$.b',json('{"x":3,"y":4}')),'$.b'); } {object} ifcapable vtab { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > | 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 | } {[1,{"abc":2.5,"def":null,"ghi":"hello"},99]} do_execsql_test json101-1.2 { SELECT hex(json_array('String "\ Test')); } {5B22537472696E67205C225C5C2054657374225D} do_catchsql_test json101-1.3 { SELECT json_array(1,printf('%.1000c','x'),x'abcd',3); } {1 {JSON cannot hold BLOB values}} do_catchsql_test json101-1.3b { SELECT jsonb_array(1,printf('%.1000c','x'),x'abcd',3); } {1 {JSON cannot hold BLOB values}} do_execsql_test json101-1.4 { SELECT json_array(-9223372036854775808,9223372036854775807,0,1,-1, 0.0, 1.0, -1.0, -1e99, +2e100, 'one','two','three', 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, NULL, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 99); } {[-9223372036854775808,9223372036854775807,0,1,-1,0.0,1.0,-1.0,-1.0e+99,2.0e+100,"one","two","three",4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,21,22,23,24,25,26,27,28,29,30,31,"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ",99]} do_execsql_test json101-1.4b { SELECT json(jsonb_array(-9223372036854775808,9223372036854775807,0,1,-1, 0.0, 1.0, -1.0, -1e99, +2e100, 'one','two','three', 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, NULL, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 99)); } {[-9223372036854775808,9223372036854775807,0,1,-1,0.0,1.0,-1.0,-1.0e+99,2.0e+100,"one","two","three",4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,21,22,23,24,25,26,27,28,29,30,31,"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ",99]} do_execsql_test json101-2.1 { SELECT json_object('a',1,'b',2.5,'c',null,'d','String Test'); } {{{"a":1,"b":2.5,"c":null,"d":"String Test"}}} do_execsql_test json101-2.1b { SELECT json(jsonb_object('a',1,'b',2.5,'c',null,'d','String Test')); } {{{"a":1,"b":2.5,"c":null,"d":"String Test"}}} do_catchsql_test json101-2.2 { SELECT json_object('a',printf('%.1000c','x'),2,2.5); } {1 {json_object() labels must be TEXT}} do_catchsql_test json101-2.2b { SELECT jsonb_object('a',printf('%.1000c','x'),2,2.5); } {1 {json_object() labels must be TEXT}} do_execsql_test json101-2.2.2 { SELECT json_object('a',json_array('xyx',77,4.5),'x',2.5); } {{{"a":["xyx",77,4.5],"x":2.5}}} do_execsql_test json101-2.2.2b { SELECT json(jsonb_object('a',json_array('xyx',77,4.5),'x',2.5)); } {{{"a":["xyx",77,4.5],"x":2.5}}} do_execsql_test json101-2.2.3 { SELECT json_object('a',jsonb_array('xyx',77,4.5),'x',2.5); } {{{"a":["xyx",77,4.5],"x":2.5}}} do_execsql_test json101-2.2.3b { SELECT json(jsonb_object('a',jsonb_array('xyx',77,4.5),'x',2.5)); } {{{"a":["xyx",77,4.5],"x":2.5}}} do_catchsql_test json101-2.3 { SELECT json_object('a',1,'b'); } {1 {json_object() requires an even number of arguments}} do_catchsql_test json101-2.4 { SELECT json_object('a',printf('%.1000c','x'),'b',x'abcd'); } {1 {JSON cannot hold BLOB values}} do_execsql_test json101-2.5 { SELECT json_object('a',printf('%.10c','x'),'b',jsonb_array(1,2,3)); } {{{"a":"xxxxxxxxxx","b":[1,2,3]}}} do_execsql_test json101-3.1 { SELECT json_replace('{"a":1,"b":2}','$.a','[3,4,5]'); } {{{"a":"[3,4,5]","b":2}}} do_execsql_test json101-3.1b { SELECT json(jsonb_replace('{"a":1,"b":2}','$.a','[3,4,5]')); } {{{"a":"[3,4,5]","b":2}}} do_execsql_test json101-3.2 { SELECT json_replace('{"a":1,"b":2}','$.a',json('[3,4,5]')); } {{{"a":[3,4,5],"b":2}}} do_execsql_test json101-3.2b { SELECT json_replace('{"a":1,"b":2}','$.a',jsonb('[3,4,5]')); } {{{"a":[3,4,5],"b":2}}} do_execsql_test json101-3.3 { SELECT json_type(json_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b'); } {text} do_execsql_test json101-3.3b { SELECT json_type(jsonb_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b'); } {text} do_execsql_test json101-3.4 { SELECT json_type(json_set('{"a":1,"b":2}','$.b',json('{"x":3,"y":4}')),'$.b'); } {object} do_execsql_test json101-3.4b { SELECT json_type(jsonb_set('{"a":1,"b":2}','$.b',jsonb('{"x":3,"y":4}')),'$.b'); } {object} ifcapable vtab { do_execsql_test json101-3.5 { SELECT fullkey, atom, '|' FROM json_tree(json_set('{}','$.x',123,'$.x',456)); } {{$} {} | {$.x} 456 |} do_execsql_test json101-3.5b { SELECT fullkey, atom, '|' FROM json_tree(jsonb_set('{}','$.x',123,'$.x',456)); } {{$} {} | {$.x} 456 |} } # Per rfc7159, any JSON value is allowed at the top level, and whitespace # is permitting before and/or after that value. # do_execsql_test json101-4.1 { CREATE TABLE j1(x); |
︙ | ︙ | |||
127 128 129 130 131 132 133 134 135 136 137 138 139 140 | # do_execsql_test json101-4.10 { SELECT count(*) FROM j1 WHERE json_type(x) IN ('object','array'); SELECT x FROM j1 WHERE json_extract(x,'$')<>x AND json_type(x) IN ('object','array'); } {4} do_execsql_test json101-5.1 { CREATE TABLE j2(id INTEGER PRIMARY KEY, json, src); INSERT INTO j2(id,json,src) VALUES(1,'{ "firstName": "John", "lastName": "Smith", | > > > > > > > | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | # do_execsql_test json101-4.10 { SELECT count(*) FROM j1 WHERE json_type(x) IN ('object','array'); SELECT x FROM j1 WHERE json_extract(x,'$')<>x AND json_type(x) IN ('object','array'); } {4} do_execsql_test json101-4.10b { CREATE TABLE j1b AS SELECT jsonb(x) AS "x" FROM j1; SELECT count(*) FROM j1b WHERE json_type(x) IN ('object','array'); SELECT json(x) FROM j1b WHERE json_extract(x,'$')<>json(x) AND json_type(x) IN ('object','array'); } {4} do_execsql_test json101-5.1 { CREATE TABLE j2(id INTEGER PRIMARY KEY, json, src); INSERT INTO j2(id,json,src) VALUES(1,'{ "firstName": "John", "lastName": "Smith", |
︙ | ︙ | |||
254 255 256 257 258 259 260 | { "id": "5002", "type": "Glazed" }, { "id": "5003", "type": "Chocolate" }, { "id": "5004", "type": "Maple" } ] } ]','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html'); SELECT count(*) FROM j2; | > > > | > > > > > > > > > | 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 | { "id": "5002", "type": "Glazed" }, { "id": "5003", "type": "Chocolate" }, { "id": "5004", "type": "Maple" } ] } ]','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html'); SELECT count(*) FROM j2; CREATE TABLE j2b(id INTEGER PRIMARY KEY, json, src); INSERT INTO J2b(id,json,src) SELECT id, jsonb(json), src FROM j2; SELECT count(*) FROM j2b; } {3 3} do_execsql_test json101-5.2 { SELECT id, json_valid(json), json_type(json), '|' FROM j2 ORDER BY id; } {1 1 object | 2 1 object | 3 1 array |} do_execsql_test json101-5.2b { SELECT id, json_valid(json,5), json_type(json), '|' FROM j2b ORDER BY id; } {1 1 object | 2 1 object | 3 1 array |} ifcapable !vtab { finish_test return } # fullkey is always the same as path+key (with appropriate formatting) # do_execsql_test json101-5.3 { SELECT j2.rowid, jx.rowid, fullkey, path, key FROM j2, json_tree(j2.json) AS jx WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']' ELSE '.'||key END); } {} do_execsql_test json101-5.3b { SELECT j2b.rowid, jx.rowid, fullkey, path, key FROM j2b, json_tree(j2b.json) AS jx WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']' ELSE '.'||key END); } {} do_execsql_test json101-5.4 { SELECT j2.rowid, jx.rowid, fullkey, path, key FROM j2, json_each(j2.json) AS jx WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']' |
︙ | ︙ | |||
304 305 306 307 308 309 310 311 312 313 314 315 316 317 | 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} |
︙ | ︙ | |||
366 367 368 369 370 371 372 373 374 375 376 377 378 379 | # do_execsql_test json101-8.1 { DROP TABLE IF EXISTS t8; CREATE TABLE t8(a,b); INSERT INTO t8(a) VALUES('abc' || char(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) || 'xyz'); UPDATE t8 SET b=json_array(a); SELECT b FROM t8; } {{["abc\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#xyz"]}} do_execsql_test json101-8.2 { SELECT a=json_extract(b,'$[0]') FROM t8; } {1} # 2017-04-12. Regression reported on the mailing list by Rolf Ade # | > > > > > > > | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | # do_execsql_test json101-8.1 { DROP TABLE IF EXISTS t8; CREATE TABLE t8(a,b); INSERT INTO t8(a) VALUES('abc' || char(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) || 'xyz'); UPDATE t8 SET b=json_array(a); SELECT b FROM t8; } {{["abc\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#xyz"]}} do_execsql_test json101-8.1b { DROP TABLE IF EXISTS t8; CREATE TABLE t8(a,b); INSERT INTO t8(a) VALUES('abc' || char(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) || 'xyz'); UPDATE t8 SET b=jsonb_array(a); SELECT json(b) FROM t8; } {{["abc\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#xyz"]}} do_execsql_test json101-8.2 { SELECT a=json_extract(b,'$[0]') FROM t8; } {1} # 2017-04-12. Regression reported on the mailing list by Rolf Ade # |
︙ | ︙ | |||
397 398 399 400 401 402 403 | do_execsql_test json101-9.3 { SELECT json_quote(12345); } {12345} do_execsql_test json101-9.4 { SELECT json_quote(null); } {"null"} do_catchsql_test json101-9.5 { | | | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | do_execsql_test json101-9.3 { SELECT json_quote(12345); } {12345} do_execsql_test json101-9.4 { SELECT json_quote(null); } {"null"} do_catchsql_test json101-9.5 { SELECT json_quote(x'3031323334'); } {1 {JSON cannot hold BLOB values}} do_catchsql_test json101-9.6 { SELECT json_quote(123,456) } {1 {wrong number of arguments to function json_quote()}} do_catchsql_test json101-9.7 { SELECT json_quote() } {1 {wrong number of arguments to function json_quote()}} |
︙ | ︙ | |||
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | "transliterate":false, "add.footnote":false, "summary.report":false} } } }'); } {} do_execsql_test json101-12.110 { SELECT json_remove(x, '$.settings.layer2."dis.legomenon".forceDisplay') FROM t12; } {{{"settings":{"layer2":{"hapax.legomenon":{"forceDisplay":true,"transliterate":true,"add.footnote":true,"summary.report":true},"dis.legomenon":{"transliterate":false,"add.footnote":false,"summary.report":true},"tris.legomenon":{"forceDisplay":true,"transliterate":false,"add.footnote":false,"summary.report":false}}}}}} do_execsql_test json101-12.120 { SELECT json_extract(x, '$.settings.layer2."tris.legomenon"."summary.report"') FROM t12; } {0} # 2018-01-26 # ticket https://www.sqlite.org/src/tktview/80177f0c226ff54f6ddd41 # Make sure the query planner knows about the arguments to table-valued functions. # do_execsql_test json101-13.100 { | > > > > > > > > > | 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 | "transliterate":false, "add.footnote":false, "summary.report":false} } } }'); } {} do_execsql_test json101-12.110 { SELECT json_remove(x, '$.settings.layer2."dis.legomenon".forceDisplay') FROM t12; } {{{"settings":{"layer2":{"hapax.legomenon":{"forceDisplay":true,"transliterate":true,"add.footnote":true,"summary.report":true},"dis.legomenon":{"transliterate":false,"add.footnote":false,"summary.report":true},"tris.legomenon":{"forceDisplay":true,"transliterate":false,"add.footnote":false,"summary.report":false}}}}}} do_execsql_test json101-12.110b { SELECT json_remove(jsonb(x), '$.settings.layer2."dis.legomenon".forceDisplay') FROM t12; } {{{"settings":{"layer2":{"hapax.legomenon":{"forceDisplay":true,"transliterate":true,"add.footnote":true,"summary.report":true},"dis.legomenon":{"transliterate":false,"add.footnote":false,"summary.report":true},"tris.legomenon":{"forceDisplay":true,"transliterate":false,"add.footnote":false,"summary.report":false}}}}}} do_execsql_test json101-12.120 { SELECT json_extract(x, '$.settings.layer2."tris.legomenon"."summary.report"') FROM t12; } {0} do_execsql_test json101-12.120b { SELECT json_extract(jsonb(x), '$.settings.layer2."tris.legomenon"."summary.report"') FROM t12; } {0} # 2018-01-26 # ticket https://www.sqlite.org/src/tktview/80177f0c226ff54f6ddd41 # Make sure the query planner knows about the arguments to table-valued functions. # do_execsql_test json101-13.100 { |
︙ | ︙ | |||
836 837 838 839 840 841 842 | # Make sure the table-valued functions contained within parentheses # work correctly. # # Bug reported via private email. See TH3 for more information. # do_execsql_test json101-15.100 { SELECT * FROM JSON_EACH('{"a":1, "b":2}'); | | | | | | 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 | # Make sure the table-valued functions contained within parentheses # work correctly. # # Bug reported via private email. See TH3 for more information. # do_execsql_test json101-15.100 { SELECT * FROM JSON_EACH('{"a":1, "b":2}'); } {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}} do_execsql_test json101-15.110 { SELECT xyz.* FROM JSON_EACH('{"a":1, "b":2}') AS xyz; } {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}} do_execsql_test json101-15.120 { SELECT * FROM (JSON_EACH('{"a":1, "b":2}')); } {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}} do_execsql_test json101-15.130 { SELECT xyz.* FROM (JSON_EACH('{"a":1, "b":2}')) AS xyz; } {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}} # 2019-11-10 # Mailing list bug report on the handling of surrogate pairs # in JSON. # do_execsql_test json101-16.10 { SELECT length(json_extract('"abc\uD834\uDD1Exyz"','$')); |
︙ | ︙ | |||
885 886 887 888 889 890 891 | SELECT json_extract('[3,{"a":4,"":[5,{"hi":6},7]},8]', '$[1].""[1].hi'); } {6} do_execsql_test json101-18.4 { SELECT json_extract('[3,{"a":4,"":[5,{"hi":6},7]},8]', '$[1].""[1]."hi"'); } {6} do_catchsql_test json101-18.5 { SELECT json_extract('{"":8}', '$.'); | | | 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 | SELECT json_extract('[3,{"a":4,"":[5,{"hi":6},7]},8]', '$[1].""[1].hi'); } {6} do_execsql_test json101-18.4 { SELECT json_extract('[3,{"a":4,"":[5,{"hi":6},7]},8]', '$[1].""[1]."hi"'); } {6} do_catchsql_test json101-18.5 { SELECT json_extract('{"":8}', '$.'); } {1 {bad JSON path: '$.'}} # 2022-08-29 https://sqlite.org/forum/forumpost/9b9e4716c0d7bbd1 # This is not a problem specifically with JSON functions. It is # a problem with transaction control. But the json() function makes # the problem more easily accessible, so it is tested here. # do_execsql_test json101-19.1 { |
︙ | ︙ | |||
1050 1051 1052 1053 1054 1055 1056 | FROM (SELECT json_set(json_set('[]','$[#]',0), '$[#]',1) AS j); } {{[0,1]} 0 1} do_execsql_test json101-23.2 { SELECT j, j->>0, j->>1 FROM (SELECT json_set('[]','$[#]',0,'$[#]',1) AS j); } {{[0,1]} 0 1} | > > | > > > > > > > > > > > > > > > > > > > > | > | 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 | FROM (SELECT json_set(json_set('[]','$[#]',0), '$[#]',1) AS j); } {{[0,1]} 0 1} do_execsql_test json101-23.2 { SELECT j, j->>0, j->>1 FROM (SELECT json_set('[]','$[#]',0,'$[#]',1) AS j); } {{[0,1]} 0 1} # Insert/Set/Replace where the path specifies substructure that # does not yet exist # proc tx x {return [string map [list ( \173 ) \175 ' \042 < \133 > \135] $x]} foreach {id start path ins set repl} { 1 {{}} {$.a.b.c} ('a':('b':('c':9))) ('a':('b':('c':9))) () 2 {{a:4}} {$.a.b.c} ('a':4) ('a':4) ('a':4) 3 {{a:{}}} {$.a.b.c} ('a':('b':('c':9))) ('a':('b':('c':9))) ('a':()) 4 {[0,1,2]} {$[3].a[0].b} <0,1,2,('a':<('b':9)>)> <0,1,2,('a':<('b':9)>)> <0,1,2> 5 {[0,1,2]} {$[1].a[0].b} <0,1,2> <0,1,2> <0,1,2> 6 {[0,{},2]} {$[1].a[0].b} <0,('a':<('b':9)>),2> <0,('a':<('b':9)>),2> <0,(),2> 7 {[0,1,2]} {$[3][0].b} <0,1,2,<('b':9)>> <0,1,2,<('b':9)>> <0,1,2> 8 {[0,1,2]} {$[1][0].b} <0,1,2> <0,1,2> <0,1,2> } { do_execsql_test json101-24.$id.insert { SELECT json_insert($start,$path,9); } [list [tx $ins]] do_execsql_test json101-24.$id.set { SELECT json_set($start,$path,9); } [list [tx $set]] do_execsql_test json101-24.$id.replace { SELECT json_replace($start,$path,9); } [list [tx $repl]] } finish_test |
Changes to test/json102.test.
︙ | ︙ | |||
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 | set testdir [file dirname $argv0] source $testdir/tester.tcl do_execsql_test json102-100 { SELECT json_object('ex','[52,3.14159]'); } {{{"ex":"[52,3.14159]"}}} do_execsql_test json102-110 { SELECT json_object('ex',json('[52,3.14159]')); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-120 { SELECT json_object('ex',json_array(52,3.14159)); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-130 { SELECT json(' { "this" : "is", "a": [ "test" ] } '); } {{{"this":"is","a":["test"]}}} do_execsql_test json102-140 { SELECT json_array(1,2,'3',4); } {{[1,2,"3",4]}} do_execsql_test json102-150 { SELECT json_array('[1,2]'); } {{["[1,2]"]}} do_execsql_test json102-160 { SELECT json_array(json_array(1,2)); } {{[[1,2]]}} do_execsql_test json102-170 { SELECT json_array(1,null,'3','[4,5]','{"six":7.7}'); } {{[1,null,"3","[4,5]","{\"six\":7.7}"]}} do_execsql_test json102-180 { SELECT json_array(1,null,'3',json('[4,5]'),json('{"six":7.7}')); } {{[1,null,"3",[4,5],{"six":7.7}]}} do_execsql_test json102-190 { SELECT json_array_length('[1,2,3,4]'); } {{4}} do_execsql_test json102-191 { SELECT json_array_length( json_remove('[1,2,3,4]','$[2]') ); } {{3}} do_execsql_test json102-200 { SELECT json_array_length('[1,2,3,4]', '$'); } {{4}} do_execsql_test json102-210 { SELECT json_array_length('[1,2,3,4]', '$[2]'); } {{0}} do_execsql_test json102-220 { SELECT json_array_length('{"one":[1,2,3]}'); } {{0}} | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | 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 | set testdir [file dirname $argv0] source $testdir/tester.tcl do_execsql_test json102-100 { SELECT json_object('ex','[52,3.14159]'); } {{{"ex":"[52,3.14159]"}}} do_execsql_test json102-100b { SELECT json(jsonb_object('ex','[52,3.14159]')); } {{{"ex":"[52,3.14159]"}}} do_execsql_test json102-110 { SELECT json_object('ex',json('[52,3.14159]')); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-110-2 { SELECT json(jsonb_object('ex',json('[52,3.14159]'))); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-110-3 { SELECT json_object('ex',jsonb('[52,3.14159]')); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-110-3 { SELECT json(jsonb_object('ex',jsonb('[52,3.14159]'))); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-120 { SELECT json_object('ex',json_array(52,3.14159)); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-120-2 { SELECT json(jsonb_object('ex',json_array(52,3.14159))); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-120-3 { SELECT json_object('ex',jsonb_array(52,3.14159)); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-120-4 { SELECT json(jsonb_object('ex',jsonb_array(52,3.14159))); } {{{"ex":[52,3.14159]}}} do_execsql_test json102-130 { SELECT json(' { "this" : "is", "a": [ "test" ] } '); } {{{"this":"is","a":["test"]}}} do_execsql_test json102-130b { SELECT json(jsonb(' { "this" : "is", "a": [ "test" ] } ')); } {{{"this":"is","a":["test"]}}} do_execsql_test json102-140 { SELECT json_array(1,2,'3',4); } {{[1,2,"3",4]}} do_execsql_test json102-140b { SELECT json(jsonb_array(1,2,'3',4)); } {{[1,2,"3",4]}} do_execsql_test json102-150 { SELECT json_array('[1,2]'); } {{["[1,2]"]}} do_execsql_test json102-150b { SELECT json(jsonb_array('[1,2]')); } {{["[1,2]"]}} do_execsql_test json102-160 { SELECT json_array(json_array(1,2)); } {{[[1,2]]}} do_execsql_test json102-160-2 { SELECT json_array(jsonb_array(1,2)); } {{[[1,2]]}} do_execsql_test json102-160-3 { SELECT json(jsonb_array(json_array(1,2))); } {{[[1,2]]}} do_execsql_test json102-160-4 { SELECT json(jsonb_array(jsonb_array(1,2))); } {{[[1,2]]}} do_execsql_test json102-170 { SELECT json_array(1,null,'3','[4,5]','{"six":7.7}'); } {{[1,null,"3","[4,5]","{\"six\":7.7}"]}} do_execsql_test json102-170b { SELECT json(jsonb_array(1,null,'3','[4,5]','{"six":7.7}')); } {{[1,null,"3","[4,5]","{\"six\":7.7}"]}} do_execsql_test json102-180 { SELECT json_array(1,null,'3',json('[4,5]'),json('{"six":7.7}')); } {{[1,null,"3",[4,5],{"six":7.7}]}} do_execsql_test json102-180-2 { SELECT json_array(1,null,'3',jsonb('[4,5]'),json('{"six":7.7}')); } {{[1,null,"3",[4,5],{"six":7.7}]}} do_execsql_test json102-180-3 { SELECT json(jsonb_array(1,null,'3',json('[4,5]'),json('{"six":7.7}'))); } {{[1,null,"3",[4,5],{"six":7.7}]}} do_execsql_test json102-180-4 { SELECT json(jsonb_array(1,null,'3',jsonb('[4,5]'),jsonb('{"six":7.7}'))); } {{[1,null,"3",[4,5],{"six":7.7}]}} do_execsql_test json102-190 { SELECT json_array_length('[1,2,3,4]'); } {{4}} do_execsql_test json102-190b { SELECT json_array_length(jsonb('[1,2,3,4]')); } {{4}} do_execsql_test json102-191 { SELECT json_array_length( json_remove('[1,2,3,4]','$[2]') ); } {{3}} do_execsql_test json102-191b { SELECT json_array_length( jsonb_remove('[1,2,3,4]','$[2]') ); } {{3}} do_execsql_test json102-200 { SELECT json_array_length('[1,2,3,4]', '$'); } {{4}} do_execsql_test json102-200b { SELECT json_array_length(jsonb('[1,2,3,4]'), '$'); } {{4}} do_execsql_test json102-210 { SELECT json_array_length('[1,2,3,4]', '$[2]'); } {{0}} do_execsql_test json102-210b { SELECT json_array_length(jsonb('[1,2,3,4]'), '$[2]'); } {{0}} do_execsql_test json102-220 { SELECT json_array_length('{"one":[1,2,3]}'); } {{0}} do_execsql_test json102-220 { SELECT json_array_length('{"one":[1,2,3]}'); } {{0}} do_execsql_test json102-230b { SELECT json_array_length(jsonb('{"one":[1,2,3]}'), '$.one'); } {{3}} do_execsql_test json102-240 { SELECT json_array_length('{"one":[1,2,3]}', '$.two'); } {{}} do_execsql_test json102-240b { SELECT json_array_length(jsonb('{"one":[1,2,3]}'), '$.two'); } {{}} do_execsql_test json102-250 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$'); } {{{"a":2,"c":[4,5,{"f":7}]}}} do_execsql_test json102-250-2 { SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$'); } {{{"a":2,"c":[4,5,{"f":7}]}}} do_execsql_test json102-250-3 { SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$')); } {{{"a":2,"c":[4,5,{"f":7}]}}} do_execsql_test json102-250-4 { SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$')); } {{{"a":2,"c":[4,5,{"f":7}]}}} do_execsql_test json102-260 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c'); } {{[4,5,{"f":7}]}} do_execsql_test json102-260-2 { SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c'); } {{[4,5,{"f":7}]}} do_execsql_test json102-260-3 { SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c')); } {{[4,5,{"f":7}]}} do_execsql_test json102-260-4 { SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c')); } {{[4,5,{"f":7}]}} do_execsql_test json102-270 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2]'); } {{{"f":7}}} do_execsql_test json102-270-2 { SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c[2]'); } {{{"f":7}}} do_execsql_test json102-270-3 { SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c[2]')); } {{{"f":7}}} do_execsql_test json102-270-4 { SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2]')); } {{{"f":7}}} do_execsql_test json102-280 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2].f'); } {{7}} do_execsql_test json102-280b { SELECT jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2].f'); } {{7}} do_execsql_test json102-290 { SELECT json_extract('{"a":2,"c":[4,5],"f":7}','$.c','$.a'); } {{[[4,5],2]}} do_execsql_test json102-290-2 { SELECT json_extract(jsonb('{"a":2,"c":[4,5],"f":7}'),'$.c','$.a'); } {{[[4,5],2]}} do_execsql_test json102-290-3 { SELECT json(jsonb_extract('{"a":2,"c":[4,5],"f":7}','$.c','$.a')); } {{[[4,5],2]}} do_execsql_test json102-290-4 { SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5],"f":7}'),'$.c','$.a')); } {{[[4,5],2]}} do_execsql_test json102-300 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x'); } {{}} do_execsql_test json102-300b { SELECT jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x'); } {{}} do_execsql_test json102-310 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x', '$.a'); } {{[null,2]}} do_execsql_test json102-310-2 { SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.x', '$.a'); } {{[null,2]}} do_execsql_test json102-310-3 { SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.x', '$.a')); } {{[null,2]}} do_execsql_test json102-310-43 { SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x', '$.a')); } {{[null,2]}} do_execsql_test json102-320 { SELECT json_insert('{"a":2,"c":4}', '$.a', 99); } {{{"a":2,"c":4}}} do_execsql_test json102-320-2 { SELECT json_insert(jsonb('{"a":2,"c":4}'), '$.a', 99); } {{{"a":2,"c":4}}} do_execsql_test json102-320-3 { SELECT json(jsonb_insert('{"a":2,"c":4}', '$.a', 99)); } {{{"a":2,"c":4}}} do_execsql_test json102-320-4 { SELECT json(jsonb_insert(jsonb('{"a":2,"c":4}'), '$.a', 99)); } {{{"a":2,"c":4}}} do_execsql_test json102-330 { SELECT json_insert('{"a":2,"c":4}', '$.e', 99); } {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-330-2 { SELECT json_insert(jsonb('{"a":2,"c":4}'), '$.e', 99); } {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-330-3 { SELECT json(jsonb_insert('{"a":2,"c":4}', '$.e', 99)); } {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-330-4 { SELECT json(jsonb_insert(jsonb('{"a":2,"c":4}'), '$.e', 99)); } {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-340 { SELECT json_replace('{"a":2,"c":4}', '$.a', 99); } {{{"a":99,"c":4}}} do_execsql_test json102-340-2 { SELECT json_replace(jsonb('{"a":2,"c":4}'), '$.a', 99); } {{{"a":99,"c":4}}} do_execsql_test json102-340-3 { SELECT json(jsonb_replace('{"a":2,"c":4}', '$.a', 99)); } {{{"a":99,"c":4}}} do_execsql_test json102-340-4 { SELECT json(jsonb_replace(jsonb('{"a":2,"c":4}'), '$.a', 99)); } {{{"a":99,"c":4}}} do_execsql_test json102-350 { SELECT json_replace('{"a":2,"c":4}', '$.e', 99); } {{{"a":2,"c":4}}} do_execsql_test json102-350-2 { SELECT json_replace(jsonb('{"a":2,"c":4}'), '$.e', 99); } {{{"a":2,"c":4}}} do_execsql_test json102-350-3 { SELECT json(jsonb_replace('{"a":2,"c":4}', '$.e', 99)); } {{{"a":2,"c":4}}} do_execsql_test json102-350-4 { SELECT json(jsonb_replace(jsonb('{"a":2,"c":4}'), '$.e', 99)); } {{{"a":2,"c":4}}} do_execsql_test json102-360 { SELECT json_set('{"a":2,"c":4}', '$.a', 99); } {{{"a":99,"c":4}}} do_execsql_test json102-360-2 { SELECT json_set(jsonb('{"a":2,"c":4}'), '$.a', 99); } {{{"a":99,"c":4}}} do_execsql_test json102-360-3 { SELECT json(jsonb_set('{"a":2,"c":4}', '$.a', 99)); } {{{"a":99,"c":4}}} do_execsql_test json102-360-4 { SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.a', 99)); } {{{"a":99,"c":4}}} do_execsql_test json102-370 { SELECT json_set('{"a":2,"c":4}', '$.e', 99); } {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-370-2 { SELECT json_set(jsonb('{"a":2,"c":4}'), '$.e', 99); } {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-370-3 { SELECT json(jsonb_set('{"a":2,"c":4}', '$.e', 99)); } {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-370-4 { SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.e', 99)); } {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-380 { SELECT json_set('{"a":2,"c":4}', '$.c', '[97,96]'); } {{{"a":2,"c":"[97,96]"}}} do_execsql_test json102-380-2 { SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', '[97,96]'); } {{{"a":2,"c":"[97,96]"}}} do_execsql_test json102-380-3 { SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', '[97,96]')); } {{{"a":2,"c":"[97,96]"}}} do_execsql_test json102-380-4 { SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', '[97,96]')); } {{{"a":2,"c":"[97,96]"}}} do_execsql_test json102-390 { SELECT json_set('{"a":2,"c":4}', '$.c', json('[97,96]')); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-390-2 { SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', json('[97,96]')); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-390-3 { SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', json('[97,96]'))); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-390-4 { SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', json('[97,96]'))); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-390-5 { SELECT json_set('{"a":2,"c":4}', '$.c', jsonb('[97,96]')); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-390-6 { SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb('[97,96]')); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-390-7 { SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', jsonb('[97,96]'))); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-390-8 { SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb('[97,96]'))); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400 { SELECT json_set('{"a":2,"c":4}', '$.c', json_array(97,96)); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400-2 { SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', json_array(97,96)); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400-3 { SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', json_array(97,96))); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400-4 { SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', json_array(97,96))); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400-5 { SELECT json_set('{"a":2,"c":4}', '$.c', jsonb_array(97,96)); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400-6 { SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb_array(97,96)); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400-7 { SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', jsonb_array(97,96))); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400-8 { SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb_array(97,96))); } {{{"a":2,"c":[97,96]}}} do_execsql_test json102-410 { SELECT json_object('a',2,'c',4); } {{{"a":2,"c":4}}} do_execsql_test json102-410b { SELECT json(jsonb_object('a',2,'c',4)); } {{{"a":2,"c":4}}} do_execsql_test json102-420 { SELECT json_object('a',2,'c','{e:5}'); } {{{"a":2,"c":"{e:5}"}}} do_execsql_test json102-420b { SELECT json(jsonb_object('a',2,'c','{e:5}')); } {{{"a":2,"c":"{e:5}"}}} do_execsql_test json102-430 { SELECT json_object('a',2,'c',json_object('e',5)); } {{{"a":2,"c":{"e":5}}}} do_execsql_test json102-430-2 { SELECT json(jsonb_object('a',2,'c',json_object('e',5))); } {{{"a":2,"c":{"e":5}}}} do_execsql_test json102-430-3 { SELECT json_object('a',2,'c',jsonb_object('e',5)); } {{{"a":2,"c":{"e":5}}}} do_execsql_test json102-430-4 { SELECT json(jsonb_object('a',2,'c',jsonb_object('e',5))); } {{{"a":2,"c":{"e":5}}}} do_execsql_test json102-440 { SELECT json_remove('[0,1,2,3,4]','$[2]'); } {{[0,1,3,4]}} do_execsql_test json102-440-2 { SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[2]'); } {{[0,1,3,4]}} do_execsql_test json102-440-3 { SELECT json(jsonb_remove('[0,1,2,3,4]','$[2]')); } {{[0,1,3,4]}} do_execsql_test json102-440-4 { SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[2]')); } {{[0,1,3,4]}} do_execsql_test json102-450 { SELECT json_remove('[0,1,2,3,4]','$[2]','$[0]'); } {{[1,3,4]}} do_execsql_test json102-450-2 { SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[2]','$[0]'); } {{[1,3,4]}} do_execsql_test json102-450-3 { SELECT json(jsonb_remove('[0,1,2,3,4]','$[2]','$[0]')); } {{[1,3,4]}} do_execsql_test json102-450-4 { SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[2]','$[0]')); } {{[1,3,4]}} do_execsql_test json102-460 { SELECT json_remove('[0,1,2,3,4]','$[0]','$[2]'); } {{[1,2,4]}} do_execsql_test json102-460-2 { SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[0]','$[2]'); } {{[1,2,4]}} do_execsql_test json102-460-3 { SELECT json(jsonb_remove('[0,1,2,3,4]','$[0]','$[2]')); } {{[1,2,4]}} do_execsql_test json102-460-4 { SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[0]','$[2]')); } {{[1,2,4]}} do_execsql_test json102-470 { SELECT json_remove('{"x":25,"y":42}'); } {{{"x":25,"y":42}}} do_execsql_test json102-470-2 { SELECT json_remove(jsonb('{"x":25,"y":42}')); } {{{"x":25,"y":42}}} do_execsql_test json102-470-3 { SELECT json(jsonb_remove('{"x":25,"y":42}')); } {{{"x":25,"y":42}}} do_execsql_test json102-470-4 { SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'))); } {{{"x":25,"y":42}}} do_execsql_test json102-480 { SELECT json_remove('{"x":25,"y":42}','$.z'); } {{{"x":25,"y":42}}} do_execsql_test json102-480-2 { SELECT json_remove(jsonb('{"x":25,"y":42}'),'$.z'); } {{{"x":25,"y":42}}} do_execsql_test json102-480-3 { SELECT json(jsonb_remove('{"x":25,"y":42}','$.z')); } {{{"x":25,"y":42}}} do_execsql_test json102-480-4 { SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$.z')); } {{{"x":25,"y":42}}} do_execsql_test json102-490 { SELECT json_remove('{"x":25,"y":42}','$.y'); } {{{"x":25}}} do_execsql_test json102-490-2 { SELECT json_remove(jsonb('{"x":25,"y":42}'),'$.y'); } {{{"x":25}}} do_execsql_test json102-490-3 { SELECT json(jsonb_remove('{"x":25,"y":42}','$.y')); } {{{"x":25}}} do_execsql_test json102-490-4 { SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$.y')); } {{{"x":25}}} do_execsql_test json102-500 { SELECT json_remove('{"x":25,"y":42}','$'); } {{}} do_execsql_test json102-500-2 { SELECT json_remove(jsonb('{"x":25,"y":42}'),'$'); } {{}} do_execsql_test json102-500-3 { SELECT json(jsonb_remove('{"x":25,"y":42}','$')); } {{}} do_execsql_test json102-500-4 { SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$')); } {{}} do_execsql_test json102-510 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}'); } {{object}} do_execsql_test json102-510b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778'); } {{object}} do_execsql_test json102-520 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$'); } {{object}} do_execsql_test json102-520b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$'); } {{object}} do_execsql_test json102-530 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a'); } {{array}} do_execsql_test json102-530b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a'); } {{array}} do_execsql_test json102-540 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[0]'); } {{integer}} do_execsql_test json102-540b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[0]'); } {{integer}} do_execsql_test json102-550 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[1]'); } {{real}} do_execsql_test json102-550b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[1]'); } {{real}} do_execsql_test json102-560 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[2]'); } {{true}} do_execsql_test json102-560b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[2]'); } {{true}} do_execsql_test json102-570 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[3]'); } {{false}} do_execsql_test json102-570b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[3]'); } {{false}} do_execsql_test json102-580 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[4]'); } {{null}} do_execsql_test json102-580b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[4]'); } {{null}} do_execsql_test json102-590 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[5]'); } {{text}} do_execsql_test json102-590b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[5]'); } {{text}} do_execsql_test json102-600 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[6]'); } {{}} do_execsql_test json102-600b { SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[6]'); } {{}} do_execsql_test json102-610 { SELECT json_valid(char(123)||'"x":35'||char(125)); } {{1}} do_execsql_test json102-620 { SELECT json_valid(char(123)||'"x":35'); } {{0}} ifcapable vtab { do_execsql_test json102-1000 { CREATE TABLE user(name,phone,phoneb); INSERT INTO user(name,phone) VALUES ('Alice','["919-555-2345","804-555-3621"]'), ('Bob','["201-555-8872"]'), ('Cindy','["704-555-9983"]'), ('Dave','["336-555-8421","704-555-4321","803-911-4421"]'); UPDATE user SET phoneb=jsonb(phone); SELECT DISTINCT user.name FROM user, json_each(user.phone) WHERE json_each.value LIKE '704-%' ORDER BY 1; } {Cindy Dave} do_execsql_test json102-1000b { SELECT DISTINCT user.name FROM user, json_each(user.phoneb) WHERE json_each.value LIKE '704-%' ORDER BY 1; } {Cindy Dave} do_execsql_test json102-1010 { UPDATE user SET phone=json_extract(phone,'$[0]') WHERE json_array_length(phone)<2; SELECT name, substr(phone,1,5) FROM user ORDER BY name; |
︙ | ︙ | |||
249 250 251 252 253 254 255 256 257 258 259 260 261 262 | 2 {$.partlist[1].uuid} c690dc14-572e-11e5-95f9-dfc8861fd535] do_execsql_test json102-1110 { SELECT big.rowid, fullkey, value FROM big, json_tree(big.json) WHERE json_tree.type NOT IN ('object','array') ORDER BY +big.rowid, +json_tree.id } $correct_answer do_execsql_test json102-1120 { SELECT big.rowid, fullkey, atom FROM big, json_tree(big.json) WHERE atom IS NOT NULL ORDER BY +big.rowid, +json_tree.id } $correct_answer | > > > > > > | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 | 2 {$.partlist[1].uuid} c690dc14-572e-11e5-95f9-dfc8861fd535] do_execsql_test json102-1110 { SELECT big.rowid, fullkey, value FROM big, json_tree(big.json) WHERE json_tree.type NOT IN ('object','array') ORDER BY +big.rowid, +json_tree.id } $correct_answer do_execsql_test json102-1110b { SELECT big.rowid, fullkey, value FROM big, json_tree(jsonb(big.json)) WHERE json_tree.type NOT IN ('object','array') ORDER BY +big.rowid, +json_tree.id } $correct_answer do_execsql_test json102-1120 { SELECT big.rowid, fullkey, atom FROM big, json_tree(big.json) WHERE atom IS NOT NULL ORDER BY +big.rowid, +json_tree.id } $correct_answer |
︙ | ︙ |
Changes to test/json105.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # This file implements tests for "[#]" extension to json-path # set testdir [file dirname $argv0] source $testdir/tester.tcl | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # This file implements tests for "[#]" extension to json-path # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix json105 # This is the example from pages 2 and 3 of RFC-7396 db eval { CREATE TABLE t1(j); INSERT INTO t1(j) VALUES('{"a":1,"b":[1,[2,3],4],"c":99}'); } proc json_extract_test {testnum path result} { |
︙ | ︙ | |||
92 93 94 95 96 97 98 | json_replace_test 70 {'$.b[1][#-1]','AAA','$.b[#-1]','BBB'} \ {'{"a":1,"b":[1,[2,"AAA"],"BBB"],"c":99}'} json_replace_test 80 {'$.b[#-1]','AAA','$.b[#-1]','BBB'} \ {'{"a":1,"b":[1,[2,3],"BBB"],"c":99}'} do_catchsql_test json105-6.10 { SELECT json_extract(j, '$.b[#-]') FROM t1; | | | | | | | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | json_replace_test 70 {'$.b[1][#-1]','AAA','$.b[#-1]','BBB'} \ {'{"a":1,"b":[1,[2,"AAA"],"BBB"],"c":99}'} json_replace_test 80 {'$.b[#-1]','AAA','$.b[#-1]','BBB'} \ {'{"a":1,"b":[1,[2,3],"BBB"],"c":99}'} do_catchsql_test json105-6.10 { SELECT json_extract(j, '$.b[#-]') FROM t1; } {1 {bad JSON path: '$.b[#-]'}} do_catchsql_test json105-6.20 { SELECT json_extract(j, '$.b[#9]') FROM t1; } {1 {bad JSON path: '$.b[#9]'}} do_catchsql_test json105-6.30 { SELECT json_extract(j, '$.b[#+2]') FROM t1; } {1 {bad JSON path: '$.b[#+2]'}} do_catchsql_test json105-6.40 { SELECT json_extract(j, '$.b[#-1') FROM t1; } {1 {bad JSON path: '$.b[#-1'}} do_catchsql_test json105-6.50 { SELECT json_extract(j, '$.b[#-1x]') FROM t1; } {1 {bad JSON path: '$.b[#-1x]'}} finish_test |
Added test/json106.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 | # 2023-12-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. # #*********************************************************************** # 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} { do_execsql_test $ii.1 { DELETE FROM t1; INSERT INTO t1(j0,j5) VALUES(random_json($ii),random_json5($ii)); SELECT json_valid(j0), json_valid(j5,2) FROM t1; } {1 1} do_execsql_test $ii.2 { SELECT count(*) FROM t1, json_tree(j0) AS rt WHERE rt.type NOT IN ('object','array') AND rt.atom IS NOT (j0 ->> rt.fullkey); } 0 do_execsql_test $ii.3 { SELECT count(*) FROM t1, json_tree(j5) AS rt WHERE rt.type NOT IN ('object','array') AND rt.atom IS NOT (j0 ->> rt.fullkey); } 0 do_execsql_test $ii.4 { DELETE FROM kv; INSERT INTO kv SELECT rt.rowid, rt.fullkey, rt.atom FROM t1, json_tree(j0) AS rt WHERE rt.type NOT IN ('object','array'); } do_execsql_test $ii.5 { SELECT count(*) FROM t1, kv WHERE key NOT LIKE '%]' AND json_remove(j5,key)->>key IS NOT NULL } 0 do_execsql_test $ii.6 { SELECT count(*) FROM t1, kv WHERE key NOT LIKE '%]' AND json_insert(json_remove(j5,key),key,val)->>key IS NOT val } 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.
︙ | ︙ | |||
248 249 250 251 252 253 254 | ############################################################################### # 9) Numbers may be IEEE 754 positive infinity, negative infinity, and NaN. do_execsql_test 9.1 { WITH c(x) AS (VALUES('{x: +Infinity}')) SELECT x->>'x', json(x) FROM c; | | | | | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | ############################################################################### # 9) Numbers may be IEEE 754 positive infinity, negative infinity, and NaN. do_execsql_test 9.1 { WITH c(x) AS (VALUES('{x: +Infinity}')) SELECT x->>'x', json(x) FROM c; } {Inf {{"x":9e999}}} do_execsql_test 9.2 { WITH c(x) AS (VALUES('{x: -Infinity}')) SELECT x->>'x', json(x) FROM c; } {-Inf {{"x":-9e999}}} do_execsql_test 9.3 { WITH c(x) AS (VALUES('{x: Infinity}')) SELECT x->>'x', json(x) FROM c; } {Inf {{"x":9e999}}} do_execsql_test 9.4 { WITH c(x) AS (VALUES('{x: NaN}')) SELECT x->>'x', json(x) FROM c; } {{} {{"x":null}}} ############################################################################### # 10) Numbers may begin with an explicit plus sign. |
︙ | ︙ | |||
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/json502.test.
︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 | do_catchsql_test 2.2 { SELECT json('{a:null,{"h":[1,[1,2,3]],"j":"abc"}:true}'); } {1 {malformed JSON}} do_catchsql_test 2.3 { SELECT '{a:null,{"h":[1,[1,2,3]],"j":"abc"}:true}'->'$h[#-1]'; } {1 {malformed JSON}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_catchsql_test 2.2 { SELECT json('{a:null,{"h":[1,[1,2,3]],"j":"abc"}:true}'); } {1 {malformed JSON}} do_catchsql_test 2.3 { SELECT '{a:null,{"h":[1,[1,2,3]],"j":"abc"}:true}'->'$h[#-1]'; } {1 {malformed JSON}} # Verify that escaped label names are compared correctly. # do_execsql_test 3.1 { SELECT '{"a\x62c":123}' ->> 'abc'; } 123 do_execsql_test 3.2 { SELECT '{"abc":123}' ->> 'a\x62c'; } 123 db null null do_execsql_test 3.3 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x); INSERT INTO t1 VALUES(json_insert('{}','$.a\',111,'$."b\\"',222)); INSERT INTO t1 VALUES(jsonb_insert('{}','$.a\',111,'$."b\\"',222)); SELECT x->'$.a\', x->'$.a\\', x->'$."a\\"', x->'$."b\\"' FROM t1; } {111 null 111 222 111 null 111 222} do_execsql_test 3.4 { SELECT json_patch('{"a\x62c":123}','{"ab\x63":456}') ->> 'abc'; } 456 ifcapable vtab { do_execsql_test 4.1 { SELECT * FROM json_tree('{"\u0017":1}','$."\x17"'); } {{\x17} 1 integer 1 1 null {$."\x17"} {$}} } finish_test |
Added test/jsonb01.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 | # 2023-11-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 cases for JSONB # set testdir [file dirname $argv0] source $testdir/tester.tcl do_execsql_test jsonb01-1.1 { CREATE TABLE t1(x JSON BLOB); INSERT INTO t1 VALUES(jsonb('{a:5,b:{x:10,y:11},c:[1,2,3,4]}')); } foreach {id path res} { 1 {$.a} {{{"b":{"x":10,"y":11},"c":[1,2,3,4]}}} 2 {$.b} {{{"a":5,"c":[1,2,3,4]}}} 3 {$.c} {{{"a":5,"b":{"x":10,"y":11}}}} 4 {$.d} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} 5 {$.b.x} {{{"a":5,"b":{"y":11},"c":[1,2,3,4]}}} 6 {$.b.y} {{{"a":5,"b":{"x":10},"c":[1,2,3,4]}}} 7 {$.c[0]} {{{"a":5,"b":{"x":10,"y":11},"c":[2,3,4]}}} 8 {$.c[1]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,3,4]}}} 9 {$.c[2]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,4]}}} 10 {$.c[3]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3]}}} 11 {$.c[4]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} 12 {$.c[#]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} 13 {$.c[#-1]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3]}}} 14 {$.c[#-2]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,4]}}} 15 {$.c[#-3]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,3,4]}}} 16 {$.c[#-4]} {{{"a":5,"b":{"x":10,"y":11},"c":[2,3,4]}}} 17 {$.c[#-5]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} 18 {$.c[#-6]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} } { 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 |
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/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/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 | # 2024 January 23 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Test special cases of corrupt database handling in mmap-mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix mmapcorrupt database_may_be_corrupt db close sqlite3_shutdown sqlite3_config_lookaside 0 0 sqlite3_initialize reset_db do_execsql_test 1.0 { PRAGMA page_size = 16384; CREATE TABLE tn1(a PRIMARY KEY) WITHOUT ROWID; CREATE TABLE t0(a PRIMARY KEY) WITHOUT ROWID; CREATE TABLE t1(a PRIMARY KEY) WITHOUT ROWID; INSERT INTO t1 VALUES('B'); } db close set sz [file size test.db] hexio_write test.db [expr $sz-3] 800380 sqlite3 db test.db do_execsql_test 2.1 { PRAGMA mmap_size = 1000000; SELECT sql FROM sqlite_schema LIMIT 1; SELECT * FROM t0; } {1000000 {CREATE TABLE tn1(a PRIMARY KEY) WITHOUT ROWID}} do_execsql_test 2.2 { INSERT INTO t0 SELECT * FROM t1; } finish_test |
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/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 | # 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.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 | | | | | | 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 | 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 | | | | | | | | 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 | 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 | > > > > > > > > > > > > > > > | 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 | } {{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.
︙ | ︙ | |||
93 94 95 96 97 98 99 | 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}] | | | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | 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 { |
︙ | ︙ |
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/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 |
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 |
︙ | ︙ |
Deleted test/releasetest_data.tcl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
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. |
︙ | ︙ | |||
492 493 494 495 496 497 498 | do_test shell1-3.14.3 { # too many arguments catchcmd "test.db" ".nullvalue FOO BAD" } {1 {Usage: .nullvalue STRING}} # .output FILENAME Send output to FILENAME do_test shell1-3.15.1 { | | > | | > > > | > | 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 | do_test shell1-3.14.3 { # too many arguments catchcmd "test.db" ".nullvalue FOO BAD" } {1 {Usage: .nullvalue STRING}} # .output FILENAME Send output to FILENAME do_test shell1-3.15.1 { catchcmd "test.db" ".output .print x" } {0 x} do_test shell1-3.15.2 { catchcmd "test.db" ".output FOO .print x .output SELECT readfile('FOO');" } {0 {x }} do_test shell1-3.15.3 { # too many arguments catchcmd "test.db" ".output FOO BAD" } {1 {ERROR: extra parameter: "BAD". Usage: .output ?FILE? Send output to FILE or stdout if FILE is omitted If FILE begins with '|' then open it as a pipe. Options: |
︙ | ︙ |
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 |
︙ | ︙ |
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/snapshot_up.test.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | # is that each connection opened as part of this permutation executes # "PRAGMA journal_mode=memory", which fails if the database is in wal mode # and there are one or more existing connections. if {[permutation]=="inmemory_journal"} { finish_test return } do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); INSERT INTO t1 VALUES(7, 8, 9); | > > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # is that each connection opened as part of this permutation executes # "PRAGMA journal_mode=memory", which fails if the database is in wal mode # and there are one or more existing connections. if {[permutation]=="inmemory_journal"} { finish_test return } db timeout 1000 do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); INSERT INTO t1 VALUES(7, 8, 9); |
︙ | ︙ |
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/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 61 62 63 | 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: --jobs NUMBER-OF-JOBS --zipvfs ZIPVFS-SOURCE-DIR | > > > > > > > | > > > > | > | | > > > > > > > | 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 | 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 --dryrun --explain --jobs NUMBER-OF-JOBS --stop-on-coredump --stop-on-error --zipvfs ZIPVFS-SOURCE-DIR 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 } #------------------------------------------------------------------------- #------------------------------------------------------------------------- |
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 | } } } 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 | > > > > | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | } } } 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 |
︙ | ︙ | |||
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 | set TRG(timeout) 10000 ;# Default busy-timeout for testrunner.db 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 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 | > > > > > > > | > | 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 | set TRG(timeout) 10000 ;# Default busy-timeout for testrunner.db 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 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!" } } |
︙ | ︙ | |||
318 319 320 321 322 323 324 325 326 327 328 329 330 331 | 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 } | > > > > > > > > | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | 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 } |
︙ | ︙ | |||
421 422 423 424 425 426 427 | if {[string range $a 0 0]=="-"} { if {($n>2 && [string match "$a*" --jobs]) || $a=="-j"} { incr ii set TRG(nJob) [lindex $argv $ii] if {$isLast} { usage } } elseif {($n>2 && [string match "$a*" --zipvfs]) || $a=="-z"} { incr ii | | > > > > > > > > > > | 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 | if {[string range $a 0 0]=="-"} { if {($n>2 && [string match "$a*" --jobs]) || $a=="-j"} { incr ii set TRG(nJob) [lindex $argv $ii] 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 } elseif {($n>2 && [string match "$a*" --explain]) || $a=="-e"} { set TRG(explain) 1 } 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] } } |
︙ | ︙ | |||
605 606 607 608 609 610 611 | $state ) } trdb last_insert_rowid } | > > > > > > > > > | | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | $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] |
︙ | ︙ | |||
654 655 656 657 658 659 660 661 662 663 664 | } 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 \ | > > > | < | > > > > > > | > > > > > > > > > > > > > > > > > | 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 | } 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\\* ." |
︙ | ︙ | |||
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 | -displayname "Zipvfs [file tail $s]" \ -cmd $cmd \ -depid [lindex $bld 0] } set ::env(SQLITE_TEST_DIR) $::testdir } 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 { set patternlist [lrange $patternlist 1 end] set clist [trd_all_configs] foreach c $clist { add_tcl_jobs "" $c $patternlist } } mdevtest { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < > > | < | < > > | > | | | | | > > > > > > > > > | 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 941 942 943 944 945 946 | -displayname "Zipvfs [file tail $s]" \ -cmd $cmd \ -depid [lindex $bld 0] } set ::env(SQLITE_TEST_DIR) $::testdir } # 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)] { 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 } } } |
︙ | ︙ | |||
829 830 831 832 833 834 835 836 837 838 839 840 841 842 | trdb eval { REPLACE INTO config VALUES('cmdline', $cmdline ); } trdb eval { REPLACE INTO config VALUES('start', $tm ); } add_jobs_from_cmdline $TRG(patternlist) } } proc script_input_ready {fd iJob jobid} { global TRG global O global T if {[eof $fd]} { | > > > > > > > > > > > | 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 | trdb eval { REPLACE INTO config VALUES('cmdline', $cmdline ); } trdb eval { REPLACE INTO config VALUES('start', $tm ); } 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; UPDATE jobs SET state='ready' WHERE depid=$jobid; } } } proc script_input_ready {fd iJob jobid} { global TRG global O global T if {[eof $fd]} { |
︙ | ︙ | |||
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | 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)] | > > > > > > > > < | < < < < < < < | 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 | 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)] mark_job_as_finished $jobid $O($iJob) $state $tm dirs_freeDir $iJob launch_some_jobs incr ::wakeup } else { set rc [catch { gets $fd line } res] if {$rc} { |
︙ | ︙ | |||
924 925 926 927 928 929 930 | set script [trd_buildscript $job(build) $srcdir $bWin] } set fd [open [file join $dir $TRG(make)] w] puts $fd $script close $fd } | > > > > > > > > > > > > > > > > > > > > > | | | > | | | | | | > | 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 | 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)] } return 1 } proc one_line_report {} { global TRG |
︙ | ︙ | |||
1031 1032 1033 1034 1035 1036 1037 | } } puts "\nTest database is $TRG(dbname)" puts "Test log is $TRG(logname)" } | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > | > < | 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 | } } 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.
︙ | ︙ | |||
26 27 28 29 30 31 32 | 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 | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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 set tcltest(osx.Apple) all_less_no_mutex_try set tcltest(win.Stdcall) veryquick set tcltest(win.Have-Not) veryquick set tcltest(win.Windows-Memdebug) veryquick set tcltest(win.Windows-Win32Heap) veryquick set tcltest(win.Windows-Sanitize) veryquick set tcltest(win.Default) full |
︙ | ︙ | |||
96 97 98 99 100 101 102 | --enable-debug --enable-all } set build(All-O0) { -O0 --enable-all } set build(All-Sanitize) { -DSQLITE_OMIT_LOOKASIDE=1 | | | > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | --enable-debug --enable-all } set build(All-O0) { -O0 --enable-all } set build(All-Sanitize) { -DSQLITE_OMIT_LOOKASIDE=1 --enable-all -fsanitize=address,undefined -fno-sanitize-recover=undefined } 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 } |
︙ | ︙ | |||
263 264 265 266 267 268 269 270 271 272 273 274 275 276 | -DSQLITE_ENABLE_FLOCKTIMEOUT=1 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_ENABLE_FTS3_TOKENIZER=1 -DSQLITE_ENABLE_PERSIST_WAL=1 -DSQLITE_ENABLE_PURGEABLE_PCACHE=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_SNAPSHOT=1 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 -DSQLITE_MAX_LENGTH=2147483645 -DSQLITE_MAX_VARIABLE_NUMBER=500000 -DSQLITE_NO_SYNC=1 -DSQLITE_OMIT_AUTORESET=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 | > | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | -DSQLITE_ENABLE_FLOCKTIMEOUT=1 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_ENABLE_FTS3_TOKENIZER=1 -DSQLITE_ENABLE_PERSIST_WAL=1 -DSQLITE_ENABLE_PURGEABLE_PCACHE=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_SETLK_TIMEOUT=2 -DSQLITE_ENABLE_SNAPSHOT=1 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 -DSQLITE_MAX_LENGTH=2147483645 -DSQLITE_MAX_VARIABLE_NUMBER=500000 -DSQLITE_NO_SYNC=1 -DSQLITE_OMIT_AUTORESET=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 |
︙ | ︙ | |||
362 363 364 365 366 367 368 369 370 371 372 373 374 375 | if {[info exists tcltest($platform.$bld)]} { set clist $tcltest($platform.$bld) if {$clist=="all"} { set clist $all_configs } elseif {$clist=="all_plus_autovacuum_crash"} { set clist [concat $all_configs autovacuum_crash] } } set clist } proc trd_extras {platform bld} { | > > > | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | if {[info exists tcltest($platform.$bld)]} { set clist $tcltest($platform.$bld) if {$clist=="all"} { set clist $all_configs } elseif {$clist=="all_plus_autovacuum_crash"} { set clist [concat $all_configs autovacuum_crash] } elseif {$clist=="all_less_no_mutex_try"} { set idx [lsearch $all_configs no_mutex_try] set clist [lreplace $all_configs $idx $idx] } } set clist } proc trd_extras {platform bld} { |
︙ | ︙ | |||
590 591 592 593 594 595 596 | # 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)]} { | > | > > > > | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 | # 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: |
︙ | ︙ | |||
629 630 631 632 633 634 635 | } set trd_test_script_properties_cache($path) $ret close $fd } set trd_test_script_properties_cache($path) } | < | 639 640 641 642 643 644 645 | } 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/trace3.test.
︙ | ︙ | |||
128 129 130 131 132 133 134 | SELECT a, b FROM t1 ORDER BY a; } set stmt [lindex [lindex $::stmtlist(record) 0] 0] set ns [lindex [lindex $::stmtlist(record) 0] 1] list $stmt [expr {$ns >= 0 && $ns <= 9999999}]; # less than 0.010 seconds } {/^-?\d+ 1$/} do_test trace3-4.4 { | > > | | | | | | | | > > > > > > > > > > > | 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 | SELECT a, b FROM t1 ORDER BY a; } set stmt [lindex [lindex $::stmtlist(record) 0] 0] set ns [lindex [lindex $::stmtlist(record) 0] 1] list $stmt [expr {$ns >= 0 && $ns <= 9999999}]; # less than 0.010 seconds } {/^-?\d+ 1$/} do_test trace3-4.4 { set cnt 0 while {1} { set ::stmtlist(record) {} db trace_v2 trace_v2_record 2 execsql { SELECT a, b FROM t1 ORDER BY a; } set stmt [lindex [lindex $::stmtlist(record) 0] 0] set ns [lindex [lindex $::stmtlist(record) 0] 1] if {$ns<0 || $ns>9999999} { #less than 0.010 seconds incr cnt if {$cnt>3} { set res "time out of bounds. Expected less than 99999999. Got $ns" break } } else { set res 1 break } } list $stmt $res } {/^-?\d+ 1$/} do_test trace3-5.1 { set ::stmtlist(record) {} db trace_v2 trace_v2_record row execsql { SELECT a, b FROM t1 ORDER BY a; |
︙ | ︙ |
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 353 354 355 | } {} 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 {} {} +} 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); | > > > > > > > | 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 {no such column: 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/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 |
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 | # 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 } 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 |
Deleted test/wapp.tcl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted test/wapptest.tcl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to test/where3.test.
︙ | ︙ | |||
487 488 489 490 491 492 493 494 495 496 | do_execsql_test where3-7.$disabled_opt.8 { SELECT x3 FROM t73 LEFT JOIN t74 ON x4=y3; } {123 123} do_execsql_test where3-7.$disabled_opt.9 { SELECT DISTINCT x3 FROM t73 LEFT JOIN t74 ON x4=y3; } {123} } finish_test | > > > > > > > > > > > > > > > > > > > > > > | 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 | do_execsql_test where3-7.$disabled_opt.8 { SELECT x3 FROM t73 LEFT JOIN t74 ON x4=y3; } {123 123} do_execsql_test where3-7.$disabled_opt.9 { SELECT DISTINCT x3 FROM t73 LEFT JOIN t74 ON x4=y3; } {123} } # 2023-12-23 # https://sqlite.org/forum/forumpost/2568d1f6e6 # # Index usage should be "x=? and y=?" - equality on both values. # Not: "x=? AND y>?" - inequality on "y" # reset_db do_execsql_test where3-8.1 { CREATE TABLE t1(a,b,c,d); INSERT INTO t1 VALUES(1,2,3,4); CREATE TABLE t2(x,y); INSERT INTO t2 VALUES(3,4); CREATE INDEX t2xy ON t2(x,y); SELECT 1 FROM t1 JOIN t2 ON x=c AND y=d WHERE d>0; } 1 do_eqp_test where3-8.2 { SELECT 1 FROM t1 JOIN t2 ON x=c AND y=d WHERE d>0; } { QUERY PLAN |--SCAN t1 `--SEARCH t2 USING COVERING INDEX t2xy (x=? AND y=?) } finish_test |
Changes to test/whereG.test.
︙ | ︙ | |||
307 308 309 310 311 312 313 314 315 316 317 318 319 320 | } {0 0 0} do_execsql_test 8.9 { SELECT * FROM t0 WHERE unlikely(t0.rowid <= '0'); } {} do_execsql_test 8.10 { SELECT * FROM t0 WHERE likelihood(t0.rowid <= '0', 0.5); } {} # 2019-12-31: assertion fault discovered by Yongheng's fuzzer. # Harmless memIsValid() due to the code generators failure to # release the registers used by OP_ResultRow. # do_execsql_test 9.10 { DROP TABLE IF EXISTS t1; | > > > > > > > > > > > > > > | 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 | } {0 0 0} do_execsql_test 8.9 { SELECT * FROM t0 WHERE unlikely(t0.rowid <= '0'); } {} do_execsql_test 8.10 { SELECT * FROM t0 WHERE likelihood(t0.rowid <= '0', 0.5); } {} # Forum https://sqlite.org/forum/forumpost/45ec3d9788 reset_db do_execsql_test 8.11 { CREATE TABLE t1(c0 INT); INSERT INTO t1(c0) VALUES (NULL); CREATE INDEX i46 ON t1(CAST( (c0 IS TRUE) AS TEXT)); CREATE VIEW v0(c2) AS SELECT CAST( (c0 IS TRUE) AS TEXT ) FROM t1; } do_execsql_test 8.12 { SELECT quote(c0), quote(c2) FROM t1, v0 WHERE (0 < LIKELY(v0.c2)); } {NULL '0'} do_execsql_test 8.13 { SELECT quote(c0), quote(c2) FROM t1, v0 WHERE (0 < LIKELY(v0.c2)) IS TRUE; } {NULL '0'} # 2019-12-31: assertion fault discovered by Yongheng's fuzzer. # Harmless memIsValid() due to the code generators failure to # release the registers used by OP_ResultRow. # do_execsql_test 9.10 { DROP TABLE IF EXISTS t1; |
︙ | ︙ |
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 |
Changes to test/window1.test.
︙ | ︙ | |||
1877 1878 1879 1880 1881 1882 1883 | INSERT INTO t1(a) VALUES(22); CREATE TABLE t3(y); INSERT INTO t3(y) VALUES(5),(11),(-9); SELECT ( SELECT max(y) OVER( ORDER BY (SELECT x FROM (SELECT sum(y) AS x FROM t1))) ) FROM t3; | | | 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 | INSERT INTO t1(a) VALUES(22); CREATE TABLE t3(y); INSERT INTO t3(y) VALUES(5),(11),(-9); SELECT ( SELECT max(y) OVER( ORDER BY (SELECT x FROM (SELECT sum(y) AS x FROM t1))) ) FROM t3; } {0 5} # 2020-06-06 ticket 1f6f353b684fc708 reset_db do_execsql_test 58.1 { CREATE TABLE a(a, b, c); INSERT INTO a VALUES(1, 2, 3); INSERT INTO a VALUES(4, 5, 6); |
︙ | ︙ |
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/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 ; } |
Changes to tool/srctree-check.tcl.
︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | # Name of the TCL interpreter # set TCLSH [info nameofexe] # Number of errors seen. # set NERR 0 ######################### autoconf/tea/configure.ac ########################### set confac [readfile $ROOT/autoconf/tea/configure.ac] set vers [readfile $ROOT/VERSION] set pattern {AC_INIT([sqlite],[} append pattern [string trim $vers] append pattern {])} if {[string first $pattern $confac]<=0} { puts "ERROR: ./autoconf/tea/configure.ac does not agree with ./VERSION" | > > > > > > > > > > > | > > | 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 | # Name of the TCL interpreter # set TCLSH [info nameofexe] # Number of errors seen. # set NERR 0 ######################### configure ########################################### set conf [readfile $ROOT/configure] set vers [readfile $ROOT/VERSION] if {[string first $vers $conf]<=0} { puts "ERROR: ./configure does not agree with ./VERSION" puts "...... Fix: run autoconf" incr NERR } unset conf ######################### autoconf/tea/configure.ac ########################### set confac [readfile $ROOT/autoconf/tea/configure.ac] set vers [readfile $ROOT/VERSION] set pattern {AC_INIT([sqlite],[} append pattern [string trim $vers] append pattern {])} if {[string first $pattern $confac]<=0} { puts "ERROR: ./autoconf/tea/configure.ac does not agree with ./VERSION" puts "...... Fix: manually edit ./autoconf/tea/configure.ac and put the" puts "...... correct version number in AC_INIT()" incr NERR } unset confac ######################### autoconf/Makefile.msc ############################### set f1 [readfile $ROOT/autoconf/Makefile.msc] exec $TCLSH $ROOT/tool/mkmsvcmin.tcl $ROOT/Makefile.msc tmp1.txt set f2 [readfile tmp1.txt] file delete tmp1.txt |
︙ | ︙ |