Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch est_count_pragma Excluding Merge-Ins
This is equivalent to a diff from fd81d8a430 to 8d2a1cca61
2017-09-22
| ||
20:18 | Merge in all the trunk enhancements of the previous 7 months. The LIKE optimization has stopped working when there is an ESCAPE - that problem will be addressed in a subsequent check-in. (Leaf check-in: 8d2a1cca61 user: drh tags: est_count_pragma) | |
16:23 | Use the updated Win32 VFS semantics for winOpen from check-in [5d03c738e9] for WinRT, et al, as well. (check-in: 2c03d8b8f0 user: mistachkin tags: trunk) | |
2017-02-16
| ||
14:02 | Merge recent enhancements from trunk. (check-in: 325ccfa95e user: drh tags: est_count_pragma) | |
2016-10-20
| ||
22:02 | Experimental est_count pragma. (check-in: 340822afbe user: drh tags: est_count_pragma) | |
18:20 | Add the ability for the PRAGMA statement to accept multiple arguments. Currently all arguments other than the first are ignored. (Leaf check-in: fd81d8a430 user: drh tags: multi-arg-pragma) | |
2016-10-18
| ||
16:36 | Minor simplification of the comparison opcodes. (check-in: 56474ebca3 user: drh tags: trunk) | |
Changes to Makefile.in.
︙ | ︙ | |||
177 178 179 180 181 182 183 | icu.lo insert.lo json1.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ random.lo resolve.lo rowset.lo rtree.lo \ | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | icu.lo insert.lo json1.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ random.lo resolve.lo rowset.lo rtree.lo \ sqlite3session.lo select.lo sqlite3rbu.lo status.lo stmt.lo \ table.lo threads.lo tokenize.lo treeview.lo trigger.lo \ update.lo util.lo vacuum.lo \ vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \ vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \ utf.lo vtab.lo # Object files for the amalgamation. |
︙ | ︙ | |||
346 347 348 349 350 351 352 | SRC += \ $(TOP)/ext/session/sqlite3session.c \ $(TOP)/ext/session/sqlite3session.h SRC += \ $(TOP)/ext/rbu/sqlite3rbu.h \ $(TOP)/ext/rbu/sqlite3rbu.c SRC += \ | | > | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | SRC += \ $(TOP)/ext/session/sqlite3session.c \ $(TOP)/ext/session/sqlite3session.h SRC += \ $(TOP)/ext/rbu/sqlite3rbu.h \ $(TOP)/ext/rbu/sqlite3rbu.c SRC += \ $(TOP)/ext/misc/json1.c \ $(TOP)/ext/misc/stmt.c # Generated source code files # SRC += \ keywordhash.h \ opcodes.c \ opcodes.h \ |
︙ | ︙ | |||
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_tok.c \ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/wholenumber.c # Source code to the library files needed by the test fixture # TESTSRC2 = \ $(TOP)/src/attach.c \ $(TOP)/src/backup.c \ | > > > | 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 | $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_tok.c \ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/mmapwarm.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.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 # Source code to the library files needed by the test fixture # TESTSRC2 = \ $(TOP)/src/attach.c \ $(TOP)/src/backup.c \ |
︙ | ︙ | |||
478 479 480 481 482 483 484 | $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c \ | | > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 | $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c \ $(TOP)/ext/session/sqlite3session.c \ $(TOP)/ext/misc/stmt.c # Header files used by all library source files. # HDR = \ $(TOP)/src/btree.h \ $(TOP)/src/btreeInt.h \ $(TOP)/src/hash.h \ |
︙ | ︙ | |||
546 547 548 549 550 551 552 | # Databases containing fuzzer test cases # FUZZDATA = \ $(TOP)/test/fuzzdata1.db \ $(TOP)/test/fuzzdata2.db \ $(TOP)/test/fuzzdata3.db \ | | > > | > > > | 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 | # Databases containing fuzzer test cases # FUZZDATA = \ $(TOP)/test/fuzzdata1.db \ $(TOP)/test/fuzzdata2.db \ $(TOP)/test/fuzzdata3.db \ $(TOP)/test/fuzzdata4.db \ $(TOP)/test/fuzzdata5.db # Standard options to testfixture # TESTOPTS = --verbose=file --output=test-out.txt # Extra compiler options for various shell tools # SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 # SHELL_OPT += -DSQLITE_ENABLE_FTS5 SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c DBFUZZ_OPT = # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # all: sqlite3.h libsqlite3.la sqlite3$(TEXE) $(HAVE_TCL:1=libtclsqlite3.la) Makefile: $(TOP)/Makefile.in |
︙ | ︙ | |||
608 609 610 611 612 613 614 | sourcetest: srcck1$(BEXE) sqlite3.c ./srcck1 sqlite3.c fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(FUZZERSHELL_OPT) \ $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS) | | > > > | > > > > | 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 | sourcetest: srcck1$(BEXE) sqlite3.c ./srcck1 sqlite3.c fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(FUZZERSHELL_OPT) \ $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS) fuzzcheck$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(LTLINK) -o $@ $(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) dbfuzz$(TEXE): $(TOP)/test/dbfuzz.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(DBFUZZ_OPT) $(TOP)/test/dbfuzz.c sqlite3.c $(TLIBS) mptester$(TEXE): sqlite3.lo $(TOP)/mptest/mptest.c $(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.lo \ $(TLIBS) -rpath "$(libdir)" MPTEST1=./mptester$(TEXE) mptest.db $(TOP)/mptest/crash01.test --repeat 20 MPTEST2=./mptester$(TEXE) mptest.db $(TOP)/mptest/multiwrite01.test --repeat 20 |
︙ | ︙ | |||
672 673 674 675 676 677 678 679 680 681 682 683 684 685 | $(LTCOMPILE) $(TEMP_STORE) -c sqlite3.c # Rules to build the LEMON compiler generator # lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c $(BCC) -o $@ $(TOP)/tool/lemon.c cp $(TOP)/tool/lempar.c . # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o # opcodes.o # | > > > > > | 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | $(LTCOMPILE) $(TEMP_STORE) -c sqlite3.c # Rules to build the LEMON compiler generator # lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c $(BCC) -o $@ $(TOP)/tool/lemon.c cp $(TOP)/tool/lempar.c . # Rules to build the program that generates the source-id # mksourceid$(BEXE): $(TOP)/tool/mksourceid.c $(BCC) -o $@ $(TOP)/tool/mksourceid.c # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o # opcodes.o # |
︙ | ︙ | |||
938 939 940 941 942 943 944 | parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/tool/addopcodes.tcl cp $(TOP)/src/parse.y . rm -f parse.h ./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y mv parse.h parse.h.temp $(TCLSH_CMD) $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h | | | 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 | parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/tool/addopcodes.tcl cp $(TOP)/src/parse.y . rm -f parse.h ./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y mv parse.h parse.h.temp $(TCLSH_CMD) $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION $(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash$(BEXE) >keywordhash.h |
︙ | ︙ | |||
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | sqlite3session.lo: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c json1.lo: $(TOP)/ext/misc/json1.c $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c # FTS5 things # FTS5_SRC = \ $(TOP)/ext/fts5/fts5.h \ $(TOP)/ext/fts5/fts5Int.h \ $(TOP)/ext/fts5/fts5_aux.c \ $(TOP)/ext/fts5/fts5_buffer.c \ | > > > | 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 | sqlite3session.lo: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c json1.lo: $(TOP)/ext/misc/json1.c $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c stmt.lo: $(TOP)/ext/misc/stmt.c $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c # FTS5 things # FTS5_SRC = \ $(TOP)/ext/fts5/fts5.h \ $(TOP)/ext/fts5/fts5Int.h \ $(TOP)/ext/fts5/fts5_aux.c \ $(TOP)/ext/fts5/fts5_buffer.c \ |
︙ | ︙ | |||
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 | # hidden when the library is built via the amalgamation). # TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE TESTFIXTURE_FLAGS += -DBUILD_sqlite TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC1 = sqlite3.c TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION)) testfixture$(TEXE): $(TESTFIXTURE_SRC) | > | 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 | # hidden when the library is built via the amalgamation). # TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE TESTFIXTURE_FLAGS += -DBUILD_sqlite TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC1 = sqlite3.c TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION)) testfixture$(TEXE): $(TESTFIXTURE_SRC) |
︙ | ︙ | |||
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 | fastfuzztest: fuzzcheck$(TEXE) $(FUZZDATA) ./fuzzcheck$(TEXE) --limit-mem 100M $(FUZZDATA) valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA) # Minimal testing that runs in less than 3 minutes # quicktest: ./testfixture$(TEXE) ./testfixture$(TEXE) $(TOP)/test/extraquick.test $(TESTOPTS) # This is the common case. Run many tests that do not take too long, # including fuzzcheck, sqlite3_analyzer, and sqldiff tests. # | > > > > > | < | 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 | fastfuzztest: fuzzcheck$(TEXE) $(FUZZDATA) ./fuzzcheck$(TEXE) --limit-mem 100M $(FUZZDATA) valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA) # The veryquick.test TCL tests. # tcltest: ./testfixture$(TEXE) ./testfixture$(TEXE) $(TOP)/test/veryquick.test $(TESTOPTS) # Minimal testing that runs in less than 3 minutes # quicktest: ./testfixture$(TEXE) ./testfixture$(TEXE) $(TOP)/test/extraquick.test $(TESTOPTS) # This is the common case. Run many tests that do not take too long, # including fuzzcheck, sqlite3_analyzer, and sqldiff tests. # test: fastfuzztest sourcetest $(TESTPROGS) tcltest # Run a test using valgrind. This can take a really long time # because valgrind is so much slower than a native machine. # valgrindtest: $(TESTPROGS) valgrindfuzz OMIT_MISUSE=1 valgrind -v ./testfixture$(TEXE) $(TOP)/test/permutations.test valgrind $(TESTOPTS) |
︙ | ︙ | |||
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 | echo "static const char *zMainloop = " >> $@ $(TCLSH_CMD) $(TOP)/tool/tostr.tcl $(TOP)/tool/spaceanal.tcl >> $@ echo "; return zMainloop; }" >> $@ sqlite3_analyzer$(TEXE): sqlite3_analyzer.c $(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS) showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.lo $(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.lo $(TLIBS) showstat4$(TEXE): $(TOP)/tool/showstat4.c sqlite3.lo $(LTLINK) -o $@ $(TOP)/tool/showstat4.c sqlite3.lo $(TLIBS) showjournal$(TEXE): $(TOP)/tool/showjournal.c sqlite3.lo | > > > > | 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 | echo "static const char *zMainloop = " >> $@ $(TCLSH_CMD) $(TOP)/tool/tostr.tcl $(TOP)/tool/spaceanal.tcl >> $@ echo "; return zMainloop; }" >> $@ sqlite3_analyzer$(TEXE): sqlite3_analyzer.c $(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS) dbdump$(TEXE): $(TOP)/ext/misc/dbdump.c sqlite3.lo $(LTLINK) -DDBDUMP_STANDALONE -o $@ \ $(TOP)/ext/misc/dbdump.c sqlite3.lo $(TLIBS) showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.lo $(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.lo $(TLIBS) showstat4$(TEXE): $(TOP)/tool/showstat4.c sqlite3.lo $(LTLINK) -o $@ $(TOP)/tool/showstat4.c sqlite3.lo $(TLIBS) showjournal$(TEXE): $(TOP)/tool/showjournal.c sqlite3.lo |
︙ | ︙ | |||
1159 1160 1161 1162 1163 1164 1165 | LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h $(LTLINK) -I. -o $@ $(TOP)/tool/logest.c wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo $(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.lo $(TLIBS) | | | > > > > > | 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 | LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h $(LTLINK) -I. -o $@ $(TOP)/tool/logest.c wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo $(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.lo $(TLIBS) speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.c $(LTLINK) $(ST_OPT) -o $@ $(TOP)/test/speedtest1.c sqlite3.c $(TLIBS) KV_OPT += -DSQLITE_DIRECT_OVERFLOW_READ kvtest$(TEXE): $(TOP)/test/kvtest.c sqlite3.c $(LTLINK) $(KV_OPT) -o $@ $(TOP)/test/kvtest.c sqlite3.c $(TLIBS) rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.lo $(LTLINK) -I. -o $@ $(TOP)/ext/rbu/rbu.c sqlite3.lo $(TLIBS) loadfts$(EXE): $(TOP)/tool/loadfts.c libsqlite3.la $(LTLINK) $(TOP)/tool/loadfts.c libsqlite3.la -o $@ $(TLIBS) |
︙ | ︙ |
Changes to Makefile.msc.
︙ | ︙ | |||
17 18 19 20 21 22 23 | USE_AMALGAMATION = 1 !ENDIF # <</mark>> # Set this non-0 to enable full warnings (-W4, etc) when compiling. # !IFNDEF USE_FULLWARN | | > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | USE_AMALGAMATION = 1 !ENDIF # <</mark>> # Set this non-0 to enable full warnings (-W4, etc) when compiling. # !IFNDEF USE_FULLWARN USE_FULLWARN = 1 !ENDIF # Set this non-0 to enable treating warnings as errors (-WX, etc) when # compiling. # !IFNDEF USE_FATAL_WARN USE_FATAL_WARN = 0 !ENDIF # Set this non-0 to enable full runtime error checks (-RTC1, etc). This # has no effect if (any) optimizations are enabled. # !IFNDEF USE_RUNTIME_CHECKS USE_RUNTIME_CHECKS = 0 |
︙ | ︙ | |||
488 489 490 491 492 493 494 495 496 497 498 499 500 501 | # same unless your are cross-compiling.) # !IF $(USE_FULLWARN)!=0 TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS) !ELSE TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS) !ENDIF TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS) # Check if we want to use the "stdcall" calling convention when compiling. # This is not supported by the compilers for non-x86 platforms. It should # also be noted here that building any target with these "stdcall" options | > > > > > > | 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 | # same unless your are cross-compiling.) # !IF $(USE_FULLWARN)!=0 TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS) !ELSE TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS) !ENDIF # Check if warnings should be treated as errors when compiling. # !IF $(USE_FATAL_WARN)!=0 TCC = $(TCC) -WX !ENDIF TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS) # Check if we want to use the "stdcall" calling convention when compiling. # This is not supported by the compilers for non-x86 platforms. It should # also be noted here that building any target with these "stdcall" options |
︙ | ︙ | |||
729 730 731 732 733 734 735 736 737 738 739 740 741 742 | TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1 RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1 !ENDIF !IF $(DEBUG)>2 TCC = $(TCC) -DSQLITE_DEBUG=1 RCC = $(RCC) -DSQLITE_DEBUG=1 !ENDIF !IF $(DEBUG)>4 || $(OSTRACE)!=0 TCC = $(TCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1 RCC = $(RCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1 !ENDIF | > > > > | 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1 RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1 !ENDIF !IF $(DEBUG)>2 TCC = $(TCC) -DSQLITE_DEBUG=1 RCC = $(RCC) -DSQLITE_DEBUG=1 !IF $(DYNAMIC_SHELL)==0 TCC = $(TCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE RCC = $(RCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE !ENDIF !ENDIF !IF $(DEBUG)>4 || $(OSTRACE)!=0 TCC = $(TCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1 RCC = $(RCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1 !ENDIF |
︙ | ︙ | |||
790 791 792 793 794 795 796 | !ENDIF !IFNDEF TCLLIBDIR TCLLIBDIR = c:\tcl\lib !ENDIF !IFNDEF LIBTCL | | | | 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 | !ENDIF !IFNDEF TCLLIBDIR TCLLIBDIR = c:\tcl\lib !ENDIF !IFNDEF LIBTCL LIBTCL = tcl86.lib !ENDIF !IFNDEF LIBTCLSTUB LIBTCLSTUB = tclstub86.lib !ENDIF !IFNDEF LIBTCLPATH LIBTCLPATH = c:\tcl\bin !ENDIF # The locations of the ICU header and library files. These variables |
︙ | ︙ | |||
824 825 826 827 828 829 830 | # This is the command to use for tclsh - normally just "tclsh", but we may # know the specific version we want to use. This variable (TCLSH_CMD) may be # overridden via the environment prior to running nmake in order to select a # specific Tcl shell to use. # !IFNDEF TCLSH_CMD | | | 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 | # This is the command to use for tclsh - normally just "tclsh", but we may # know the specific version we want to use. This variable (TCLSH_CMD) may be # overridden via the environment prior to running nmake in order to select a # specific Tcl shell to use. # !IFNDEF TCLSH_CMD TCLSH_CMD = tclsh !ENDIF # <</mark>> # Compiler options needed for programs that use the readline() library. # !IFNDEF READLINE_FLAGS READLINE_FLAGS = -DHAVE_READLINE=0 |
︙ | ︙ | |||
1273 1274 1275 1276 1277 1278 1279 | $(TOP)\ext\fts3\fts3_unicode.c \ $(TOP)\ext\fts3\fts3_unicode2.c \ $(TOP)\ext\fts3\fts3_write.c \ $(TOP)\ext\icu\icu.c \ $(TOP)\ext\rtree\rtree.c \ $(TOP)\ext\session\sqlite3session.c \ $(TOP)\ext\rbu\sqlite3rbu.c \ | | > | 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 | $(TOP)\ext\fts3\fts3_unicode.c \ $(TOP)\ext\fts3\fts3_unicode2.c \ $(TOP)\ext\fts3\fts3_write.c \ $(TOP)\ext\icu\icu.c \ $(TOP)\ext\rtree\rtree.c \ $(TOP)\ext\session\sqlite3session.c \ $(TOP)\ext\rbu\sqlite3rbu.c \ $(TOP)\ext\misc\json1.c \ $(TOP)\ext\misc\stmt.c # Extension header files, part 1. # SRC08 = \ $(TOP)\ext\fts1\fts1.h \ $(TOP)\ext\fts1\fts1_hash.h \ $(TOP)\ext\fts1\fts1_tokenizer.h \ |
︙ | ︙ | |||
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 | $(TOP)\ext\misc\eval.c \ $(TOP)\ext\misc\fileio.c \ $(TOP)\ext\misc\fuzzer.c \ $(TOP)\ext\fts5\fts5_tcl.c \ $(TOP)\ext\fts5\fts5_test_mi.c \ $(TOP)\ext\fts5\fts5_test_tok.c \ $(TOP)\ext\misc\ieee754.c \ $(TOP)\ext\misc\nextchar.c \ $(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\regexp.c \ $(TOP)\ext\misc\series.c \ $(TOP)\ext\misc\spellfix.c \ $(TOP)\ext\misc\totype.c \ $(TOP)\ext\misc\wholenumber.c # Source code to the library files needed by the test fixture # (non-amalgamation) # TESTSRC2 = \ $(SRC00) \ | > > > | 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 | $(TOP)\ext\misc\eval.c \ $(TOP)\ext\misc\fileio.c \ $(TOP)\ext\misc\fuzzer.c \ $(TOP)\ext\fts5\fts5_tcl.c \ $(TOP)\ext\fts5\fts5_test_mi.c \ $(TOP)\ext\fts5\fts5_test_tok.c \ $(TOP)\ext\misc\ieee754.c \ $(TOP)\ext\misc\mmapwarm.c \ $(TOP)\ext\misc\nextchar.c \ $(TOP)\ext\misc\percentile.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 # Source code to the library files needed by the test fixture # (non-amalgamation) # TESTSRC2 = \ $(SRC00) \ |
︙ | ︙ | |||
1475 1476 1477 1478 1479 1480 1481 | # Databases containing fuzzer test cases # FUZZDATA = \ $(TOP)\test\fuzzdata1.db \ $(TOP)\test\fuzzdata2.db \ $(TOP)\test\fuzzdata3.db \ | | > | | > > > > > > | 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 | # Databases containing fuzzer test cases # FUZZDATA = \ $(TOP)\test\fuzzdata1.db \ $(TOP)\test\fuzzdata2.db \ $(TOP)\test\fuzzdata3.db \ $(TOP)\test\fuzzdata4.db \ $(TOP)\test\fuzzdata5.db # <</mark>> # Additional compiler options for the shell. These are only effective # when the shell is not being dynamically linked. # !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_STMTVTAB !ENDIF # <<mark>> # Extra compiler options for various test tools. # MPTESTER_COMPILE_OPTS = -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS5 FUZZERSHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 FUZZCHECK_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.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 DBSELFTEST_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 ST_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 # Standard options to testfixture. # TESTOPTS = --verbose=file --output=test-out.txt # Extra targets for the "all" target that require Tcl. # |
︙ | ︙ | |||
1533 1534 1535 1536 1537 1538 1539 | $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP) $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL $(CORE_LINK_OPTS) /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) # <<block2>> sqlite3.def: libsqlite3.lib echo EXPORTS > sqlite3.def dumpbin /all libsqlite3.lib \ | | | 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 | $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP) $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL $(CORE_LINK_OPTS) /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) # <<block2>> sqlite3.def: libsqlite3.lib echo EXPORTS > sqlite3.def dumpbin /all libsqlite3.lib \ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup)?_[^@]*)(?:@\d+)?$$" \1 \ | sort >> sqlite3.def # <</block2>> $(SQLITE3EXE): $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H) $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c $(SHELL_CORE_SRC) \ /link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) |
︙ | ︙ | |||
1560 1561 1562 1563 1564 1565 1566 | sourcetest: srcck1.exe sqlite3.c srcck1.exe sqlite3.c fuzzershell.exe: $(TOP)\tool\fuzzershell.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(FUZZERSHELL_COMPILE_OPTS) $(TOP)\tool\fuzzershell.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) | | > > > | > > > | 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 | sourcetest: srcck1.exe sqlite3.c srcck1.exe sqlite3.c fuzzershell.exe: $(TOP)\tool\fuzzershell.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(FUZZERSHELL_COMPILE_OPTS) $(TOP)\tool\fuzzershell.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) dbfuzz.exe: $(TOP)\test\dbfuzz.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(DBFUZZ_COMPILE_OPTS) $(TOP)\test\dbfuzz.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) fuzzcheck.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(FUZZCHECK_COMPILE_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) ossshell.exe: $(OSSSHELL_SRC) $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(FUZZCHECK_COMPILE_OPTS) $(OSSSHELL_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) mptester.exe: $(TOP)\mptest\mptest.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(MPTESTER_COMPILE_OPTS) $(TOP)\mptest\mptest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) MPTEST1 = mptester mptest.db $(TOP)\mptest\crash01.test --repeat 20 MPTEST2 = mptester mptest.db $(TOP)\mptest\multiwrite01.test --repeat 20 |
︙ | ︙ | |||
1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 | lempar.c: $(TOP)\tool\lempar.c copy $(TOP)\tool\lempar.c . lemon.exe: $(TOP)\tool\lemon.c lempar.c $(BCC) $(NO_WARN) -Daccess=_access \ -Fe$@ $(TOP)\tool\lemon.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS) # Rules to build individual *.lo files from generated *.c files. This # applies to: # # parse.lo # opcodes.lo # parse.lo: parse.c $(HDR) | > > > > > > | 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 | lempar.c: $(TOP)\tool\lempar.c copy $(TOP)\tool\lempar.c . lemon.exe: $(TOP)\tool\lemon.c lempar.c $(BCC) $(NO_WARN) -Daccess=_access \ -Fe$@ $(TOP)\tool\lemon.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS) # <<mark>> # Rules to build the source-id generator tool # mksourceid.exe: $(TOP)\tool\mksourceid.c $(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\mksourceid.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS) # Rules to build individual *.lo files from generated *.c files. This # applies to: # # parse.lo # opcodes.lo # parse.lo: parse.c $(HDR) |
︙ | ︙ | |||
1911 1912 1913 1914 1915 1916 1917 | parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\tool\addopcodes.tcl del /Q parse.y parse.h parse.h.temp 2>NUL copy $(TOP)\src\parse.y . .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) parse.y move parse.h parse.h.temp $(TCLSH_CMD) $(TOP)\tool\addopcodes.tcl parse.h.temp > parse.h | | | 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 | parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\tool\addopcodes.tcl del /Q parse.y parse.h parse.h.temp 2>NUL copy $(TOP)\src\parse.y . .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) parse.y move parse.h parse.h.temp $(TCLSH_CMD) $(TOP)\tool\addopcodes.tcl parse.h.temp > parse.h $(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) $(MKSQLITE3H_ARGS) sqlite3ext.h: .target_source !IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0 type tsrc\sqlite3ext.h | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*\)" "(SQLITE_CALLBACK *)" \ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*" "(SQLITE_APICALL *" > sqlite3ext.h copy /Y sqlite3ext.h tsrc\sqlite3ext.h |
︙ | ︙ | |||
2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 | # hidden when the library is built via the amalgamation). # TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN) TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024 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 | > | 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 | # hidden when the library is built via the amalgamation). # TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN) 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 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 |
︙ | ︙ | |||
2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 | $(TCLSH_CMD) $(TOP)\tool\tostr.tcl $(TOP)\tool\spaceanal.tcl >> $@ echo ; return zMainloop; } >> $@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS) $(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \ /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) testloadext.lo: $(TOP)\src\test_loadext.c $(LTCOMPILE) $(NO_WARN) -c $(TOP)\src\test_loadext.c testloadext.dll: testloadext.lo $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ testloadext.lo showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C) $(SQLITE3H) | > > > > | | | | | | > | | | | | > > > > > > > | > > > | > | | 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 | $(TCLSH_CMD) $(TOP)\tool\tostr.tcl $(TOP)\tool\spaceanal.tcl >> $@ echo ; return zMainloop; } >> $@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS) $(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \ /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) dbdump.exe: $(TOP)\ext\misc\dbdump.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DDBDUMP_STANDALONE $(TOP)\ext\misc\dbdump.c $(SQLITE3C) \ /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) testloadext.lo: $(TOP)\src\test_loadext.c $(LTCOMPILE) $(NO_WARN) -c $(TOP)\src\test_loadext.c testloadext.dll: testloadext.lo $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ testloadext.lo showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(TOP)\tool\showdb.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) showstat4.exe: $(TOP)\tool\showstat4.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(TOP)\tool\showstat4.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) showjournal.exe: $(TOP)\tool\showjournal.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(TOP)\tool\showjournal.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(TOP)\tool\showwal.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) changeset.exe: $(TOP)\ext\session\changeset.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 \ $(TOP)\ext\session\changeset.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) fts3view.exe: $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) rollback-test.exe: $(TOP)\tool\rollback-test.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(TOP)\tool\rollback-test.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) LogEst.exe: $(TOP)\tool\logest.c $(SQLITE3H) $(LTLINK) $(NO_WARN) $(TOP)\tool\LogEst.c /link $(LDFLAGS) $(LTLINKOPTS) wordcount.exe: $(TOP)\test\wordcount.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(TOP)\test\wordcount.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(ST_COMPILE_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION \ $(TOP)\test\speedtest1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) kvtest.exe: $(TOP)\test\kvtest.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(KV_COMPILE_OPTS) \ $(TOP)\test\kvtest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) dbselftest.exe: $(TOP)\test\dbselftest.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(DBSELFTEST_COMPILE_OPTS) $(TOP)\test\dbselftest.c $(SQLITE3C) rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU \ $(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) LSMDIR=$(TOP)\ext\lsm1 !INCLUDE $(LSMDIR)\Makefile.msc moreclean: clean del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL # <</mark>> clean: del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL del /Q *.bsc *.def *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL del /Q $(SQLITE3EXE) $(SQLITE3DLL) Replace.exe 2>NUL # <<mark>> del /Q sqlite3.c sqlite3.h 2>NUL del /Q opcodes.c opcodes.h 2>NUL del /Q lemon.* lempar.c parse.* 2>NUL del /Q mksourceid.* mkkeywordhash.* keywordhash.h 2>NUL del /Q notasharedlib.* 2>NUL -rmdir /Q/S .deps 2>NUL -rmdir /Q/S .libs 2>NUL -rmdir /Q/S tsrc 2>NUL del /Q .target_source 2>NUL del /Q tclsqlite3.exe $(SQLITETCLH) $(SQLITETCLDECLSH) 2>NUL del /Q lsm.dll lsmtest.exe 2>NUL del /Q testloadext.dll 2>NUL del /Q testfixture.exe test.db 2>NUL del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe dbdump.exe 2>NUL del /Q changeset.exe 2>NUL del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe 2>NUL del /Q mptester.exe wordcount.exe rbu.exe srcck1.exe 2>NUL del /Q sqlite3.c sqlite3-*.c 2>NUL del /Q sqlite3rc.h 2>NUL del /Q shell.c sqlite3ext.h sqlite3session.h 2>NUL del /Q sqlite3_analyzer.exe sqlite3_analyzer.c 2>NUL del /Q sqlite-*-output.vsix 2>NUL del /Q fuzzershell.exe fuzzcheck.exe sqldiff.exe dbhash.exe 2>NUL del /Q fts5.* fts5parse.* 2>NUL # <</mark>> |
Changes to README.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <h1 align="center">SQLite Source Repository</h1> This repository contains the complete source code for the SQLite database engine. Some test scripts are also include. However, many other test scripts and most of the documentation are managed separately. If you are reading this on a Git mirror someplace, you are doing it wrong. The [official repository](https://www.sqlite.org/src/) is better. Go there now. ## Compiling First create a directory in which to place the build products. It is recommended, but not required, that the build directory be separate from the source directory. Cd into the build directory and then from the build directory run the configure script found at the root of the source tree. Then run "make". For example: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | > > | | > | > | | | | < | 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 | <h1 align="center">SQLite Source Repository</h1> This repository contains the complete source code for the SQLite database engine. Some test scripts are also include. However, many other test scripts and most of the documentation are managed separately. If you are reading this on a Git mirror someplace, you are doing it wrong. The [official repository](https://www.sqlite.org/src/) is better. Go there now. ## Obtaining The Code SQLite sources are managed using the [Fossil](https://www.fossil-scm.org/), a distributed version control system that was specifically designed to support SQLite development. If you do not want to use Fossil, you can download tarballs or ZIP archives as follows: * Lastest trunk check-in: <https://www.sqlite.org/src/tarball/sqlite.tar.gz> or <https://www.sqlite.org/src/zip/sqlite.zip>. * Latest release: <https://www.sqlite.org/src/tarball/sqlite.tar.gz?r=release> or <https://www.sqlite.org/src/zip/sqlite.zip?r=release>. * For other check-ins, substitute an appropriate branch name or tag or hash prefix for "release" in the URLs of the previous bullet. Or browse the [timeline](https://www.sqlite.org/src/timeline) to locate the check-in desired, click on its information page link, then click on the "Tarball" or "ZIP Archive" links on the information page. If you do want to use Fossil to check out the source tree, first install Fossil version 2.0 or later. (Source tarballs and precompiled binaries available [here](https://www.fossil-scm.org/fossil/uv/download.html). Fossil is a stand-alone program. To install, simply download or build the single executable file and put that file someplace on your $PATH.) Then run commands like this: mkdir ~/sqlite cd ~/sqlite fossil clone https://www.sqlite.org/src sqlite.fossil fossil open sqlite.fossil After setting up a repository using the steps above, you can always update to the lastest version using: fossil update trunk ;# latest trunk check-in fossil update release ;# latest official release Or type "fossil ui" to get a web-based user interface. ## Compiling First create a directory in which to place the build products. It is recommended, but not required, that the build directory be separate from the source directory. Cd into the build directory and then from the build directory run the configure script found at the root of the source tree. Then run "make". For example: tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite" mkdir bld ;# Build will occur in a sibling directory cd bld ;# Change to the build directory ../sqlite/configure ;# Run the configure script make ;# Run the makefile. make sqlite3.c ;# Build the "amalgamation" source file make test ;# Run some tests (requires Tcl) See the makefile for additional targets. The configure script uses autoconf 2.61 and libtool. If the configure script does not work out for you, there is a generic makefile named "Makefile.linux-gcc" in the top directory of the source tree that you can copy and edit to suit your needs. Comments on the generic makefile show what changes are needed. ## Using MSVC On Windows, all applicable build products can be compiled with MSVC. First open the command prompt window associated with the desired compiler version (e.g. "Developer Command Prompt for VS2013"). Next, use NMAKE with the provided "Makefile.msc" to build one of the supported targets. For example: mkdir bld cd bld nmake /f Makefile.msc TOP=..\sqlite nmake /f Makefile.msc sqlite3.c TOP=..\sqlite nmake /f Makefile.msc sqlite3.dll TOP=..\sqlite nmake /f Makefile.msc sqlite3.exe TOP=..\sqlite nmake /f Makefile.msc test TOP=..\sqlite There are several build options that can be set via the NMAKE command line. For example, to build for WinRT, simply add "FOR_WINRT=1" argument to the "sqlite3.dll" command line above. When debugging into the SQLite code, adding the "DEBUG=1" argument to one of the above command lines is recommended. SQLite does not require [Tcl](http://www.tcl.tk/) to run, but a Tcl installation is required by the makefiles (including those for MSVC). SQLite contains a lot of generated code and Tcl is used to do much of that code generation. The makefiles also require AWK. ## Source Code Tour Most of the core source files are in the **src/** subdirectory. The **src/** folder also contains files used to build the "testfixture" test harness. The names of the source files used by "testfixture" all begin with "test". The **src/** also contains the "shell.c" file which is the main program for the "sqlite3.exe" [command-line shell](https://sqlite.org/cli.html) and 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. Addtional test code is found in other source repositories. See [How SQLite Is Tested](http://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. The **tool/** subdirectory contains various scripts and programs used |
︙ | ︙ | |||
96 97 98 99 100 101 102 | 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](http://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion. | | | | < > > > > > > > | | | | > | | 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 | 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](http://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. The SQL language parser is **parse.c** which is generate from a grammar in the src/parse.y file. The conversion of "parse.y" into "parse.c" is done by the [lemon](./doc/lemon.html) LALR(1) parser generator. The source code for lemon is at tool/lemon.c. Lemon uses the tool/lempar.c file as a template for generating its parser. Lemon also generates the **parse.h** header file, at the same time it generates parse.c. But the parse.h header file is modified further (to add additional symbols) using the ./addopcodes.awk AWK script. The **opcodes.h** header file contains macros that define the numbers corresponding to opcodes in the "VDBE" virtual machine. The opcodes.h file is generated by the scanning the src/vdbe.c source file. The AWK script at ./mkopcodeh.awk does this scan and generates opcodes.h. A second AWK script, ./mkopcodec.awk, then scans opcodes.h to generate the **opcodes.c** source file, which contains a reverse mapping from opcode-number to opcode-name that is used for EXPLAIN output. The **keywordhash.h** header file contains the definition of a hash table that maps SQL language keywords (ex: "CREATE", "SELECT", "INDEX", etc.) into the numeric codes used by the parse.c parser. The keywordhash.h file is generated by a C-language program at tool mkkeywordhash.c. The **pragma.h** header file contains various definitions used to parse and implement the PRAGMA statements. The header is generated by a script **tool/mkpragmatab.tcl**. If you want to add a new PRAGMA, edit the **tool/mkpragmatab.tcl** file to insert the information needed by the parser for your new PRAGMA, then run the script to regenerate the **pragma.h** header file. ### The Amalgamation All of the individual C source code and header files (both manually-edited and automatically-generated) can be combined into a single big source file **sqlite3.c** called "the amalgamation". The amalgamation is the recommended way of using SQLite in a larger application. Combining all individual source code files into a single big source code file allows the C compiler to perform more cross-procedure analysis and generate better code. SQLite runs about 5% faster when compiled from the amalgamation versus when compiled from individual source files. The amalgamation is generated from the tool/mksqlite3c.tcl Tcl script. First, all of the individual source files must be gathered into the tsrc/ subdirectory (using the equivalent of "make target_source") then the tool/mksqlite3c.tcl script is run to copy them all together in just the right order while resolving internal "#include" references. The amalgamation source file is more than 200K lines long. Some symbolic debuggers (most notably MSVC) are unable to deal with files longer than 64K lines. To work around this, a separate Tcl script, tool/split-sqlite3c.tcl, can be run on the amalgamation to break it up into a single small C file called **sqlite3-all.c** that does #include on about five other files named **sqlite3-1.c**, **sqlite3-2.c**, ..., **sqlite3-5.c**. In this way, 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](http://www.sqlite.org/arch.html) for details. Other documents that are useful in (helping to understand how SQLite works include the [file format](http://www.sqlite.org/fileformat2.html) description, the [virtual machine](http://www.sqlite.org/opcode.html) that runs prepared statements, the description of [how transactions work](http://www.sqlite.org/atomiccommit.html), and the [overview of the query planner](http://www.sqlite.org/optoverview.html). Years of effort have gone into optimizating 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: * **sqlite.h.in** - This file defines the public interface to the SQLite library. Readers will need to be familiar with this interface before trying to understand how the library works internally. * **sqliteInt.h** - this header file defines many of the data objects used internally by SQLite. * **parse.y** - This file describes the LALR(1) grammar that SQLite uses to parse SQL statements, and the actions that are taken at each step in the parsing process. * **vdbe.c** - This file implements the virtual machine that runs prepared statements. There are various helper files whose names begin with "vdbe". The VDBE has access to the vdbeInt.h header file which defines internal data objects. The rest of SQLite interacts |
︙ | ︙ | |||
214 215 216 217 218 219 220 | is the file that, when linked against sqlite3.a, generates the "sqlite3.exe" command-line shell. * **tclsqlite.c** - This file implements the Tcl bindings for SQLite. It is not part of the core SQLite library. But as most of the tests in this repository are written in Tcl, the Tcl language bindings are important. | | | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | is the file that, when linked against sqlite3.a, generates the "sqlite3.exe" command-line shell. * **tclsqlite.c** - This file implements the Tcl bindings for SQLite. It is not part of the core SQLite library. But as most of the tests in this repository are written in Tcl, the Tcl language bindings are important. There are many other source files. Each has a succinct header comment that describes its purpose and role within the larger system. ## Contacts The main SQLite webpage is [http://www.sqlite.org/](http://www.sqlite.org/) with geographically distributed backups at [http://www2.sqlite.org/](http://www2.sqlite.org) and [http://www3.sqlite.org/](http://www3.sqlite.org). |
Changes to VERSION.
|
| | | 1 | 3.21.0 |
Changes to autoconf/Makefile.msc.
︙ | ︙ | |||
17 18 19 20 21 22 23 | # TOP = . # Set this non-0 to enable full warnings (-W4, etc) when compiling. # !IFNDEF USE_FULLWARN | | > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | # TOP = . # Set this non-0 to enable full warnings (-W4, etc) when compiling. # !IFNDEF USE_FULLWARN USE_FULLWARN = 1 !ENDIF # Set this non-0 to enable treating warnings as errors (-WX, etc) when # compiling. # !IFNDEF USE_FATAL_WARN USE_FATAL_WARN = 0 !ENDIF # Set this non-0 to enable full runtime error checks (-RTC1, etc). This # has no effect if (any) optimizations are enabled. # !IFNDEF USE_RUNTIME_CHECKS USE_RUNTIME_CHECKS = 0 |
︙ | ︙ | |||
449 450 451 452 453 454 455 456 457 458 459 460 461 462 | # same unless your are cross-compiling.) # !IF $(USE_FULLWARN)!=0 TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS) !ELSE TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS) !ENDIF TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -fp:precise RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS) # Check if we want to use the "stdcall" calling convention when compiling. # This is not supported by the compilers for non-x86 platforms. It should # also be noted here that building any target with these "stdcall" options | > > > > > > | 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | # same unless your are cross-compiling.) # !IF $(USE_FULLWARN)!=0 TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS) !ELSE TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS) !ENDIF # Check if warnings should be treated as errors when compiling. # !IF $(USE_FATAL_WARN)!=0 TCC = $(TCC) -WX !ENDIF TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -fp:precise RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS) # Check if we want to use the "stdcall" calling convention when compiling. # This is not supported by the compilers for non-x86 platforms. It should # also be noted here that building any target with these "stdcall" options |
︙ | ︙ | |||
628 629 630 631 632 633 634 635 636 637 638 639 640 641 | TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1 RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1 !ENDIF !IF $(DEBUG)>2 TCC = $(TCC) -DSQLITE_DEBUG=1 RCC = $(RCC) -DSQLITE_DEBUG=1 !ENDIF !IF $(DEBUG)>4 || $(OSTRACE)!=0 TCC = $(TCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1 RCC = $(RCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1 !ENDIF | > > > > | 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 | TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1 RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1 !ENDIF !IF $(DEBUG)>2 TCC = $(TCC) -DSQLITE_DEBUG=1 RCC = $(RCC) -DSQLITE_DEBUG=1 !IF $(DYNAMIC_SHELL)==0 TCC = $(TCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE RCC = $(RCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE !ENDIF !ENDIF !IF $(DEBUG)>4 || $(OSTRACE)!=0 TCC = $(TCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1 RCC = $(RCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1 !ENDIF |
︙ | ︙ | |||
906 907 908 909 910 911 912 | !ENDIF # Additional compiler options for the shell. These are only effective # when the shell is not being dynamically linked. # !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 | | | 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 | !ENDIF # Additional compiler options for the shell. These are only effective # when the shell is not being dynamically linked. # !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_STMTVTAB !ENDIF # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # all: dll shell |
︙ | ︙ | |||
933 934 935 936 937 938 939 | Replace.exe: $(CSC) /target:exe $(TOP)\Replace.cs sqlite3.def: Replace.exe $(LIBOBJ) echo EXPORTS > sqlite3.def dumpbin /all $(LIBOBJ) \ | | | 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 | Replace.exe: $(CSC) /target:exe $(TOP)\Replace.cs sqlite3.def: Replace.exe $(LIBOBJ) echo EXPORTS > sqlite3.def dumpbin /all $(LIBOBJ) \ | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \ | sort >> sqlite3.def $(SQLITE3EXE): $(TOP)\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H) $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\shell.c $(SHELL_CORE_SRC) \ /link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) |
︙ | ︙ |
Changes to autoconf/configure.ac.
︙ | ︙ | |||
51 52 53 54 55 56 57 | AS_IF([ test x"$enable_editline" != xno ],[ AC_CHECK_HEADERS([editline/readline.h],[ sLIBS=$LIBS LIBS="" AC_SEARCH_LIBS([readline],[edit],[ AC_DEFINE([HAVE_EDITLINE],1,Define to use BSD editline) | | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | AS_IF([ test x"$enable_editline" != xno ],[ AC_CHECK_HEADERS([editline/readline.h],[ sLIBS=$LIBS LIBS="" AC_SEARCH_LIBS([readline],[edit],[ AC_DEFINE([HAVE_EDITLINE],1,Define to use BSD editline) READLINE_LIBS="$LIBS -ltinfo" enable_readline=no ],[],[-ltinfo]) AS_UNSET(ac_cv_search_readline) LIBS=$sLIBS ]) ]) AS_IF([ test x"$enable_readline" != xno ],[ AC_CHECK_HEADERS([readline/readline.h],[ |
︙ | ︙ |
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.21.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.21.0' PACKAGE_STRING='sqlite 3.21.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include <stdio.h> #ifdef HAVE_SYS_TYPES_H |
︙ | ︙ | |||
905 906 907 908 909 910 911 912 913 914 915 916 917 918 | enable_load_extension enable_memsys5 enable_memsys3 enable_fts3 enable_fts4 enable_fts5 enable_json1 enable_rtree enable_session enable_gcov ' ac_precious_vars='build_alias host_alias target_alias | > | 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 | enable_load_extension enable_memsys5 enable_memsys3 enable_fts3 enable_fts4 enable_fts5 enable_json1 enable_update_limit enable_rtree enable_session enable_gcov ' ac_precious_vars='build_alias host_alias target_alias |
︙ | ︙ | |||
1459 1460 1461 1462 1463 1464 1465 | # # 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 | | | 1460 1461 1462 1463 1464 1465 1466 1467 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 \`configure' configures sqlite 3.21.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. |
︙ | ︙ | |||
1524 1525 1526 1527 1528 1529 1530 | --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 | | | 1525 1526 1527 1528 1529 1530 1531 1532 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 short | recursive ) echo "Configuration of sqlite 3.21.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] |
︙ | ︙ | |||
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 | Disable loading of external extensions --enable-memsys5 Enable MEMSYS5 --enable-memsys3 Enable MEMSYS3 --enable-fts3 Enable the FTS3 extension --enable-fts4 Enable the FTS4 extension --enable-fts5 Enable the FTS5 extension --enable-json1 Enable the JSON1 extension --enable-rtree Enable the RTREE extension --enable-session Enable the SESSION extension --enable-gcov Enable coverage testing using gcov Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) | > | 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 | Disable loading of external extensions --enable-memsys5 Enable MEMSYS5 --enable-memsys3 Enable MEMSYS3 --enable-fts3 Enable the FTS3 extension --enable-fts4 Enable the FTS4 extension --enable-fts5 Enable the FTS5 extension --enable-json1 Enable the JSON1 extension --enable-update-limit Enable the UPDATE/DELETE LIMIT clause --enable-rtree Enable the RTREE extension --enable-session Enable the SESSION extension --enable-gcov Enable coverage testing using gcov Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) |
︙ | ︙ | |||
1648 1649 1650 1651 1652 1653 1654 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF | | | 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 | 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.21.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 |
︙ | ︙ | |||
2067 2068 2069 2070 2071 2072 2073 | 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. | | | 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 | 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.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { |
︙ | ︙ | |||
3925 3926 3927 3928 3929 3930 3931 | { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext | | | | | 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 | { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:3934: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:3937: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:3940: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 |
︙ | ︙ | |||
5137 5138 5139 5140 5141 5142 5143 | ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. | | | 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 | ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '#line 5146 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in |
︙ | ︙ | |||
6662 6663 6664 6665 6666 6667 6668 | # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` | | | | 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 | # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:6671: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:6675: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes |
︙ | ︙ | |||
7001 7002 7003 7004 7005 7006 7007 | # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` | | | | 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 | # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:7010: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:7014: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes |
︙ | ︙ | |||
7106 7107 7108 7109 7110 7111 7112 | # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` | | | | 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 | # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:7115: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:7119: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then |
︙ | ︙ | |||
7161 7162 7163 7164 7165 7166 7167 | # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` | | | | 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 | # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:7170: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:7174: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then |
︙ | ︙ | |||
9541 9542 9543 9544 9545 9546 9547 | else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF | | | 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 | else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line 9550 "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include <dlfcn.h> #endif #include <stdio.h> |
︙ | ︙ | |||
9637 9638 9639 9640 9641 9642 9643 | else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF | | | 9639 9640 9641 9642 9643 9644 9645 9646 9647 9648 9649 9650 9651 9652 9653 | else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line 9646 "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include <dlfcn.h> #endif #include <stdio.h> |
︙ | ︙ | |||
10298 10299 10300 10301 10302 10303 10304 | USE_AMALGAMATION=1 ######### # See whether we can run specific tclsh versions known to work well; # if not, then we fall back to plain tclsh. # TODO: try other versions before falling back? # | | | 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 10312 10313 10314 | USE_AMALGAMATION=1 ######### # See whether we can run specific tclsh versions known to work well; # if not, then we fall back to plain tclsh. # TODO: try other versions before falling back? # for ac_prog in tclsh8.7 tclsh8.6 tclsh8.5 tclsh do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_TCLSH_CMD+:} false; then : $as_echo_n "(cached) " >&6 |
︙ | ︙ | |||
11248 11249 11250 11251 11252 11253 11254 | if test "${enable_debug+set}" = set; then : enableval=$enable_debug; use_debug=$enableval else use_debug=no fi if test "${use_debug}" = "yes" ; then | | | 11250 11251 11252 11253 11254 11255 11256 11257 11258 11259 11260 11261 11262 11263 11264 | if test "${enable_debug+set}" = set; then : enableval=$enable_debug; use_debug=$enableval else use_debug=no fi if test "${use_debug}" = "yes" ; then TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0" else TARGET_DEBUG="-DNDEBUG" fi ######### # See whether we should use the amalgamation to build |
︙ | ︙ | |||
11352 11353 11354 11355 11356 11357 11358 | else enable_memsys5=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS5" >&5 $as_echo_n "checking whether to support MEMSYS5... " >&6; } if test "${enable_memsys5}" = "yes"; then | | | | | | 11354 11355 11356 11357 11358 11359 11360 11361 11362 11363 11364 11365 11366 11367 11368 11369 11370 11371 11372 11373 11374 11375 11376 11377 11378 11379 11380 11381 11382 11383 11384 11385 11386 11387 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 11406 11407 11408 11409 11410 11411 11412 11413 | else enable_memsys5=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS5" >&5 $as_echo_n "checking whether to support MEMSYS5... " >&6; } if test "${enable_memsys5}" = "yes"; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS5" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Check whether --enable-memsys3 was given. if test "${enable_memsys3+set}" = set; then : enableval=$enable_memsys3; enable_memsys3=yes else enable_memsys3=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS3" >&5 $as_echo_n "checking whether to support MEMSYS3... " >&6; } if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS3" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ######### # See whether we should enable Full Text Search extensions # Check whether --enable-fts3 was given. if test "${enable_fts3+set}" = set; then : enableval=$enable_fts3; enable_fts3=yes else enable_fts3=no fi if test "${enable_fts3}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS3" fi # Check whether --enable-fts4 was given. if test "${enable_fts4+set}" = set; then : enableval=$enable_fts4; enable_fts4=yes else enable_fts4=no fi if test "${enable_fts4}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5 $as_echo_n "checking for library containing log... " >&6; } if ${ac_cv_search_log+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext |
︙ | ︙ | |||
11463 11464 11465 11466 11467 11468 11469 | if test "${enable_fts5+set}" = set; then : enableval=$enable_fts5; enable_fts5=yes else enable_fts5=no fi if test "${enable_fts5}" = "yes" ; then | | | 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 | if test "${enable_fts5+set}" = set; then : enableval=$enable_fts5; enable_fts5=yes else enable_fts5=no fi if test "${enable_fts5}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5 $as_echo_n "checking for library containing log... " >&6; } if ${ac_cv_search_log+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext |
︙ | ︙ | |||
11532 11533 11534 11535 11536 11537 11538 | if test "${enable_json1+set}" = set; then : enableval=$enable_json1; enable_json1=yes else enable_json1=no fi if test "${enable_json1}" = "yes" ; then | | > > > > > > > > > > > > > > | | | | | 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 | if test "${enable_json1+set}" = set; then : enableval=$enable_json1; enable_json1=yes else enable_json1=no fi if test "${enable_json1}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1" fi ######### # See whether we should enable the LIMIT clause on UPDATE and DELETE # statements. # Check whether --enable-update-limit was given. if test "${enable_update_limit+set}" = set; then : enableval=$enable_update_limit; enable_udlimit=yes else enable_udlimit=no fi if test "${enable_udlimit}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT" fi ######### # See whether we should enable RTREE # Check whether --enable-rtree was given. if test "${enable_rtree+set}" = set; then : enableval=$enable_rtree; enable_rtree=yes else enable_rtree=no fi if test "${enable_rtree}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_RTREE" fi ######### # See whether we should enable the SESSION extension # Check whether --enable-session was given. if test "${enable_session+set}" = set; then : enableval=$enable_session; enable_session=yes else enable_session=no fi if test "${enable_session}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION" OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK" fi ######### # attempt to duplicate any OMITS and ENABLES into the ${OPT_FEATURE_FLAGS} parameter for option in $CFLAGS $CPPFLAGS do case $option in -DSQLITE_OMIT*) OPT_FEATURE_FLAGS="$OPT_FEATURE_FLAGS $option";; -DSQLITE_ENABLE*) OPT_FEATURE_FLAGS="$OPT_FEATURE_FLAGS $option";; esac done |
︙ | ︙ | |||
12147 12148 12149 12150 12151 12152 12153 | 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=" | | | 12163 12164 12165 12166 12167 12168 12169 12170 12171 12172 12173 12174 12175 12176 12177 | 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.21.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 $@ |
︙ | ︙ | |||
12213 12214 12215 12216 12217 12218 12219 | 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="\\ | | | 12229 12230 12231 12232 12233 12234 12235 12236 12237 12238 12239 12240 12241 12242 12243 | 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.21.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.
︙ | ︙ | |||
116 117 118 119 120 121 122 | USE_AMALGAMATION=1 ######### # See whether we can run specific tclsh versions known to work well; # if not, then we fall back to plain tclsh. # TODO: try other versions before falling back? # | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | USE_AMALGAMATION=1 ######### # See whether we can run specific tclsh versions known to work well; # if not, then we fall back to plain tclsh. # TODO: try other versions before falling back? # AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.7 tclsh8.6 tclsh8.5 tclsh], none) if test "$TCLSH_CMD" = "none"; then # If we can't find a local tclsh, then building the amalgamation will fail. # We act as though --disable-amalgamation has been used. echo "Warning: can't find tclsh - defaulting to non-amalgamation build." USE_AMALGAMATION=0 TCLSH_CMD="tclsh" fi |
︙ | ︙ | |||
556 557 558 559 560 561 562 | AC_SEARCH_LIBS(fdatasync, [rt]) ######### # check for debug enabled AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[enable debugging & verbose explain]), [use_debug=$enableval],[use_debug=no]) if test "${use_debug}" = "yes" ; then | | | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | AC_SEARCH_LIBS(fdatasync, [rt]) ######### # check for debug enabled AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[enable debugging & verbose explain]), [use_debug=$enableval],[use_debug=no]) if test "${use_debug}" = "yes" ; then TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0" else TARGET_DEBUG="-DNDEBUG" fi AC_SUBST(TARGET_DEBUG) ######### # See whether we should use the amalgamation to build |
︙ | ︙ | |||
592 593 594 595 596 597 598 | # Do we want to support memsys3 and/or memsys5 # AC_ARG_ENABLE(memsys5, AC_HELP_STRING([--enable-memsys5],[Enable MEMSYS5]), [enable_memsys5=yes],[enable_memsys5=no]) AC_MSG_CHECKING([whether to support MEMSYS5]) if test "${enable_memsys5}" = "yes"; then | | | | | | | > > > > > > > > > > | | | | | 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 | # Do we want to support memsys3 and/or memsys5 # AC_ARG_ENABLE(memsys5, AC_HELP_STRING([--enable-memsys5],[Enable MEMSYS5]), [enable_memsys5=yes],[enable_memsys5=no]) AC_MSG_CHECKING([whether to support MEMSYS5]) if test "${enable_memsys5}" = "yes"; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS5" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi AC_ARG_ENABLE(memsys3, AC_HELP_STRING([--enable-memsys3],[Enable MEMSYS3]), [enable_memsys3=yes],[enable_memsys3=no]) AC_MSG_CHECKING([whether to support MEMSYS3]) if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS3" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ######### # See whether we should enable Full Text Search extensions AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3], [Enable the FTS3 extension]), [enable_fts3=yes],[enable_fts3=no]) if test "${enable_fts3}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS3" fi AC_ARG_ENABLE(fts4, AC_HELP_STRING([--enable-fts4], [Enable the FTS4 extension]), [enable_fts4=yes],[enable_fts4=no]) if test "${enable_fts4}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4" AC_SEARCH_LIBS([log],[m]) fi AC_ARG_ENABLE(fts5, AC_HELP_STRING([--enable-fts5], [Enable the FTS5 extension]), [enable_fts5=yes],[enable_fts5=no]) if test "${enable_fts5}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5" AC_SEARCH_LIBS([log],[m]) fi ######### # See whether we should enable JSON1 AC_ARG_ENABLE(json1, AC_HELP_STRING([--enable-json1], [Enable the JSON1 extension]), [enable_json1=yes],[enable_json1=no]) if test "${enable_json1}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1" fi ######### # See whether we should enable the LIMIT clause on UPDATE and DELETE # statements. AC_ARG_ENABLE(update-limit, AC_HELP_STRING([--enable-update-limit], [Enable the UPDATE/DELETE LIMIT clause]), [enable_udlimit=yes],[enable_udlimit=no]) if test "${enable_udlimit}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT" fi ######### # See whether we should enable RTREE AC_ARG_ENABLE(rtree, AC_HELP_STRING([--enable-rtree], [Enable the RTREE extension]), [enable_rtree=yes],[enable_rtree=no]) if test "${enable_rtree}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_RTREE" fi ######### # See whether we should enable the SESSION extension AC_ARG_ENABLE(session, AC_HELP_STRING([--enable-session], [Enable the SESSION extension]), [enable_session=yes],[enable_session=no]) if test "${enable_session}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION" OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK" fi ######### # attempt to duplicate any OMITS and ENABLES into the ${OPT_FEATURE_FLAGS} parameter for option in $CFLAGS $CPPFLAGS do case $option in -DSQLITE_OMIT*) OPT_FEATURE_FLAGS="$OPT_FEATURE_FLAGS $option";; -DSQLITE_ENABLE*) OPT_FEATURE_FLAGS="$OPT_FEATURE_FLAGS $option";; esac done |
︙ | ︙ |
Changes to doc/lemon.html.
1 2 3 4 | <html> <head> <title>The Lemon Parser Generator</title> </head> | | | | | > > > > > > > > > > > > > > > > > > > > | | | 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 | <html> <head> <title>The Lemon Parser Generator</title> </head> <body bgcolor='white'> <h1 align='center'>The Lemon Parser Generator</h1> <p>Lemon is an LALR(1) parser generator for C. It does the same job as "bison" and "yacc". But Lemon is not a bison or yacc clone. Lemon uses a different grammar syntax which is designed to reduce the number of coding errors. Lemon also uses a parsing engine that is faster than yacc and bison and which is both reentrant and threadsafe. (Update: Since the previous sentence was written, bison has also been updated so that it too can generate a reentrant and threadsafe parser.) Lemon also implements features that can be used to eliminate resource leaks, making it suitable for use in long-running programs such as graphical user interfaces or embedded controllers.</p> <p>This document is an introduction to the Lemon parser generator.</p> <h2>Security Note</h2> <p>The language parser code created by Lemon is very robust and is well-suited for use in internet-facing applications that need to safely process maliciously crafted inputs. <p>The "lemon.exe" command-line tool itself works great when given a valid input grammar file and almost always gives helpful error messages for malformed inputs. However, it is possible for a malicious user to craft a grammar file that will cause lemon.exe to crash. We do not see this as a problem, as lemon.exe is not intended to be used with hostile inputs. To summarize:</p> <ul> <li>Parser code generated by lemon → Robust and secure <li>The "lemon.exe" command line tool itself → Not so much </ul> <h2>Theory of Operation</h2> <p>The main goal of Lemon is to translate a context free grammar (CFG) for a particular language into C code that implements a parser for that language. The program has two inputs: <ul> <li>The grammar specification. <li>A parser template file. </ul> Typically, only the grammar specification is supplied by the programmer. Lemon comes with a default parser template which works fine for most applications. But the user is free to substitute a different parser template if desired.</p> <p>Depending on command-line options, Lemon will generate up to three output files. <ul> <li>C code to implement the parser. <li>A header file defining an integer ID for each terminal symbol. <li>An information file that describes the states of the generated parser automaton. </ul> By default, all three of these output files are generated. |
︙ | ︙ | |||
66 67 68 69 70 71 72 | <h3>Command Line Options</h3> <p>The behavior of Lemon can be modified using command-line options. You can obtain a list of the available command-line options together with a brief explanation of what each does by typing <pre> | | | > | > > | | | | 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 | <h3>Command Line Options</h3> <p>The behavior of Lemon can be modified using command-line options. You can obtain a list of the available command-line options together with a brief explanation of what each does by typing <pre> lemon "-?" </pre> As of this writing, the following command-line options are supported: <ul> <li><b>-b</b> Show only the basis for each parser state in the report file. <li><b>-c</b> Do not compress the generated action tables. The parser will be a little larger and slower, but it will detect syntax errors sooner. <li><b>-D<i>name</i></b> Define C preprocessor macro <i>name</i>. This macro is usable by "<tt><a href='#pifdef'>%ifdef</a></tt>" and "<tt><a href='#pifdef'>%ifndef</a></tt>" lines in the grammar file. <li><b>-g</b> Do not generate a parser. Instead write the input grammar to standard output with all comments, actions, and other extraneous text removed. <li><b>-l</b> Omit "#line" directives in the generated parser C code. <li><b>-m</b> Cause the output C source code to be compatible with the "makeheaders" program. <li><b>-p</b> Display all conflicts that are resolved by <a href='#precrules'>precedence rules</a>. <li><b>-q</b> Suppress generation of the report file. <li><b>-r</b> Do not sort or renumber the parser states as part of optimization. <li><b>-s</b> Show parser statistics before existing. |
︙ | ︙ | |||
141 142 143 144 145 146 147 | be parsed. This is accomplished by calling the following function once for each token: <pre> Parse(pParser, hTokenID, sTokenData, pArg); </pre> The first argument to the Parse() routine is the pointer returned by ParseAlloc(). | | | | | | | | | | < > | | | | | | | | | | 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 | be parsed. This is accomplished by calling the following function once for each token: <pre> Parse(pParser, hTokenID, sTokenData, pArg); </pre> The first argument to the Parse() routine is the pointer returned by ParseAlloc(). The second argument is a small positive integer that tells the parser the type of the next token in the data stream. There is one token type for each terminal symbol in the grammar. The gram.h file generated by Lemon contains #define statements that map symbolic terminal symbol names into appropriate integer values. A value of 0 for the second argument is a special flag to the parser to indicate that the end of input has been reached. The third argument is the value of the given token. By default, the type of the third argument is "void*", but the grammar will usually redefine this type to be some kind of structure. Typically the second argument will be a broad category of tokens such as "identifier" or "number" and the third argument will be the name of the identifier or the value of the number.</p> <p>The Parse() function may have either three or four arguments, depending on the grammar. If the grammar specification file requests it (via the <tt><a href='#extraarg'>%extra_argument</a></tt> directive), the Parse() function will have a fourth parameter that can be of any type chosen by the programmer. The parser doesn't do anything with this argument except to pass it through to action routines. This is a convenient mechanism for passing state information down to the action routines without having to use global variables.</p> <p>A typical use of a Lemon parser might look something like the following: <pre> 1 ParseTree *ParseFile(const char *zFilename){ 2 Tokenizer *pTokenizer; 3 void *pParser; 4 Token sToken; 5 int hTokenId; 6 ParserState sState; 7 8 pTokenizer = TokenizerCreate(zFilename); 9 pParser = ParseAlloc( malloc ); 10 InitParserState(&sState); 11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){ 12 Parse(pParser, hTokenId, sToken, &sState); 13 } 14 Parse(pParser, 0, sToken, &sState); 15 ParseFree(pParser, free ); 16 TokenizerFree(pTokenizer); 17 return sState.treeRoot; 18 } </pre> This example shows a user-written routine that parses a file of text and returns a pointer to the parse tree. (All error-handling code is omitted from this example to keep it simple.) We assume the existence of some kind of tokenizer which is created using TokenizerCreate() on line 8 and deleted by TokenizerFree() on line 16. The GetNextToken() function on line 11 retrieves the next token from the input file and puts its type in the integer variable hTokenId. The sToken variable is assumed to be some kind of structure that contains details about each token, such as its complete text, what line it occurs on, etc.</p> <p>This example also assumes the existence of structure of type ParserState that holds state information about a particular parse. An instance of such a structure is created on line 6 and initialized on line 10. A pointer to this structure is passed into the Parse() routine as the optional 4th argument. The action routine specified by the grammar for the parser can use the ParserState structure to hold whatever information is useful and appropriate. In the example, we note that the treeRoot field of the ParserState structure is left pointing to the root of the parse tree.</p> <p>The core of this example as it relates to Lemon is as follows: <pre> ParseFile(){ pParser = ParseAlloc( malloc ); while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){ Parse(pParser, hTokenId, sToken); } Parse(pParser, 0, sToken); ParseFree(pParser, free ); } </pre> Basically, what a program has to do to use a Lemon-generated parser |
︙ | ︙ | |||
273 274 275 276 277 278 279 | <p>The main purpose of the grammar specification file for Lemon is to define the grammar for the parser. But the input file also specifies additional information Lemon requires to do its job. Most of the work in using Lemon is in writing an appropriate grammar file.</p> | | | | | | | | | | 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 | <p>The main purpose of the grammar specification file for Lemon is to define the grammar for the parser. But the input file also specifies additional information Lemon requires to do its job. Most of the work in using Lemon is in writing an appropriate grammar file.</p> <p>The grammar file for Lemon is, for the most part, free format. It does not have sections or divisions like yacc or bison. Any declaration can occur at any point in the file. Lemon ignores whitespace (except where it is needed to separate tokens), and it honors the same commenting conventions as C and C++.</p> <h3>Terminals and Nonterminals</h3> <p>A terminal symbol (token) is any string of alphanumeric and/or underscore characters that begins with an uppercase letter. A terminal can contain lowercase letters after the first character, but the usual convention is to make terminals all uppercase. A nonterminal, on the other hand, is any string of alphanumeric and underscore characters than begins with a lowercase letter. Again, the usual convention is to make nonterminals use all lowercase letters.</p> <p>In Lemon, terminal and nonterminal symbols do not need to be declared or identified in a separate section of the grammar file. Lemon is able to generate a list of all terminals and nonterminals by examining the grammar rules, and it can always distinguish a terminal from a nonterminal by checking the case of the first character of the name.</p> <p>Yacc and bison allow terminal symbols to have either alphanumeric |
︙ | ︙ | |||
315 316 317 318 319 320 321 | Each grammar rule consists of a nonterminal symbol followed by the special symbol "::=" and then a list of terminals and/or nonterminals. The rule is terminated by a period. The list of terminals and nonterminals on the right-hand side of the rule can be empty. Rules can occur in any order, except that the left-hand side of the first rule is assumed to be the start symbol for the grammar (unless | > | | 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | Each grammar rule consists of a nonterminal symbol followed by the special symbol "::=" and then a list of terminals and/or nonterminals. The rule is terminated by a period. The list of terminals and nonterminals on the right-hand side of the rule can be empty. Rules can occur in any order, except that the left-hand side of the first rule is assumed to be the start symbol for the grammar (unless specified otherwise using the <tt><a href='#start_symbol'>%start_symbol</a></tt> directive described below.) A typical sequence of grammar rules might look something like this: <pre> expr ::= expr PLUS expr. expr ::= expr TIMES expr. expr ::= LPAREN expr RPAREN. expr ::= VALUE. </pre> |
︙ | ︙ | |||
358 359 360 361 362 363 364 | rule and say "$7" when you really mean "$8".</p> <p>Lemon avoids the need to count grammar symbols by assigning symbolic names to each symbol in a grammar rule and then using those symbolic names in the action. In yacc or bison, one would write this: <pre> | | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | rule and say "$7" when you really mean "$8".</p> <p>Lemon avoids the need to count grammar symbols by assigning symbolic names to each symbol in a grammar rule and then using those symbolic names in the action. In yacc or bison, one would write this: <pre> expr -> expr PLUS expr { $$ = $1 + $3; }; </pre> But in Lemon, the same rule becomes the following: <pre> expr(A) ::= expr(B) PLUS expr(C). { A = B+C; } </pre> In the Lemon rule, any symbol in parentheses after a grammar rule symbol becomes a place holder for that symbol in the grammar rule. |
︙ | ︙ | |||
398 399 400 401 402 403 404 | <p>Lemon resolves parsing ambiguities in exactly the same way as yacc and bison. A shift-reduce conflict is resolved in favor of the shift, and a reduce-reduce conflict is resolved by reducing whichever rule comes first in the grammar file.</p> <p>Just like in | | | | | | | | | 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | <p>Lemon resolves parsing ambiguities in exactly the same way as yacc and bison. A shift-reduce conflict is resolved in favor of the shift, and a reduce-reduce conflict is resolved by reducing whichever rule comes first in the grammar file.</p> <p>Just like in yacc and bison, Lemon allows a measure of control over the resolution of parsing conflicts using precedence rules. A precedence value can be assigned to any terminal symbol using the <tt><a href='#pleft'>%left</a></tt>, <tt><a href='#pright'>%right</a></tt> or <tt><a href='#pnonassoc'>%nonassoc</a></tt> directives. Terminal symbols mentioned in earlier directives have a lower precedence than terminal symbols mentioned in later directives. For example:</p> <p><pre> %left AND. %left OR. %nonassoc EQ NE GT GE LT LE. %left PLUS MINUS. |
︙ | ︙ | |||
481 482 483 484 485 486 487 | <ul> <li> If either the token to be shifted or the rule to be reduced lacks precedence information, then resolve in favor of the shift, but report a parsing conflict. <li> If the precedence of the token to be shifted is greater than the precedence of the rule to reduce, then resolve in favor of the shift. No parsing conflict is reported. | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | 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 | <ul> <li> If either the token to be shifted or the rule to be reduced lacks precedence information, then resolve in favor of the shift, but report a parsing conflict. <li> If the precedence of the token to be shifted is greater than the precedence of the rule to reduce, then resolve in favor of the shift. No parsing conflict is reported. <li> If the precedence of the token to be shifted is less than the precedence of the rule to reduce, then resolve in favor of the reduce action. No parsing conflict is reported. <li> If the precedences are the same and the shift token is right-associative, then resolve in favor of the shift. No parsing conflict is reported. <li> If the precedences are the same and the shift token is left-associative, then resolve in favor of the reduce. No parsing conflict is reported. <li> Otherwise, resolve the conflict by doing the shift, and report a parsing conflict. </ul> Reduce-reduce conflicts are resolved this way: <ul> <li> If either reduce rule lacks precedence information, then resolve in favor of the rule that appears first in the grammar, and report a parsing conflict. <li> If both rules have precedence and the precedence is different, then resolve the dispute in favor of the rule with the highest precedence, and do not report a conflict. <li> Otherwise, resolve the conflict by reducing by the rule that appears first in the grammar, and report a parsing conflict. </ul> <h3>Special Directives</h3> <p>The input grammar to Lemon consists of grammar rules and special directives. We've described all the grammar rules, so now we'll talk about the special directives.</p> <p>Directives in Lemon can occur in any order. You can put them before the grammar rules, or after the grammar rules, or in the midst of the grammar rules. It doesn't matter. The relative order of directives used to assign precedence to terminals is important, but other than that, the order of directives in Lemon is arbitrary.</p> <p>Lemon supports the following special directives: <ul> <li><tt><a href='#pcode'>%code</a></tt> <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'>%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'>%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_class'>%token_class</a></tt> <li><tt><a href='#token_destructor'>%token_destructor</a></tt> <li><tt><a href='#token_prefix'>%token_prefix</a></tt> <li><tt><a href='#token_type'>%token_type</a></tt> <li><tt><a href='#ptype'>%type</a></tt> <li><tt><a href='#pwildcard'>%wildcard</a></tt> </ul> Each of these directives will be described separately in the following sections:</p> <a name='pcode'></a> <h4>The <tt>%code</tt> directive</h4> <p>The <tt>%code</tt> directive is used to specify additional C code that is added to the end of the main output file. This is similar to the <tt><a href='#pinclude'>%include</a></tt> directive except that <tt>%include</tt> is inserted at the beginning of the main output file.</p> <p><tt>%code</tt> is typically used to include some action routines or perhaps a tokenizer or even the "main()" function as part of the output file.</p> <a name='default_destructor'></a> <h4>The <tt>%default_destructor</tt> directive</h4> <p>The <tt>%default_destructor</tt> directive specifies a destructor to use for non-terminals that do not have their own destructor specified by a separate <tt>%destructor</tt> directive. See the documentation on the <tt><a name='#destructor'>%destructor</a></tt> directive below for additional information.</p> <p>In some grammars, many different non-terminal symbols have the same data type and hence the same destructor. This directive is a convenient way to specify the same destructor for all those non-terminals using a single statement.</p> <a name='default_type'></a> <h4>The <tt>%default_type</tt> directive</h4> <p>The <tt>%default_type</tt> directive specifies the data type of non-terminal symbols that do not have their own data type defined using a separate <tt><a href='#ptype'>%type</a></tt> directive.</p> <a name='destructor'></a> <h4>The <tt>%destructor</tt> directive</h4> <p>The <tt>%destructor</tt> directive is used to specify a destructor for a non-terminal symbol. (See also the <tt><a href='#token_destructor'>%token_destructor</a></tt> directive which is used to specify a destructor for terminal symbols.)</p> <p>A non-terminal's destructor is called to dispose of the non-terminal's value whenever the non-terminal is popped from the stack. This includes all of the following circumstances: <ul> <li> When a rule reduces and the value of a non-terminal on |
︙ | ︙ | |||
611 612 613 614 615 616 617 | <p>Consider an example: <pre> %type nt {void*} %destructor nt { free($$); } nt(A) ::= ID NUM. { A = malloc( 100 ); } </pre> | | | | | | | | | | | | | | | | > | | | | | > | | > | | | | | | | | | | | | | > | > | | | | > | | | | | < | > | | | | | | | | | | > > > > > > > > > > > > | | > > | > | | | > | | | | | | | > | | | > | | 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 | <p>Consider an example: <pre> %type nt {void*} %destructor nt { free($$); } nt(A) ::= ID NUM. { A = malloc( 100 ); } </pre> This example is a bit contrived, but it serves to illustrate how destructors work. The example shows a non-terminal named "nt" that holds values of type "void*". When the rule for an "nt" reduces, it sets the value of the non-terminal to space obtained from malloc(). Later, when the nt non-terminal is popped from the stack, the destructor will fire and call free() on this malloced space, thus avoiding a memory leak. (Note that the symbol "$$" in the destructor code is replaced by the value of the non-terminal.)</p> <p>It is important to note that the value of a non-terminal is passed to the destructor whenever the non-terminal is removed from the stack, unless the non-terminal is used in a C-code action. If the non-terminal is used by C-code, then it is assumed that the C-code will take care of destroying it. More commonly, the value is used to build some larger structure, and we don't want to destroy it, which is why the destructor is not called in this circumstance.</p> <p>Destructors help avoid memory leaks by automatically freeing allocated objects when they go out of scope. To do the same using yacc or bison is much more difficult.</p> <a name='extraarg'></a> <h4>The <tt>%extra_argument</tt> directive</h4> The <tt>%extra_argument</tt> directive instructs Lemon to add a 4th parameter to the parameter list of the Parse() function it generates. Lemon doesn't do anything itself with this extra argument, but it does make the argument available to C-code action routines, destructors, and so forth. For example, if the grammar file contains:</p> <p><pre> %extra_argument { MyStruct *pAbc } </pre></p> <p>Then the Parse() function generated will have an 4th parameter of type "MyStruct*" and all action routines will have access to a variable named "pAbc" that is the value of the 4th parameter in the most recent call to Parse().</p> <a name='pfallback'></a> <h4>The <tt>%fallback</tt> directive</h4> <p>The <tt>%fallback</tt> directive specifies an alternative meaning for one or more tokens. The alternative meaning is tried if the original token would have generated a syntax error.</p> <p>The <tt>%fallback</tt> directive was added to support robust parsing of SQL syntax in <a href='https://www.sqlite.org/'>SQLite</a>. The SQL language contains a large assortment of keywords, each of which appears as a different token to the language parser. SQL contains so many keywords that it can be difficult for programmers to keep up with them all. Programmers will, therefore, sometimes mistakenly use an obscure language keyword for an identifier. The <tt>%fallback</tt> directive provides a mechanism to tell the parser: "If you are unable to parse this keyword, try treating it as an identifier instead."</p> <p>The syntax of <tt>%fallback</tt> is as follows: <blockquote> <tt>%fallback</tt> <i>ID</i> <i>TOKEN...</i> <b>.</b> </blockquote></p> <p>In words, the <tt>%fallback</tt> directive is followed by a list of token names terminated by a period. The first token name is the fallback token — the token to which all the other tokens fall back to. The second and subsequent arguments are tokens which fall back to the token identified by the first argument.</p> <a name='pifdef'></a> <h4>The <tt>%ifdef</tt>, <tt>%ifndef</tt>, and <tt>%endif</tt> directives</h4> <p>The <tt>%ifdef</tt>, <tt>%ifndef</tt>, and <tt>%endif</tt> directives are similar to #ifdef, #ifndef, and #endif in the C-preprocessor, just not as general. Each of these directives must begin at the left margin. No whitespace is allowed between the "%" and the directive name.</p> <p>Grammar text in between "<tt>%ifdef MACRO</tt>" and the next nested "<tt>%endif</tt>" is ignored unless the "-DMACRO" command-line option is used. Grammar text betwen "<tt>%ifndef MACRO</tt>" and the next nested "<tt>%endif</tt>" is included except when the "-DMACRO" command-line option is used.</p> <p>Note that the argument to <tt>%ifdef</tt> and <tt>%ifndef</tt> must be a single preprocessor symbol name, not a general expression. There is no "<tt>%else</tt>" directive.</p> <a name='pinclude'></a> <h4>The <tt>%include</tt> directive</h4> <p>The <tt>%include</tt> directive specifies C code that is included at the top of the generated parser. You can include any text you want — the Lemon parser generator copies it blindly. If you have multiple <tt>%include</tt> directives in your grammar file, their values are concatenated so that all <tt>%include</tt> code ultimately appears near the top of the generated parser, in the same order as it appeared in the grammar.</p> <p>The <tt>%include</tt> directive is very handy for getting some extra #include preprocessor statements at the beginning of the generated parser. For example:</p> <p><pre> %include {#include <unistd.h>} </pre></p> <p>This might be needed, for example, if some of the C actions in the grammar call functions that are prototyped in unistd.h.</p> <a name='pleft'></a> <h4>The <tt>%left</tt> directive</h4> The <tt>%left</tt> directive is used (along with the <tt><a href='#pright'>%right</a></tt> and <tt><a href='#pnonassoc'>%nonassoc</a></tt> directives) to declare precedences of terminal symbols. Every terminal symbol whose name appears after a <tt>%left</tt> directive but before the next period (".") is given the same left-associative precedence value. Subsequent <tt>%left</tt> directives have higher precedence. For example:</p> <p><pre> %left AND. %left OR. %nonassoc EQ NE GT GE LT LE. %left PLUS MINUS. %left TIMES DIVIDE MOD. %right EXP NOT. </pre></p> <p>Note the period that terminates each <tt>%left</tt>, <tt>%right</tt> or <tt>%nonassoc</tt> directive.</p> <p>LALR(1) grammars can get into a situation where they require a large amount of stack space if you make heavy use or right-associative operators. For this reason, it is recommended that you use <tt>%left</tt> rather than <tt>%right</tt> whenever possible.</p> <a name='pname'></a> <h4>The <tt>%name</tt> directive</h4> <p>By default, the functions generated by Lemon all begin with the five-character string "Parse". You can change this string to something different using the <tt>%name</tt> directive. For instance:</p> <p><pre> %name Abcde </pre></p> <p>Putting this directive in the grammar file will cause Lemon to generate functions named <ul> <li> AbcdeAlloc(), <li> AbcdeFree(), <li> AbcdeTrace(), and <li> Abcde(). </ul> The <tt>%name</tt> directive allows you to generate two or more different parsers and link them all into the same executable.</p> <a name='pnonassoc'></a> <h4>The <tt>%nonassoc</tt> directive</h4> <p>This directive is used to assign non-associative precedence to one or more terminal symbols. See the section on <a href='#precrules'>precedence rules</a> or on the <tt><a href='#pleft'>%left</a></tt> directive for additional information.</p> <a name='parse_accept'></a> <h4>The <tt>%parse_accept</tt> directive</h4> <p>The <tt>%parse_accept</tt> directive specifies a block of C code that is executed whenever the parser accepts its input string. To "accept" an input string means that the parser was able to process all tokens without error.</p> <p>For example:</p> <p><pre> %parse_accept { printf("parsing complete!\n"); } </pre></p> <a name='parse_failure'></a> <h4>The <tt>%parse_failure</tt> directive</h4> <p>The <tt>%parse_failure</tt> directive specifies a block of C code that is executed whenever the parser fails complete. This code is not executed until the parser has tried and failed to resolve an input error using is usual error recovery strategy. The routine is only invoked when parsing is unable to continue.</p> <p><pre> %parse_failure { fprintf(stderr,"Giving up. Parser is hopelessly lost...\n"); } </pre></p> <a name='pright'></a> <h4>The <tt>%right</tt> directive</h4> <p>This directive is used to assign right-associative precedence to one or more terminal symbols. See the section on <a href='#precrules'>precedence rules</a> or on the <a href='#pleft'>%left</a> directive for additional information.</p> <a name='stack_overflow'></a> <h4>The <tt>%stack_overflow</tt> directive</h4> <p>The <tt>%stack_overflow</tt> directive specifies a block of C code that is executed if the parser's internal stack ever overflows. Typically this just prints an error message. After a stack overflow, the parser will be unable to continue and must be reset.</p> <p><pre> %stack_overflow { fprintf(stderr,"Giving up. Parser stack overflow\n"); } </pre></p> <p>You can help prevent parser stack overflows by avoiding the use of right recursion and right-precedence operators in your grammar. Use left recursion and and left-precedence operators instead to encourage rules to reduce sooner and keep the stack size down. For example, do rules like this: <pre> list ::= list element. // left-recursion. Good! list ::= . </pre> Not like this: <pre> list ::= element list. // right-recursion. Bad! list ::= . </pre></p> <a name='stack_size'></a> <h4>The <tt>%stack_size</tt> directive</h4> <p>If stack overflow is a problem and you can't resolve the trouble by using left-recursion, then you might want to increase the size of the parser's stack using this directive. Put an positive integer after the <tt>%stack_size</tt> directive and Lemon will generate a parse with a stack of the requested size. The default value is 100.</p> <p><pre> %stack_size 2000 </pre></p> <a name='start_symbol'></a> <h4>The <tt>%start_symbol</tt> directive</h4> <p>By default, the start symbol for the grammar that Lemon generates is the first non-terminal that appears in the grammar file. But you can choose a different start symbol using the <tt>%start_symbol</tt> directive.</p> <p><pre> %start_symbol prog </pre></p> <a name='syntax_error'></a> <h4>The <tt>%syntax_error</tt> directive</h4> <p>See <a href='#error_processing'>Error Processing</a>.</p> <a name='token_class'></a> <h4>The <tt>%token_class</tt> directive</h4> <p>Undocumented. Appears to be related to the MULTITERMINAL concept. <a href='http://sqlite.org/src/fdiff?v1=796930d5fc2036c7&v2=624b24c5dc048e09&sbs=0'>Implementation</a>.</p> <a name='token_destructor'></a> <h4>The <tt>%token_destructor</tt> directive</h4> <p>The <tt>%destructor</tt> directive assigns a destructor to a non-terminal symbol. (See the description of the <tt><a href='%destructor'>%destructor</a></tt> directive above.) The <tt>%token_destructor</tt> directive does the same thing for all terminal symbols.</p> <p>Unlike non-terminal symbols which may each have a different data type for their values, terminals all use the same data type (defined by the <tt><a href='#token_type'>%token_type</a></tt> directive) and so they use a common destructor. Other than that, the token destructor works just like the non-terminal destructors.</p> <a name='token_prefix'></a> <h4>The <tt>%token_prefix</tt> directive</h4> <p>Lemon generates #defines that assign small integer constants to each terminal symbol in the grammar. If desired, Lemon will add a prefix specified by this directive to each of the #defines it generates.</p> <p>So if the default output of Lemon looked like this: <pre> #define AND 1 #define MINUS 2 #define OR 3 #define PLUS 4 </pre> You can insert a statement into the grammar like this: <pre> %token_prefix TOKEN_ </pre> to cause Lemon to produce these symbols instead: <pre> #define TOKEN_AND 1 #define TOKEN_MINUS 2 #define TOKEN_OR 3 #define TOKEN_PLUS 4 </pre></p> <a name='token_type'></a><a name='ptype'></a> <h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4> <p>These directives are used to specify the data types for values on the parser's stack associated with terminal and non-terminal symbols. The values of all terminal symbols must be of the same type. This turns out to be the same data type as the 3rd parameter to the Parse() function generated by Lemon. Typically, you will make the value of a terminal symbol by a pointer to some kind of token structure. Like this:</p> <p><pre> %token_type {Token*} </pre></p> <p>If the data type of terminals is not specified, the default value is "void*".</p> <p>Non-terminal symbols can each have their own data types. Typically the data type of a non-terminal is a pointer to the root of a parse tree structure that contains all information about that non-terminal. For example:</p> <p><pre> %type expr {Expr*} </pre></p> <p>Each entry on the parser's stack is actually a union containing instances of all data types for every non-terminal and terminal symbol. Lemon will automatically use the correct element of this union depending on what the corresponding non-terminal or terminal symbol is. But the grammar designer should keep in mind that the size of the union will be the size of its largest element. So if you have a single non-terminal whose data type requires 1K of storage, then your 100 entry parser stack will require 100K of heap space. If you are willing and able to pay that price, fine. You just need to know.</p> <a name='pwildcard'></a> <h4>The <tt>%wildcard</tt> directive</h4> <p>The <tt>%wildcard</tt> directive is followed by a single token name and a 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 name='error_processing'></a> <h3>Error Processing</h3> <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> <p>When a Lemon-generated parser encounters a syntax error, it first invokes the code specified by the <tt>%syntax_error</tt> directive, if any. It then enters its error recovery strategy. The error recovery strategy is to begin popping the parsers stack until it enters a state where it is permitted to shift a special non-terminal symbol named "error". It then shifts this non-terminal and continues parsing. The <tt>%syntax_error</tt> routine will not be called again until at least three new tokens have been successfully shifted.</p> <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> </body> </html> |
Added ext/README.md.
> > > > > > > > | 1 2 3 4 5 6 7 8 | ## Loadable Extensions Various [loadable extensions](https://www.sqlite.org/loadext.html) for SQLite are found in subfolders. Most subfolders are dedicated to a single loadable extension (for example FTS5, or RTREE). But the misc/ subfolder contains a collection of smaller single-file extensions. |
Deleted ext/README.txt.
|
| < < |
Changes to ext/fts3/fts3.c.
︙ | ︙ | |||
345 346 347 348 349 350 351 | if( (v & mask2)==0 ){ var = v; return ret; } /* ** Read a 64-bit variable-length integer from memory starting at p[0]. ** Return the number of bytes read, or 0 on error. ** The value is stored in *v. */ | | > | | | | > > | 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 | if( (v & mask2)==0 ){ var = v; return ret; } /* ** Read a 64-bit variable-length integer from memory starting at p[0]. ** Return the number of bytes read, or 0 on error. ** The value is stored in *v. */ int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){ const unsigned char *p = (const unsigned char*)pBuf; const unsigned char *pStart = p; u32 a; u64 b; int shift; GETVARINT_INIT(a, p, 0, 0x00, 0x80, *v, 1); GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *v, 2); GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *v, 3); GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *v, 4); b = (a & 0x0FFFFFFF ); for(shift=28; shift<=63; shift+=7){ u64 c = *p++; b += (c&0x7F) << shift; if( (c & 0x80)==0 ) break; } *v = b; return (int)(p - pStart); } /* ** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to ** a non-negative 32-bit integer before it is returned. */ int sqlite3Fts3GetVarint32(const char *p, int *pi){ u32 a; #ifndef fts3GetVarint32 GETVARINT_INIT(a, p, 0, 0x00, 0x80, *pi, 1); #else a = (*p++); assert( a & 0x80 ); #endif GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *pi, 2); GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *pi, 3); GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *pi, 4); a = (a & 0x0FFFFFFF ); *pi = (int)(a | ((u32)(*p & 0x07) << 28)); assert( 0==(a & 0x80000000) ); assert( *pi>=0 ); return 5; } /* ** Return the number of bytes required to encode v as a varint */ int sqlite3Fts3VarintLen(sqlite3_uint64 v){ |
︙ | ︙ | |||
488 489 490 491 492 493 494 495 496 497 498 499 500 501 | Fts3Table *p = (Fts3Table *)pVtab; int i; assert( p->nPendingData==0 ); assert( p->pSegments==0 ); /* Free any prepared statements held */ for(i=0; i<SizeofArray(p->aStmt); i++){ sqlite3_finalize(p->aStmt[i]); } sqlite3_free(p->zSegmentsTbl); sqlite3_free(p->zReadExprlist); sqlite3_free(p->zWriteExprlist); sqlite3_free(p->zContentTbl); | > | 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | Fts3Table *p = (Fts3Table *)pVtab; int i; assert( p->nPendingData==0 ); assert( p->pSegments==0 ); /* Free any prepared statements held */ sqlite3_finalize(p->pSeekStmt); for(i=0; i<SizeofArray(p->aStmt); i++){ sqlite3_finalize(p->aStmt[i]); } sqlite3_free(p->zSegmentsTbl); sqlite3_free(p->zReadExprlist); sqlite3_free(p->zWriteExprlist); sqlite3_free(p->zContentTbl); |
︙ | ︙ | |||
1213 1214 1215 1216 1217 1218 1219 | }else{ for(iOpt=0; iOpt<SizeofArray(aFts4Opt); iOpt++){ struct Fts4Option *pOp = &aFts4Opt[iOpt]; if( nKey==pOp->nOpt && !sqlite3_strnicmp(z, pOp->zOpt, pOp->nOpt) ){ break; } } | < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > | 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 | }else{ for(iOpt=0; iOpt<SizeofArray(aFts4Opt); iOpt++){ struct Fts4Option *pOp = &aFts4Opt[iOpt]; if( nKey==pOp->nOpt && !sqlite3_strnicmp(z, pOp->zOpt, pOp->nOpt) ){ break; } } switch( iOpt ){ case 0: /* MATCHINFO */ if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){ sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo: %s", zVal); rc = SQLITE_ERROR; } bNoDocsize = 1; break; case 1: /* PREFIX */ sqlite3_free(zPrefix); zPrefix = zVal; zVal = 0; break; case 2: /* COMPRESS */ sqlite3_free(zCompress); zCompress = zVal; zVal = 0; break; case 3: /* UNCOMPRESS */ sqlite3_free(zUncompress); zUncompress = zVal; zVal = 0; break; case 4: /* ORDER */ if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3)) && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4)) ){ sqlite3Fts3ErrMsg(pzErr, "unrecognized order: %s", zVal); rc = SQLITE_ERROR; } bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); break; case 5: /* CONTENT */ sqlite3_free(zContent); zContent = zVal; zVal = 0; break; case 6: /* LANGUAGEID */ assert( iOpt==6 ); sqlite3_free(zLanguageid); zLanguageid = zVal; zVal = 0; break; case 7: /* NOTINDEXED */ azNotindexed[nNotindexed++] = zVal; zVal = 0; break; default: assert( iOpt==SizeofArray(aFts4Opt) ); sqlite3Fts3ErrMsg(pzErr, "unrecognized parameter: %s", z); rc = SQLITE_ERROR; break; } sqlite3_free(zVal); } } /* Otherwise, the argument is a column name. */ else { |
︙ | ︙ | |||
1359 1360 1361 1362 1363 1364 1365 | p->db = db; p->nColumn = nCol; p->nPendingData = 0; p->azColumn = (char **)&p[1]; p->pTokenizer = pTokenizer; p->nMaxPendingData = FTS3_MAX_PENDING_DATA; p->bHasDocsize = (isFts4 && bNoDocsize==0); | | | | | 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 | p->db = db; p->nColumn = nCol; p->nPendingData = 0; p->azColumn = (char **)&p[1]; p->pTokenizer = pTokenizer; p->nMaxPendingData = FTS3_MAX_PENDING_DATA; p->bHasDocsize = (isFts4 && bNoDocsize==0); p->bHasStat = (u8)isFts4; p->bFts4 = (u8)isFts4; p->bDescIdx = (u8)bDescIdx; p->nAutoincrmerge = 0xff; /* 0xff means setting unknown */ p->zContentTbl = zContent; p->zLanguageid = zLanguageid; zContent = 0; zLanguageid = 0; TESTONLY( p->inTransaction = -1 ); TESTONLY( p->mxSavepoint = -1 ); |
︙ | ︙ | |||
1392 1393 1394 1395 1396 1397 1398 | zCsr += nDb; /* Fill in the azColumn array */ for(iCol=0; iCol<nCol; iCol++){ char *z; int n = 0; z = (char *)sqlite3Fts3NextToken(aCol[iCol], &n); | > | > | 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 | zCsr += nDb; /* Fill in the azColumn array */ for(iCol=0; iCol<nCol; iCol++){ char *z; int n = 0; z = (char *)sqlite3Fts3NextToken(aCol[iCol], &n); if( n>0 ){ memcpy(zCsr, z, n); } zCsr[n] = '\0'; sqlite3Fts3Dequote(zCsr); p->azColumn[iCol] = zCsr; zCsr += n+1; assert( zCsr <= &((char *)p)[nByte] ); } |
︙ | ︙ | |||
1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 | *ppCsr = pCsr = (sqlite3_vtab_cursor *)sqlite3_malloc(sizeof(Fts3Cursor)); if( !pCsr ){ return SQLITE_NOMEM; } memset(pCsr, 0, sizeof(Fts3Cursor)); return SQLITE_OK; } /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. */ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | < < < < | > > > > | | | | | | > < < | | 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 | *ppCsr = pCsr = (sqlite3_vtab_cursor *)sqlite3_malloc(sizeof(Fts3Cursor)); if( !pCsr ){ return SQLITE_NOMEM; } memset(pCsr, 0, sizeof(Fts3Cursor)); return SQLITE_OK; } /* ** Finalize the statement handle at pCsr->pStmt. ** ** Or, if that statement handle is one created by fts3CursorSeekStmt(), ** and the Fts3Table.pSeekStmt slot is currently NULL, save the statement ** pointer there instead of finalizing it. */ static void fts3CursorFinalizeStmt(Fts3Cursor *pCsr){ if( pCsr->bSeekStmt ){ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; if( p->pSeekStmt==0 ){ p->pSeekStmt = pCsr->pStmt; sqlite3_reset(pCsr->pStmt); pCsr->pStmt = 0; } pCsr->bSeekStmt = 0; } sqlite3_finalize(pCsr->pStmt); } /* ** Free all resources currently held by the cursor passed as the only ** argument. */ static void fts3ClearCursor(Fts3Cursor *pCsr){ fts3CursorFinalizeStmt(pCsr); sqlite3Fts3FreeDeferredTokens(pCsr); sqlite3_free(pCsr->aDoclist); sqlite3Fts3MIBufferFree(pCsr->pMIBuffer); sqlite3Fts3ExprFree(pCsr->pExpr); memset(&(&pCsr->base)[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); } /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. */ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); fts3ClearCursor(pCsr); assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); sqlite3_free(pCsr); return SQLITE_OK; } /* ** If pCsr->pStmt has not been prepared (i.e. if pCsr->pStmt==0), then ** compose and prepare an SQL statement of the form: ** ** "SELECT <columns> FROM %_content WHERE rowid = ?" ** ** (or the equivalent for a content=xxx table) and set pCsr->pStmt to ** it. If an error occurs, return an SQLite error code. */ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){ int rc = SQLITE_OK; if( pCsr->pStmt==0 ){ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; char *zSql; if( p->pSeekStmt ){ pCsr->pStmt = p->pSeekStmt; p->pSeekStmt = 0; }else{ zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); if( !zSql ) return SQLITE_NOMEM; rc = sqlite3_prepare_v3(p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0); sqlite3_free(zSql); } if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1; } return rc; } /* ** Position the pCsr->pStmt statement so that it is on the row ** of the %_content table that contains the last match. Return ** SQLITE_OK on success. */ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ int rc = SQLITE_OK; if( pCsr->isRequireSeek ){ rc = fts3CursorSeekStmt(pCsr); if( rc==SQLITE_OK ){ sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); pCsr->isRequireSeek = 0; if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ return SQLITE_OK; }else{ rc = sqlite3_reset(pCsr->pStmt); |
︙ | ︙ | |||
1817 1818 1819 1820 1821 1822 1823 | ** the size of zBuffer if required. */ if( !isFirstTerm ){ zCsr += fts3GetVarint32(zCsr, &nPrefix); } isFirstTerm = 0; zCsr += fts3GetVarint32(zCsr, &nSuffix); | > | | 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 | ** the size of zBuffer if required. */ if( !isFirstTerm ){ zCsr += fts3GetVarint32(zCsr, &nPrefix); } isFirstTerm = 0; zCsr += fts3GetVarint32(zCsr, &nSuffix); assert( nPrefix>=0 && nSuffix>=0 ); if( &zCsr[nSuffix]>zEnd ){ rc = FTS_CORRUPT_VTAB; goto finish_scan; } if( nPrefix+nSuffix>nAlloc ){ char *zNew; nAlloc = (nPrefix+nSuffix) * 2; zNew = (char *)sqlite3_realloc(zBuffer, nAlloc); |
︙ | ︙ | |||
2627 2628 2629 2630 2631 2632 2633 | nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta); pOut[nOut++] = 0x02; bWritten = 1; } fts3ColumnlistCopy(0, &p); } | | | 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 | nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta); pOut[nOut++] = 0x02; bWritten = 1; } fts3ColumnlistCopy(0, &p); } while( p<pEnd ){ sqlite3_int64 iCol; p++; p += sqlite3Fts3GetVarint(p, &iCol); if( *p==0x02 ){ if( bWritten==0 ){ nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta); bWritten = 1; |
︙ | ︙ | |||
3186 3187 3188 3189 3190 3191 3192 | if( eSearch!=FTS3_FULLSCAN_SEARCH ) pCons = apVal[iIdx++]; if( idxNum & FTS3_HAVE_LANGID ) pLangid = apVal[iIdx++]; if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++]; if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++]; assert( iIdx==nVal ); /* In case the cursor has been used before, clear it now. */ | | < < < < | 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 | if( eSearch!=FTS3_FULLSCAN_SEARCH ) pCons = apVal[iIdx++]; if( idxNum & FTS3_HAVE_LANGID ) pLangid = apVal[iIdx++]; if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++]; if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++]; assert( iIdx==nVal ); /* In case the cursor has been used before, clear it now. */ fts3ClearCursor(pCsr); /* Set the lower and upper bounds on docids to return */ pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64); pCsr->iMaxDocid = fts3DocidRange(pDocidLe, LARGEST_INT64); if( idxStr ){ pCsr->bDesc = (idxStr[0]=='D'); |
︙ | ︙ | |||
3248 3249 3250 3251 3252 3253 3254 | ); }else{ zSql = sqlite3_mprintf("SELECT %s ORDER BY rowid %s", p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") ); } if( zSql ){ | | | | > > > > > | 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 | ); }else{ zSql = sqlite3_mprintf("SELECT %s ORDER BY rowid %s", p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") ); } if( zSql ){ rc = sqlite3_prepare_v3(p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0); sqlite3_free(zSql); }else{ rc = SQLITE_NOMEM; } }else if( eSearch==FTS3_DOCID_SEARCH ){ rc = fts3CursorSeekStmt(pCsr); if( rc==SQLITE_OK ){ rc = sqlite3_bind_value(pCsr->pStmt, 1, pCons); } } if( rc!=SQLITE_OK ) return rc; return fts3NextMethod(pCursor); } /* ** This is the xEof method of the virtual table. SQLite calls this ** routine to find out if it has reached the end of a result set. */ static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){ Fts3Cursor *pCsr = (Fts3Cursor*)pCursor; if( pCsr->isEof ){ fts3ClearCursor(pCsr); pCsr->isEof = 1; } return pCsr->isEof; } /* ** This is the xRowid method. The SQLite core calls this routine to ** retrieve the rowid for the current row of the result set. fts3 ** exposes %_content.docid as the rowid for the virtual table. The ** rowid should be written to *pRowid. |
︙ | ︙ | |||
3307 3308 3309 3310 3311 3312 3313 | int rc = SQLITE_OK; /* Return Code */ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; Fts3Table *p = (Fts3Table *)pCursor->pVtab; /* The column value supplied by SQLite must be in range. */ assert( iCol>=0 && iCol<=p->nColumn+2 ); | | | | > > | > > | > | | < < | | > > > > | < | < > | < < < < < | | > > > | < > | 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 | int rc = SQLITE_OK; /* Return Code */ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; Fts3Table *p = (Fts3Table *)pCursor->pVtab; /* The column value supplied by SQLite must be in range. */ assert( iCol>=0 && iCol<=p->nColumn+2 ); switch( iCol-p->nColumn ){ case 0: /* The special 'table-name' column */ sqlite3_result_pointer(pCtx, pCsr, "fts3cursor", 0); break; case 1: /* The docid column */ sqlite3_result_int64(pCtx, pCsr->iPrevId); break; case 2: if( pCsr->pExpr ){ sqlite3_result_int64(pCtx, pCsr->iLangid); break; }else if( p->zLanguageid==0 ){ sqlite3_result_int(pCtx, 0); break; }else{ iCol = p->nColumn; /* fall-through */ } default: /* A user column. Or, if this is a full-table scan, possibly the ** language-id column. Seek the cursor. */ rc = fts3CursorSeek(0, pCsr); if( rc==SQLITE_OK && sqlite3_data_count(pCsr->pStmt)-1>iCol ){ sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); } break; } assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); return rc; } /* |
︙ | ︙ | |||
3382 3383 3384 3385 3386 3387 3388 | ** of blocks from the segments table. But this is not considered overhead ** as it would also be required by a crisis-merge that used the same input ** segments. */ const u32 nMinMerge = 64; /* Minimum amount of incr-merge work to do */ Fts3Table *p = (Fts3Table*)pVtab; | | > > > < | | < < < < | < < | > | 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 | ** of blocks from the segments table. But this is not considered overhead ** as it would also be required by a crisis-merge that used the same input ** segments. */ const u32 nMinMerge = 64; /* Minimum amount of incr-merge work to do */ Fts3Table *p = (Fts3Table*)pVtab; int rc; i64 iLastRowid = sqlite3_last_insert_rowid(p->db); rc = sqlite3Fts3PendingTermsFlush(p); if( rc==SQLITE_OK && p->nLeafAdd>(nMinMerge/16) && p->nAutoincrmerge && p->nAutoincrmerge!=0xff ){ int mxLevel = 0; /* Maximum relative level value in db */ int A; /* Incr-merge parameter A */ rc = sqlite3Fts3MaxLevel(p, &mxLevel); assert( rc==SQLITE_OK || mxLevel==0 ); A = p->nLeafAdd * mxLevel; A += (A/2); if( A>(int)nMinMerge ) rc = sqlite3Fts3Incrmerge(p, A, p->nAutoincrmerge); } sqlite3Fts3SegmentsClose(p); sqlite3_set_last_insert_rowid(p->db, iLastRowid); return rc; } /* ** If it is currently unknown whether or not the FTS table has an %_stat ** table (if p->bHasStat==2), attempt to determine this (set p->bHasStat ** to 0 or 1). Return SQLITE_OK if successful, or an SQLite error code ** if an error occurs. */ static int fts3SetHasStat(Fts3Table *p){ int rc = SQLITE_OK; if( p->bHasStat==2 ){ char *zTbl = sqlite3_mprintf("%s_stat", p->zName); if( zTbl ){ int res = sqlite3_table_column_metadata(p->db, p->zDb, zTbl, 0,0,0,0,0,0); sqlite3_free(zTbl); p->bHasStat = (res==SQLITE_OK); }else{ rc = SQLITE_NOMEM; } } return rc; } |
︙ | ︙ | |||
3527 3528 3529 3530 3531 3532 3533 | */ static int fts3FunctionArg( sqlite3_context *pContext, /* SQL function call context */ const char *zFunc, /* Function name */ sqlite3_value *pVal, /* argv[0] passed to function */ Fts3Cursor **ppCsr /* OUT: Store cursor handle here */ ){ | > | > | < < > | < < | | 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 | */ static int fts3FunctionArg( sqlite3_context *pContext, /* SQL function call context */ const char *zFunc, /* Function name */ sqlite3_value *pVal, /* argv[0] passed to function */ Fts3Cursor **ppCsr /* OUT: Store cursor handle here */ ){ int rc; *ppCsr = (Fts3Cursor*)sqlite3_value_pointer(pVal, "fts3cursor"); if( (*ppCsr)!=0 ){ rc = SQLITE_OK; }else{ char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc); sqlite3_result_error(pContext, zErr, -1); sqlite3_free(zErr); rc = SQLITE_ERROR; } return rc; } /* ** Implementation of the snippet() function for FTS3 */ static void fts3SnippetFunc( sqlite3_context *pContext, /* SQLite function call context */ |
︙ | ︙ | |||
3925 3926 3927 3928 3929 3930 3931 | #ifdef SQLITE_TEST if( rc==SQLITE_OK ){ rc = sqlite3Fts3ExprInitTestInterface(db); } #endif /* Create the virtual table wrapper around the hash-table and overload | | | 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 | #ifdef SQLITE_TEST if( rc==SQLITE_OK ){ rc = sqlite3Fts3ExprInitTestInterface(db); } #endif /* Create the virtual table wrapper around the hash-table and overload ** the four scalar functions. If this is successful, register the ** module with sqlite. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) |
︙ | ︙ | |||
4508 4509 4510 4511 4512 4513 4514 | Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; u8 bEof = 0; /* This is only called if it is guaranteed that the phrase has at least ** one incremental token. In which case the bIncr flag is set. */ assert( p->bIncr==1 ); | | | 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 | Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; u8 bEof = 0; /* This is only called if it is guaranteed that the phrase has at least ** one incremental token. In which case the bIncr flag is set. */ assert( p->bIncr==1 ); if( p->nToken==1 ){ rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr, &pDL->iDocid, &pDL->pList, &pDL->nList ); if( pDL->pList==0 ) bEof = 1; }else{ int bDescDoclist = pCsr->bDesc; struct TokenDoclist a[MAX_INCR_PHRASE_TOKENS]; |
︙ | ︙ | |||
4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 | ** The average document size in pages is calculated by first calculating ** determining the average size in bytes, B. If B is less than the amount ** of data that will fit on a single leaf page of an intkey table in ** this database, then the average docsize is 1. Otherwise, it is 1 plus ** the number of overflow pages consumed by a record B bytes in size. */ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){ if( pCsr->nRowAvg==0 ){ /* The average document size, which is required to calculate the cost ** of each doclist, has not yet been determined. Read the required ** data from the %_stat table to calculate it. ** ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3 ** varints, where nCol is the number of columns in the FTS3 table. ** The first varint is the number of documents currently stored in ** the table. The following nCol varints contain the total amount of ** data stored in all rows of each column of the table, from left ** to right. */ | > < | 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 | ** The average document size in pages is calculated by first calculating ** determining the average size in bytes, B. If B is less than the amount ** of data that will fit on a single leaf page of an intkey table in ** this database, then the average docsize is 1. Otherwise, it is 1 plus ** the number of overflow pages consumed by a record B bytes in size. */ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){ int rc = SQLITE_OK; if( pCsr->nRowAvg==0 ){ /* The average document size, which is required to calculate the cost ** of each doclist, has not yet been determined. Read the required ** data from the %_stat table to calculate it. ** ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3 ** varints, where nCol is the number of columns in the FTS3 table. ** The first varint is the number of documents currently stored in ** the table. The following nCol varints contain the total amount of ** data stored in all rows of each column of the table, from left ** to right. */ Fts3Table *p = (Fts3Table*)pCsr->base.pVtab; sqlite3_stmt *pStmt; sqlite3_int64 nDoc = 0; sqlite3_int64 nByte = 0; const char *pEnd; const char *a; |
︙ | ︙ | |||
4780 4781 4782 4783 4784 4785 4786 | return FTS_CORRUPT_VTAB; } pCsr->nDoc = nDoc; pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz); assert( pCsr->nRowAvg>0 ); rc = sqlite3_reset(pStmt); | < | | 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 | return FTS_CORRUPT_VTAB; } pCsr->nDoc = nDoc; pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz); assert( pCsr->nRowAvg>0 ); rc = sqlite3_reset(pStmt); } *pnPage = pCsr->nRowAvg; return rc; } /* ** This function is called to select the tokens (if any) that will be ** deferred. The array aTC[] has already been populated when this is ** called. ** |
︙ | ︙ | |||
5134 5135 5136 5137 5138 5139 5140 | }else{ fts3EvalNextRow(pCsr, pRight, pRc); } } pExpr->iDocid = pLeft->iDocid; pExpr->bEof = (pLeft->bEof || pRight->bEof); if( pExpr->eType==FTSQUERY_NEAR && pExpr->bEof ){ | > | | 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 | }else{ fts3EvalNextRow(pCsr, pRight, pRc); } } pExpr->iDocid = pLeft->iDocid; pExpr->bEof = (pLeft->bEof || pRight->bEof); if( pExpr->eType==FTSQUERY_NEAR && pExpr->bEof ){ assert( pRight->eType==FTSQUERY_PHRASE ); if( pRight->pPhrase->doclist.aAll ){ Fts3Doclist *pDl = &pRight->pPhrase->doclist; while( *pRc==SQLITE_OK && pRight->bEof==0 ){ memset(pDl->pList, 0, pDl->nList); fts3EvalNextRow(pCsr, pRight, pRc); } } if( pLeft->pPhrase && pLeft->pPhrase->doclist.aAll ){ |
︙ | ︙ | |||
5163 5164 5165 5166 5167 5168 5169 | sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); assert( pRight->bStart || pLeft->iDocid==pRight->iDocid ); if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ fts3EvalNextRow(pCsr, pLeft, pRc); | | | 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 | sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); assert( pRight->bStart || pLeft->iDocid==pRight->iDocid ); if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ fts3EvalNextRow(pCsr, pLeft, pRc); }else if( pLeft->bEof || iCmp>0 ){ fts3EvalNextRow(pCsr, pRight, pRc); }else{ fts3EvalNextRow(pCsr, pLeft, pRc); fts3EvalNextRow(pCsr, pRight, pRc); } pExpr->bEof = (pLeft->bEof && pRight->bEof); |
︙ | ︙ | |||
5255 5256 5257 5258 5259 5260 5261 | ** ** The right-hand child of a NEAR node is always a phrase. The ** left-hand child may be either a phrase or a NEAR node. There are ** no exceptions to this - it's the way the parser in fts3_expr.c works. */ if( *pRc==SQLITE_OK && pExpr->eType==FTSQUERY_NEAR | < > < < < | | | | | | | | | | | | | | | | | | | | | | | | | | < | 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 | ** ** The right-hand child of a NEAR node is always a phrase. The ** left-hand child may be either a phrase or a NEAR node. There are ** no exceptions to this - it's the way the parser in fts3_expr.c works. */ if( *pRc==SQLITE_OK && pExpr->eType==FTSQUERY_NEAR && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR) ){ Fts3Expr *p; int nTmp = 0; /* Bytes of temp space */ char *aTmp; /* Temp space for PoslistNearMerge() */ /* Allocate temporary working space. */ for(p=pExpr; p->pLeft; p=p->pLeft){ assert( p->pRight->pPhrase->doclist.nList>0 ); nTmp += p->pRight->pPhrase->doclist.nList; } nTmp += p->pPhrase->doclist.nList; aTmp = sqlite3_malloc(nTmp*2); if( !aTmp ){ *pRc = SQLITE_NOMEM; res = 0; }else{ char *aPoslist = p->pPhrase->doclist.pList; int nToken = p->pPhrase->nToken; for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){ Fts3Phrase *pPhrase = p->pRight->pPhrase; int nNear = p->nNear; res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); } aPoslist = pExpr->pRight->pPhrase->doclist.pList; nToken = pExpr->pRight->pPhrase->nToken; for(p=pExpr->pLeft; p && res; p=p->pLeft){ int nNear; Fts3Phrase *pPhrase; assert( p->pParent && p->pParent->pLeft==p ); nNear = p->pParent->nNear; pPhrase = ( p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase ); res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase); } } sqlite3_free(aTmp); } return res; } /* ** This function is a helper function for sqlite3Fts3EvalTestDeferred(). |
︙ | ︙ |
Changes to ext/fts3/fts3Int.h.
︙ | ︙ | |||
226 227 228 229 230 231 232 233 234 235 236 237 238 239 | int nAutoincrmerge; /* Value configured by 'automerge' */ u32 nLeafAdd; /* Number of leaf blocks added this trans */ /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. */ sqlite3_stmt *aStmt[40]; char *zReadExprlist; char *zWriteExprlist; int nNodeSize; /* Soft limit for node size */ u8 bFts4; /* True for FTS4, false for FTS3 */ u8 bHasStat; /* True if %_stat table exists (2==unknown) */ | > | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | int nAutoincrmerge; /* Value configured by 'automerge' */ u32 nLeafAdd; /* Number of leaf blocks added this trans */ /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. */ sqlite3_stmt *aStmt[40]; sqlite3_stmt *pSeekStmt; /* Cache for fts3CursorSeekStmt() */ char *zReadExprlist; char *zWriteExprlist; int nNodeSize; /* Soft limit for node size */ u8 bFts4; /* True for FTS4, false for FTS3 */ u8 bHasStat; /* True if %_stat table exists (2==unknown) */ |
︙ | ︙ | |||
295 296 297 298 299 300 301 302 303 304 305 306 307 308 | ** the xOpen method. Cursors are destroyed using the xClose method. */ struct Fts3Cursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ i16 eSearch; /* Search strategy (see below) */ u8 isEof; /* True if at End Of Results */ u8 isRequireSeek; /* True if must seek pStmt to %_content row */ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ Fts3Expr *pExpr; /* Parsed MATCH query string */ int iLangid; /* Language being queried for */ int nPhrase; /* Number of matchable phrases in query */ Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ | > | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 | ** the xOpen method. Cursors are destroyed using the xClose method. */ struct Fts3Cursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ i16 eSearch; /* Search strategy (see below) */ u8 isEof; /* True if at End Of Results */ u8 isRequireSeek; /* True if must seek pStmt to %_content row */ u8 bSeekStmt; /* True if pStmt is a seek */ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ Fts3Expr *pExpr; /* Parsed MATCH query string */ int iLangid; /* Language being queried for */ int nPhrase; /* Number of matchable phrases in query */ Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ |
︙ | ︙ |
Changes to ext/fts3/fts3_unicode.c.
︙ | ︙ | |||
132 133 134 135 136 137 138 | unicode_tokenizer *p, /* Tokenizer to add exceptions to */ int bAlnum, /* Replace Isalnum() return value with this */ const char *zIn, /* Array of characters to make exceptions */ int nIn /* Length of z in bytes */ ){ const unsigned char *z = (const unsigned char *)zIn; const unsigned char *zTerm = &z[nIn]; | | | | | | | | | | 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 | unicode_tokenizer *p, /* Tokenizer to add exceptions to */ int bAlnum, /* Replace Isalnum() return value with this */ const char *zIn, /* Array of characters to make exceptions */ int nIn /* Length of z in bytes */ ){ const unsigned char *z = (const unsigned char *)zIn; const unsigned char *zTerm = &z[nIn]; unsigned int iCode; int nEntry = 0; assert( bAlnum==0 || bAlnum==1 ); while( z<zTerm ){ READ_UTF8(z, zTerm, iCode); assert( (sqlite3FtsUnicodeIsalnum((int)iCode) & 0xFFFFFFFE)==0 ); if( sqlite3FtsUnicodeIsalnum((int)iCode)!=bAlnum && sqlite3FtsUnicodeIsdiacritic((int)iCode)==0 ){ nEntry++; } } if( nEntry ){ int *aNew; /* New aiException[] array */ int nNew; /* Number of valid entries in array aNew[] */ aNew = sqlite3_realloc(p->aiException, (p->nException+nEntry)*sizeof(int)); if( aNew==0 ) return SQLITE_NOMEM; nNew = p->nException; z = (const unsigned char *)zIn; while( z<zTerm ){ READ_UTF8(z, zTerm, iCode); if( sqlite3FtsUnicodeIsalnum((int)iCode)!=bAlnum && sqlite3FtsUnicodeIsdiacritic((int)iCode)==0 ){ int i, j; for(i=0; i<nNew && aNew[i]<(int)iCode; i++); for(j=nNew; j>i; j--) aNew[j] = aNew[j-1]; aNew[i] = (int)iCode; nNew++; } } p->aiException = aNew; p->nException = nNew; } |
︙ | ︙ | |||
314 315 316 317 318 319 320 | int *pnToken, /* OUT: Number of bytes at *paToken */ int *piStart, /* OUT: Starting offset of token */ int *piEnd, /* OUT: Ending offset of token */ int *piPos /* OUT: Position integer of token */ ){ unicode_cursor *pCsr = (unicode_cursor *)pC; unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer); | | | | | | | 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 | int *pnToken, /* OUT: Number of bytes at *paToken */ int *piStart, /* OUT: Starting offset of token */ int *piEnd, /* OUT: Ending offset of token */ int *piPos /* OUT: Position integer of token */ ){ unicode_cursor *pCsr = (unicode_cursor *)pC; unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer); unsigned int iCode = 0; char *zOut; const unsigned char *z = &pCsr->aInput[pCsr->iOff]; const unsigned char *zStart = z; const unsigned char *zEnd; const unsigned char *zTerm = &pCsr->aInput[pCsr->nInput]; /* Scan past any delimiter characters before the start of the next token. ** Return SQLITE_DONE early if this takes us all the way to the end of ** the input. */ while( z<zTerm ){ READ_UTF8(z, zTerm, iCode); if( unicodeIsAlnum(p, (int)iCode) ) break; zStart = z; } if( zStart>=zTerm ) return SQLITE_DONE; zOut = pCsr->zToken; do { int iOut; /* Grow the output buffer if required. */ if( (zOut-pCsr->zToken)>=(pCsr->nAlloc-4) ){ char *zNew = sqlite3_realloc(pCsr->zToken, pCsr->nAlloc+64); if( !zNew ) return SQLITE_NOMEM; zOut = &zNew[zOut - pCsr->zToken]; pCsr->zToken = zNew; pCsr->nAlloc += 64; } /* Write the folded case of the last character read to the output */ zEnd = z; iOut = sqlite3FtsUnicodeFold((int)iCode, p->bRemoveDiacritic); if( iOut ){ WRITE_UTF8(zOut, iOut); } /* If the cursor is not at EOF, read the next character */ if( z>=zTerm ) break; READ_UTF8(z, zTerm, iCode); }while( unicodeIsAlnum(p, (int)iCode) || sqlite3FtsUnicodeIsdiacritic((int)iCode) ); /* Set the output variables and return. */ pCsr->iOff = (int)(z - pCsr->aInput); *paToken = pCsr->zToken; *pnToken = (int)(zOut - pCsr->zToken); *piStart = (int)(zStart - pCsr->aInput); |
︙ | ︙ |
Changes to ext/fts3/fts3_unicode2.c.
︙ | ︙ | |||
123 124 125 126 127 128 129 | 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060, 0x380400F0, }; static const unsigned int aAscii[4] = { 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001, }; | | | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060, 0x380400F0, }; static const unsigned int aAscii[4] = { 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001, }; if( (unsigned int)c<128 ){ return ( (aAscii[c >> 5] & ((unsigned int)1 << (c & 0x001F)))==0 ); }else if( (unsigned int)c<(1<<22) ){ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; int iRes = 0; int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; int iLo = 0; while( iHi>=iLo ){ int iTest = (iHi + iLo) / 2; if( key >= aEntry[iTest] ){ |
︙ | ︙ | |||
318 319 320 321 322 323 324 | 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462, 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511, 65514, 65521, 65527, 65528, 65529, }; int ret = c; | < > > < | | | | | < | 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 | 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462, 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511, 65514, 65521, 65527, 65528, 65529, }; int ret = c; assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 ); if( c<128 ){ if( c>='A' && c<='Z' ) ret = c + ('a' - 'A'); }else if( c<65536 ){ const struct TableEntry *p; int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; int iLo = 0; int iRes = -1; assert( c>aEntry[0].iCode ); while( iHi>=iLo ){ int iTest = (iHi + iLo) / 2; int cmp = (c - aEntry[iTest].iCode); if( cmp>=0 ){ iRes = iTest; iLo = iTest+1; }else{ iHi = iTest-1; } } assert( iRes>=0 && c>=aEntry[iRes].iCode ); p = &aEntry[iRes]; if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){ ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF; assert( ret>0 ); } if( bRemoveDiacritic ) ret = remove_diacritic(ret); } else if( c>=66560 && c<66600 ){ ret = c + 40; } return ret; } #endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */ #endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */ |
Changes to ext/fts3/fts3_write.c.
︙ | ︙ | |||
403 404 405 406 407 408 409 | zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); } if( !zSql ){ rc = SQLITE_NOMEM; }else{ | > | | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); } if( !zSql ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v3(p->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &pStmt, NULL); sqlite3_free(zSql); assert( rc==SQLITE_OK || pStmt==0 ); p->aStmt[eStmt] = pStmt; } } if( apVal ){ int i; |
︙ | ︙ | |||
4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 | return rc; } /* ** Convert the text beginning at *pz into an integer and return ** its value. Advance *pz to point to the first character past ** the integer. */ static int fts3Getint(const char **pz){ const char *z = *pz; int i = 0; | > > > | | 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 | return rc; } /* ** Convert the text beginning at *pz into an integer and return ** its value. Advance *pz to point to the first character past ** the integer. ** ** This function used for parameters to merge= and incrmerge= ** commands. */ static int fts3Getint(const char **pz){ const char *z = *pz; int i = 0; while( (*z)>='0' && (*z)<='9' && i<214748363 ) i = 10*i + *(z++) - '0'; *pz = z; return i; } /* ** Process statements of the form: ** |
︙ | ︙ |
Added ext/fts3/tool/fts3cov.sh.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/bin/sh set -e srcdir=`dirname $(dirname $(dirname $(dirname $0)))` ./testfixture $srcdir/test/fts3.test --output=fts3cov-out.txt echo "" for f in `ls $srcdir/ext/fts3/*.c` do f=`basename $f` echo -ne "$f: " gcov -b $f | grep Taken | sed 's/Taken at least once://' done |
Changes to ext/fts3/unicode/mkunicode.tcl.
︙ | ︙ | |||
223 224 225 226 227 228 229 | puts "** is less than zero." puts "*/" puts "int ${zFunc}\(int c)\{" an_print_range_array $lRange an_print_ascii_bitmap $lRange puts { if( (unsigned int)c<128 ){ | | | 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | puts "** is less than zero." puts "*/" puts "int ${zFunc}\(int c)\{" an_print_range_array $lRange an_print_ascii_bitmap $lRange puts { if( (unsigned int)c<128 ){ return ( (aAscii[c >> 5] & ((unsigned int)1 << (c & 0x001F)))==0 ); }else if( (unsigned int)c<(1<<22) ){ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; int iRes = 0; int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; int iLo = 0; while( iHi>=iLo ){ int iTest = (iHi + iLo) / 2; |
︙ | ︙ |
Changes to ext/fts5/fts5Int.h.
︙ | ︙ | |||
26 27 28 29 30 31 32 | typedef unsigned char u8; typedef unsigned int u32; typedef unsigned short u16; typedef short i16; typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; | > | > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | typedef unsigned char u8; typedef unsigned int u32; typedef unsigned short u16; typedef short i16; typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; #ifndef ArraySize # define ArraySize(x) ((int)(sizeof(x) / sizeof(x[0]))) #endif #define testcase(x) #define ALWAYS(x) 1 #define NEVER(x) 0 #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define MAX(x,y) (((x) > (y)) ? (x) : (y)) |
︙ | ︙ | |||
440 441 442 443 444 445 446 | Fts5Index *p, /* Index to write to */ int bDelete, /* True if current operation is a delete */ i64 iDocid /* Docid to add or remove data from */ ); /* ** Flush any data stored in the in-memory hash tables to the database. | | | | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | Fts5Index *p, /* Index to write to */ int bDelete, /* True if current operation is a delete */ i64 iDocid /* Docid to add or remove data from */ ); /* ** Flush any data stored in the in-memory hash tables to the database. ** Also close any open blob handles. */ int sqlite3Fts5IndexSync(Fts5Index *p); /* ** Discard any data stored in the in-memory hash tables. Do not write it ** to the database. Additionally, assume that the contents of the %_data ** table may have changed on disk. So any in-memory caches of %_data ** records must be invalidated. */ |
︙ | ︙ | |||
612 613 614 615 616 617 618 | int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol); int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnAvg); int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow); | | | 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 | int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol); int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnAvg); int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow); int sqlite3Fts5StorageSync(Fts5Storage *p); int sqlite3Fts5StorageRollback(Fts5Storage *p); int sqlite3Fts5StorageConfigValue( Fts5Storage *p, const char*, sqlite3_value*, int ); int sqlite3Fts5StorageDeleteAll(Fts5Storage *p); |
︙ | ︙ | |||
648 649 650 651 652 653 654 655 656 657 658 659 660 661 | const char *p; /* Token text (not NULL terminated) */ int n; /* Size of buffer p in bytes */ }; /* Parse a MATCH expression. */ int sqlite3Fts5ExprNew( Fts5Config *pConfig, const char *zExpr, Fts5Expr **ppNew, char **pzErr ); /* ** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc); | > | 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | const char *p; /* Token text (not NULL terminated) */ int n; /* Size of buffer p in bytes */ }; /* Parse a MATCH expression. */ int sqlite3Fts5ExprNew( Fts5Config *pConfig, int iCol, /* Column on LHS of MATCH operator */ const char *zExpr, Fts5Expr **ppNew, char **pzErr ); /* ** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc); |
︙ | ︙ | |||
732 733 734 735 736 737 738 | ); void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase*); void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*); void sqlite3Fts5ParseNodeFree(Fts5ExprNode*); void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); | | | 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 | ); void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase*); void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*); void sqlite3Fts5ParseNodeFree(Fts5ExprNode*); void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNode*, Fts5Colset*); Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse*, Fts5Colset*); void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p); void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*); /* ** End of interface to code in fts5_expr.c. **************************************************************************/ |
︙ | ︙ |
Changes to ext/fts5/fts5_buffer.c.
︙ | ︙ | |||
63 64 65 66 67 68 69 | void sqlite3Fts5BufferAppendBlob( int *pRc, Fts5Buffer *pBuf, u32 nData, const u8 *pData ){ assert_nc( *pRc || nData>=0 ); | > | | | > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | void sqlite3Fts5BufferAppendBlob( int *pRc, Fts5Buffer *pBuf, u32 nData, const u8 *pData ){ assert_nc( *pRc || nData>=0 ); 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 ** ensures that the byte following the buffer data is set to 0x00, even ** though this byte is not included in the pBuf->n count. */ |
︙ | ︙ | |||
242 243 244 245 246 247 248 | return SQLITE_OK; } void *sqlite3Fts5MallocZero(int *pRc, int nByte){ void *pRet = 0; if( *pRc==SQLITE_OK ){ pRet = sqlite3_malloc(nByte); | | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | return SQLITE_OK; } void *sqlite3Fts5MallocZero(int *pRc, int nByte){ void *pRet = 0; if( *pRc==SQLITE_OK ){ pRet = sqlite3_malloc(nByte); if( pRet==0 ){ if( nByte>0 ) *pRc = SQLITE_NOMEM; }else{ memset(pRet, 0, nByte); } } return pRet; } |
︙ | ︙ |
Changes to ext/fts5/fts5_expr.c.
︙ | ︙ | |||
209 210 211 212 213 214 215 216 217 218 219 220 221 222 | } static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc((int)t); } static void fts5ParseFree(void *p){ sqlite3_free(p); } int sqlite3Fts5ExprNew( Fts5Config *pConfig, /* FTS5 Configuration */ const char *zExpr, /* Expression text */ Fts5Expr **ppNew, char **pzErr ){ Fts5Parse sParse; Fts5Token token; const char *z = zExpr; | > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | } static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc((int)t); } static void fts5ParseFree(void *p){ sqlite3_free(p); } int sqlite3Fts5ExprNew( Fts5Config *pConfig, /* FTS5 Configuration */ int iCol, const char *zExpr, /* Expression text */ Fts5Expr **ppNew, char **pzErr ){ Fts5Parse sParse; Fts5Token token; const char *z = zExpr; |
︙ | ︙ | |||
232 233 234 235 236 237 238 239 240 241 242 243 244 245 | sParse.pConfig = pConfig; do { t = fts5ExprGetToken(&sParse, &z, &token); sqlite3Fts5Parser(pEngine, t, token, &sParse); }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); sqlite3Fts5ParserFree(pEngine, fts5ParseFree); assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 ); if( sParse.rc==SQLITE_OK ){ *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr)); if( pNew==0 ){ sParse.rc = SQLITE_NOMEM; sqlite3Fts5ParseNodeFree(sParse.pExpr); | > > > > > > > > > > > > | 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 | sParse.pConfig = pConfig; do { t = fts5ExprGetToken(&sParse, &z, &token); sqlite3Fts5Parser(pEngine, t, token, &sParse); }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); sqlite3Fts5ParserFree(pEngine, fts5ParseFree); /* If the LHS of the MATCH expression was a user column, apply the ** implicit column-filter. */ if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){ int n = sizeof(Fts5Colset); Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); if( pColset ){ pColset->nCol = 1; pColset->aiCol[0] = iCol; sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); } } assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 ); if( sParse.rc==SQLITE_OK ){ *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr)); if( pNew==0 ){ sParse.rc = SQLITE_NOMEM; sqlite3Fts5ParseNodeFree(sParse.pExpr); |
︙ | ︙ | |||
742 743 744 745 746 747 748 749 750 751 752 753 754 | } /* ** Initialize all term iterators in the pNear object. If any term is found ** to match no documents at all, return immediately without initializing any ** further iterators. */ static int fts5ExprNearInitAll( Fts5Expr *pExpr, Fts5ExprNode *pNode ){ Fts5ExprNearset *pNear = pNode->pNear; | > > > > | < < | > > > > > | | | > | > | | | | | | | | | | | > | | | | | | > > | < | | > > | | | 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 | } /* ** Initialize all term iterators in the pNear object. If any term is found ** to match no documents at all, return immediately without initializing any ** further iterators. ** ** If an error occurs, return an SQLite error code. Otherwise, return ** SQLITE_OK. It is not considered an error if some term matches zero ** documents. */ static int fts5ExprNearInitAll( Fts5Expr *pExpr, Fts5ExprNode *pNode ){ Fts5ExprNearset *pNear = pNode->pNear; int i; assert( pNode->bNomatch==0 ); for(i=0; i<pNear->nPhrase; i++){ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; if( pPhrase->nTerm==0 ){ pNode->bEof = 1; return SQLITE_OK; }else{ int j; for(j=0; j<pPhrase->nTerm; j++){ Fts5ExprTerm *pTerm = &pPhrase->aTerm[j]; Fts5ExprTerm *p; int bHit = 0; for(p=pTerm; p; p=p->pSynonym){ int rc; if( p->pIter ){ sqlite3Fts5IterClose(p->pIter); p->pIter = 0; } rc = sqlite3Fts5IndexQuery( pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm), (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; if( 0==sqlite3Fts5IterEof(p->pIter) ){ bHit = 1; } } if( bHit==0 ){ pNode->bEof = 1; return SQLITE_OK; } } } } pNode->bEof = 0; return SQLITE_OK; } /* ** If pExpr is an ASC iterator, this function returns a value with the ** same sign as: ** ** (iLhs - iRhs) |
︙ | ︙ | |||
916 917 918 919 920 921 922 | if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){ pNode->bNomatch = 0; pNode->bEof = 1; return rc; } }else{ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; | | | 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 | if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){ pNode->bNomatch = 0; pNode->bEof = 1; return rc; } }else{ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; if( pIter->iRowid==iLast || pIter->bEof ) continue; bMatch = 0; if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){ return rc; } } } } |
︙ | ︙ | |||
1093 1094 1095 1096 1097 1098 1099 | Fts5ExprNode *p1 = pNode->apChild[i]; assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 ); if( p1->bEof==0 ){ if( (p1->iRowid==iLast) || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0) ){ int rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom); | | > > > | 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 | Fts5ExprNode *p1 = pNode->apChild[i]; assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 ); if( p1->bEof==0 ){ if( (p1->iRowid==iLast) || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0) ){ int rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom); if( rc!=SQLITE_OK ){ pNode->bNomatch = 0; return rc; } } } } fts5ExprNodeTest_OR(pExpr, pNode); return SQLITE_OK; } |
︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 | bMatch = 1; for(iChild=0; iChild<pAnd->nChild; iChild++){ Fts5ExprNode *pChild = pAnd->apChild[iChild]; int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid); if( cmp>0 ){ /* Advance pChild until it points to iLast or laster */ rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast); | | > > > | 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 | bMatch = 1; for(iChild=0; iChild<pAnd->nChild; iChild++){ Fts5ExprNode *pChild = pAnd->apChild[iChild]; int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid); if( cmp>0 ){ /* Advance pChild until it points to iLast or laster */ rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast); if( rc!=SQLITE_OK ){ pAnd->bNomatch = 0; return rc; } } /* If the child node is now at EOF, so is the parent AND node. Otherwise, ** the child node is guaranteed to have advanced at least as far as ** rowid iLast. So if it is not at exactly iLast, pChild->iRowid is the ** new lastest rowid seen so far. */ assert( pChild->bEof || fts5RowidCmp(pExpr, iLast, pChild->iRowid)<=0 ); |
︙ | ︙ | |||
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 | Fts5ExprNode *pNode, int bFromValid, i64 iFrom ){ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom); if( rc==SQLITE_OK ){ rc = fts5ExprNodeTest_AND(pExpr, pNode); } return rc; } static int fts5ExprNodeTest_NOT( Fts5Expr *pExpr, /* Expression pPhrase belongs to */ Fts5ExprNode *pNode /* FTS5_NOT node to advance */ | > > | 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 | Fts5ExprNode *pNode, int bFromValid, i64 iFrom ){ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom); if( rc==SQLITE_OK ){ rc = fts5ExprNodeTest_AND(pExpr, pNode); }else{ pNode->bNomatch = 0; } return rc; } static int fts5ExprNodeTest_NOT( Fts5Expr *pExpr, /* Expression pPhrase belongs to */ Fts5ExprNode *pNode /* FTS5_NOT node to advance */ |
︙ | ︙ | |||
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 | int bFromValid, i64 iFrom ){ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom); if( rc==SQLITE_OK ){ rc = fts5ExprNodeTest_NOT(pExpr, pNode); } return rc; } /* ** If pNode currently points to a match, this function returns SQLITE_OK ** without modifying it. Otherwise, pNode is advanced until it does point ** to a match or EOF is reached. | > > > | 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 | int bFromValid, i64 iFrom ){ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom); if( rc==SQLITE_OK ){ rc = fts5ExprNodeTest_NOT(pExpr, pNode); } if( rc!=SQLITE_OK ){ pNode->bNomatch = 0; } return rc; } /* ** If pNode currently points to a match, this function returns SQLITE_OK ** without modifying it. Otherwise, pNode is advanced until it does point ** to a match or EOF is reached. |
︙ | ︙ | |||
1327 1328 1329 1330 1331 1332 1333 | p->pIndex = pIdx; p->bDesc = bDesc; rc = fts5ExprNodeFirst(p, pRoot); /* If not at EOF but the current rowid occurs earlier than iFirst in ** the iteration order, move to document iFirst or later. */ | > > | > | 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 | p->pIndex = pIdx; p->bDesc = bDesc; rc = fts5ExprNodeFirst(p, pRoot); /* If not at EOF but the current rowid occurs earlier than iFirst in ** the iteration order, move to document iFirst or later. */ if( rc==SQLITE_OK && 0==pRoot->bEof && fts5RowidCmp(p, pRoot->iRowid, iFirst)<0 ){ rc = fts5ExprNodeNext(p, pRoot, 1, iFirst); } /* If the iterator is not at a real match, skip forward until it is. */ while( pRoot->bNomatch ){ assert( pRoot->bEof==0 && rc==SQLITE_OK ); rc = fts5ExprNodeNext(p, pRoot, 0, 0); |
︙ | ︙ | |||
1581 1582 1583 1584 1585 1586 1587 | char *z = 0; memset(&sCtx, 0, sizeof(TokenCtx)); sCtx.pPhrase = pAppend; rc = fts5ParseStringFromToken(pToken, &z); if( rc==SQLITE_OK ){ | | | 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 | 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); rc = sqlite3Fts5Tokenize(pConfig, flags, z, n, &sCtx, fts5ParseTokenize); } sqlite3_free(z); if( rc || (rc = sCtx.rc) ){ |
︙ | ︙ | |||
1855 1856 1857 1858 1859 1860 1861 1862 1863 | assert( pParse->rc!=SQLITE_OK ); sqlite3_free(pColset); } return pRet; } void sqlite3Fts5ParseSetColset( Fts5Parse *pParse, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > < < | > | < < < | < | 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 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 | assert( pParse->rc!=SQLITE_OK ); sqlite3_free(pColset); } return pRet; } /* ** If argument pOrig is NULL, or if (*pRc) is set to anything other than ** SQLITE_OK when this function is called, NULL is returned. ** ** Otherwise, a copy of (*pOrig) is made into memory obtained from ** sqlite3Fts5MallocZero() and a pointer to it returned. If the allocation ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned. */ static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ Fts5Colset *pRet; if( pOrig ){ int nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); if( pRet ){ memcpy(pRet, pOrig, nByte); } }else{ pRet = 0; } return pRet; } /* ** Remove from colset pColset any columns that are not also in colset pMerge. */ static void fts5MergeColset(Fts5Colset *pColset, Fts5Colset *pMerge){ int iIn = 0; /* Next input in pColset */ int iMerge = 0; /* Next input in pMerge */ int iOut = 0; /* Next output slot in pColset */ while( iIn<pColset->nCol && iMerge<pMerge->nCol ){ int iDiff = pColset->aiCol[iIn] - pMerge->aiCol[iMerge]; if( iDiff==0 ){ pColset->aiCol[iOut++] = pMerge->aiCol[iMerge]; iMerge++; iIn++; }else if( iDiff>0 ){ iMerge++; }else{ iIn++; } } pColset->nCol = iOut; } /* ** Recursively apply colset pColset to expression node pNode and all of ** its decendents. If (*ppFree) is not NULL, it contains a spare copy ** of pColset. This function may use the spare copy and set (*ppFree) to ** zero, or it may create copies of pColset using fts5CloneColset(). */ static void fts5ParseSetColset( Fts5Parse *pParse, Fts5ExprNode *pNode, Fts5Colset *pColset, Fts5Colset **ppFree ){ if( pParse->rc==SQLITE_OK ){ assert( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING || pNode->eType==FTS5_AND || pNode->eType==FTS5_OR || pNode->eType==FTS5_NOT || pNode->eType==FTS5_EOF ); if( pNode->eType==FTS5_STRING || pNode->eType==FTS5_TERM ){ Fts5ExprNearset *pNear = pNode->pNear; if( pNear->pColset ){ fts5MergeColset(pNear->pColset, pColset); if( pNear->pColset->nCol==0 ){ pNode->eType = FTS5_EOF; pNode->xNext = 0; } }else if( *ppFree ){ pNear->pColset = pColset; *ppFree = 0; }else{ pNear->pColset = fts5CloneColset(&pParse->rc, pColset); } }else{ int i; assert( pNode->eType!=FTS5_EOF || pNode->nChild==0 ); for(i=0; i<pNode->nChild; i++){ fts5ParseSetColset(pParse, pNode->apChild[i], pColset, ppFree); } } } } /* ** Apply colset pColset to expression node pExpr and all of its descendents. */ void sqlite3Fts5ParseSetColset( Fts5Parse *pParse, Fts5ExprNode *pExpr, Fts5Colset *pColset ){ Fts5Colset *pFree = pColset; if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ pParse->rc = SQLITE_ERROR; pParse->zErr = sqlite3_mprintf( "fts5: column queries are not supported (detail=none)" ); }else{ fts5ParseSetColset(pParse, pExpr, pColset, &pFree); } sqlite3_free(pFree); } static void fts5ExprAssignXNext(Fts5ExprNode *pNode){ switch( pNode->eType ){ case FTS5_STRING: { Fts5ExprNearset *pNear = pNode->pNear; if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 |
︙ | ︙ | |||
2327 2328 2329 2330 2331 2332 2333 | azConfig[i++] = (const char*)sqlite3_value_text(apVal[iArg]); } zExpr = (const char*)sqlite3_value_text(apVal[0]); rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr); if( rc==SQLITE_OK ){ | | | 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 | azConfig[i++] = (const char*)sqlite3_value_text(apVal[iArg]); } zExpr = (const char*)sqlite3_value_text(apVal[0]); rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr); if( rc==SQLITE_OK ){ rc = sqlite3Fts5ExprNew(pConfig, pConfig->nCol, zExpr, &pExpr, &zErr); } if( rc==SQLITE_OK ){ char *zText; if( pExpr->pRoot->xNext==0 ){ zText = sqlite3_mprintf(""); }else if( bTcl ){ zText = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pRoot); |
︙ | ︙ |
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 | 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 (a nul-terminated string) 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 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 |
︙ | ︙ | |||
58 59 60 61 62 63 64 | struct Fts5HashEntry { Fts5HashEntry *pHashNext; /* Next hash entry with same hash-key */ Fts5HashEntry *pScanNext; /* Next entry in sorted order */ int nAlloc; /* Total size of allocation */ int iSzPoslist; /* Offset of space for 4-byte poslist size */ int nData; /* Total bytes of data (incl. structure) */ | | < > > | < | | 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 | struct Fts5HashEntry { Fts5HashEntry *pHashNext; /* Next hash entry with same hash-key */ Fts5HashEntry *pScanNext; /* Next entry in sorted order */ int nAlloc; /* Total size of allocation */ int iSzPoslist; /* Offset of space for 4-byte poslist size */ int nData; /* Total bytes of data (incl. structure) */ int nKey; /* Length of key in bytes */ u8 bDel; /* Set delete-flag @ iSzPoslist */ u8 bContent; /* Set content-flag (detail=none mode) */ i16 iCol; /* Column of last value written */ int iPos; /* Position of last value written */ i64 iRowid; /* Rowid of last value written */ }; /* ** Eqivalent to: ** ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } */ #define fts5EntryKey(p) ( ((char *)(&(p)[1])) ) /* ** Allocate a new hash table. */ int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){ int rc = SQLITE_OK; |
︙ | ︙ | |||
166 167 168 169 170 171 172 | apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*)); if( !apNew ) return SQLITE_NOMEM; memset(apNew, 0, nNew*sizeof(Fts5HashEntry*)); for(i=0; i<pHash->nSlot; i++){ while( apOld[i] ){ | | | > | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*)); if( !apNew ) return SQLITE_NOMEM; 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), (int)strlen(fts5EntryKey(p))); p->pHashNext = apNew[iHash]; apNew[iHash] = p; } } sqlite3_free(apOld); pHash->nSlot = nNew; |
︙ | ︙ | |||
240 241 242 243 244 245 246 | int bNew; /* If non-delete entry should be written */ 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){ | > | | > | | > | | | | | | 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 | int bNew; /* If non-delete entry should be written */ 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 && memcmp(&zKey[1], pToken, nToken)==0 ){ break; } } /* If an existing hash entry cannot be found, create a new one. */ if( p==0 ){ /* Figure out how much space to allocate */ char *zKey; int nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64; if( nByte<128 ) nByte = 128; /* Grow the Fts5Hash.aSlot[] array if necessary. */ if( (pHash->nEntry*2)>=pHash->nSlot ){ int rc = fts5HashResize(pHash); if( rc!=SQLITE_OK ) return rc; iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); } /* Allocate new Fts5HashEntry and add it to the hash table. */ p = (Fts5HashEntry*)sqlite3_malloc(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, sizeof(Fts5HashEntry)); p->nAlloc = nByte; zKey = fts5EntryKey(p); zKey[0] = bByte; memcpy(&zKey[1], pToken, nToken); assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) ); p->nKey = nToken; zKey[nToken+1] = '\0'; p->nData = nToken+1 + 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; |
︙ | ︙ | |||
389 390 391 392 393 394 395 | *ppOut = p2; p2 = 0; }else if( p2==0 ){ *ppOut = p1; p1 = 0; }else{ int i = 0; | > > | | | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | *ppOut = p2; p2 = 0; }else if( p2==0 ){ *ppOut = p1; p1 = 0; }else{ int i = 0; char *zKey1 = fts5EntryKey(p1); char *zKey2 = fts5EntryKey(p2); while( zKey1[i]==zKey2[i] ) i++; if( ((u8)zKey1[i])>((u8)zKey2[i]) ){ /* p2 is smaller */ *ppOut = p2; ppOut = &p2->pScanNext; p2 = p2->pScanNext; }else{ /* p1 is smaller */ *ppOut = p1; |
︙ | ︙ | |||
434 435 436 437 438 439 440 | ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot); 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){ | | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot); 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 || 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; } ap[i] = pEntry; |
︙ | ︙ | |||
467 468 469 470 471 472 473 474 475 476 | int sqlite3Fts5HashQuery( Fts5Hash *pHash, /* Hash table to query */ const char *pTerm, int nTerm, /* Query term */ const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */ int *pnDoclist /* OUT: Size of doclist in bytes */ ){ unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm); Fts5HashEntry *p; for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ | > > | | | | 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 | int sqlite3Fts5HashQuery( Fts5Hash *pHash, /* Hash table to query */ const char *pTerm, int nTerm, /* Query term */ const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */ int *pnDoclist /* OUT: Size of doclist in bytes */ ){ 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( memcmp(zKey, pTerm, nTerm)==0 && zKey[nTerm]==0 ) break; } if( p ){ fts5HashAddPoslistSize(pHash, p); *ppDoclist = (const u8*)&zKey[nTerm+1]; *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); }else{ *ppDoclist = 0; *pnDoclist = 0; } return SQLITE_OK; } |
︙ | ︙ | |||
509 510 511 512 513 514 515 | 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) ){ | > | | | | | 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 | 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); int nTerm = (int)strlen(zKey); fts5HashAddPoslistSize(pHash, p); *pzTerm = zKey; *ppDoclist = (const u8*)&zKey[nTerm+1]; *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); }else{ *pzTerm = 0; *ppDoclist = 0; *pnDoclist = 0; } } |
Changes to ext/fts5/fts5_index.c.
︙ | ︙ | |||
624 625 626 627 628 629 630 | if( p->pReader ){ sqlite3_blob *pReader = p->pReader; p->pReader = 0; sqlite3_blob_close(pReader); } } | < | 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | if( p->pReader ){ sqlite3_blob *pReader = p->pReader; p->pReader = 0; sqlite3_blob_close(pReader); } } /* ** Retrieve a record from the %_data table. ** ** If an error occurs, NULL is returned and an error left in the ** Fts5Index object. */ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ |
︙ | ︙ | |||
725 726 727 728 729 730 731 | static int fts5IndexPrepareStmt( Fts5Index *p, sqlite3_stmt **ppStmt, char *zSql ){ if( p->rc==SQLITE_OK ){ if( zSql ){ | | > | 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 | static int fts5IndexPrepareStmt( Fts5Index *p, sqlite3_stmt **ppStmt, char *zSql ){ if( p->rc==SQLITE_OK ){ if( zSql ){ p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, ppStmt, 0); }else{ p->rc = SQLITE_NOMEM; } } sqlite3_free(zSql); return p->rc; } |
︙ | ︙ | |||
774 775 776 777 778 779 780 | char *zSql = sqlite3_mprintf( "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?", pConfig->zDb, pConfig->zName ); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ | | > | 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 | char *zSql = sqlite3_mprintf( "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?", pConfig->zDb, pConfig->zName ); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &p->pDeleter, 0); sqlite3_free(zSql); } if( rc!=SQLITE_OK ){ p->rc = rc; return; } } |
︙ | ︙ | |||
2035 2036 2037 2038 2039 2040 2041 | &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist ); } } else if( pLeaf->nn>pLeaf->szLeaf ){ pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( &pLeaf->p[pLeaf->szLeaf], iOff | | | 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 | &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist ); } } else if( pLeaf->nn>pLeaf->szLeaf ){ pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( &pLeaf->p[pLeaf->szLeaf], iOff ); pIter->iLeafOffset = iOff; pIter->iEndofDoclist = iOff; bNewTerm = 1; } assert_nc( iOff<pLeaf->szLeaf ); if( iOff>pLeaf->szLeaf ){ p->rc = FTS5_CORRUPT; |
︙ | ︙ | |||
2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 | ** code is inlined. ** ** Later: Switched back to fts5SegIterLoadNPos() because it supports ** detail=none mode. Not ideal. */ int nSz; assert( p->rc==SQLITE_OK ); fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); pIter->bDel = (nSz & 0x0001); pIter->nPos = nSz>>1; assert_nc( pIter->nPos>=0 ); } } } | > | 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 | ** code is inlined. ** ** Later: Switched back to fts5SegIterLoadNPos() because it supports ** detail=none mode. Not ideal. */ int nSz; assert( p->rc==SQLITE_OK ); assert( pIter->iLeafOffset<=pIter->pLeaf->nn ); fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); pIter->bDel = (nSz & 0x0001); pIter->nPos = nSz>>1; assert_nc( pIter->nPos>=0 ); } } } |
︙ | ︙ | |||
2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 | static void fts5MultiIterNext( Fts5Index *p, Fts5Iter *pIter, int bFrom, /* True if argument iFrom is valid */ i64 iFrom /* Advance at least as far as this */ ){ int bUseFrom = bFrom; while( p->rc==SQLITE_OK ){ int iFirst = pIter->aFirst[1].iFirst; int bNewTerm = 0; Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; assert( p->rc==SQLITE_OK ); if( bUseFrom && pSeg->pDlidx ){ fts5SegIterNextFrom(p, pSeg, iFrom); | > | 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 | static void fts5MultiIterNext( Fts5Index *p, Fts5Iter *pIter, int bFrom, /* True if argument iFrom is valid */ i64 iFrom /* Advance at least as far as this */ ){ int bUseFrom = bFrom; assert( pIter->base.bEof==0 ); while( p->rc==SQLITE_OK ){ int iFirst = pIter->aFirst[1].iFirst; int bNewTerm = 0; Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; assert( p->rc==SQLITE_OK ); if( bUseFrom && pSeg->pDlidx ){ fts5SegIterNextFrom(p, pSeg, iFrom); |
︙ | ︙ | |||
2873 2874 2875 2876 2877 2878 2879 | static void fts5MultiIterNext2( Fts5Index *p, Fts5Iter *pIter, int *pbNewTerm /* OUT: True if *might* be new term */ ){ assert( pIter->bSkipEmpty ); if( p->rc==SQLITE_OK ){ | > | < < | 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 | static void fts5MultiIterNext2( Fts5Index *p, Fts5Iter *pIter, int *pbNewTerm /* OUT: True if *might* be new term */ ){ assert( pIter->bSkipEmpty ); if( p->rc==SQLITE_OK ){ *pbNewTerm = 0; do{ int iFirst = pIter->aFirst[1].iFirst; Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; int bNewTerm = 0; assert( p->rc==SQLITE_OK ); pSeg->xNext(p, pSeg, &bNewTerm); if( pSeg->pLeaf==0 || bNewTerm || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg) ){ fts5MultiIterAdvanced(p, pIter, iFirst, 1); fts5MultiIterSetEof(pIter); *pbNewTerm = 1; } fts5AssertMultiIterSetup(p, pIter); }while( fts5MultiIterIsEmpty(p, pIter) ); } } |
︙ | ︙ | |||
3062 3063 3064 3065 3066 3067 3068 | xChunk(p, pCtx, pChunk, nChunk); nRem -= nChunk; fts5DataRelease(pData); if( nRem<=0 ){ break; }else{ pgno++; | | | 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 | xChunk(p, pCtx, pChunk, nChunk); nRem -= nChunk; fts5DataRelease(pData); if( nRem<=0 ){ break; }else{ pgno++; pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno)); if( pData==0 ) break; pChunk = &pData->p[4]; nChunk = MIN(nRem, pData->szLeaf - 4); if( pgno==pgnoSave ){ assert( pSeg->pNextLeaf==0 ); pSeg->pNextLeaf = pData; pData = 0; |
︙ | ︙ | |||
3153 3154 3155 3156 3157 3158 3159 | while( p<pEnd && *p!=0x01 ){ while( *p++ & 0x80 ); } return p - (*pa); } | | > | | < | | | | | | | | < > | 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 | while( p<pEnd && *p!=0x01 ){ while( *p++ & 0x80 ); } return p - (*pa); } static void fts5IndexExtractColset( int *pRc, Fts5Colset *pColset, /* Colset to filter on */ const u8 *pPos, int nPos, /* Position list */ Fts5Buffer *pBuf /* Output buffer */ ){ if( *pRc==SQLITE_OK ){ int i; fts5BufferZero(pBuf); for(i=0; i<pColset->nCol; i++){ const u8 *pSub = pPos; int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]); if( nSub ){ fts5BufferAppendBlob(pRc, pBuf, nSub, pSub); } } } } /* ** xSetOutputs callback used by detail=none tables. */ static void fts5IterSetOutputs_None(Fts5Iter *pIter, Fts5SegIter *pSeg){ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_NONE ); |
︙ | ︙ | |||
3293 3294 3295 3296 3297 3298 3299 3300 | /* All data is stored on the current page. Populate the output ** variables to point into the body of the page object. */ const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset]; if( pColset->nCol==1 ){ pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]); pIter->base.pData = a; }else{ fts5BufferZero(&pIter->poslist); | > | | 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 | /* All data is stored on the current page. Populate the output ** variables to point into the body of the page object. */ const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset]; if( pColset->nCol==1 ){ pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]); pIter->base.pData = a; }else{ int *pRc = &pIter->pIndex->rc; fts5BufferZero(&pIter->poslist); fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, &pIter->poslist); pIter->base.pData = pIter->poslist.p; pIter->base.nData = pIter->poslist.n; } }else{ /* The data is distributed over two or more pages. Copy it into the ** Fts5Iter.poslist buffer and then set the output pointer to point ** to this buffer. */ |
︙ | ︙ | |||
3839 3840 3841 3842 3843 3844 3845 | } static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){ static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 }; Fts5PageWriter *pPage = &pWriter->writer; i64 iRowid; | < < < | 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 | } static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){ static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 }; Fts5PageWriter *pPage = &pWriter->writer; i64 iRowid; assert( (pPage->pgidx.n==0)==(pWriter->bFirstTermInPage) ); /* Set the szLeaf header field. */ assert( 0==fts5GetU16(&pPage->buf.p[2]) ); fts5PutU16(&pPage->buf.p[2], (u16)pPage->buf.n); if( pWriter->bFirstTermInPage ){ |
︙ | ︙ | |||
4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 | int nInput; /* Number of input segments */ Fts5SegWriter writer; /* Writer object */ Fts5StructureSegment *pSeg; /* Output segment */ Fts5Buffer term; int bOldest; /* True if the output segment is the oldest */ int eDetail = p->pConfig->eDetail; const int flags = FTS5INDEX_QUERY_NOOUTPUT; assert( iLvl<pStruct->nLevel ); assert( pLvl->nMerge<=pLvl->nSeg ); memset(&writer, 0, sizeof(Fts5SegWriter)); memset(&term, 0, sizeof(Fts5Buffer)); if( pLvl->nMerge ){ | > | 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 | int nInput; /* Number of input segments */ Fts5SegWriter writer; /* Writer object */ Fts5StructureSegment *pSeg; /* Output segment */ Fts5Buffer term; int bOldest; /* True if the output segment is the oldest */ int eDetail = p->pConfig->eDetail; const int flags = FTS5INDEX_QUERY_NOOUTPUT; int bTermWritten = 0; /* True if current term already output */ assert( iLvl<pStruct->nLevel ); assert( pLvl->nMerge<=pLvl->nSeg ); memset(&writer, 0, sizeof(Fts5SegWriter)); memset(&term, 0, sizeof(Fts5Buffer)); if( pLvl->nMerge ){ |
︙ | ︙ | |||
4243 4244 4245 4246 4247 4248 4249 | fts5MultiIterNext(p, pIter, 0, 0) ){ Fts5SegIter *pSegIter = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; int nPos; /* position-list size field value */ int nTerm; const u8 *pTerm; | < < < > > | > > > > > | | 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 | fts5MultiIterNext(p, pIter, 0, 0) ){ Fts5SegIter *pSegIter = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; int nPos; /* position-list size field value */ int nTerm; const u8 *pTerm; pTerm = fts5MultiIterTerm(pIter, &nTerm); if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){ if( pnRem && writer.nLeafWritten>nRem ){ break; } fts5BufferSet(&p->rc, &term, nTerm, pTerm); bTermWritten =0; } /* Check for key annihilation. */ if( pSegIter->nPos==0 && (bOldest || pSegIter->bDel==0) ) continue; if( p->rc==SQLITE_OK && bTermWritten==0 ){ /* This is a new term. Append a term to the output segment. */ fts5WriteAppendTerm(p, &writer, nTerm, pTerm); bTermWritten = 1; } /* Append the rowid to the output */ /* WRITEPOSLISTSIZE */ fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter)); if( eDetail==FTS5_DETAIL_NONE ){ |
︙ | ︙ | |||
5086 5087 5088 5089 5090 5091 5092 | } fts5MultiIterFree(p1); pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n); if( pData ){ pData->p = (u8*)&pData[1]; pData->nn = pData->szLeaf = doclist.n; | | | 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 | } fts5MultiIterFree(p1); pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n); 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); } fts5StructureRelease(pStruct); sqlite3_free(aBuf); |
︙ | ︙ | |||
5125 5126 5127 5128 5129 5130 5131 | p->bDelete = bDelete; return fts5IndexReturn(p); } /* ** Commit data to disk. */ | | | | 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 | p->bDelete = bDelete; return fts5IndexReturn(p); } /* ** Commit data to disk. */ int sqlite3Fts5IndexSync(Fts5Index *p){ assert( p->rc==SQLITE_OK ); fts5IndexFlush(p); fts5CloseReader(p); return fts5IndexReturn(p); } /* ** Discard any data stored in the in-memory hash tables. Do not write it ** to the database. Additionally, assume that the contents of the %_data ** table may have changed on disk. So any in-memory caches of %_data |
︙ | ︙ | |||
5325 5326 5327 5328 5329 5330 5331 | Fts5Buffer buf = {0, 0, 0}; /* 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 */ | | | 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 | Fts5Buffer buf = {0, 0, 0}; /* 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 */ if( nToken ) 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 |
︙ | ︙ | |||
5374 5375 5376 5377 5378 5379 5380 | if( p->rc==SQLITE_OK ){ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); } } if( p->rc ){ | | | 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 | if( p->rc==SQLITE_OK ){ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); } } if( p->rc ){ sqlite3Fts5IterClose((Fts5IndexIter*)pRet); pRet = 0; fts5CloseReader(p); } *ppIter = &pRet->base; sqlite3Fts5BufferFree(&buf); } |
︙ | ︙ | |||
5824 5825 5826 5827 5828 5829 5830 | int iIdxLeaf = sqlite3_column_int(pStmt, 2); int bIdxDlidx = sqlite3_column_int(pStmt, 3); /* If the leaf in question has already been trimmed from the segment, ** ignore this b-tree entry. Otherwise, load it into memory. */ if( iIdxLeaf<pSeg->pgnoFirst ) continue; iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, iIdxLeaf); | | | 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 | int iIdxLeaf = sqlite3_column_int(pStmt, 2); int bIdxDlidx = sqlite3_column_int(pStmt, 3); /* If the leaf in question has already been trimmed from the segment, ** ignore this b-tree entry. Otherwise, load it into memory. */ if( iIdxLeaf<pSeg->pgnoFirst ) continue; iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, iIdxLeaf); pLeaf = fts5LeafRead(p, iRow); if( pLeaf==0 ) break; /* Check that the leaf contains at least one term, and that it is equal ** to or larger than the split-key in zIdxTerm. Also check that if there ** is also a rowid pointer within the leaf page header, it points to a ** location before the term. */ if( pLeaf->nn<=pLeaf->szLeaf ){ |
︙ | ︙ |
Changes to ext/fts5/fts5_main.c.
︙ | ︙ | |||
502 503 504 505 506 507 508 509 510 511 512 513 514 515 | ** * An == rowid constraint: cost=10.0 ** ** Costs are not modified by the ORDER BY clause. */ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ Fts5Table *pTab = (Fts5Table*)pVTab; Fts5Config *pConfig = pTab->pConfig; int idxFlags = 0; /* Parameter passed through to xFilter() */ int bHasMatch; int iNext; int i; struct Constraint { int op; /* Mask against sqlite3_index_constraint.op */ | > | 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 | ** * An == rowid constraint: cost=10.0 ** ** Costs are not modified by the ORDER BY clause. */ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ Fts5Table *pTab = (Fts5Table*)pVTab; Fts5Config *pConfig = pTab->pConfig; const int nCol = pConfig->nCol; int idxFlags = 0; /* Parameter passed through to xFilter() */ int bHasMatch; int iNext; int i; struct Constraint { int op; /* Mask against sqlite3_index_constraint.op */ |
︙ | ︙ | |||
527 528 529 530 531 532 533 | FTS5_BI_ROWID_LE, 0, 0, -1}, {SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE, FTS5_BI_ROWID_GE, 0, 0, -1}, }; int aColMap[3]; aColMap[0] = -1; | | | | | | | > > | > | < | | | | | > > > > > > > > | 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 | FTS5_BI_ROWID_LE, 0, 0, -1}, {SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE, FTS5_BI_ROWID_GE, 0, 0, -1}, }; int aColMap[3]; aColMap[0] = -1; aColMap[1] = nCol; aColMap[2] = nCol+1; /* Set idxFlags flags for all WHERE clause terms that will be used. */ for(i=0; i<pInfo->nConstraint; i++){ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; int iCol = p->iColumn; if( (p->op==SQLITE_INDEX_CONSTRAINT_MATCH && iCol>=0 && iCol<=nCol) || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol==nCol) ){ /* A MATCH operator or equivalent */ if( p->usable ){ idxFlags = (idxFlags & 0xFFFF) | FTS5_BI_MATCH | (iCol << 16); aConstraint[0].iConsIndex = i; }else{ /* As there exists an unusable MATCH constraint this is an ** unusable plan. Set a prohibitively high cost. */ pInfo->estimatedCost = 1e50; return SQLITE_OK; } }else{ int j; for(j=1; j<ArraySize(aConstraint); j++){ struct Constraint *pC = &aConstraint[j]; if( iCol==aColMap[pC->iCol] && p->op & pC->op && p->usable ){ pC->iConsIndex = i; idxFlags |= pC->fts5op; } } } } /* Set idxFlags flags for the ORDER BY clause */ if( pInfo->nOrderBy==1 ){ |
︙ | ︙ | |||
868 869 870 871 872 873 874 | va_list ap; va_start(ap, zFmt); zSql = sqlite3_vmprintf(zFmt, ap); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ | | > | 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 | va_list ap; va_start(ap, zFmt); zSql = sqlite3_vmprintf(zFmt, ap); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &pRet, 0); if( rc!=SQLITE_OK ){ *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db)); } sqlite3_free(zSql); } va_end(ap); |
︙ | ︙ | |||
1004 1005 1006 1007 1008 1009 1010 | const char *zRank = pCsr->zRank; const char *zRankArgs = pCsr->zRankArgs; if( zRankArgs ){ char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs); if( zSql ){ sqlite3_stmt *pStmt = 0; | | > | 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | const char *zRank = pCsr->zRank; const char *zRankArgs = pCsr->zRankArgs; if( zRankArgs ){ char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs); if( zSql ){ sqlite3_stmt *pStmt = 0; rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &pStmt, 0); sqlite3_free(zSql); assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 ); if( rc==SQLITE_OK ){ if( SQLITE_ROW==sqlite3_step(pStmt) ){ int nByte; pCsr->nRankArg = sqlite3_column_count(pStmt); nByte = sizeof(sqlite3_value*)*pCsr->nRankArg; |
︙ | ︙ | |||
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 | int bDesc; /* True if ORDER BY [rank|rowid] DESC */ int bOrderByRank; /* True if ORDER BY rank */ sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */ sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */ sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ char **pzErrmsg = pConfig->pzErrmsg; UNUSED_PARAM(zUnused); UNUSED_PARAM(nVal); if( pCsr->ePlan ){ fts5FreeCursorComponents(pCsr); | > | 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | int bDesc; /* True if ORDER BY [rank|rowid] DESC */ int bOrderByRank; /* True if ORDER BY rank */ sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */ sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */ sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ int iCol; /* Column on LHS of MATCH operator */ char **pzErrmsg = pConfig->pzErrmsg; UNUSED_PARAM(zUnused); UNUSED_PARAM(nVal); if( pCsr->ePlan ){ fts5FreeCursorComponents(pCsr); |
︙ | ︙ | |||
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 | ** order as the corresponding entries in the struct at the top of ** fts5BestIndexMethod(). */ if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++]; if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++]; if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++]; if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++]; if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++]; assert( iVal==nVal ); bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0); pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0); /* Set the cursor upper and lower rowid limits. Only some strategies ** actually use them. This is ok, as the xBestIndex() method leaves the ** sqlite3_index_constraint.omit flag clear for range constraints | > > | 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 | ** order as the corresponding entries in the struct at the top of ** fts5BestIndexMethod(). */ if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++]; if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++]; if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++]; if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++]; if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++]; iCol = (idxNum>>16); assert( iCol>=0 && iCol<=pConfig->nCol ); assert( iVal==nVal ); bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0); pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0); /* Set the cursor upper and lower rowid limits. Only some strategies ** actually use them. This is ok, as the xBestIndex() method leaves the ** sqlite3_index_constraint.omit flag clear for range constraints |
︙ | ︙ | |||
1195 1196 1197 1198 1199 1200 1201 | if( zExpr[0]=='*' ){ /* The user has issued a query of the form "MATCH '*...'". This ** indicates that the MATCH expression is not a full text query, ** but a request for an internal parameter. */ rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]); }else{ char **pzErr = &pTab->base.zErrMsg; | | | 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 | if( zExpr[0]=='*' ){ /* The user has issued a query of the form "MATCH '*...'". This ** indicates that the MATCH expression is not a full text query, ** but a request for an internal parameter. */ rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]); }else{ char **pzErr = &pTab->base.zErrMsg; rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr); if( rc==SQLITE_OK ){ if( bOrderByRank ){ pCsr->ePlan = FTS5_PLAN_SORTED_MATCH; rc = fts5CursorFirstSorted(pTab, pCsr, bDesc); }else{ pCsr->ePlan = FTS5_PLAN_MATCH; rc = fts5CursorFirst(pTab, pCsr, bDesc); |
︙ | ︙ | |||
1575 1576 1577 1578 1579 1580 1581 | */ static int fts5SyncMethod(sqlite3_vtab *pVtab){ int rc; Fts5Table *pTab = (Fts5Table*)pVtab; fts5CheckTransactionState(pTab, FTS5_SYNC, 0); pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; fts5TripCursors(pTab); | | | 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 | */ static int fts5SyncMethod(sqlite3_vtab *pVtab){ int rc; Fts5Table *pTab = (Fts5Table*)pVtab; fts5CheckTransactionState(pTab, FTS5_SYNC, 0); pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; fts5TripCursors(pTab); rc = sqlite3Fts5StorageSync(pTab->pStorage); pTab->pConfig->pzErrmsg = 0; return rc; } /* ** Implementation of xBegin() method. */ |
︙ | ︙ | |||
2386 2387 2388 2389 2390 2391 2392 | ** Flush the contents of the pending-terms table to disk. */ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5Table *pTab = (Fts5Table*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); fts5TripCursors(pTab); | | | | 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 | ** Flush the contents of the pending-terms table to disk. */ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5Table *pTab = (Fts5Table*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); fts5TripCursors(pTab); return sqlite3Fts5StorageSync(pTab->pStorage); } /* ** The xRelease() method. ** ** This is a no-op. */ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5Table *pTab = (Fts5Table*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); fts5TripCursors(pTab); return sqlite3Fts5StorageSync(pTab->pStorage); } /* ** The xRollbackTo() method. ** ** Discard the contents of the pending terms table. */ |
︙ | ︙ | |||
2589 2590 2591 2592 2593 2594 2595 | sqlite3_free(pGlobal); } static void fts5Fts5Func( sqlite3_context *pCtx, /* Function call context */ int nArg, /* Number of args */ | | | | | | | < | 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 | sqlite3_free(pGlobal); } static void fts5Fts5Func( sqlite3_context *pCtx, /* Function call context */ int nArg, /* Number of args */ sqlite3_value **apArg /* Function arguments */ ){ Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx); fts5_api **ppApi; UNUSED_PARAM(nArg); assert( nArg==1 ); ppApi = (fts5_api**)sqlite3_value_pointer(apArg[0], "fts5_api_ptr"); if( ppApi ) *ppApi = &pGlobal->api; } /* ** Implementation of fts5_source_id() function. */ static void fts5SourceIdFunc( sqlite3_context *pCtx, /* Function call context */ |
︙ | ︙ | |||
2662 2663 2664 2665 2666 2667 2668 | if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api); if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db); if( rc==SQLITE_OK ){ rc = sqlite3_create_function( | | | 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 | if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api); if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db); if( rc==SQLITE_OK ){ rc = sqlite3_create_function( db, "fts5", 1, SQLITE_UTF8, p, fts5Fts5Func, 0, 0 ); } if( rc==SQLITE_OK ){ rc = sqlite3_create_function( db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0 ); } |
︙ | ︙ |
Changes to ext/fts5/fts5_storage.c.
︙ | ︙ | |||
132 133 134 135 136 137 138 | zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName); break; } if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ | | > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName); break; } if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v3(pC->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &p->aStmt[eStmt], 0); sqlite3_free(zSql); if( rc!=SQLITE_OK && pzErrMsg ){ *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); } } } |
︙ | ︙ | |||
214 215 216 217 218 219 220 | pConfig->zDb, pConfig->zName, zTail, zName, zTail ); } } int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){ Fts5Config *pConfig = pStorage->pConfig; | | | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | pConfig->zDb, pConfig->zName, zTail, zName, zTail ); } } int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){ Fts5Config *pConfig = pStorage->pConfig; int rc = sqlite3Fts5StorageSync(pStorage); fts5StorageRenameOne(pConfig, &rc, "data", zName); fts5StorageRenameOne(pConfig, &rc, "idx", zName); fts5StorageRenameOne(pConfig, &rc, "config", zName); if( pConfig->bColumnsize ){ fts5StorageRenameOne(pConfig, &rc, "docsize", zName); } |
︙ | ︙ | |||
541 542 543 544 545 546 547 | if( rc==SQLITE_OK ){ sqlite3_bind_int64(pDel, 1, iDel); sqlite3_step(pDel); rc = sqlite3_reset(pDel); } } | < < < < < | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 | if( rc==SQLITE_OK ){ sqlite3_bind_int64(pDel, 1, iDel); sqlite3_step(pDel); rc = sqlite3_reset(pDel); } } return rc; } /* ** Delete all entries in the FTS5 index. */ int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ |
︙ | ︙ | |||
749 750 751 752 753 754 755 | /* Write the %_docsize record */ if( rc==SQLITE_OK ){ rc = fts5StorageInsertDocsize(p, iRowid, &buf); } sqlite3_free(buf.p); | < < < < < | 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | /* Write the %_docsize record */ if( rc==SQLITE_OK ){ rc = fts5StorageInsertDocsize(p, iRowid, &buf); } sqlite3_free(buf.p); return rc; } static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){ Fts5Config *pConfig = p->pConfig; char *zSql; int rc; |
︙ | ︙ | |||
1087 1088 1089 1090 1091 1092 1093 | } return rc; } /* ** Flush any data currently held in-memory to disk. */ | | > > | | > | > > | | 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 | } return rc; } /* ** Flush any data currently held in-memory to disk. */ int sqlite3Fts5StorageSync(Fts5Storage *p){ int rc = SQLITE_OK; i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db); if( p->bTotalsValid ){ rc = fts5StorageSaveTotals(p); p->bTotalsValid = 0; } if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexSync(p->pIndex); } sqlite3_set_last_insert_rowid(p->pConfig->db, iLastRowid); return rc; } int sqlite3Fts5StorageRollback(Fts5Storage *p){ p->bTotalsValid = 0; return sqlite3Fts5IndexRollback(p->pIndex); } |
︙ | ︙ |
Changes to ext/fts5/fts5_tcl.c.
︙ | ︙ | |||
95 96 97 98 99 100 101 | int rc = f5tDbPointer(interp, pObj, &db); if( rc!=TCL_OK ){ return TCL_ERROR; }else{ sqlite3_stmt *pStmt = 0; fts5_api *pApi = 0; | | | | < < < | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | int rc = f5tDbPointer(interp, pObj, &db); if( rc!=TCL_OK ){ return TCL_ERROR; }else{ sqlite3_stmt *pStmt = 0; fts5_api *pApi = 0; rc = sqlite3_prepare_v2(db, "SELECT fts5(?1)", -1, &pStmt, 0); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); return TCL_ERROR; } sqlite3_bind_pointer(pStmt, 1, (void*)&pApi, "fts5_api_ptr", 0); sqlite3_step(pStmt); if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); return TCL_ERROR; } *ppDb = db; |
︙ | ︙ |
Changes to ext/fts5/fts5_test_mi.c.
︙ | ︙ | |||
69 70 71 72 73 74 75 | ** handle (accessible using sqlite3_errcode()/errmsg()). */ static int fts5_api_from_db(sqlite3 *db, fts5_api **ppApi){ sqlite3_stmt *pStmt = 0; int rc; *ppApi = 0; | | > | < < < < | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | ** handle (accessible using sqlite3_errcode()/errmsg()). */ static int fts5_api_from_db(sqlite3 *db, fts5_api **ppApi){ sqlite3_stmt *pStmt = 0; int rc; *ppApi = 0; rc = sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0); if( rc==SQLITE_OK ){ sqlite3_bind_pointer(pStmt, 1, (void*)ppApi, "fts5_api_ptr", 0); (void)sqlite3_step(pStmt); rc = sqlite3_finalize(pStmt); } return rc; } |
︙ | ︙ | |||
418 419 420 421 422 423 424 | /* Register the implementation of matchinfo() */ rc = pApi->xCreateFunction(pApi, "matchinfo", 0, fts5MatchinfoFunc, 0); return rc; } #endif /* SQLITE_ENABLE_FTS5 */ | < | 415 416 417 418 419 420 421 | /* Register the implementation of matchinfo() */ rc = pApi->xCreateFunction(pApi, "matchinfo", 0, fts5MatchinfoFunc, 0); return rc; } #endif /* SQLITE_ENABLE_FTS5 */ |
Changes to ext/fts5/fts5_test_tok.c.
︙ | ︙ | |||
36 37 38 39 40 41 42 | ** end: Byte offset of the byte immediately following the end of the ** token within the input string. ** pos: Token offset of token within input. ** */ #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_FTS5) | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | ** end: Byte offset of the byte immediately following the end of the ** token within the input string. ** pos: Token offset of token within input. ** */ #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_FTS5) #include "fts5.h" #include <string.h> #include <assert.h> typedef struct Fts5tokTable Fts5tokTable; typedef struct Fts5tokCursor Fts5tokCursor; typedef struct Fts5tokRow Fts5tokRow; |
︙ | ︙ | |||
178 179 180 181 182 183 184 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ char **pzErr /* OUT: sqlite3_malloc'd error message */ ){ fts5_api *pApi = (fts5_api*)pCtx; Fts5tokTable *pTab = 0; int rc; char **azDequote = 0; | | | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ char **pzErr /* OUT: sqlite3_malloc'd error message */ ){ fts5_api *pApi = (fts5_api*)pCtx; Fts5tokTable *pTab = 0; int rc; char **azDequote = 0; int nDequote = 0; rc = sqlite3_declare_vtab(db, "CREATE TABLE x(input HIDDEN, token, start, end, position)" ); if( rc==SQLITE_OK ){ nDequote = argc-3; |
︙ | ︙ |
Changes to ext/fts5/fts5_vocab.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ** row: ** CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term)); ** ** One row for each term in the database. The value of $doc is set to ** the number of fts5 rows that contain at least one instance of term ** $term. Field $cnt is set to the total number of instances of term ** $term in the database. */ #include "fts5Int.h" typedef struct Fts5VocabTable Fts5VocabTable; typedef struct Fts5VocabCursor Fts5VocabCursor; struct Fts5VocabTable { sqlite3_vtab base; char *zFts5Tbl; /* Name of fts5 table */ char *zFts5Db; /* Db containing fts5 table */ sqlite3 *db; /* Database handle */ Fts5Global *pGlobal; /* FTS5 global object for this database */ | > > > > > | | > > > > | | > > | 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 | ** row: ** CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term)); ** ** One row for each term in the database. The value of $doc is set to ** the number of fts5 rows that contain at least one instance of term ** $term. Field $cnt is set to the total number of instances of term ** $term in the database. ** ** instance: ** CREATE TABLE vocab(term, doc, col, offset, PRIMARY KEY(<all-fields>)); ** ** One row for each term instance in the database. */ #include "fts5Int.h" typedef struct Fts5VocabTable Fts5VocabTable; typedef struct Fts5VocabCursor Fts5VocabCursor; struct Fts5VocabTable { sqlite3_vtab base; char *zFts5Tbl; /* Name of fts5 table */ char *zFts5Db; /* Db containing fts5 table */ sqlite3 *db; /* Database handle */ Fts5Global *pGlobal; /* FTS5 global object for this database */ int eType; /* FTS5_VOCAB_COL, ROW or INSTANCE */ }; struct Fts5VocabCursor { sqlite3_vtab_cursor base; sqlite3_stmt *pStmt; /* Statement holding lock on pIndex */ Fts5Index *pIndex; /* Associated FTS5 index */ int bEof; /* True if this cursor is at EOF */ Fts5IndexIter *pIter; /* Term/rowid iterator object */ int nLeTerm; /* Size of zLeTerm in bytes */ char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ /* These are used by 'col' tables only */ Fts5Config *pConfig; /* Fts5 table configuration */ int iCol; i64 *aCnt; i64 *aDoc; /* Output values used by all tables. */ i64 rowid; /* This table's current rowid value */ Fts5Buffer term; /* Current value of 'term' column */ /* Output values Used by 'instance' tables only */ i64 iInstPos; int iInstOff; }; #define FTS5_VOCAB_COL 0 #define FTS5_VOCAB_ROW 1 #define FTS5_VOCAB_INSTANCE 2 #define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt" #define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt" #define FTS5_VOCAB_INST_SCHEMA "term, doc, col, offset" /* ** Bits for the mask used as the idxNum value by xBestIndex/xFilter. */ #define FTS5_VOCAB_TERM_EQ 0x01 #define FTS5_VOCAB_TERM_GE 0x02 #define FTS5_VOCAB_TERM_LE 0x04 |
︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 | sqlite3Fts5Dequote(zCopy); if( sqlite3_stricmp(zCopy, "col")==0 ){ *peType = FTS5_VOCAB_COL; }else if( sqlite3_stricmp(zCopy, "row")==0 ){ *peType = FTS5_VOCAB_ROW; }else { *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy); rc = SQLITE_ERROR; } sqlite3_free(zCopy); } | > > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | sqlite3Fts5Dequote(zCopy); if( sqlite3_stricmp(zCopy, "col")==0 ){ *peType = FTS5_VOCAB_COL; }else if( sqlite3_stricmp(zCopy, "row")==0 ){ *peType = FTS5_VOCAB_ROW; }else if( sqlite3_stricmp(zCopy, "instance")==0 ){ *peType = FTS5_VOCAB_INSTANCE; }else { *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy); rc = SQLITE_ERROR; } sqlite3_free(zCopy); } |
︙ | ︙ | |||
157 158 159 160 161 162 163 | int argc, /* Number of elements in argv array */ const char * const *argv, /* xCreate/xConnect argument array */ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ char **pzErr /* Write any error message here */ ){ const char *azSchema[] = { "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")", | | > | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | int argc, /* Number of elements in argv array */ const char * const *argv, /* xCreate/xConnect argument array */ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ char **pzErr /* Write any error message here */ ){ const char *azSchema[] = { "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")", "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")", "CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA ")" }; Fts5VocabTable *pRet = 0; int rc = SQLITE_OK; /* Return code */ int bDb; bDb = (argc==6 && strlen(argv[1])==4 && memcmp("temp", argv[1], 4)==0); |
︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 240 241 242 243 244 | char **pzErr /* OUT: sqlite3_malloc'd error message */ ){ return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr); } /* ** Implementation of the xBestIndex method. */ static int fts5VocabBestIndexMethod( sqlite3_vtab *pUnused, sqlite3_index_info *pInfo ){ int i; int iTermEq = -1; | > > > > > > > > > | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | char **pzErr /* OUT: sqlite3_malloc'd error message */ ){ return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr); } /* ** Implementation of the xBestIndex method. ** ** Only constraints of the form: ** ** term <= ? ** term == ? ** term >= ? ** ** are interpreted. Less-than and less-than-or-equal are treated ** identically, as are greater-than and greater-than-or-equal. */ static int fts5VocabBestIndexMethod( sqlite3_vtab *pUnused, sqlite3_index_info *pInfo ){ int i; int iTermEq = -1; |
︙ | ︙ | |||
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 | fts5VocabResetCursor(pCsr); sqlite3Fts5BufferFree(&pCsr->term); sqlite3_finalize(pCsr->pStmt); sqlite3_free(pCsr); return SQLITE_OK; } /* ** Advance the cursor to the next row in the table. */ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; int rc = SQLITE_OK; int nCol = pCsr->pConfig->nCol; pCsr->rowid++; if( pTab->eType==FTS5_VOCAB_COL ){ for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ if( pCsr->aDoc[pCsr->iCol] ) break; } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | fts5VocabResetCursor(pCsr); sqlite3Fts5BufferFree(&pCsr->term); sqlite3_finalize(pCsr->pStmt); sqlite3_free(pCsr); return SQLITE_OK; } static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){ int rc = SQLITE_OK; if( sqlite3Fts5IterEof(pCsr->pIter) ){ pCsr->bEof = 1; }else{ const char *zTerm; int nTerm; zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); if( pCsr->nLeTerm>=0 ){ int nCmp = MIN(nTerm, pCsr->nLeTerm); int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp); if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){ pCsr->bEof = 1; } } sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); } return rc; } static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ int eDetail = pCsr->pConfig->eDetail; int rc = SQLITE_OK; Fts5IndexIter *pIter = pCsr->pIter; i64 *pp = &pCsr->iInstPos; int *po = &pCsr->iInstOff; while( eDetail==FTS5_DETAIL_NONE || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp) ){ pCsr->iInstPos = 0; pCsr->iInstOff = 0; rc = sqlite3Fts5IterNextScan(pCsr->pIter); if( rc==SQLITE_OK ){ rc = fts5VocabInstanceNewTerm(pCsr); if( eDetail==FTS5_DETAIL_NONE ) break; } if( rc ){ pCsr->bEof = 1; break; } } return rc; } /* ** Advance the cursor to the next row in the table. */ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; int rc = SQLITE_OK; int nCol = pCsr->pConfig->nCol; pCsr->rowid++; if( pTab->eType==FTS5_VOCAB_INSTANCE ){ return fts5VocabInstanceNext(pCsr); } if( pTab->eType==FTS5_VOCAB_COL ){ for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ if( pCsr->aDoc[pCsr->iCol] ) break; } } if( pTab->eType!=FTS5_VOCAB_COL || pCsr->iCol>=nCol ){ if( sqlite3Fts5IterEof(pCsr->pIter) ){ pCsr->bEof = 1; }else{ const char *zTerm; int nTerm; zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); |
︙ | ︙ | |||
416 417 418 419 420 421 422 423 424 425 426 427 428 | sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); memset(pCsr->aCnt, 0, nCol * sizeof(i64)); memset(pCsr->aDoc, 0, nCol * sizeof(i64)); pCsr->iCol = 0; assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW ); while( rc==SQLITE_OK ){ const u8 *pPos; int nPos; /* Position list */ i64 iPos = 0; /* 64-bit position read from poslist */ int iOff = 0; /* Current offset within position list */ pPos = pCsr->pIter->pData; nPos = pCsr->pIter->nData; | > | > | < < | > | > | > > < < < | < < < > > > | | < > | 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 | sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); memset(pCsr->aCnt, 0, nCol * sizeof(i64)); memset(pCsr->aDoc, 0, nCol * sizeof(i64)); pCsr->iCol = 0; assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW ); while( rc==SQLITE_OK ){ int eDetail = pCsr->pConfig->eDetail; const u8 *pPos; int nPos; /* Position list */ i64 iPos = 0; /* 64-bit position read from poslist */ int iOff = 0; /* Current offset within position list */ pPos = pCsr->pIter->pData; nPos = pCsr->pIter->nData; switch( pTab->eType ){ case FTS5_VOCAB_ROW: if( eDetail==FTS5_DETAIL_FULL ){ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ pCsr->aCnt[0]++; } } pCsr->aDoc[0]++; break; case FTS5_VOCAB_COL: if( eDetail==FTS5_DETAIL_FULL ){ int iCol = -1; while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ int ii = FTS5_POS2COLUMN(iPos); pCsr->aCnt[ii]++; if( iCol!=ii ){ if( ii>=nCol ){ rc = FTS5_CORRUPT; break; } pCsr->aDoc[ii]++; iCol = ii; } } }else if( eDetail==FTS5_DETAIL_COLUMNS ){ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){ assert_nc( iPos>=0 && iPos<nCol ); if( iPos>=nCol ){ rc = FTS5_CORRUPT; break; } pCsr->aDoc[iPos]++; } }else{ assert( eDetail==FTS5_DETAIL_NONE ); pCsr->aDoc[0]++; } break; default: assert( pTab->eType==FTS5_VOCAB_INSTANCE ); break; } if( rc==SQLITE_OK ){ rc = sqlite3Fts5IterNextScan(pCsr->pIter); } if( pTab->eType==FTS5_VOCAB_INSTANCE ) break; if( rc==SQLITE_OK ){ zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ){ break; } if( sqlite3Fts5IterEof(pCsr->pIter) ) break; |
︙ | ︙ | |||
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 | static int fts5VocabFilterMethod( sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ int idxNum, /* Strategy index */ const char *zUnused, /* Unused */ int nUnused, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; int rc = SQLITE_OK; int iVal = 0; int f = FTS5INDEX_QUERY_SCAN; const char *zTerm = 0; int nTerm = 0; | > > | 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | static int fts5VocabFilterMethod( sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ int idxNum, /* Strategy index */ const char *zUnused, /* Unused */ int nUnused, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; int eType = pTab->eType; int rc = SQLITE_OK; int iVal = 0; int f = FTS5INDEX_QUERY_SCAN; const char *zTerm = 0; int nTerm = 0; |
︙ | ︙ | |||
541 542 543 544 545 546 547 | rc = SQLITE_NOMEM; }else{ memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1); } } } | < > > > | > > > | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | rc = SQLITE_NOMEM; }else{ memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1); } } } if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); } if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ rc = fts5VocabInstanceNewTerm(pCsr); } if( rc==SQLITE_OK && !pCsr->bEof && (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE) ){ rc = fts5VocabNextMethod(pCursor); } return rc; } /* |
︙ | ︙ | |||
587 588 589 590 591 592 593 | sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); } }else if( iCol==2 ){ iVal = pCsr->aDoc[pCsr->iCol]; }else{ iVal = pCsr->aCnt[pCsr->iCol]; } | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); } }else if( iCol==2 ){ iVal = pCsr->aDoc[pCsr->iCol]; }else{ iVal = pCsr->aCnt[pCsr->iCol]; } }else if( eType==FTS5_VOCAB_ROW ){ assert( iCol==1 || iCol==2 ); if( iCol==1 ){ iVal = pCsr->aDoc[0]; }else{ iVal = pCsr->aCnt[0]; } }else{ int eDetail = pCsr->pConfig->eDetail; assert( eType==FTS5_VOCAB_INSTANCE ); switch( iCol ){ case 1: sqlite3_result_int64(pCtx, pCsr->pIter->iRowid); break; case 2: { int ii = -1; if( eDetail==FTS5_DETAIL_FULL ){ ii = FTS5_POS2COLUMN(pCsr->iInstPos); }else if( eDetail==FTS5_DETAIL_COLUMNS ){ ii = pCsr->iInstPos; } if( ii>=0 && ii<pCsr->pConfig->nCol ){ const char *z = pCsr->pConfig->azCol[ii]; sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); } break; } default: { assert( iCol==3 ); if( eDetail==FTS5_DETAIL_FULL ){ int ii = FTS5_POS2OFFSET(pCsr->iInstPos); sqlite3_result_int(pCtx, ii); } break; } } } if( iVal>0 ) sqlite3_result_int64(pCtx, iVal); return SQLITE_OK; } |
︙ | ︙ |
Changes to ext/fts5/fts5parse.y.
︙ | ︙ | |||
85 86 87 88 89 90 91 | %type cnearset {Fts5ExprNode*} %type expr {Fts5ExprNode*} %type exprlist {Fts5ExprNode*} %destructor cnearset { sqlite3Fts5ParseNodeFree($$); } %destructor expr { sqlite3Fts5ParseNodeFree($$); } %destructor exprlist { sqlite3Fts5ParseNodeFree($$); } | < < < < < < < < < < < < < < < < < < < < < < < < < < | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | %type cnearset {Fts5ExprNode*} %type expr {Fts5ExprNode*} %type exprlist {Fts5ExprNode*} %destructor cnearset { sqlite3Fts5ParseNodeFree($$); } %destructor expr { sqlite3Fts5ParseNodeFree($$); } %destructor exprlist { sqlite3Fts5ParseNodeFree($$); } %type colset {Fts5Colset*} %destructor colset { sqlite3_free($$); } %type colsetlist {Fts5Colset*} %destructor colsetlist { sqlite3_free($$); } colset(A) ::= MINUS LCP colsetlist(X) RCP. { A = sqlite3Fts5ParseColsetInvert(pParse, X); |
︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 142 143 144 145 146 | } colsetlist(A) ::= colsetlist(Y) STRING(X). { A = sqlite3Fts5ParseColset(pParse, Y, &X); } colsetlist(A) ::= STRING(X). { A = sqlite3Fts5ParseColset(pParse, 0, &X); } %type nearset {Fts5ExprNearset*} %type nearphrases {Fts5ExprNearset*} %destructor nearset { sqlite3Fts5ParseNearsetFree($$); } %destructor nearphrases { sqlite3Fts5ParseNearsetFree($$); } nearset(A) ::= phrase(X). { A = sqlite3Fts5ParseNearset(pParse, 0, X); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } colsetlist(A) ::= colsetlist(Y) STRING(X). { A = sqlite3Fts5ParseColset(pParse, Y, &X); } colsetlist(A) ::= STRING(X). { A = sqlite3Fts5ParseColset(pParse, 0, &X); } expr(A) ::= expr(X) AND expr(Y). { A = sqlite3Fts5ParseNode(pParse, FTS5_AND, X, Y, 0); } expr(A) ::= expr(X) OR expr(Y). { A = sqlite3Fts5ParseNode(pParse, FTS5_OR, X, Y, 0); } expr(A) ::= expr(X) NOT expr(Y). { A = sqlite3Fts5ParseNode(pParse, FTS5_NOT, X, Y, 0); } expr(A) ::= colset(X) COLON LP expr(Y) RP. { sqlite3Fts5ParseSetColset(pParse, Y, X); A = Y; } expr(A) ::= LP expr(X) RP. {A = X;} expr(A) ::= exprlist(X). {A = X;} exprlist(A) ::= cnearset(X). {A = X;} exprlist(A) ::= exprlist(X) cnearset(Y). { A = sqlite3Fts5ParseImplicitAnd(pParse, X, Y); } cnearset(A) ::= nearset(X). { A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, X); } cnearset(A) ::= colset(X) COLON nearset(Y). { A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, Y); sqlite3Fts5ParseSetColset(pParse, A, X); } %type nearset {Fts5ExprNearset*} %type nearphrases {Fts5ExprNearset*} %destructor nearset { sqlite3Fts5ParseNearsetFree($$); } %destructor nearphrases { sqlite3Fts5ParseNearsetFree($$); } nearset(A) ::= phrase(X). { A = sqlite3Fts5ParseNearset(pParse, 0, X); } |
︙ | ︙ |
Changes to ext/fts5/test/fts5aa.test.
︙ | ︙ | |||
437 438 439 440 441 442 443 | # exception. But since bm25() can now used the cached structure record, # it never sees the corruption introduced by funk() and so the following # statement no longer fails. # do_catchsql_test 16.2 { SELECT funk(), bm25(n1), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d' } {0 {{} -1e-06 {}}} | | | 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | # exception. But since bm25() can now used the cached structure record, # it never sees the corruption introduced by funk() and so the following # statement no longer fails. # do_catchsql_test 16.2 { 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 { CREATE VIRTUAL TABLE b2 USING fts5(x, detail=%DETAIL%); INSERT INTO b2 VALUES('a'); |
︙ | ︙ | |||
557 558 559 560 561 562 563 564 565 | 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 } | > > > > > > > > > > > > | | > > > > > > > > | > > > > > > > > | 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 | 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%); } 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%); 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} } finish_test |
Changes to ext/fts5/test/fts5ab.test.
︙ | ︙ | |||
290 291 292 293 294 295 296 | INSERT INTO x1 VALUES($doc); } } ;# foreach_detail_mode... finish_test | < | 290 291 292 293 294 295 296 | INSERT INTO x1 VALUES($doc); } } ;# foreach_detail_mode... finish_test |
Changes to ext/fts5/test/fts5ac.test.
︙ | ︙ | |||
272 273 274 275 276 277 278 | } { do_execsql_test 2.3.$tn { SELECT fts5_expr_tcl($expr, 'N $x') } [list $tclexpr] } finish_test | < | 272 273 274 275 276 277 278 | } { do_execsql_test 2.3.$tn { SELECT fts5_expr_tcl($expr, 'N $x') } [list $tclexpr] } finish_test |
Changes to ext/fts5/test/fts5ad.test.
︙ | ︙ | |||
227 228 229 230 231 232 233 | 28 {a f*} 29 {a* f*} 30 {a* fghij*} } { set res [prefix_query $prefix] if {$bAsc} { set res [lsort -integer -increasing $res] } set n [llength $res] | < < | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | 28 {a f*} 29 {a* f*} 30 {a* fghij*} } { set res [prefix_query $prefix] if {$bAsc} { set res [lsort -integer -increasing $res] } set n [llength $res] do_execsql_test $T.$bAsc.$tn.$n $sql $res } } catchsql COMMIT } } finish_test |
Changes to ext/fts5/test/fts5ae.test.
︙ | ︙ | |||
305 306 307 308 309 310 311 | SELECT fts5_test_phrasecount(t9) FROM t9 WHERE t9 MATCH $q LIMIT 1 } $cnt } } finish_test | < | 305 306 307 308 309 310 311 | SELECT fts5_test_phrasecount(t9) FROM t9 WHERE t9 MATCH $q LIMIT 1 } $cnt } } finish_test |
Changes to ext/fts5/test/fts5af.test.
︙ | ︙ | |||
174 175 176 177 178 179 180 | do_execsql_test 5.1 { SELECT snippet(p1, 0, '[', ']', '...', 6) FROM p1('x'); } {{[x] a a a a a...}} } ;# foreach_detail_mode finish_test | < | 174 175 176 177 178 179 180 | do_execsql_test 5.1 { SELECT snippet(p1, 0, '[', ']', '...', 6) FROM p1('x'); } {{[x] a a a a a...}} } ;# foreach_detail_mode finish_test |
Changes to ext/fts5/test/fts5ag.test.
︙ | ︙ | |||
138 139 140 141 142 143 144 | } } } ;# foreach_detail_mode finish_test | < | 138 139 140 141 142 143 144 | } } } ;# foreach_detail_mode finish_test |
Changes to ext/fts5/test/fts5ah.test.
︙ | ︙ | |||
163 164 165 166 167 168 169 | } {10000} } ;# foreach_detail_mode #db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r} finish_test | < | 163 164 165 166 167 168 169 | } {10000} } ;# foreach_detail_mode #db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r} finish_test |
Changes to ext/fts5/test/fts5ai.test.
︙ | ︙ | |||
51 52 53 54 55 56 57 | do_execsql_test 1.2 { INSERT INTO t1(t1) VALUES('integrity-check'); } } finish_test | < | 51 52 53 54 55 56 57 | do_execsql_test 1.2 { INSERT INTO t1(t1) VALUES('integrity-check'); } } finish_test |
Changes to ext/fts5/test/fts5aj.test.
︙ | ︙ | |||
62 63 64 65 66 67 68 | } } do_execsql_test 2.0 { INSERT INTO t1(t1) VALUES('integrity-check') } finish_test | < | 62 63 64 65 66 67 68 | } } do_execsql_test 2.0 { INSERT INTO t1(t1) VALUES('integrity-check') } finish_test |
Changes to ext/fts5/test/fts5ak.test.
︙ | ︙ | |||
143 144 145 146 147 148 149 | {[a b c] [c d e]} {[a b c d e]} } } finish_test | < | 143 144 145 146 147 148 149 | {[a b c] [c d e]} {[a b c d e]} } } finish_test |
Changes to ext/fts5/test/fts5al.test.
︙ | ︙ | |||
73 74 75 76 77 78 79 | 1 "" 2 "fname" 3 "fname(X'234ab')" 4 "myfunc(-1.,'abc')" } { do_test 2.2.$tn { catchsql { INSERT INTO ft1(ft1, rank) VALUES('rank', $defn) } | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | 1 "" 2 "fname" 3 "fname(X'234ab')" 4 "myfunc(-1.,'abc')" } { do_test 2.2.$tn { catchsql { INSERT INTO ft1(ft1, rank) VALUES('rank', $defn) } } {1 {SQL logic error}} } #------------------------------------------------------------------------- # Assorted tests of the tcl interface for creating extension functions. # do_execsql_test 3.1 { |
︙ | ︙ | |||
293 294 295 296 297 298 299 | SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH NULL } {1 {parse error in rank function: }} } ;# foreach_detail_mode finish_test | < | 293 294 295 296 297 298 299 | SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH NULL } {1 {parse error in rank function: }} } ;# foreach_detail_mode finish_test |
Changes to ext/fts5/test/fts5alter.test.
︙ | ︙ | |||
85 86 87 88 89 90 91 | do_execsql_test 3.1 { CREATE VIRTUAL TABLE abc USING fts5(a); INSERT INTO abc(rowid, a) VALUES(1, 'a'); BEGIN; INSERT INTO abc(rowid, a) VALUES(2, 'a'); } | < < | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | do_execsql_test 3.1 { CREATE VIRTUAL TABLE abc USING fts5(a); INSERT INTO abc(rowid, a) VALUES(1, 'a'); BEGIN; INSERT INTO abc(rowid, a) VALUES(2, 'a'); } do_execsql_test 3.2 { SELECT rowid FROM abc WHERE abc MATCH 'a'; } {1 2} do_execsql_test 3.3 { COMMIT; SELECT rowid FROM abc WHERE abc MATCH 'a'; } {1 2} finish_test |
Changes to ext/fts5/test/fts5auto.test.
︙ | ︙ | |||
338 339 340 341 342 343 344 | } { do_auto_test 4.$tn yy $expr } finish_test | < | 338 339 340 341 342 343 344 | } { do_auto_test 4.$tn yy $expr } finish_test |
Changes to ext/fts5/test/fts5aux.test.
︙ | ︙ | |||
236 237 238 239 240 241 242 | 4 {"a a a" "b" "a d"} {"[a] [a] [a]" "[a] d"} 1 {"b d" "a b"} {"[b] [d]" "[a] b"} 2 {"d b" "a d"} {"[d] [b]" "[a] d"} 3 {"a a d"} {"[a] [a] d"} } { execsql { DELETE FROM x1 } foreach row $lRow { execsql { INSERT INTO x1 VALUES($row) } } | < | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | 4 {"a a a" "b" "a d"} {"[a] [a] [a]" "[a] d"} 1 {"b d" "a b"} {"[b] [d]" "[a] b"} 2 {"d b" "a d"} {"[d] [b]" "[a] d"} 3 {"a a d"} {"[a] [a] d"} } { execsql { DELETE FROM x1 } foreach row $lRow { execsql { INSERT INTO x1 VALUES($row) } } do_execsql_test 8.$tn { SELECT highlight(x1, 0, '[', ']') FROM x1 WHERE x1 MATCH 'a OR (b AND d)'; } $res } #------------------------------------------------------------------------- # Test the built-in bm25() demo. |
︙ | ︙ | |||
275 276 277 278 279 280 281 | } { 9 10 } finish_test | < | 274 275 276 277 278 279 280 | } { 9 10 } finish_test |
Changes to ext/fts5/test/fts5auxdata.test.
︙ | ︙ | |||
108 109 110 111 112 113 114 | db eval { SELECT aux_function_2(f1, 2, 'A'), aux_function_2(f1, 2, 'B') FROM f1 WHERE f1 MATCH 'a' ORDER BY rowid ASC } finish_test | < | 108 109 110 111 112 113 114 | db eval { SELECT aux_function_2(f1, 2, 'A'), aux_function_2(f1, 2, 'B') FROM f1 WHERE f1 MATCH 'a' ORDER BY rowid ASC } finish_test |
Changes to ext/fts5/test/fts5bigpl.test.
︙ | ︙ | |||
57 58 59 60 61 62 63 | set doc [string repeat "$t " 150000000] execsql { INSERT INTO t1 VALUES($doc) } } execsql { INSERT INTO t1(t1) VALUES('integrity-check') } } {} finish_test | < | 57 58 59 60 61 62 63 | set doc [string repeat "$t " 150000000] execsql { INSERT INTO t1 VALUES($doc) } } execsql { INSERT INTO t1(t1) VALUES('integrity-check') } } {} finish_test |
Changes to ext/fts5/test/fts5bigtok.test.
︙ | ︙ | |||
60 61 62 63 64 65 66 | do_execsql_test 2.[string range $v 0 0] { SELECT rowid FROM t1($v) ORDER BY rowid DESC } [lsort -integer -decr $res] } } finish_test | < < | 60 61 62 63 64 65 66 | do_execsql_test 2.[string range $v 0 0] { SELECT rowid FROM t1($v) ORDER BY rowid DESC } [lsort -integer -decr $res] } } finish_test |
Changes to ext/fts5/test/fts5colset.test.
︙ | ︙ | |||
40 41 42 43 44 45 46 | 5 " - {d d c} : a" {1 2} 6 "- {d c b a} : a" {} 7 "-{\"a\"} : b" {1 2 3} 8 "- c : a" {1 2 4} 9 "-c : a" {1 2 4} 10 "-\"c\" : a" {1 2 4} } { | < > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > < < | 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 | 5 " - {d d c} : a" {1 2} 6 "- {d c b a} : a" {} 7 "-{\"a\"} : b" {1 2 3} 8 "- c : a" {1 2 4} 9 "-c : a" {1 2 4} 10 "-\"c\" : a" {1 2 4} } { do_execsql_test 1.$tn { SELECT rowid FROM t1($q) } $res } foreach {tn q res} { 0 {{a} : (a AND ":")} {} 1 "{a b c} : (a AND d)" {2 3} 2 "{a b c} : (a AND b:d)" {3} 3 "{a b c} : (a AND d:d)" {} 4 "{b} : ( {b a} : ( {c b a} : ( {d b c a} : ( d OR c ) ) ) )" {3 4} 5 "{a} : ( {b a} : ( {c b a} : ( {d b c a} : ( d OR c ) ) ) )" {2 3} 6 "{a} : ( {b a} : ( {c b} : ( {d b c a} : ( d OR c ) ) ) )" {} 7 "{a b c} : (b:a AND c:b)" {2} } { do_execsql_test 2.$tn { SELECT rowid FROM t1($q) } $res } foreach {tn w res} { 0 "a MATCH 'a'" {1} 1 "b MATCH 'a'" {2} 2 "b MATCH '{a b c} : a'" {2} 3 "b MATCH 'a OR b'" {1 2} 4 "b MATCH 'a OR a:b'" {2} 5 "b MATCH 'a OR b:b'" {1 2} } { do_execsql_test 3.$tn " SELECT rowid FROM t1 WHERE $w " $res } do_catchsql_test 4.1 { SELECT * FROM t1 WHERE rowid MATCH 'a' } {1 {unable to use function MATCH in the requested context}} } finish_test |
Changes to ext/fts5/test/fts5columnsize.test.
︙ | ︙ | |||
139 140 141 142 143 144 145 | # do_execsql_test 4.1.1 { CREATE VIRTUAL TABLE t5 USING fts5(x, columnsize=0); INSERT INTO t5 VALUES('1 2 3 4'); INSERT INTO t5 VALUES('2 4 6 8'); } | < | 139 140 141 142 143 144 145 146 147 148 149 150 | # do_execsql_test 4.1.1 { CREATE VIRTUAL TABLE t5 USING fts5(x, columnsize=0); INSERT INTO t5 VALUES('1 2 3 4'); INSERT INTO t5 VALUES('2 4 6 8'); } do_execsql_test 4.1.2 { INSERT INTO t5(t5) VALUES('integrity-check'); } finish_test |
Changes to ext/fts5/test/fts5config.test.
︙ | ︙ | |||
62 63 64 65 66 67 68 | 5 "f1(x':;')" 6 "f1(x'[]')" 7 "f1(x'{}')" 8 "f1('abc)" } { do_catchsql_test 3.$tn { INSERT INTO t1(t1, rank) VALUES('rank', $val); | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 5 "f1(x':;')" 6 "f1(x'[]')" 7 "f1(x'{}')" 8 "f1('abc)" } { do_catchsql_test 3.$tn { INSERT INTO t1(t1, rank) VALUES('rank', $val); } {1 {SQL logic error}} } #------------------------------------------------------------------------- # The parsing of SQL literals specified as part of 'rank' options. # do_execsql_test 4.0 { CREATE VIRTUAL TABLE zzz USING fts5(one); |
︙ | ︙ | |||
106 107 108 109 110 111 112 | #------------------------------------------------------------------------- # Misquoting in tokenize= and other options. # do_catchsql_test 5.1 { CREATE VIRTUAL TABLE xx USING fts5(x, tokenize="porter 'ascii"); } {1 {parse error in tokenize directive}} | < | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | #------------------------------------------------------------------------- # Misquoting in tokenize= and other options. # do_catchsql_test 5.1 { CREATE VIRTUAL TABLE xx USING fts5(x, tokenize="porter 'ascii"); } {1 {parse error in tokenize directive}} do_catchsql_test 5.2 { CREATE VIRTUAL TABLE xx USING fts5(x, [y[]); } {0 {}} do_catchsql_test 5.3 { CREATE VIRTUAL TABLE yy USING fts5(x, [y]]); } {1 {unrecognized token: "]"}} |
︙ | ︙ | |||
165 166 167 168 169 170 171 | # 9.5.* 'hashsize' options. # do_execsql_test 9.0 { CREATE VIRTUAL TABLE abc USING fts5(a, b); } {} do_catchsql_test 9.1.1 { INSERT INTO abc(abc, rank) VALUES('pgsz', -5); | | | | | | | | | | | | | 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 | # 9.5.* 'hashsize' options. # do_execsql_test 9.0 { CREATE VIRTUAL TABLE abc USING fts5(a, b); } {} do_catchsql_test 9.1.1 { INSERT INTO abc(abc, rank) VALUES('pgsz', -5); } {1 {SQL logic error}} do_catchsql_test 9.1.2 { INSERT INTO abc(abc, rank) VALUES('pgsz', 50000000); } {1 {SQL logic error}} do_catchsql_test 9.1.3 { INSERT INTO abc(abc, rank) VALUES('pgsz', 66.67); } {1 {SQL logic error}} do_catchsql_test 9.2.1 { INSERT INTO abc(abc, rank) VALUES('automerge', -5); } {1 {SQL logic error}} do_catchsql_test 9.2.2 { INSERT INTO abc(abc, rank) VALUES('automerge', 50000000); } {1 {SQL logic error}} do_catchsql_test 9.2.3 { INSERT INTO abc(abc, rank) VALUES('automerge', 66.67); } {1 {SQL logic error}} do_execsql_test 9.2.4 { INSERT INTO abc(abc, rank) VALUES('automerge', 1); } {} do_catchsql_test 9.3.1 { INSERT INTO abc(abc, rank) VALUES('crisismerge', -5); } {1 {SQL logic error}} do_catchsql_test 9.3.2 { INSERT INTO abc(abc, rank) VALUES('crisismerge', 66.67); } {1 {SQL logic error}} do_execsql_test 9.3.3 { INSERT INTO abc(abc, rank) VALUES('crisismerge', 1); } {} do_execsql_test 9.3.4 { INSERT INTO abc(abc, rank) VALUES('crisismerge', 50000000); } {} do_catchsql_test 9.4.1 { INSERT INTO abc(abc, rank) VALUES('nosuchoption', 1); } {1 {SQL logic error}} do_catchsql_test 9.5.1 { INSERT INTO abc(abc, rank) VALUES('hashsize', 'not an integer'); } {1 {SQL logic error}} do_catchsql_test 9.5.2 { INSERT INTO abc(abc, rank) VALUES('hashsize', -500000); } {1 {SQL logic error}} do_catchsql_test 9.5.3 { INSERT INTO abc(abc, rank) VALUES('hashsize', 500000); } {0 {}} #------------------------------------------------------------------------- # Too many prefix indexes. Maximum allowed is 31. # |
︙ | ︙ | |||
241 242 243 244 245 246 247 | } { set res [list 1 {malformed detail=... directive}] do_catchsql_test 11.$tn "CREATE VIRTUAL TABLE f1 USING fts5(x, $opt)" $res } do_catchsql_test 12.1 { INSERT INTO t1(t1, rank) VALUES('rank', NULL);; | | | < | 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 | } { set res [list 1 {malformed detail=... directive}] do_catchsql_test 11.$tn "CREATE VIRTUAL TABLE f1 USING fts5(x, $opt)" $res } do_catchsql_test 12.1 { INSERT INTO t1(t1, rank) VALUES('rank', NULL);; } {1 {SQL logic error}} #------------------------------------------------------------------------- # errors in the 'usermerge' option # do_execsql_test 13.0 { CREATE VIRTUAL TABLE tt USING fts5(ttt); } foreach {tn val} { 1 -1 2 4.2 3 17 4 1 } { set sql "INSERT INTO tt(tt, rank) VALUES('usermerge', $val)" do_catchsql_test 13.$tn $sql {1 {SQL logic error}} } finish_test |
Changes to ext/fts5/test/fts5conflict.test.
︙ | ︙ | |||
62 63 64 65 66 67 68 | REPLACE INTO tbl VALUES(1, '4 5 6', '3 2 1'); DELETE FROM tbl WHERE a=100; INSERT INTO fts_idx(fts_idx) VALUES('integrity-check'); } finish_test | < < | 62 63 64 65 66 67 68 | REPLACE INTO tbl VALUES(1, '4 5 6', '3 2 1'); DELETE FROM tbl WHERE a=100; INSERT INTO fts_idx(fts_idx) VALUES('integrity-check'); } finish_test |
Added ext/fts5/test/fts5connect.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 | # 2017 August 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. # #************************************************************************* # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5connect ifcapable !fts5 { finish_test return } #------------------------------------------------------------------------- # The tests in this file test the outcome of a schema-reset happening # within the xConnect() method of an FTS5 table. At one point this # was causing a problem in SQLite. Each test proceeds as follows: # # 1. Connection [db] opens the db and reads from some unrelated, non-FTS5 # table causing SQLite to load the db schema into memory. # # 2. Connection [db2] opens the db and modifies the db schema. # # 3. Connection [db] reads or writes an existing fts5 table. That the # schema has been modified is detected inside the fts5 xConnect() # callback that is invoked by sqlite3_prepare(). # # 4. Verify that the statement in 3 has worked. SQLite should detect # that the schema has changed and successfully prepare the # statement against the new schema. # # Test plan: # # 1.*: Trigger the xConnect()/schema-reset using statements executed # directly against an FTS5 table. # # 2.*: Using various statements executed by various BEFORE triggers. # # 3.*: Using various statements executed by various AFTER triggers. # # 4.*: Using various statements executed by various INSTEAD OF triggers. # do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft1 USING fts5(a, b); CREATE TABLE abc(x INTEGER PRIMARY KEY); CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b); INSERT INTO ft1 VALUES('one', 'two'); INSERT INTO ft1 VALUES('three', 'four'); } foreach {tn sql res} { 1 "SELECT * FROM ft1" {one two three four} 2 "REPLACE INTO ft1(rowid, a, b) VALUES(1, 'five', 'six')" {} 3 "SELECT * FROM ft1" {five six three four} 4 "INSERT INTO ft1 VALUES('seven', 'eight')" {} 5 "SELECT * FROM ft1" {five six three four seven eight} 6 "DELETE FROM ft1 WHERE rowid=2" {} 7 "UPDATE ft1 SET b='nine' WHERE rowid=1" {} 8 "SELECT * FROM ft1" {five nine seven eight} } { catch { db close } catch { db2 close } sqlite3 db test.db sqlite3 db2 test.db do_test 1.$tn.1 { db eval { INSERT INTO abc DEFAULT VALUES } db2 eval { CREATE TABLE newtable(x,y); DROP TABLE newtable } } {} do_execsql_test 1.$tn.2 $sql $res do_execsql_test 1.$tn.3 { INSERT INTO ft1(ft1) VALUES('integrity-check'); } } do_execsql_test 2.0 { CREATE VIRTUAL TABLE ft2 USING fts5(a, b); CREATE TABLE t2(a, b); CREATE TABLE log(txt); CREATE TRIGGER t2_ai AFTER INSERT ON t2 BEGIN INSERT INTO ft2(rowid, a, b) VALUES(new.rowid, new.a, new.b); INSERT INTO log VALUES('insert'); END; CREATE TRIGGER t2_ad AFTER DELETE ON t2 BEGIN DELETE FROM ft2 WHERE rowid = old.rowid; INSERT INTO log VALUES('delete'); END; CREATE TRIGGER t2_au AFTER UPDATE ON t2 BEGIN UPDATE ft2 SET a=new.a, b=new.b WHERE rowid=new.rowid; INSERT INTO log VALUES('update'); END; INSERT INTO t2 VALUES('one', 'two'); INSERT INTO t2 VALUES('three', 'four'); } foreach {tn sql res} { 1 "SELECT * FROM t2" {one two three four} 2 "REPLACE INTO t2(rowid, a, b) VALUES(1, 'five', 'six')" {} 3 "SELECT * FROM ft2" {five six three four} 4 "INSERT INTO t2 VALUES('seven', 'eight')" {} 5 "SELECT * FROM ft2" {five six three four seven eight} 6 "DELETE FROM t2 WHERE rowid=2" {} 7 "UPDATE t2 SET b='nine' WHERE rowid=1" {} 8 "SELECT * FROM ft2" {five nine seven eight} } { catch { db close } catch { db2 close } sqlite3 db test.db sqlite3 db2 test.db do_test 2.$tn.1 { db eval { INSERT INTO abc DEFAULT VALUES } db2 eval { CREATE TABLE newtable(x,y); DROP TABLE newtable } } {} do_execsql_test 2.$tn.2 $sql $res do_execsql_test 2.$tn.3 { INSERT INTO ft2(ft2) VALUES('integrity-check'); } } do_execsql_test 3.0 { CREATE VIRTUAL TABLE ft3 USING fts5(a, b); CREATE TABLE t3(a, b); CREATE TRIGGER t3_ai BEFORE INSERT ON t3 BEGIN INSERT INTO ft3(rowid, a, b) VALUES(new.rowid, new.a, new.b); INSERT INTO log VALUES('insert'); END; CREATE TRIGGER t3_ad BEFORE DELETE ON t3 BEGIN DELETE FROM ft3 WHERE rowid = old.rowid; INSERT INTO log VALUES('delete'); END; CREATE TRIGGER t3_au BEFORE UPDATE ON t3 BEGIN UPDATE ft3 SET a=new.a, b=new.b WHERE rowid=new.rowid; INSERT INTO log VALUES('update'); END; INSERT INTO t3(rowid, a, b) VALUES(1, 'one', 'two'); INSERT INTO t3(rowid, a, b) VALUES(2, 'three', 'four'); } foreach {tn sql res} { 1 "SELECT * FROM t3" {one two three four} 2 "REPLACE INTO t3(rowid, a, b) VALUES(1, 'five', 'six')" {} 3 "SELECT * FROM ft3" {five six three four} 4 "INSERT INTO t3(rowid, a, b) VALUES(3, 'seven', 'eight')" {} 5 "SELECT * FROM ft3" {five six three four seven eight} 6 "DELETE FROM t3 WHERE rowid=2" {} 7 "UPDATE t3 SET b='nine' WHERE rowid=1" {} 8 "SELECT * FROM ft3" {five nine seven eight} } { catch { db close } catch { db2 close } sqlite3 db test.db sqlite3 db2 test.db do_test 3.$tn.1 { db eval { INSERT INTO abc DEFAULT VALUES } db2 eval { CREATE TABLE newtable(x,y); DROP TABLE newtable } } {} do_execsql_test 3.$tn.2 $sql $res do_execsql_test 3.$tn.3 { INSERT INTO ft3(ft3) VALUES('integrity-check'); } } do_execsql_test 4.0 { CREATE VIRTUAL TABLE ft4 USING fts5(a, b); CREATE VIEW v4 AS SELECT rowid, * FROM ft4; CREATE TRIGGER t4_ai INSTEAD OF INSERT ON v4 BEGIN INSERT INTO ft4(rowid, a, b) VALUES(new.rowid, new.a, new.b); INSERT INTO log VALUES('insert'); END; CREATE TRIGGER t4_ad INSTEAD OF DELETE ON v4 BEGIN DELETE FROM ft4 WHERE rowid = old.rowid; INSERT INTO log VALUES('delete'); END; CREATE TRIGGER t4_au INSTEAD OF UPDATE ON v4 BEGIN UPDATE ft4 SET a=new.a, b=new.b WHERE rowid=new.rowid; INSERT INTO log VALUES('update'); END; INSERT INTO ft4(rowid, a, b) VALUES(1, 'one', 'two'); INSERT INTO ft4(rowid, a, b) VALUES(2, 'three', 'four'); } foreach {tn sql res} { 1 "SELECT * FROM ft4" {one two three four} 2 "REPLACE INTO v4(rowid, a, b) VALUES(1, 'five', 'six')" {} 3 "SELECT * FROM ft4" {five six three four} 4 "INSERT INTO v4(rowid, a, b) VALUES(3, 'seven', 'eight')" {} 5 "SELECT * FROM ft4" {five six three four seven eight} 6 "DELETE FROM v4 WHERE rowid=2" {} 7 "UPDATE v4 SET b='nine' WHERE rowid=1" {} 8 "SELECT * FROM ft4" {five nine seven eight} } { catch { db close } catch { db2 close } sqlite3 db test.db sqlite3 db2 test.db do_test 4.$tn.1 { db eval { INSERT INTO abc DEFAULT VALUES } db2 eval { CREATE TABLE newtable(x,y); DROP TABLE newtable } } {} do_execsql_test 4.$tn.2 $sql $res do_execsql_test 4.$tn.3 { INSERT INTO ft3(ft3) VALUES('integrity-check'); } } finish_test |
Changes to ext/fts5/test/fts5content.test.
︙ | ︙ | |||
251 252 253 254 255 256 257 | do_execsql_test 6.2 { DROP TABLE xx; SELECT name FROM sqlite_master; } {} finish_test | < | 251 252 253 254 255 256 257 | do_execsql_test 6.2 { DROP TABLE xx; SELECT name FROM sqlite_master; } {} finish_test |
Changes to ext/fts5/test/fts5corrupt.test.
︙ | ︙ | |||
92 93 94 95 96 97 98 | do_catchsql_test 3.1 { DELETE FROM t3_content WHERE rowid = 3; SELECT * FROM t3 WHERE t3 MATCH 'o'; } {1 {database disk image is malformed}} finish_test | < | 92 93 94 95 96 97 98 | do_catchsql_test 3.1 { DELETE FROM t3_content WHERE rowid = 3; SELECT * FROM t3 WHERE t3 MATCH 'o'; } {1 {database disk image is malformed}} finish_test |
Changes to ext/fts5/test/fts5corrupt2.test.
︙ | ︙ | |||
265 266 267 268 269 270 271 | do_catchsql_test 6.2 { SELECT colsize(x5, 0) FROM x5 WHERE x5 MATCH 'a' } {1 SQLITE_CORRUPT_VTAB} sqlite3_fts5_may_be_corrupt 0 finish_test | < | 265 266 267 268 269 270 271 | do_catchsql_test 6.2 { SELECT colsize(x5, 0) FROM x5 WHERE x5 MATCH 'a' } {1 SQLITE_CORRUPT_VTAB} sqlite3_fts5_may_be_corrupt 0 finish_test |
Changes to ext/fts5/test/fts5corrupt3.test.
︙ | ︙ | |||
405 406 407 408 409 410 411 | } {} do_catchsql_test 9.2.2 { SELECT * FROM t1('one AND two'); } {1 {database disk image is malformed}} sqlite3_fts5_may_be_corrupt 0 finish_test | < | 405 406 407 408 409 410 411 | } {} do_catchsql_test 9.2.2 { SELECT * FROM t1('one AND two'); } {1 {database disk image is malformed}} sqlite3_fts5_may_be_corrupt 0 finish_test |
Added ext/fts5/test/fts5delete.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 | # 2017 May 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. # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the FTS5 module. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5delete # If SQLITE_ENABLE_FTS5 is not defined, omit this file. ifcapable !fts5 { finish_test return } fts5_aux_test_functions db do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(x); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<5000 ) INSERT INTO t1(rowid, x) SELECT i, (i/2)*2 FROM s; } do_test 1.1 { execsql BEGIN for {set i 1} {$i<=5000} {incr i} { if {$i % 2} { execsql { INSERT INTO t1 VALUES($i) } } else { execsql { DELETE FROM t1 WHERE rowid = $i } } } execsql COMMIT } {} do_test 1.2 { execsql { INSERT INTO t1(t1, rank) VALUES('usermerge', 2); } for {set i 0} {$i < 5} {incr i} { execsql { INSERT INTO t1(t1, rank) VALUES('merge', 1) } execsql { INSERT INTO t1(t1) VALUES('integrity-check') } } } {} finish_test |
Changes to ext/fts5/test/fts5detail.test.
︙ | ︙ | |||
237 238 239 240 241 242 243 | (SELECT sum(length(block)) from t2_data) < (SELECT sum(length(block)) from t3_data) } {1} finish_test | < | 237 238 239 240 241 242 243 | (SELECT sum(length(block)) from t2_data) < (SELECT sum(length(block)) from t3_data) } {1} finish_test |
Changes to ext/fts5/test/fts5determin.test.
︙ | ︙ | |||
59 60 61 62 63 64 65 | } {} do_determin_test 1.4 } finish_test | < < | 59 60 61 62 63 64 65 | } {} do_determin_test 1.4 } finish_test |
Changes to ext/fts5/test/fts5dlidx.test.
︙ | ︙ | |||
62 63 64 65 66 67 68 | append doc " y" } } execsql { INSERT INTO t1(rowid, x) VALUES($rowid, $doc) } } execsql COMMIT | < | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | append doc " y" } } execsql { INSERT INTO t1(rowid, x) VALUES($rowid, $doc) } } execsql COMMIT do_test $tn.1 { execsql { INSERT INTO t1(t1) VALUES('integrity-check') } } {} do_fb_test $tn.3.1 { SELECT rowid FROM t1 WHERE t1 MATCH 'a AND x' } $xdoc do_fb_test $tn.3.2 { SELECT rowid FROM t1 WHERE t1 MATCH 'x AND a' } $xdoc |
︙ | ︙ | |||
120 121 122 123 124 125 126 | INSERT INTO t1(rowid,x) SELECT i, $str FROM iii; COMMIT; } do_execsql_test $tn.1 { SELECT rowid FROM t1 WHERE t1 MATCH 'b AND a' } {1} | < | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | INSERT INTO t1(rowid,x) SELECT i, $str FROM iii; COMMIT; } do_execsql_test $tn.1 { SELECT rowid FROM t1 WHERE t1 MATCH 'b AND a' } {1} do_execsql_test $tn.2 { SELECT rowid FROM t1 WHERE t1 MATCH 'b AND a' ORDER BY rowid DESC } {1} } do_dlidx_test2 2.1 [expr 20] [expr 1<<57] [expr (1<<57) + 128] |
︙ | ︙ | |||
193 194 195 196 197 198 199 | } } ;# foreach_detail_mode finish_test | < | 191 192 193 194 195 196 197 | } } ;# foreach_detail_mode finish_test |
Changes to ext/fts5/test/fts5doclist.test.
︙ | ︙ | |||
40 41 42 43 44 45 46 | do_execsql_test 1.2 { INSERT INTO ccc(ccc) VALUES('integrity-check'); } finish_test | < | 40 41 42 43 44 45 46 | do_execsql_test 1.2 { INSERT INTO ccc(ccc) VALUES('integrity-check'); } finish_test |
Changes to ext/fts5/test/fts5eb.test.
︙ | ︙ | |||
77 78 79 80 81 82 83 | do_execsql_test 3.3 { SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"/" OR "just"' ORDER BY rank; } {1 -1e-06} finish_test | < < < | 77 78 79 80 81 82 83 | do_execsql_test 3.3 { SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"/" OR "just"' ORDER BY rank; } {1 -1e-06} finish_test |
Changes to ext/fts5/test/fts5fault1.test.
︙ | ︙ | |||
347 348 349 350 351 352 353 | if {$ls != "2 0"} { error "fts5_level_segs says {$ls}" } } } finish_test | < | 347 348 349 350 351 352 353 | if {$ls != "2 0"} { error "fts5_level_segs says {$ls}" } } } finish_test |
Changes to ext/fts5/test/fts5fault2.test.
︙ | ︙ | |||
133 134 135 136 137 138 139 | ); } } -test { faultsim_test_result {0 {}} } finish_test | < | 133 134 135 136 137 138 139 | ); } } -test { faultsim_test_result {0 {}} } finish_test |
Changes to ext/fts5/test/fts5fault3.test.
︙ | ︙ | |||
106 107 108 109 110 111 112 | } -test { faultsim_test_result [list 0 {}] } finish_test | < | 106 107 108 109 110 111 112 | } -test { faultsim_test_result [list 0 {}] } finish_test |
Changes to ext/fts5/test/fts5fault4.test.
︙ | ︙ | |||
391 392 393 394 395 396 397 | } -body { db eval { ALTER TABLE "tbl one" RENAME TO "tbl two" } } -test { faultsim_test_result {0 {}} } finish_test | < | 391 392 393 394 395 396 397 | } -body { db eval { ALTER TABLE "tbl one" RENAME TO "tbl two" } } -test { faultsim_test_result {0 {}} } finish_test |
Changes to ext/fts5/test/fts5fault5.test.
︙ | ︙ | |||
101 102 103 104 105 106 107 | db eval { SELECT term FROM tv WHERE term BETWEEN '1' AND '2'; } } -test { faultsim_test_result {0 {1 10 11 12 13 14 15 16 17 18 19 2}} } | < | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | db eval { SELECT term FROM tv WHERE term BETWEEN '1' AND '2'; } } -test { faultsim_test_result {0 {1 10 11 12 13 14 15 16 17 18 19 2}} } do_execsql_test 3.3.0 { SELECT * FROM tv2; } { 0 x 1 {} 1 x 1 {} 10 x 1 {} 11 x 1 {} 12 x 1 {} 13 x 1 {} 14 x 1 {} 15 x 1 {} 16 x 1 {} 17 x 1 {} 18 x 1 {} 19 x 1 {} 2 x 1 {} 3 x 1 {} 4 x 1 {} 5 x 1 {} 6 x 1 {} 7 x 1 {} 8 x 1 {} 9 x 1 {} |
︙ | ︙ | |||
126 127 128 129 130 131 132 | 9 x 1 {} ]] } finish_test | < | 125 126 127 128 129 130 131 | 9 x 1 {} ]] } finish_test |
Changes to ext/fts5/test/fts5fault6.test.
︙ | ︙ | |||
276 277 278 279 280 281 282 | } } -test { faultsim_test_result {0 1} } #------------------------------------------------------------------------- catch { db close } | < < | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | } } -test { faultsim_test_result {0 1} } #------------------------------------------------------------------------- catch { db close } do_faultsim_test 6 -faults oom* -prep { sqlite_orig db test.db sqlite3_db_config_lookaside db 0 0 0 } -test { faultsim_test_result {0 {}} {1 {initialization of fts5 failed: }} if {$testrc==0} { db eval { CREATE VIRTUAL TABLE temp.t1 USING fts5(x) } } db close } finish_test |
Changes to ext/fts5/test/fts5fault7.test.
︙ | ︙ | |||
112 113 114 115 116 117 118 | do_faultsim_test 2.2 -faults oom-* -body { db eval { SELECT * FROM xy('""') } } -test { faultsim_test_result {0 {}} } finish_test | < | 112 113 114 115 116 117 118 | do_faultsim_test 2.2 -faults oom-* -body { db eval { SELECT * FROM xy('""') } } -test { faultsim_test_result {0 {}} } finish_test |
Changes to ext/fts5/test/fts5fault8.test.
︙ | ︙ | |||
78 79 80 81 82 83 84 | execsql { INSERT INTO x2(x2) VALUES('optimize') } } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} } finish_test | < | 78 79 80 81 82 83 84 | execsql { INSERT INTO x2(x2) VALUES('optimize') } } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} } finish_test |
Changes to ext/fts5/test/fts5fault9.test.
︙ | ︙ | |||
149 150 151 152 153 154 155 | faultsim_test_result [list 0 {1 3}] } } ;# foreach_detail_mode... finish_test | < | 149 150 151 152 153 154 155 | faultsim_test_result [list 0 {1 3}] } } ;# foreach_detail_mode... finish_test |
Changes to ext/fts5/test/fts5faultA.test.
︙ | ︙ | |||
57 58 59 60 61 62 63 | sqlite3 db test.db } -body { execsql { SELECT rowid FROM o2('a+b+c NOT xyz') } } -test { faultsim_test_result {0 {1 2}} } finish_test | < | 57 58 59 60 61 62 63 | sqlite3 db test.db } -body { execsql { SELECT rowid FROM o2('a+b+c NOT xyz') } } -test { faultsim_test_result {0 {1 2}} } finish_test |
Changes to ext/fts5/test/fts5faultB.test.
︙ | ︙ | |||
74 75 76 77 78 79 80 | do_faultsim_test 2.4 -faults oom* -body { execsql { SELECT mit(matchinfo(t1, 's')) FROM t1('a b c') } } -test { faultsim_test_result {0 {{3 2} {2 3}}} } | > | > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_faultsim_test 2.4 -faults oom* -body { execsql { SELECT mit(matchinfo(t1, 's')) FROM t1('a b c') } } -test { faultsim_test_result {0 {{3 2} {2 3}}} } #------------------------------------------------------------------------- # reset_db do_execsql_test 3.0 { CREATE VIRTUAL TABLE x1 USING fts5(z); } do_faultsim_test 3.1 -faults oom* -body { execsql { SELECT rowid FROM x1('c') WHERE rowid>1; } } -test { faultsim_test_result {0 {}} } do_execsql_test 3.2 { INSERT INTO x1 VALUES('a b c'); INSERT INTO x1 VALUES('b c d'); INSERT INTO x1 VALUES('c d e'); INSERT INTO x1 VALUES('d e f'); } do_faultsim_test 3.3 -faults oom* -body { execsql { SELECT rowid FROM x1('c') WHERE rowid>1; } } -test { faultsim_test_result {0 {2 3}} } #------------------------------------------------------------------------- # Test OOM injection with nested colsets. # reset_db do_execsql_test 4.0 { CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, d); INSERT INTO t1 VALUES('a', 'b', 'c', 'd'); -- 1 INSERT INTO t1 VALUES('d', 'a', 'b', 'c'); -- 2 INSERT INTO t1 VALUES('c', 'd', 'a', 'b'); -- 3 INSERT INTO t1 VALUES('b', 'c', 'd', 'a'); -- 4 } do_faultsim_test 4.1 -faults oom* -body { execsql { SELECT rowid FROM t1('{a b c} : (b:a AND c:b)'); } } -test { faultsim_test_result {0 2} } do_faultsim_test 4.2 -faults oom* -body { execsql { SELECT rowid FROM t1('{a b c} : (a AND d)') } } -test { faultsim_test_result {0 {2 3}} } finish_test |
Added ext/fts5/test/fts5faultD.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 | # 2016 February 2 # # 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 focused on OOM errors. # source [file join [file dirname [info script]] fts5_common.tcl] source $testdir/malloc_common.tcl set testprefix fts5faultA # If SQLITE_ENABLE_FTS3 is defined, omit this file. ifcapable !fts5 { finish_test return } foreach_detail_mode $testprefix { if {"%DETAIL%"=="none"} continue do_execsql_test 1.0 { CREATE VIRTUAL TABLE o1 USING fts5(a, b, c, detail=%DETAIL%); INSERT INTO o1(o1, rank) VALUES('pgsz', 32); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<60 ) INSERT INTO o1 SELECT 'A', 'B', 'C' FROM s; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<60 ) INSERT INTO o1 SELECT 'C', 'A', 'B' FROM s; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<60 ) INSERT INTO o1 SELECT 'B', 'C', 'A' FROM s; } do_faultsim_test 1 -faults int* -prep { sqlite3 db test.db } -body { execsql { SELECT count(*) FROM o1('a') } } -test { faultsim_test_result {0 180} {1 {vtable constructor failed: o1}} } do_faultsim_test 2 -faults int* -prep { sqlite3 db test.db } -body { execsql { SELECT * FROM o1('a:a AND {b c}:b') ORDER BY rank } expr 1 } -test { faultsim_test_result {0 1} {1 {vtable constructor failed: o1}} } do_faultsim_test 3 -faults int* -prep { sqlite3 db test.db } -body { execsql { SELECT * FROM o1('{b c}:b NOT a:a') ORDER BY rank } expr 1 } -test { faultsim_test_result {0 1} {1 {vtable constructor failed: o1}} } do_faultsim_test 4 -faults int* -prep { sqlite3 db test.db } -body { execsql { SELECT * FROM o1('b:b OR a:a') } expr 1 } -test { faultsim_test_result {0 1} {1 {vtable constructor failed: o1}} } do_faultsim_test 5 -faults int* -prep { sqlite3 db test.db } -body { execsql { SELECT count(*) FROM o1('c:b') } expr 1 } -test { faultsim_test_result {0 1} {1 {vtable constructor failed: o1}} } } finish_test |
Changes to ext/fts5/test/fts5full.test.
︙ | ︙ | |||
36 37 38 39 40 41 42 | execsql { INSERT INTO x8 VALUES( rnddoc(5) ); } } } msg] $msg } {1 {database or disk is full}} finish_test | < | 36 37 38 39 40 41 42 | execsql { INSERT INTO x8 VALUES( rnddoc(5) ); } } } msg] $msg } {1 {database or disk is full}} finish_test |
Changes to ext/fts5/test/fts5fuzz1.test.
︙ | ︙ | |||
86 87 88 89 90 91 92 | reset_db do_catchsql_test 4.1 { CREATE VIRTUAL TABLE f2 USING fts5(o, t); SELECT * FROM f2('(8 AND 9)`AND 10'); } {1 {fts5: syntax error near "`"}} finish_test | < | 86 87 88 89 90 91 92 | reset_db do_catchsql_test 4.1 { CREATE VIRTUAL TABLE f2 USING fts5(o, t); SELECT * FROM f2('(8 AND 9)`AND 10'); } {1 {fts5: syntax error near "`"}} finish_test |
Changes to ext/fts5/test/fts5hash.test.
︙ | ︙ | |||
117 118 119 120 121 122 123 | set hash [sqlite3_fts5_token_hash 1024 $big] while {1} { set small [random_token] if {[sqlite3_fts5_token_hash 1024 $small]==$hash} break } execsql { CREATE VIRTUAL TABLE t2 USING fts5(x, detail=%DETAIL%) } | < < | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | set hash [sqlite3_fts5_token_hash 1024 $big] while {1} { set small [random_token] if {[sqlite3_fts5_token_hash 1024 $small]==$hash} break } execsql { CREATE VIRTUAL TABLE t2 USING fts5(x, detail=%DETAIL%) } execsql { INSERT INTO t2 VALUES($small || ' ' || $big); } } {} } ;# foreach_detail_mode finish_test |
Changes to ext/fts5/test/fts5integrity.test.
︙ | ︙ | |||
206 207 208 209 210 211 212 | if {$res == [lsort -integer $res2]} { incr ok } } set ok } {1000} } finish_test | < | 206 207 208 209 210 211 212 | if {$res == [lsort -integer $res2]} { incr ok } } set ok } {1000} } finish_test |
Added ext/fts5/test/fts5lastrowid.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 | # 2017 Feb 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. # #*********************************************************************** # # Tests of the last_insert_rowid functionality with fts5. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5lastrowid # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(str); } do_execsql_test 1.1 { INSERT INTO t1 VALUES('one string'); INSERT INTO t1 VALUES('two string'); INSERT INTO t1 VALUES('three string'); SELECT last_insert_rowid(); } {3} do_execsql_test 1.2 { BEGIN; INSERT INTO t1 VALUES('one string'); INSERT INTO t1 VALUES('two string'); INSERT INTO t1 VALUES('three string'); COMMIT; SELECT last_insert_rowid(); } {6} do_execsql_test 1.3 { INSERT INTO t1(rowid, str) VALUES(-22, 'some more text'); SELECT last_insert_rowid(); } {-22} do_execsql_test 1.4 { BEGIN; INSERT INTO t1(rowid, str) VALUES(45, 'some more text'); INSERT INTO t1(rowid, str) VALUES(46, 'some more text'); INSERT INTO t1(rowid, str) VALUES(222, 'some more text'); SELECT last_insert_rowid(); COMMIT; SELECT last_insert_rowid(); } {222 222} do_execsql_test 1.5 { CREATE TABLE x1(x); INSERT INTO x1 VALUES('john'), ('paul'), ('george'), ('ringo'); INSERT INTO t1 SELECT x FROM x1; SELECT last_insert_rowid(); } {226} do_execsql_test 1.6 { INSERT INTO t1(rowid, str) SELECT rowid+10, x FROM x1; SELECT last_insert_rowid(); } {14} finish_test |
Added ext/fts5/test/fts5leftjoin.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 | # 2014 June 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. # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the FTS5 module. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5leftjoin # If SQLITE_ENABLE_FTS5 is not defined, omit this file. ifcapable !fts5 { finish_test return } do_execsql_test 1.0 { CREATE VIRTUAL TABLE vt USING fts5(x); INSERT INTO vt VALUES('abc'); INSERT INTO vt VALUES('xyz'); CREATE TABLE t1(a INTEGER PRIMARY KEY); INSERT INTO t1 VALUES(1), (2); } do_execsql_test 1.1 { SELECT * FROM t1 LEFT JOIN ( SELECT rowid AS rrr, * FROM vt WHERE vt MATCH 'abc' ) ON t1.a = rrr } {1 1 abc 2 {} {}} do_execsql_test 1.2 { SELECT * FROM t1 LEFT JOIN vt ON (vt MATCH 'abc') } {1 abc 2 abc} finish_test |
Changes to ext/fts5/test/fts5matchinfo.test.
︙ | ︙ | |||
468 469 470 471 472 473 474 | } ;# foreach_detail_mode #------------------------------------------------------------------------- # Test that a bad fts5() return is detected # reset_db proc xyz {} {} | | < | 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 | } ;# foreach_detail_mode #------------------------------------------------------------------------- # Test that a bad fts5() return is detected # reset_db proc xyz {} {} db func fts5 -argcount 1 xyz do_test 13.1 { list [catch { sqlite3_fts5_register_matchinfo db } msg] $msg } {1 SQLITE_ERROR} #------------------------------------------------------------------------- # Test that an invalid matchinfo() flag is detected # reset_db sqlite3_fts5_register_matchinfo db do_execsql_test 14.1 { CREATE VIRTUAL TABLE x1 USING fts5(z); INSERT INTO x1 VALUES('a b c a b c a b c'); } {} do_catchsql_test 14.2 { SELECT matchinfo(x1, 'd') FROM x1('a b c'); } {1 {unrecognized matchinfo flag: d}} finish_test |
Changes to ext/fts5/test/fts5merge.test.
︙ | ︙ | |||
237 238 239 240 241 242 243 | do_execsql_test 6.3 { INSERT INTO g1(g1) VALUES('integrity-check'); } finish_test | < | 237 238 239 240 241 242 243 | do_execsql_test 6.3 { INSERT INTO g1(g1) VALUES('integrity-check'); } finish_test |
Changes to ext/fts5/test/fts5merge2.test.
︙ | ︙ | |||
51 52 53 54 55 56 57 | do_execsql_test 1.2 { INSERT INTO t1(t1) VALUES('integrity-check'); } } finish_test | < | 51 52 53 54 55 56 57 | do_execsql_test 1.2 { INSERT INTO t1(t1) VALUES('integrity-check'); } } finish_test |
Changes to ext/fts5/test/fts5multiclient.test.
︙ | ︙ | |||
41 42 43 44 45 46 47 | sql1 { INSERT INTO t1 VALUES('a b c') } sql3 { INSERT INTO t1(t1) VALUES('integrity-check') } } {} };# do_multiclient_test };# foreach_detail_mode finish_test | < | 41 42 43 44 45 46 47 | sql1 { INSERT INTO t1 VALUES('a b c') } sql3 { INSERT INTO t1(t1) VALUES('integrity-check') } } {} };# do_multiclient_test };# foreach_detail_mode finish_test |
Changes to ext/fts5/test/fts5near.test.
︙ | ︙ | |||
64 65 66 67 68 69 70 | do_near_test 1.23 "a b c d e f g h i" { NEAR(a+b+c+d i b+c, 4) } 0 do_near_test 1.24 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 5) } 1 do_near_test 1.25 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 4) } 0 finish_test | < | 64 65 66 67 68 69 70 | do_near_test 1.23 "a b c d e f g h i" { NEAR(a+b+c+d i b+c, 4) } 0 do_near_test 1.24 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 5) } 1 do_near_test 1.25 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 4) } 0 finish_test |
Changes to ext/fts5/test/fts5onepass.test.
︙ | ︙ | |||
174 175 176 177 178 179 180 | UPDATE ttt SET x = 'A B C' WHERE rowid = 4; INSERT INTO ttt(rowid, x) VALUES(6, 'd e f'); COMMIT; } {} do_test 4.2.2 { fts5_level_segs ttt } {3} finish_test | < | 174 175 176 177 178 179 180 | UPDATE ttt SET x = 'A B C' WHERE rowid = 4; INSERT INTO ttt(rowid, x) VALUES(6, 'd e f'); COMMIT; } {} do_test 4.2.2 { fts5_level_segs ttt } {3} finish_test |
Changes to ext/fts5/test/fts5optimize.test.
︙ | ︙ | |||
102 103 104 105 106 107 108 | do_execsql_test 2.$tn.5 { INSERT INTO t1(t1) VALUES('integrity-check'); } do_test 2.$tn.6 { fts5_segcount t1 } 1 } finish_test | < | 102 103 104 105 106 107 108 | do_execsql_test 2.$tn.5 { INSERT INTO t1(t1) VALUES('integrity-check'); } do_test 2.$tn.6 { fts5_segcount t1 } 1 } finish_test |
Changes to ext/fts5/test/fts5phrase.test.
︙ | ︙ | |||
112 113 114 115 116 117 118 | FROM t3('a:f+f') } { 31 {h *f f*} {i j g e c} {j j f c a i j} 50 {*f f* c} {f f b i i} {f f a j e c i} } finish_test | < | 112 113 114 115 116 117 118 | FROM t3('a:f+f') } { 31 {h *f f*} {i j g e c} {j j f c a i j} 50 {*f f* c} {f f b i i} {f f a j e c i} } finish_test |
Changes to ext/fts5/test/fts5plan.test.
︙ | ︙ | |||
26 27 28 29 30 31 32 | CREATE VIRTUAL TABLE f1 USING fts5(ff); } do_eqp_test 1.1 { SELECT * FROM t1, f1 WHERE f1 MATCH t1.x } { 0 0 0 {SCAN TABLE t1} | | | < | 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 | CREATE VIRTUAL TABLE f1 USING fts5(ff); } do_eqp_test 1.1 { SELECT * FROM t1, f1 WHERE f1 MATCH t1.x } { 0 0 0 {SCAN TABLE t1} 0 1 1 {SCAN TABLE f1 VIRTUAL TABLE INDEX 65537:} } do_eqp_test 1.2 { SELECT * FROM t1, f1 WHERE f1 > t1.x } { 0 0 1 {SCAN TABLE f1 VIRTUAL TABLE INDEX 0:} 0 1 0 {SCAN TABLE t1} } do_eqp_test 1.3 { SELECT * FROM f1 WHERE f1 MATCH ? ORDER BY ff } { 0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 65537:} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_eqp_test 1.4 { SELECT * FROM f1 ORDER BY rank } { 0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 0:} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_eqp_test 1.5 { SELECT * FROM f1 WHERE rank MATCH ? } { 0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 2:} } finish_test |
Changes to ext/fts5/test/fts5porter.test.
︙ | ︙ | |||
11799 11800 11801 11802 11803 11804 11805 | lindex [sqlite3_fts5_tokenize db porter $in] 0 } $out incr i } finish_test | < | 11799 11800 11801 11802 11803 11804 11805 | lindex [sqlite3_fts5_tokenize db porter $in] 0 } $out incr i } finish_test |
Changes to ext/fts5/test/fts5porter2.test.
︙ | ︙ | |||
63 64 65 66 67 68 69 | lindex [sqlite3_fts5_tokenize db porter $in] 0 } $out incr i } finish_test | < | 63 64 65 66 67 68 69 | lindex [sqlite3_fts5_tokenize db porter $in] 0 } $out incr i } finish_test |
Changes to ext/fts5/test/fts5prefix.test.
1 2 3 4 5 6 7 8 9 10 11 | # 2015 Jan 13 # # 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 | # 2015 Jan 13 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains tests focused on prefix indexes. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5prefix # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { |
︙ | ︙ | |||
337 338 339 340 341 342 343 | do_execsql_test 7.$tn { SELECT md5sum(id, block) FROM tt_data } [list $::checksum] } } finish_test | < < | 337 338 339 340 341 342 343 | do_execsql_test 7.$tn { SELECT md5sum(id, block) FROM tt_data } [list $::checksum] } } finish_test |
Changes to ext/fts5/test/fts5query.test.
︙ | ︙ | |||
75 76 77 78 79 80 81 | } {} incr ret } } finish_test | < < | 75 76 77 78 79 80 81 | } {} incr ret } } finish_test |
Changes to ext/fts5/test/fts5rank.test.
︙ | ︙ | |||
86 87 88 89 90 91 92 93 94 95 96 97 98 99 | execsql { SELECT rowid FROM tt('a') ORDER BY rank; } db2 } {1 3 2} do_test 2.7 { execsql { SELECT rowid FROM tt('a') ORDER BY rank; } db } {1 3 2} #-------------------------------------------------------------------------- # At one point there was a problem with queries such as: # # ... MATCH 'x OR y' ORDER BY rank; # # if there were zero occurrences of token 'y' in the dataset. The | > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | execsql { SELECT rowid FROM tt('a') ORDER BY rank; } db2 } {1 3 2} do_test 2.7 { execsql { SELECT rowid FROM tt('a') ORDER BY rank; } db } {1 3 2} db2 close #-------------------------------------------------------------------------- # At one point there was a problem with queries such as: # # ... MATCH 'x OR y' ORDER BY rank; # # if there were zero occurrences of token 'y' in the dataset. The |
︙ | ︙ | |||
147 148 149 150 151 152 153 | VTest MATCH 'wrinkle in time OR a wrinkle in time' ORDER BY rank; } {{wrinkle in time} {Bill Smith}} finish_test | < | 148 149 150 151 152 153 154 | VTest MATCH 'wrinkle in time OR a wrinkle in time' ORDER BY rank; } {{wrinkle in time} {Bill Smith}} finish_test |
Changes to ext/fts5/test/fts5rebuild.test.
︙ | ︙ | |||
60 61 62 63 64 65 66 | CREATE VIRTUAL TABLE nc USING fts5(doc, content=); } do_catchsql_test 2.2 { INSERT INTO nc(nc) VALUES('rebuild'); } {1 {'rebuild' may not be used with a contentless fts5 table}} finish_test | < | 60 61 62 63 64 65 66 | CREATE VIRTUAL TABLE nc USING fts5(doc, content=); } do_catchsql_test 2.2 { INSERT INTO nc(nc) VALUES('rebuild'); } {1 {'rebuild' may not be used with a contentless fts5 table}} finish_test |
Changes to ext/fts5/test/fts5restart.test.
︙ | ︙ | |||
145 146 147 148 149 150 151 | } set res } {500 400 300} finish_test | < | 145 146 147 148 149 150 151 | } set res } {500 400 300} finish_test |
Changes to ext/fts5/test/fts5rowid.test.
︙ | ︙ | |||
212 213 214 215 216 217 218 | } {36} #db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM x5_data} {puts $r} finish_test | < | 212 213 214 215 216 217 218 | } {36} #db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM x5_data} {puts $r} finish_test |
Changes to ext/fts5/test/fts5simple.test.
︙ | ︙ | |||
407 408 409 410 411 412 413 | do_catchsql_test 19.2 { SELECT * FROM x1 WHERE x1 MATCH 'c0 AND (c1 AND (c2 AND (c3 AND (c4 AND (c5 AND (c6 AND (c7 AND (c8 AND (c9 AND (c10 AND (c11 AND (c12 AND (c13 AND (c14 AND (c15 AND (c16 AND (c17 AND (c18 AND (c19 AND (c20 AND (c21 AND (c22 AND (c23 AND (c24 AND (c25 AND (c26 AND (c27 AND (c28 AND (c29 AND (c30 AND (c31 AND (c32 AND (c33 AND (c34 AND (c35 AND (c36 AND (c37 AND (c38 AND (c39 AND (c40 AND (c41 AND (c42 AND (c43 AND (c44 AND (c45 AND (c46 AND (c47 AND (c48 AND (c49 AND (c50 AND (c51 AND (c52 AND (c53 AND (c54 AND (c55 AND (c56 AND (c57 AND (c58 AND (c59 AND (c60 AND (c61 AND (c62 AND (c63 AND (c64 AND (c65 AND (c66 AND (c67 AND (c68 AND (c69 AND (c70 AND (c71 AND (c72 AND (c73 AND (c74 AND (c75 AND (c76 AND (c77 AND (c78 AND (c79 AND (c80 AND (c81 AND (c82 AND (c83 AND (c84 AND (c85 AND (c86 AND (c87 AND (c88 AND (c89 AND (c90 AND (c91 AND (c92 AND (c93 AND (c94 AND (c95 AND (c96 AND (c97 AND (c98 AND (c99 AND (c100 AND (c101 AND (c102 AND (c103 AND (c104 AND (c105 AND (c106 AND (c107 AND (c108 AND (c109 AND (c110 AND (c111 AND (c112 AND (c113 AND (c114 AND (c115 AND (c116 AND (c117 AND (c118 AND (c119 AND (c120 AND (c121 AND (c122 AND (c123 AND (c124 AND (c125 AND (c126 AND (c127 AND (c128 AND (c129 AND (c130 AND (c131 AND (c132 AND (c133 AND (c134 AND (c135 AND (c136 AND (c137 AND (c138 AND (c139 AND (c140 AND (c141 AND (c142 AND (c143 AND (c144 AND (c145 AND (c146 AND (c147 AND (c148 AND (c149 AND (c150 AND (c151 AND (c152 AND (c153 AND (c154 AND (c155 AND (c156 AND (c157 AND (c158 AND (c159 AND (c160 AND (c161 AND (c162 AND (c163 AND (c164 AND (c165 AND (c166 AND (c167 AND (c168 AND (c169 AND (c170 AND (c171 AND (c172 AND (c173 AND (c174 AND (c175 AND (c176 AND (c177 AND (c178 AND (c179 AND (c180 AND (c181 AND (c182 AND (c183 AND (c184 AND (c185 AND (c186 AND (c187 AND (c188 AND (c189 AND (c190 AND (c191 AND (c192 AND (c193 AND (c194 AND (c195 AND (c196 AND (c197 AND (c198 AND (c199 AND c200)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))'; } {1 {fts5: parser stack overflow}} #------------------------------------------------------------------------- reset_db | < | 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | do_catchsql_test 19.2 { SELECT * FROM x1 WHERE x1 MATCH 'c0 AND (c1 AND (c2 AND (c3 AND (c4 AND (c5 AND (c6 AND (c7 AND (c8 AND (c9 AND (c10 AND (c11 AND (c12 AND (c13 AND (c14 AND (c15 AND (c16 AND (c17 AND (c18 AND (c19 AND (c20 AND (c21 AND (c22 AND (c23 AND (c24 AND (c25 AND (c26 AND (c27 AND (c28 AND (c29 AND (c30 AND (c31 AND (c32 AND (c33 AND (c34 AND (c35 AND (c36 AND (c37 AND (c38 AND (c39 AND (c40 AND (c41 AND (c42 AND (c43 AND (c44 AND (c45 AND (c46 AND (c47 AND (c48 AND (c49 AND (c50 AND (c51 AND (c52 AND (c53 AND (c54 AND (c55 AND (c56 AND (c57 AND (c58 AND (c59 AND (c60 AND (c61 AND (c62 AND (c63 AND (c64 AND (c65 AND (c66 AND (c67 AND (c68 AND (c69 AND (c70 AND (c71 AND (c72 AND (c73 AND (c74 AND (c75 AND (c76 AND (c77 AND (c78 AND (c79 AND (c80 AND (c81 AND (c82 AND (c83 AND (c84 AND (c85 AND (c86 AND (c87 AND (c88 AND (c89 AND (c90 AND (c91 AND (c92 AND (c93 AND (c94 AND (c95 AND (c96 AND (c97 AND (c98 AND (c99 AND (c100 AND (c101 AND (c102 AND (c103 AND (c104 AND (c105 AND (c106 AND (c107 AND (c108 AND (c109 AND (c110 AND (c111 AND (c112 AND (c113 AND (c114 AND (c115 AND (c116 AND (c117 AND (c118 AND (c119 AND (c120 AND (c121 AND (c122 AND (c123 AND (c124 AND (c125 AND (c126 AND (c127 AND (c128 AND (c129 AND (c130 AND (c131 AND (c132 AND (c133 AND (c134 AND (c135 AND (c136 AND (c137 AND (c138 AND (c139 AND (c140 AND (c141 AND (c142 AND (c143 AND (c144 AND (c145 AND (c146 AND (c147 AND (c148 AND (c149 AND (c150 AND (c151 AND (c152 AND (c153 AND (c154 AND (c155 AND (c156 AND (c157 AND (c158 AND (c159 AND (c160 AND (c161 AND (c162 AND (c163 AND (c164 AND (c165 AND (c166 AND (c167 AND (c168 AND (c169 AND (c170 AND (c171 AND (c172 AND (c173 AND (c174 AND (c175 AND (c176 AND (c177 AND (c178 AND (c179 AND (c180 AND (c181 AND (c182 AND (c183 AND (c184 AND (c185 AND (c186 AND (c187 AND (c188 AND (c189 AND (c190 AND (c191 AND (c192 AND (c193 AND (c194 AND (c195 AND (c196 AND (c197 AND (c198 AND (c199 AND c200)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))'; } {1 {fts5: parser stack overflow}} #------------------------------------------------------------------------- reset_db do_execsql_test 20.0 { CREATE VIRTUAL TABLE x1 USING fts5(x); INSERT INTO x1(x1, rank) VALUES('pgsz', 32); INSERT INTO x1(rowid, x) VALUES(11111, 'onetwothree'); } do_test 20.1 { for {set i 1} {$i <= 200} {incr i} { |
︙ | ︙ |
Changes to ext/fts5/test/fts5simple2.test.
︙ | ︙ | |||
327 328 329 330 331 332 333 334 335 336 337 | INSERT INTO t2(rowid, x) VALUES(1, 'a b c'); INSERT INTO t2(rowid, x) VALUES(456, 'a b c'); INSERT INTO t2(rowid, x) VALUES(1000, 'a b c'); COMMIT; UPDATE t2 SET x=x; DELETE FROM t2; } #db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM t2_data} {puts $r} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | 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 | INSERT INTO t2(rowid, x) VALUES(1, 'a b c'); INSERT INTO t2(rowid, x) VALUES(456, 'a b c'); INSERT INTO t2(rowid, x) VALUES(1000, 'a b c'); COMMIT; UPDATE t2 SET x=x; DELETE FROM t2; } #------------------------------------------------------------------------- # reset_db do_execsql_test 17.0 { 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 } reset_db do_execsql_test 17.4 { 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'); SELECT * FROM t2('y:a*') WHERE rowid>66; } do_execsql_test 17.5 { SELECT * FROM t2('x:b* OR y:a*') } do_execsql_test 17.5 { COMMIT ; SELECT * FROM t2('x:b* OR y:a*') } do_execsql_test 17.6 { SELECT * FROM t2('x:b* OR y:a*') WHERE rowid>55 } #db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM t2_data} {puts $r} finish_test |
Changes to ext/fts5/test/fts5simple3.test.
︙ | ︙ | |||
76 77 78 79 80 81 82 | CREATE VIRTUAL TABLE x3 USING fts5(one); INSERT INTO x3 VALUES('a b c'); INSERT INTO x3 VALUES('c b a'); INSERT INTO x3 VALUES('o t t'); SELECT * FROM x3('x OR y OR z'); } | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 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 | CREATE VIRTUAL TABLE x3 USING fts5(one); INSERT INTO x3 VALUES('a b c'); INSERT INTO x3 VALUES('c b a'); INSERT INTO x3 VALUES('o t t'); SELECT * FROM x3('x OR y OR z'); } #------------------------------------------------------------------------- # Test that a crash occuring when the second or subsequent tokens in a # phrase matched zero rows has been fixed. # do_execsql_test 4.0 { CREATE VIRTUAL TABLE t1 USING fts5(x); INSERT INTO t1 VALUES('ab'); INSERT INTO t1 VALUES('cd'); INSERT INTO t1 VALUES('ab cd'); INSERT INTO t1 VALUES('ab cdXXX'); INSERT INTO t1 VALUES('abXXX cd'); } do_execsql_test 4.1 { SELECT * FROM t1('"ab cd" OR "ab cd" *'); } {{ab cd} {ab cdXXX}} do_execsql_test 4.2 { SELECT * FROM t1('"xy zz" OR "ab cd" *'); } {{ab cd} {ab cdXXX}} do_execsql_test 4.3 { SELECT * FROM t1('"xy zz" OR "xy zz" *'); } do_execsql_test 4.4 { SELECT * FROM t1('"ab cd" OR "xy zz" *'); } {{ab cd}} do_execsql_test 4.5 { CREATE VIRTUAL TABLE t2 USING fts5(x); INSERT INTO t2 VALUES('ab'); INSERT INTO t2 VALUES('cd'); INSERT INTO t2 VALUES('ef'); } do_execsql_test 4.6 { SELECT * FROM t2('ab + xyz'); } finish_test |
Changes to ext/fts5/test/fts5synonym.test.
︙ | ︙ | |||
148 149 150 151 152 153 154 | reset_db fts5_tclnum_register db foreach {tn expr res} { 1 {abc} {"abc"} 2 {one} {"one"|"i"|"1"} 3 {3} {"3"|"iii"|"three"} | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | reset_db fts5_tclnum_register db foreach {tn expr res} { 1 {abc} {"abc"} 2 {one} {"one"|"i"|"1"} 3 {3} {"3"|"iii"|"three"} 4 {3*} {"3" *} } { do_execsql_test 4.1.$tn { SELECT fts5_expr($expr, 'tokenize=tclnum') } [list $res] } do_execsql_test 4.2.1 { |
︙ | ︙ | |||
417 418 419 420 421 422 423 | do_execsql_test 7.1.2 { INSERT INTO t2(t2) VALUES('integrity-check'); } } ;# foreach_detail_mode finish_test | < | 417 418 419 420 421 422 423 | do_execsql_test 7.1.2 { INSERT INTO t2(t2) VALUES('integrity-check'); } } ;# foreach_detail_mode finish_test |
Changes to ext/fts5/test/fts5synonym2.test.
︙ | ︙ | |||
157 158 159 160 161 162 163 | } } } finish_test | < | 157 158 159 160 161 162 163 | } } } finish_test |
Changes to ext/fts5/test/fts5tok1.test.
︙ | ︙ | |||
105 106 107 108 109 110 111 | do_catchsql_test 2.0 { CREATE VIRTUAL TABLE tX USING fts5tokenize(nosuchtokenizer); } {1 {vtable constructor failed: tX}} do_catchsql_test 2.1 { CREATE VIRTUAL TABLE t4 USING fts5tokenize; SELECT * FROM t4; | | | 105 106 107 108 109 110 111 112 113 114 115 | do_catchsql_test 2.0 { CREATE VIRTUAL TABLE tX USING fts5tokenize(nosuchtokenizer); } {1 {vtable constructor failed: tX}} do_catchsql_test 2.1 { CREATE VIRTUAL TABLE t4 USING fts5tokenize; SELECT * FROM t4; } {1 {SQL logic error}} finish_test |
Changes to ext/fts5/test/fts5tokenizer.test.
︙ | ︙ | |||
258 259 260 261 262 263 264 | CREATE VIRTUAL TABLE e7 USING fts5vocab(e6, 'row'); SELECT term FROM e7; ROLLBACK; } { brown dog fox jump lazi over quick the } | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | CREATE VIRTUAL TABLE e7 USING fts5vocab(e6, 'row'); SELECT term FROM e7; ROLLBACK; } { brown dog fox jump lazi over quick the } #------------------------------------------------------------------------- # Check that the FTS5_TOKENIZE_PREFIX flag is passed to the tokenizer # implementation. # reset_db proc tcl_create {args} { return "tcl_tokenize" } sqlite3_fts5_create_tokenizer db tcl tcl_create set ::flags [list] proc tcl_tokenize {tflags text} { lappend ::flags $tflags foreach {w iStart iEnd} [fts5_tokenize_split $text] { sqlite3_fts5_token $w $iStart $iEnd } } do_execsql_test 9.1.1 { CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=tcl); INSERT INTO t1 VALUES('abc'); INSERT INTO t1 VALUES('xyz'); } {} do_test 9.1.2 { set ::flags } {document document} set ::flags [list] do_execsql_test 9.2.1 { SELECT * FROM t1('abc'); } {abc} do_test 9.2.2 { set ::flags } {query} set ::flags [list] do_execsql_test 9.3.1 { SELECT * FROM t1('ab*'); } {abc} do_test 9.3.2 { set ::flags } {prefixquery} set ::flags [list] do_execsql_test 9.4.1 { SELECT * FROM t1('"abc xyz" *'); } {} do_test 9.4.2 { set ::flags } {prefixquery} set ::flags [list] do_execsql_test 9.5.1 { SELECT * FROM t1('"abc xyz*"'); } {} do_test 9.5.2 { set ::flags } {query} finish_test |
Changes to ext/fts5/test/fts5unicode.test.
︙ | ︙ | |||
46 47 48 49 50 51 52 | CREATE VIRTUAL TABLE t1 USING fts5(x); CREATE VIRTUAL TABLE t2 USING fts5(x, tokenize = unicode61); CREATE VIRTUAL TABLE t3 USING fts5(x, tokenize = ascii); INSERT INTO t1 VALUES('\xC0\xC8\xCC'); INSERT INTO t2 VALUES('\xC0\xC8\xCC'); INSERT INTO t3 VALUES('\xC0\xC8\xCC'); " | < < | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | CREATE VIRTUAL TABLE t1 USING fts5(x); CREATE VIRTUAL TABLE t2 USING fts5(x, tokenize = unicode61); CREATE VIRTUAL TABLE t3 USING fts5(x, tokenize = ascii); INSERT INTO t1 VALUES('\xC0\xC8\xCC'); INSERT INTO t2 VALUES('\xC0\xC8\xCC'); INSERT INTO t3 VALUES('\xC0\xC8\xCC'); " do_execsql_test 2.1 " SELECT 't1' FROM t1 WHERE t1 MATCH '\xE0\xE8\xEC'; SELECT 't2' FROM t2 WHERE t2 MATCH '\xE0\xE8\xEC'; SELECT 't3' FROM t3 WHERE t3 MATCH '\xE0\xE8\xEC'; " {t1 t2} finish_test |
Changes to ext/fts5/test/fts5unicode2.test.
︙ | ︙ | |||
277 278 279 280 281 282 283 | INSERT INTO t9(a) VALUES('abc%88def %89ghi%90'); } } {0 {}} #------------------------------------------------------------------------- | < | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | INSERT INTO t9(a) VALUES('abc%88def %89ghi%90'); } } {0 {}} #------------------------------------------------------------------------- do_unicode_token_test3 5.1 {tokenchars {}} { sqlite3_reset sqlite3_column_int } { sqlite3 sqlite3 reset reset sqlite3 sqlite3 column column |
︙ | ︙ |
Changes to ext/fts5/test/fts5unicode3.test.
︙ | ︙ | |||
122 123 124 125 126 127 128 | } append str {'");} execsql $str } {} finish_test | < | 122 123 124 125 126 127 128 | } append str {'");} execsql $str } {} finish_test |
Changes to ext/fts5/test/fts5unindexed.test.
︙ | ︙ | |||
72 73 74 75 76 77 78 | INSERT INTO t4(t4, rowid, a, b, c) VALUES('delete', 20, 'j k l', '', 'p q r'); DELETE FROM x4 WHERE rowid=20; INSERT INTO t4(t4) VALUES('integrity-check'); } {} finish_test | < | 72 73 74 75 76 77 78 | INSERT INTO t4(t4, rowid, a, b, c) VALUES('delete', 20, 'j k l', '', 'p q r'); DELETE FROM x4 WHERE rowid=20; INSERT INTO t4(t4) VALUES('integrity-check'); } {} finish_test |
Changes to ext/fts5/test/fts5update.test.
︙ | ︙ | |||
113 114 115 116 117 118 119 | } {} do_execsql_test 2.2.integrity { INSERT INTO x2(x2) VALUES('integrity-check'); } } finish_test | < < | 113 114 115 116 117 118 119 | } {} do_execsql_test 2.2.integrity { INSERT INTO x2(x2) VALUES('integrity-check'); } } finish_test |
Changes to ext/fts5/test/fts5version.test.
︙ | ︙ | |||
57 58 59 60 61 62 63 | db close sqlite3 db test.db catchsql { SELECT * FROM t1 WHERE t1 MATCH 'a' } } {1 {invalid fts5 file format (found 0, expected 4) - run 'rebuild'}} finish_test | < | 57 58 59 60 61 62 63 | db close sqlite3 db test.db catchsql { SELECT * FROM t1 WHERE t1 MATCH 'a' } } {1 {invalid fts5 file format (found 0, expected 4) - run 'rebuild'}} finish_test |
Changes to ext/fts5/test/fts5vocab.test.
︙ | ︙ | |||
206 207 208 209 210 211 212 | INSERT INTO temp.t1 VALUES('1 5 3'); INSERT INTO aux.t1 VALUES('x y z'); INSERT INTO aux.t1 VALUES('m n o'); INSERT INTO aux.t1 VALUES('x n z'); } | < | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | INSERT INTO temp.t1 VALUES('1 5 3'); INSERT INTO aux.t1 VALUES('x y z'); INSERT INTO aux.t1 VALUES('m n o'); INSERT INTO aux.t1 VALUES('x n z'); } do_execsql_test 5.1 { CREATE VIRTUAL TABLE temp.vm USING fts5vocab(main, t1, row); CREATE VIRTUAL TABLE temp.vt1 USING fts5vocab(t1, row); CREATE VIRTUAL TABLE temp.vt2 USING fts5vocab(temp, t1, row); CREATE VIRTUAL TABLE temp.va USING fts5vocab(aux, t1, row); } |
︙ | ︙ |
Added ext/fts5/test/fts5vocab2.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 | # 2017 August 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. # #*********************************************************************** # # The tests in this file focus on testing the fts5vocab module. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5vocab # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return } do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(a, b); CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, instance); INSERT INTO t1 VALUES('one two', 'two three'); INSERT INTO t1 VALUES('three four', 'four five five five'); } do_execsql_test 1.1 { SELECT * FROM v1; } { five 2 b 1 five 2 b 2 five 2 b 3 four 2 a 1 four 2 b 0 one 1 a 0 three 1 b 1 three 2 a 0 two 1 a 1 two 1 b 0 } do_execsql_test 1.2 { SELECT * FROM v1 WHERE term='three'; } { three 1 b 1 three 2 a 0 } do_execsql_test 1.3 { BEGIN; DELETE FROM t1 WHERE rowid=2; SELECT * FROM v1; ROLLBACK; } { one 1 a 0 three 1 b 1 two 1 a 1 two 1 b 0 } do_execsql_test 1.4 { BEGIN; DELETE FROM t1 WHERE rowid=1; SELECT * FROM v1; ROLLBACK; } { five 2 b 1 five 2 b 2 five 2 b 3 four 2 a 1 four 2 b 0 three 2 a 0 } do_execsql_test 1.5 { DELETE FROM t1; SELECT * FROM v1; } { } #------------------------------------------------------------------------- # do_execsql_test 2.0 { DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS v1; CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=column); CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, instance); INSERT INTO t1 VALUES('one two', 'two three'); INSERT INTO t1 VALUES('three four', 'four five five five'); } do_execsql_test 2.1 { SELECT * FROM v1; } { five 2 b {} four 2 a {} four 2 b {} one 1 a {} three 1 b {} three 2 a {} two 1 a {} two 1 b {} } do_execsql_test 2.2 { SELECT * FROM v1 WHERE term='three'; } { three 1 b {} three 2 a {} } do_execsql_test 2.3 { BEGIN; DELETE FROM t1 WHERE rowid=2; SELECT * FROM v1; ROLLBACK; } { one 1 a {} three 1 b {} two 1 a {} two 1 b {} } do_execsql_test 2.4 { BEGIN; DELETE FROM t1 WHERE rowid=1; SELECT * FROM v1; ROLLBACK; } { five 2 b {} four 2 a {} four 2 b {} three 2 a {} } do_execsql_test 2.5 { DELETE FROM t1; SELECT * FROM v1; } { } #------------------------------------------------------------------------- # do_execsql_test 3.0 { DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS v1; CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=none); CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, instance); INSERT INTO t1 VALUES('one two', 'two three'); INSERT INTO t1 VALUES('three four', 'four five five five'); } do_execsql_test 3.1 { SELECT * FROM v1; } { five 2 {} {} four 2 {} {} one 1 {} {} three 1 {} {} three 2 {} {} two 1 {} {} } do_execsql_test 3.2 { SELECT * FROM v1 WHERE term='three'; } { three 1 {} {} three 2 {} {} } do_execsql_test 3.3 { BEGIN; DELETE FROM t1 WHERE rowid=2; SELECT * FROM v1; ROLLBACK; } { one 1 {} {} three 1 {} {} two 1 {} {} } do_execsql_test 3.4 { BEGIN; DELETE FROM t1 WHERE rowid=1; SELECT * FROM v1; ROLLBACK; } { five 2 {} {} four 2 {} {} three 2 {} {} } do_execsql_test 3.5 { DELETE FROM t1; SELECT * FROM v1; } { } finish_test |
Changes to ext/icu/icu.c.
︙ | ︙ | |||
98 99 100 101 102 103 104 | ** false (0) if they are different. */ static int icuLikeCompare( const uint8_t *zPattern, /* LIKE pattern */ const uint8_t *zString, /* The UTF-8 string to compare against */ const UChar32 uEsc /* The escape character */ ){ | | | | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | ** false (0) if they are different. */ static int icuLikeCompare( const uint8_t *zPattern, /* LIKE pattern */ const uint8_t *zString, /* The UTF-8 string to compare against */ const UChar32 uEsc /* The escape character */ ){ static const uint32_t MATCH_ONE = (uint32_t)'_'; static const uint32_t MATCH_ALL = (uint32_t)'%'; int prevEscape = 0; /* True if the previous character was uEsc */ while( 1 ){ /* Read (and consume) the next character from the input pattern. */ uint32_t uPattern; SQLITE_ICU_READ_UTF8(zPattern, uPattern); if( uPattern==0 ) break; /* There are now 4 possibilities: ** ** 1. uPattern is an unescaped match-all character "%", ** 2. uPattern is an unescaped match-one character "_", |
︙ | ︙ | |||
148 149 150 151 152 153 154 | return 0; }else if( !prevEscape && uPattern==MATCH_ONE ){ /* Case 2. */ if( *zString==0 ) return 0; SQLITE_ICU_SKIP_UTF8(zString); | | | | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | return 0; }else if( !prevEscape && uPattern==MATCH_ONE ){ /* Case 2. */ if( *zString==0 ) return 0; SQLITE_ICU_SKIP_UTF8(zString); }else if( !prevEscape && uPattern==(uint32_t)uEsc){ /* Case 3. */ prevEscape = 1; }else{ /* Case 4. */ uint32_t uString; SQLITE_ICU_READ_UTF8(zString, uString); uString = (uint32_t)u_foldCase((UChar32)uString, U_FOLD_CASE_DEFAULT); uPattern = (uint32_t)u_foldCase((UChar32)uPattern, U_FOLD_CASE_DEFAULT); if( uString!=uPattern ){ return 0; } prevEscape = 0; } } |
︙ | ︙ | |||
489 490 491 492 493 494 495 | } } /* ** Register the ICU extension functions with database db. */ int sqlite3IcuInit(sqlite3 *db){ | | | | | | | | | | | < | | | | < | | < < < > | | > > | 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 | } } /* ** Register the ICU extension functions with database db. */ int sqlite3IcuInit(sqlite3 *db){ static const struct IcuScalar { const char *zName; /* Function name */ unsigned char nArg; /* Number of arguments */ unsigned short enc; /* Optimal text encoding */ unsigned char iContext; /* sqlite3_user_data() context */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); } scalars[] = { {"icu_load_collation", 2, SQLITE_UTF8, 1, icuLoadCollation}, {"regexp", 2, SQLITE_ANY|SQLITE_DETERMINISTIC, 0, icuRegexpFunc}, {"lower", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16}, {"lower", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16}, {"upper", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16}, {"upper", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16}, {"lower", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16}, {"lower", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16}, {"upper", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16}, {"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16}, {"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc}, {"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc}, }; int rc = SQLITE_OK; int i; for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){ const struct IcuScalar *p = &scalars[i]; rc = sqlite3_create_function( db, p->zName, p->nArg, p->enc, p->iContext ? (void*)db : (void*)0, p->xFunc, 0, 0 ); } return rc; } #if !SQLITE_CORE |
︙ | ︙ |
Added ext/lsm1/Makefile.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # # This Makefile is designed for use with main.mk in the root directory of # this project. After including main.mk, the users makefile should contain: # # LSMDIR=$(TOP)/ext/lsm1/ # LSMOPTS=-fPIC # include $(LSMDIR)/Makefile # # The most useful targets are [lsmtest] and [lsm.so]. # LSMOBJ = \ lsm_ckpt.o \ lsm_file.o \ lsm_log.o \ lsm_main.o \ lsm_mem.o \ lsm_mutex.o \ lsm_shared.o \ lsm_sorted.o \ lsm_str.o \ lsm_tree.o \ lsm_unix.o \ lsm_win32.o \ lsm_varint.o \ lsm_vtab.o LSMHDR = \ $(LSMDIR)/lsm.h \ $(LSMDIR)/lsmInt.h LSMTESTSRC = $(LSMDIR)/lsm-test/lsmtest1.c $(LSMDIR)/lsm-test/lsmtest2.c \ $(LSMDIR)/lsm-test/lsmtest3.c $(LSMDIR)/lsm-test/lsmtest4.c \ $(LSMDIR)/lsm-test/lsmtest5.c $(LSMDIR)/lsm-test/lsmtest6.c \ $(LSMDIR)/lsm-test/lsmtest7.c $(LSMDIR)/lsm-test/lsmtest8.c \ $(LSMDIR)/lsm-test/lsmtest9.c \ $(LSMDIR)/lsm-test/lsmtest_datasource.c \ $(LSMDIR)/lsm-test/lsmtest_func.c $(LSMDIR)/lsm-test/lsmtest_io.c \ $(LSMDIR)/lsm-test/lsmtest_main.c $(LSMDIR)/lsm-test/lsmtest_mem.c \ $(LSMDIR)/lsm-test/lsmtest_tdb.c $(LSMDIR)/lsm-test/lsmtest_tdb3.c \ $(LSMDIR)/lsm-test/lsmtest_util.c $(LSMDIR)/lsm-test/lsmtest_win32.c # all: lsm.so LSMOPTS += -DLSM_MUTEX_PTHREADS=1 -I$(LSMDIR) lsm.so: $(LSMOBJ) $(TCCX) -shared -o lsm.so $(LSMOBJ) %.o: $(LSMDIR)/%.c $(LSMHDR) sqlite3.h $(TCCX) $(LSMOPTS) -c $< lsmtest$(EXE): $(LSMOBJ) $(LSMTESTSRC) $(LSMTESTHDR) sqlite3.o # $(TCPPX) -c $(TOP)/lsm-test/lsmtest_tdb2.cc $(TCCX) $(LSMOPTS) $(LSMTESTSRC) $(LSMOBJ) sqlite3.o -o lsmtest$(EXE) $(THREADLIB) |
Added ext/lsm1/Makefile.msc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # # This Makefile is designed for use with Makefile.msc in the root directory # of this project. The Makefile.msc should contain: # # LSMDIR=$(TOP)\ext\lsm1 # !INCLUDE $(LSMDIR)\Makefile.msc # # The most useful targets are [lsmtest.exe] and [lsm.dll]. # LSMOBJ = \ lsm_ckpt.lo \ lsm_file.lo \ lsm_log.lo \ lsm_main.lo \ lsm_mem.lo \ lsm_mutex.lo \ lsm_shared.lo \ lsm_sorted.lo \ lsm_str.lo \ lsm_tree.lo \ lsm_unix.lo \ lsm_win32.lo \ lsm_varint.lo \ lsm_vtab.lo LSMHDR = \ $(LSMDIR)\lsm.h \ $(LSMDIR)\lsmInt.h LSMTESTSRC = $(LSMDIR)\lsm-test\lsmtest1.c $(LSMDIR)\lsm-test\lsmtest2.c \ $(LSMDIR)\lsm-test\lsmtest3.c $(LSMDIR)\lsm-test\lsmtest4.c \ $(LSMDIR)\lsm-test\lsmtest5.c $(LSMDIR)\lsm-test\lsmtest6.c \ $(LSMDIR)\lsm-test\lsmtest7.c $(LSMDIR)\lsm-test\lsmtest8.c \ $(LSMDIR)\lsm-test\lsmtest9.c \ $(LSMDIR)\lsm-test\lsmtest_datasource.c \ $(LSMDIR)\lsm-test\lsmtest_func.c $(LSMDIR)\lsm-test\lsmtest_io.c \ $(LSMDIR)\lsm-test\lsmtest_main.c $(LSMDIR)\lsm-test\lsmtest_mem.c \ $(LSMDIR)\lsm-test\lsmtest_tdb.c $(LSMDIR)\lsm-test\lsmtest_tdb3.c \ $(LSMDIR)\lsm-test\lsmtest_util.c $(LSMDIR)\lsm-test\lsmtest_win32.c # all: lsm.dll lsmtest.exe LSMOPTS = $(NO_WARN) -DLSM_MUTEX_WIN32=1 -I$(LSMDIR) !IF $(DEBUG)>2 LSMOPTS = $(LSMOPTS) -DLSM_DEBUG=1 !ENDIF !IF $(MEMDEBUG)!=0 LSMOPTS = $(LSMOPTS) -DLSM_DEBUG_MEM=1 !ENDIF lsm_ckpt.lo: $(LSMDIR)\lsm_ckpt.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_ckpt.c lsm_file.lo: $(LSMDIR)\lsm_file.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_file.c lsm_log.lo: $(LSMDIR)\lsm_log.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_log.c lsm_main.lo: $(LSMDIR)\lsm_main.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_main.c lsm_mem.lo: $(LSMDIR)\lsm_mem.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_mem.c lsm_mutex.lo: $(LSMDIR)\lsm_mutex.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_mutex.c lsm_shared.lo: $(LSMDIR)\lsm_shared.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_shared.c lsm_sorted.lo: $(LSMDIR)\lsm_sorted.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_sorted.c lsm_str.lo: $(LSMDIR)\lsm_str.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_str.c lsm_tree.lo: $(LSMDIR)\lsm_tree.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_tree.c lsm_unix.lo: $(LSMDIR)\lsm_unix.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_unix.c lsm_win32.lo: $(LSMDIR)\lsm_win32.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_win32.c lsm_varint.lo: $(LSMDIR)\lsm_varint.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_varint.c lsm_vtab.lo: $(LSMDIR)\lsm_vtab.c $(LSMHDR) $(SQLITE3H) $(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_vtab.c lsm.dll: $(LSMOBJ) $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ $(LSMOBJ) copy /Y $@ $(LSMDIR)\$@ lsmtest.exe: $(LSMOBJ) $(LSMTESTSRC) $(LSMTESTHDR) $(LIBOBJ) $(LTLINK) $(LSMOPTS) $(LSMTESTSRC) /link $(LSMOBJ) $(LIBOBJ) copy /Y $@ $(LSMDIR)\$@ |
Added ext/lsm1/lsm-test/README.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Organization of test case files: lsmtest1.c: Data tests. Tests that perform many inserts and deletes on a database file, then verify that the contents of the database can be queried. lsmtest2.c: Crash tests. Tests that attempt to verify that the database recovers correctly following an application or system crash. lsmtest3.c: Rollback tests. Tests that focus on the explicit rollback of transactions and sub-transactions. lsmtest4.c: Multi-client tests. lsmtest5.c: Multi-client tests with a different thread for each client. lsmtest6.c: OOM injection tests. lsmtest7.c: API tests. lsmtest8.c: Writer crash tests. Tests in this file attempt to verify that the system recovers and other clients proceed unaffected if a process fails in the middle of a write transaction. The difference from lsmtest2.c is that this file tests live-recovery (recovery from a failure that occurs while other clients are still running) whereas lsmtest2.c tests recovery from a system or power failure. lsmtest9.c: More data tests. These focus on testing that calling lsm_work(nMerge=1) to compact the database does not corrupt it. In other words, that databases containing block-redirects can be read and written. |
Added ext/lsm1/lsm-test/lsmtest.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 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 | #ifndef __WRAPPER_INT_H_ #define __WRAPPER_INT_H_ #include "lsmtest_tdb.h" #include "sqlite3.h" #include "lsm.h" #include <assert.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #ifndef _WIN32 # include <unistd.h> #endif #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <ctype.h> #include <stdlib.h> #include <errno.h> #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 # include "windows.h" # define gettimeofday win32GetTimeOfDay # define F_OK (0) # define sleep(sec) Sleep(1000 * (sec)) # define usleep(usec) Sleep(((usec) + 999) / 1000) # ifdef _MSC_VER # include <io.h> # define snprintf _snprintf # define fsync(fd) FlushFileBuffers((HANDLE)_get_osfhandle((fd))) # define fdatasync(fd) FlushFileBuffers((HANDLE)_get_osfhandle((fd))) # define __va_copy(dst,src) ((dst) = (src)) # define ftruncate(fd,sz) ((_chsize_s((fd), (sz))==0) ? 0 : -1) # else # error Unsupported C compiler for Windows. # endif int win32GetTimeOfDay(struct timeval *, void *); #endif #ifndef _LSM_INT_H typedef unsigned int u32; typedef unsigned char u8; typedef long long int i64; typedef unsigned long long int u64; #endif #define ArraySize(x) ((int)(sizeof(x) / sizeof((x)[0]))) #define MIN(x,y) ((x)<(y) ? (x) : (y)) #define MAX(x,y) ((x)>(y) ? (x) : (y)) #define unused_parameter(x) (void)(x) #define TESTDB_DEFAULT_PAGE_SIZE 4096 #define TESTDB_DEFAULT_CACHE_SIZE 2048 #ifndef _O_BINARY # define _O_BINARY (0) #endif /* ** Ideally, these should be in wrapper.c. But they are here instead so that ** they can be used by the C++ database wrappers in wrapper2.cc. */ typedef struct DatabaseMethods DatabaseMethods; struct TestDb { DatabaseMethods const *pMethods; /* Database methods */ const char *zLibrary; /* Library name for tdb_open() */ }; struct DatabaseMethods { int (*xClose)(TestDb *); int (*xWrite)(TestDb *, void *, int , void *, int); int (*xDelete)(TestDb *, void *, int); int (*xDeleteRange)(TestDb *, void *, int, void *, int); int (*xFetch)(TestDb *, void *, int, void **, int *); int (*xScan)(TestDb *, void *, int, void *, int, void *, int, void (*)(void *, void *, int , void *, int) ); int (*xBegin)(TestDb *, int); int (*xCommit)(TestDb *, int); int (*xRollback)(TestDb *, int); }; /* ** Functions in wrapper2.cc (a C++ source file). wrapper2.cc contains the ** wrapper for Kyoto Cabinet. Kyoto cabinet has a C API, but ** the primary interface is the C++ API. */ int test_kc_open(const char*, const char *zFilename, int bClear, TestDb **ppDb); int test_kc_close(TestDb *); int test_kc_write(TestDb *, void *, int , void *, int); int test_kc_delete(TestDb *, void *, int); int test_kc_delete_range(TestDb *, void *, int, void *, int); int test_kc_fetch(TestDb *, void *, int, void **, int *); int test_kc_scan(TestDb *, void *, int, void *, int, void *, int, void (*)(void *, void *, int , void *, int) ); int test_mdb_open(const char*, const char *zFile, int bClear, TestDb **ppDb); int test_mdb_close(TestDb *); int test_mdb_write(TestDb *, void *, int , void *, int); int test_mdb_delete(TestDb *, void *, int); int test_mdb_fetch(TestDb *, void *, int, void **, int *); int test_mdb_scan(TestDb *, void *, int, void *, int, void *, int, void (*)(void *, void *, int , void *, int) ); /* ** Functions in wrapper3.c. This file contains the tdb wrapper for lsm. ** The wrapper for lsm is a bit more involved than the others, as it ** includes code for a couple of different lsm configurations, and for ** various types of fault injection and robustness testing. */ int test_lsm_open(const char*, const char *zFile, int bClear, TestDb **ppDb); int test_lsm_lomem_open(const char*, const char*, int bClear, TestDb **ppDb); int test_lsm_zip_open(const char*, const char*, int bClear, TestDb **ppDb); int test_lsm_small_open(const char*, const char*, int bClear, TestDb **ppDb); int test_lsm_mt2(const char*, const char *zFile, int bClear, TestDb **ppDb); int test_lsm_mt3(const char*, const char *zFile, int bClear, TestDb **ppDb); int tdb_lsm_configure(lsm_db *, const char *); /* Functions in lsmtest_tdb4.c */ int test_bt_open(const char*, const char *zFile, int bClear, TestDb **ppDb); int test_fbt_open(const char*, const char *zFile, int bClear, TestDb **ppDb); int test_fbts_open(const char*, const char *zFile, int bClear, TestDb **ppDb); /* Functions in testutil.c. */ int testPrngInit(void); u32 testPrngValue(u32 iVal); void testPrngArray(u32 iVal, u32 *aOut, int nOut); void testPrngString(u32 iVal, char *aOut, int nOut); void testErrorInit(int argc, char **); void testPrintError(const char *zFormat, ...); void testPrintUsage(const char *zArgs); void testPrintFUsage(const char *zFormat, ...); void testTimeInit(void); int testTimeGet(void); /* Functions in testmem.c. */ void testMallocInstall(lsm_env *pEnv); void testMallocUninstall(lsm_env *pEnv); void testMallocCheck(lsm_env *pEnv, int *, int *, FILE *); void testMallocOom(lsm_env *pEnv, int, int, void(*)(void*), void *); void testMallocOomEnable(lsm_env *pEnv, int); /* lsmtest.c */ TestDb *testOpen(const char *zSystem, int, int *pRc); void testReopen(TestDb **ppDb, int *pRc); void testClose(TestDb **ppDb); void testFetch(TestDb *, void *, int, void *, int, int *); void testWrite(TestDb *, void *, int, void *, int, int *); void testDelete(TestDb *, void *, int, int *); void testDeleteRange(TestDb *, void *, int, void *, int, int *); void testWriteStr(TestDb *, const char *, const char *zVal, int *pRc); void testFetchStr(TestDb *, const char *, const char *, int *pRc); void testBegin(TestDb *pDb, int iTrans, int *pRc); void testCommit(TestDb *pDb, int iTrans, int *pRc); void test_failed(void); char *testMallocPrintf(const char *zFormat, ...); char *testMallocVPrintf(const char *zFormat, va_list ap); int testGlobMatch(const char *zPattern, const char *zStr); void testScanCompare(TestDb *, TestDb *, int, void *, int, void *, int, int *); void testFetchCompare(TestDb *, TestDb *, void *, int, int *); void *testMalloc(int); void *testMallocCopy(void *pCopy, int nByte); void *testRealloc(void *, int); void testFree(void *); /* lsmtest_bt.c */ int do_bt(int nArg, char **azArg); /* testio.c */ int testVfsConfigureDb(TestDb *pDb); /* testfunc.c */ int do_show(int nArg, char **azArg); int do_work(int nArg, char **azArg); /* testio.c */ int do_io(int nArg, char **azArg); /* lsmtest2.c */ void do_crash_test(const char *zPattern, int *pRc); int do_rollback_test(int nArg, char **azArg); /* test3.c */ void test_rollback(const char *zSystem, const char *zPattern, int *pRc); /* test4.c */ void test_mc(const char *zSystem, const char *zPattern, int *pRc); /* test5.c */ void test_mt(const char *zSystem, const char *zPattern, int *pRc); /* lsmtest6.c */ void test_oom(const char *zPattern, int *pRc); void testDeleteLsmdb(const char *zFile); void testSaveDb(const char *zFile, const char *zAuxExt); void testRestoreDb(const char *zFile, const char *zAuxExt); void testCopyLsmdb(const char *zFrom, const char *zTo); /* lsmtest7.c */ void test_api(const char *zPattern, int *pRc); /* lsmtest8.c */ void do_writer_crash_test(const char *zPattern, int *pRc); /************************************************************************* ** Interface to functionality in test_datasource.c. */ typedef struct Datasource Datasource; typedef struct DatasourceDefn DatasourceDefn; struct DatasourceDefn { int eType; /* A TEST_DATASOURCE_* value */ int nMinKey; /* Minimum key size */ int nMaxKey; /* Maximum key size */ int nMinVal; /* Minimum value size */ int nMaxVal; /* Maximum value size */ }; #define TEST_DATASOURCE_RANDOM 1 #define TEST_DATASOURCE_SEQUENCE 2 char *testDatasourceName(const DatasourceDefn *); Datasource *testDatasourceNew(const DatasourceDefn *); void testDatasourceFree(Datasource *); void testDatasourceEntry(Datasource *, int, void **, int *, void **, int *); /* End of test_datasource.c interface. *************************************************************************/ void testDatasourceFetch( TestDb *pDb, /* Database handle */ Datasource *pData, int iKey, int *pRc /* IN/OUT: Error code */ ); void testWriteDatasource(TestDb *, Datasource *, int, int *); void testWriteDatasourceRange(TestDb *, Datasource *, int, int, int *); void testDeleteDatasource(TestDb *, Datasource *, int, int *); void testDeleteDatasourceRange(TestDb *, Datasource *, int, int, int *); /* test1.c */ void test_data_1(const char *, const char *, int *pRc); void test_data_2(const char *, const char *, int *pRc); void test_data_3(const char *, const char *, int *pRc); void testDbContents(TestDb *, Datasource *, int, int, int, int, int, int *); void testCaseProgress(int, int, int, int *); int testCaseNDot(void); void testCompareDb(Datasource *, int, int, TestDb *, TestDb *, int *); int testControlDb(TestDb **ppDb); typedef struct CksumDb CksumDb; CksumDb *testCksumArrayNew(Datasource *, int, int, int); char *testCksumArrayGet(CksumDb *, int); void testCksumArrayFree(CksumDb *); void testCaseStart(int *pRc, char *zFmt, ...); void testCaseFinish(int rc); void testCaseSkip(void); int testCaseBegin(int *, const char *, const char *, ...); #define TEST_CKSUM_BYTES 29 int testCksumDatabase(TestDb *pDb, char *zOut); int testCountDatabase(TestDb *pDb); void testCompareInt(int, int, int *); void testCompareStr(const char *z1, const char *z2, int *pRc); /* lsmtest9.c */ void test_data_4(const char *, const char *, int *pRc); /* ** Similar to the Tcl_GetIndexFromObjStruct() Tcl library function. */ #define testArgSelect(w,x,y,z) testArgSelectX(w,x,sizeof(w[0]),y,z) int testArgSelectX(void *, const char *, int, const char *, int *); #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif |
Added ext/lsm1/lsm-test/lsmtest1.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 | #include "lsmtest.h" #define DATA_SEQUENTIAL TEST_DATASOURCE_SEQUENCE #define DATA_RANDOM TEST_DATASOURCE_RANDOM typedef struct Datatest1 Datatest1; typedef struct Datatest2 Datatest2; /* ** An instance of the following structure contains parameters used to ** customize the test function in this file. Test procedure: ** ** 1. Create a data-source based on the "datasource definition" vars. ** ** 2. Insert nRow key value pairs into the database. ** ** 3. Delete all keys from the database. Deletes are done in the same ** order as the inserts. ** ** During steps 2 and 3 above, after each Datatest1.nVerify inserts or ** deletes, the following: ** ** a. Run Datasource.nTest key lookups and check the results are as expected. ** ** b. If Datasource.bTestScan is true, run a handful (8) of range ** queries (scanning forwards and backwards). Check that the results ** are as expected. ** ** c. Close and reopen the database. Then run (a) and (b) again. */ struct Datatest1 { /* Datasource definition */ DatasourceDefn defn; /* Test procedure parameters */ int nRow; /* Number of rows to insert then delete */ int nVerify; /* How often to verify the db contents */ int nTest; /* Number of keys to test (0==all) */ int bTestScan; /* True to do scan tests */ }; /* ** An instance of the following data structure is used to describe the ** second type of test case in this file. The chief difference between ** these tests and those described by Datatest1 is that these tests also ** experiment with range-delete operations. Tests proceed as follows: ** ** 1. Open the datasource described by Datatest2.defn. ** ** 2. Open a connection on an empty database. ** ** 3. Do this Datatest2.nIter times: ** ** a) Insert Datatest2.nWrite key-value pairs from the datasource. ** ** b) Select two pseudo-random keys and use them as the start ** and end points of a range-delete operation. ** ** c) Verify that the contents of the database are as expected (see ** below for details). ** ** d) Close and then reopen the database handle. ** ** e) Verify that the contents of the database are still as expected. ** ** The inserts and range deletes are run twice - once on the database being ** tested and once using a control system (sqlite3, kc etc. - something that ** works). In order to verify that the contents of the db being tested are ** correct, the test runs a bunch of scans and lookups on both the test and ** control databases. If the results are the same, the test passes. */ struct Datatest2 { DatasourceDefn defn; int nRange; int nWrite; /* Number of writes per iteration */ int nIter; /* Total number of iterations to run */ }; /* ** Generate a unique name for the test case pTest with database system ** zSystem. */ static char *getName(const char *zSystem, int bRecover, Datatest1 *pTest){ char *zRet; char *zData; zData = testDatasourceName(&pTest->defn); zRet = testMallocPrintf("data.%s.%s.rec=%d.%d.%d", zSystem, zData, bRecover, pTest->nRow, pTest->nVerify ); testFree(zData); return zRet; } int testControlDb(TestDb **ppDb){ #ifdef HAVE_KYOTOCABINET return tdb_open("kyotocabinet", "tmp.db", 1, ppDb); #else return tdb_open("sqlite3", "", 1, ppDb); #endif } void testDatasourceFetch( TestDb *pDb, /* Database handle */ Datasource *pData, int iKey, int *pRc /* IN/OUT: Error code */ ){ void *pKey; int nKey; /* Database key to query for */ void *pVal; int nVal; /* Expected result of query */ testDatasourceEntry(pData, iKey, &pKey, &nKey, &pVal, &nVal); testFetch(pDb, pKey, nKey, pVal, nVal, pRc); } /* ** This function is called to test that the contents of database pDb ** are as expected. In this case, expected is defined as containing ** key-value pairs iFirst through iLast, inclusive, from data source ** pData. In other words, a loop like the following could be used to ** construct a database with identical contents from scratch. ** ** for(i=iFirst; i<=iLast; i++){ ** testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal); ** // insert (pKey, nKey) -> (pVal, nVal) into database ** } ** ** The key domain consists of keys 0 to (nRow-1), inclusive, from ** data source pData. For both scan and lookup tests, keys are selected ** pseudo-randomly from within this set. ** ** This function runs nLookupTest lookup tests and nScanTest scan tests. ** ** A lookup test consists of selecting a key from the domain and querying ** pDb for it. The test fails if the presence of the key and, if present, ** the associated value do not match the expectations defined above. ** ** A scan test involves selecting a key from the domain and running ** the following queries: ** ** 1. Scan all keys equal to or greater than the key, in ascending order. ** 2. Scan all keys equal to or smaller than the key, in descending order. ** ** Additionally, if nLookupTest is greater than zero, the following are ** run once: ** ** 1. Scan all keys in the db, in ascending order. ** 2. Scan all keys in the db, in descending order. ** ** As you would assume, the test fails if the returned values do not match ** expectations. */ void testDbContents( TestDb *pDb, /* Database handle being tested */ Datasource *pData, /* pDb contains data from here */ int nRow, /* Size of key domain */ int iFirst, /* Index of first key from pData in pDb */ int iLast, /* Index of last key from pData in pDb */ int nLookupTest, /* Number of lookup tests to run */ int nScanTest, /* Number of scan tests to run */ int *pRc /* IN/OUT: Error code */ ){ int j; int rc = *pRc; if( rc==0 && nScanTest ){ TestDb *pDb2 = 0; /* Open a control db (i.e. one that we assume works) */ rc = testControlDb(&pDb2); for(j=iFirst; rc==0 && j<=iLast; j++){ void *pKey; int nKey; /* Database key to insert */ void *pVal; int nVal; /* Database value to insert */ testDatasourceEntry(pData, j, &pKey, &nKey, &pVal, &nVal); rc = tdb_write(pDb2, pKey, nKey, pVal, nVal); } if( rc==0 ){ int iKey1; int iKey2; void *pKey1; int nKey1; /* Start key */ void *pKey2; int nKey2; /* Final key */ iKey1 = testPrngValue((iFirst<<8) + (iLast<<16)) % nRow; iKey2 = testPrngValue((iLast<<8) + (iFirst<<16)) % nRow; testDatasourceEntry(pData, iKey1, &pKey2, &nKey1, 0, 0); pKey1 = testMalloc(nKey1+1); memcpy(pKey1, pKey2, nKey1+1); testDatasourceEntry(pData, iKey2, &pKey2, &nKey2, 0, 0); testScanCompare(pDb2, pDb, 0, 0, 0, 0, 0, &rc); testScanCompare(pDb2, pDb, 0, 0, 0, pKey2, nKey2, &rc); testScanCompare(pDb2, pDb, 0, pKey1, nKey1, 0, 0, &rc); testScanCompare(pDb2, pDb, 0, pKey1, nKey1, pKey2, nKey2, &rc); testScanCompare(pDb2, pDb, 1, 0, 0, 0, 0, &rc); testScanCompare(pDb2, pDb, 1, 0, 0, pKey2, nKey2, &rc); testScanCompare(pDb2, pDb, 1, pKey1, nKey1, 0, 0, &rc); testScanCompare(pDb2, pDb, 1, pKey1, nKey1, pKey2, nKey2, &rc); testFree(pKey1); } tdb_close(pDb2); } /* Test some lookups. */ for(j=0; rc==0 && j<nLookupTest; j++){ int iKey; /* Datasource key to test */ void *pKey; int nKey; /* Database key to query for */ void *pVal; int nVal; /* Expected result of query */ if( nLookupTest>=nRow ){ iKey = j; }else{ iKey = testPrngValue(j + (iFirst<<8) + (iLast<<16)) % nRow; } testDatasourceEntry(pData, iKey, &pKey, &nKey, &pVal, &nVal); if( iFirst>iKey || iKey>iLast ){ pVal = 0; nVal = -1; } testFetch(pDb, pKey, nKey, pVal, nVal, &rc); } *pRc = rc; } /* ** This function should be called during long running test cases to output ** the progress dots (...) to stdout. */ void testCaseProgress(int i, int n, int nDot, int *piDot){ int iDot = *piDot; while( iDot < ( ((nDot*2+1) * i) / (n*2) ) ){ printf("."); fflush(stdout); iDot++; } *piDot = iDot; } int testCaseNDot(void){ return 20; } #if 0 static void printScanCb( void *pCtx, void *pKey, int nKey, void *pVal, int nVal ){ printf("%s\n", (char *)pKey); fflush(stdout); } #endif void testReopenRecover(TestDb **ppDb, int *pRc){ if( *pRc==0 ){ const char *zLib = tdb_library_name(*ppDb); const char *zDflt = tdb_default_db(zLib); testCopyLsmdb(zDflt, "bak.db"); testClose(ppDb); testCopyLsmdb("bak.db", zDflt); *pRc = tdb_open(zLib, 0, 0, ppDb); } } static void doDataTest1( const char *zSystem, /* Database system to test */ int bRecover, Datatest1 *p, /* Structure containing test parameters */ int *pRc /* OUT: Error code */ ){ int i; int iDot; int rc = LSM_OK; Datasource *pData; TestDb *pDb; /* Start the test case, open a database and allocate the datasource. */ pDb = testOpen(zSystem, 1, &rc); pData = testDatasourceNew(&p->defn); i = 0; iDot = 0; while( rc==LSM_OK && i<p->nRow ){ /* Insert some data */ testWriteDatasourceRange(pDb, pData, i, p->nVerify, &rc); i += p->nVerify; /* Check that the db content is correct. */ testDbContents(pDb, pData, p->nRow, 0, i-1, p->nTest, p->bTestScan, &rc); if( bRecover ){ testReopenRecover(&pDb, &rc); }else{ testReopen(&pDb, &rc); } /* Check that the db content is still correct. */ testDbContents(pDb, pData, p->nRow, 0, i-1, p->nTest, p->bTestScan, &rc); /* Update the progress dots... */ testCaseProgress(i, p->nRow, testCaseNDot()/2, &iDot); } i = 0; iDot = 0; while( rc==LSM_OK && i<p->nRow ){ /* Delete some entries */ testDeleteDatasourceRange(pDb, pData, i, p->nVerify, &rc); i += p->nVerify; /* Check that the db content is correct. */ testDbContents(pDb, pData, p->nRow, i, p->nRow-1,p->nTest,p->bTestScan,&rc); /* Close and reopen the database. */ if( bRecover ){ testReopenRecover(&pDb, &rc); }else{ testReopen(&pDb, &rc); } /* Check that the db content is still correct. */ testDbContents(pDb, pData, p->nRow, i, p->nRow-1,p->nTest,p->bTestScan,&rc); /* Update the progress dots... */ testCaseProgress(i, p->nRow, testCaseNDot()/2, &iDot); } /* Free the datasource, close the database and finish the test case. */ testDatasourceFree(pData); tdb_close(pDb); testCaseFinish(rc); *pRc = rc; } void test_data_1( const char *zSystem, /* Database system name */ const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ Datatest1 aTest[] = { { {DATA_RANDOM, 500,600, 1000,2000}, 1000, 100, 10, 0}, { {DATA_RANDOM, 20,25, 100,200}, 1000, 250, 1000, 1}, { {DATA_RANDOM, 8,10, 100,200}, 1000, 250, 1000, 1}, { {DATA_RANDOM, 8,10, 10,20}, 1000, 250, 1000, 1}, { {DATA_RANDOM, 8,10, 1000,2000}, 1000, 250, 1000, 1}, { {DATA_RANDOM, 8,100, 10000,20000}, 100, 25, 100, 1}, { {DATA_RANDOM, 80,100, 10,20}, 1000, 250, 1000, 1}, { {DATA_RANDOM, 5000,6000, 10,20}, 100, 25, 100, 1}, { {DATA_SEQUENTIAL, 5,10, 10,20}, 1000, 250, 1000, 1}, { {DATA_SEQUENTIAL, 5,10, 100,200}, 1000, 250, 1000, 1}, { {DATA_SEQUENTIAL, 5,10, 1000,2000}, 1000, 250, 1000, 1}, { {DATA_SEQUENTIAL, 5,100, 10000,20000}, 100, 25, 100, 1}, { {DATA_RANDOM, 10,10, 100,100}, 100000, 1000, 100, 0}, { {DATA_SEQUENTIAL, 10,10, 100,100}, 100000, 1000, 100, 0}, }; int i; int bRecover; for(bRecover=0; bRecover<2; bRecover++){ if( bRecover==1 && memcmp(zSystem, "lsm", 3) ) break; for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){ char *zName = getName(zSystem, bRecover, &aTest[i]); if( testCaseBegin(pRc, zPattern, "%s", zName) ){ doDataTest1(zSystem, bRecover, &aTest[i], pRc); } testFree(zName); } } } void testCompareDb( Datasource *pData, int nData, int iSeed, TestDb *pControl, TestDb *pDb, int *pRc ){ int i; static int nCall = 0; nCall++; testScanCompare(pControl, pDb, 0, 0, 0, 0, 0, pRc); testScanCompare(pControl, pDb, 1, 0, 0, 0, 0, pRc); if( *pRc==0 ){ int iKey1; int iKey2; void *pKey1; int nKey1; /* Start key */ void *pKey2; int nKey2; /* Final key */ iKey1 = testPrngValue(iSeed) % nData; iKey2 = testPrngValue(iSeed+1) % nData; testDatasourceEntry(pData, iKey1, &pKey2, &nKey1, 0, 0); pKey1 = testMalloc(nKey1+1); memcpy(pKey1, pKey2, nKey1+1); testDatasourceEntry(pData, iKey2, &pKey2, &nKey2, 0, 0); testScanCompare(pControl, pDb, 0, 0, 0, pKey2, nKey2, pRc); testScanCompare(pControl, pDb, 0, pKey1, nKey1, 0, 0, pRc); testScanCompare(pControl, pDb, 0, pKey1, nKey1, pKey2, nKey2, pRc); testScanCompare(pControl, pDb, 1, 0, 0, pKey2, nKey2, pRc); testScanCompare(pControl, pDb, 1, pKey1, nKey1, 0, 0, pRc); testScanCompare(pControl, pDb, 1, pKey1, nKey1, pKey2, nKey2, pRc); testFree(pKey1); } for(i=0; i<nData && *pRc==0; i++){ void *pKey; int nKey; testDatasourceEntry(pData, i, &pKey, &nKey, 0, 0); testFetchCompare(pControl, pDb, pKey, nKey, pRc); } } static void doDataTest2( const char *zSystem, /* Database system to test */ int bRecover, Datatest2 *p, /* Structure containing test parameters */ int *pRc /* OUT: Error code */ ){ TestDb *pDb; TestDb *pControl; Datasource *pData; int i; int rc = LSM_OK; int iDot = 0; /* Start the test case, open a database and allocate the datasource. */ pDb = testOpen(zSystem, 1, &rc); pData = testDatasourceNew(&p->defn); rc = testControlDb(&pControl); if( tdb_lsm(pDb) ){ int nBuf = 32 * 1024 * 1024; lsm_config(tdb_lsm(pDb), LSM_CONFIG_AUTOFLUSH, &nBuf); } for(i=0; rc==0 && i<p->nIter; i++){ void *pKey1; int nKey1; void *pKey2; int nKey2; int ii; int nRange = MIN(p->nIter*p->nWrite, p->nRange); for(ii=0; rc==0 && ii<p->nWrite; ii++){ int iKey = (i*p->nWrite + ii) % p->nRange; testWriteDatasource(pControl, pData, iKey, &rc); testWriteDatasource(pDb, pData, iKey, &rc); } testDatasourceEntry(pData, i+1000000, &pKey1, &nKey1, 0, 0); pKey1 = testMallocCopy(pKey1, nKey1); testDatasourceEntry(pData, i+2000000, &pKey2, &nKey2, 0, 0); testDeleteRange(pDb, pKey1, nKey1, pKey2, nKey2, &rc); testDeleteRange(pControl, pKey1, nKey1, pKey2, nKey2, &rc); testFree(pKey1); testCompareDb(pData, nRange, i, pControl, pDb, &rc); if( bRecover ){ testReopenRecover(&pDb, &rc); }else{ testReopen(&pDb, &rc); } testCompareDb(pData, nRange, i, pControl, pDb, &rc); /* Update the progress dots... */ testCaseProgress(i, p->nIter, testCaseNDot(), &iDot); } testClose(&pDb); testClose(&pControl); testDatasourceFree(pData); testCaseFinish(rc); *pRc = rc; } static char *getName2(const char *zSystem, int bRecover, Datatest2 *pTest){ char *zRet; char *zData; zData = testDatasourceName(&pTest->defn); zRet = testMallocPrintf("data2.%s.%s.rec=%d.%d.%d.%d", zSystem, zData, bRecover, pTest->nRange, pTest->nWrite, pTest->nIter ); testFree(zData); return zRet; } void test_data_2( const char *zSystem, /* Database system name */ const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ Datatest2 aTest[] = { /* defn, nRange, nWrite, nIter */ { {DATA_RANDOM, 20,25, 100,200}, 10000, 10, 50 }, { {DATA_RANDOM, 20,25, 100,200}, 10000, 200, 50 }, { {DATA_RANDOM, 20,25, 100,200}, 100, 10, 1000 }, { {DATA_RANDOM, 20,25, 100,200}, 100, 200, 50 }, }; int i; int bRecover; for(bRecover=0; bRecover<2; bRecover++){ if( bRecover==1 && memcmp(zSystem, "lsm", 3) ) break; for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){ char *zName = getName2(zSystem, bRecover, &aTest[i]); if( testCaseBegin(pRc, zPattern, "%s", zName) ){ doDataTest2(zSystem, bRecover, &aTest[i], pRc); } testFree(zName); } } } /************************************************************************* ** Test case data3.* */ typedef struct Datatest3 Datatest3; struct Datatest3 { int nRange; /* Keys are between 1 and this value, incl. */ int nIter; /* Number of iterations */ int nWrite; /* Number of writes per iteration */ int nDelete; /* Number of deletes per iteration */ int nValMin; /* Minimum value size for writes */ int nValMax; /* Maximum value size for writes */ }; void testPutU32(u8 *aBuf, u32 iVal){ aBuf[0] = (iVal >> 24) & 0xFF; aBuf[1] = (iVal >> 16) & 0xFF; aBuf[2] = (iVal >> 8) & 0xFF; aBuf[3] = (iVal >> 0) & 0xFF; } void dt3PutKey(u8 *aBuf, int iKey){ assert( iKey<100000 && iKey>=0 ); sprintf((char *)aBuf, "%.5d", iKey); } static void doDataTest3( const char *zSystem, /* Database system to test */ Datatest3 *p, /* Structure containing test parameters */ int *pRc /* OUT: Error code */ ){ int iDot = 0; int rc = *pRc; TestDb *pDb; u8 *abPresent; /* Array of boolean */ char *aVal; /* Buffer to hold values */ int i; u32 iSeq = 10; /* prng counter */ abPresent = (u8 *)testMalloc(p->nRange+1); aVal = (char *)testMalloc(p->nValMax+1); pDb = testOpen(zSystem, 1, &rc); for(i=0; i<p->nIter && rc==0; i++){ int ii; testCaseProgress(i, p->nIter, testCaseNDot(), &iDot); /* Perform nWrite inserts */ for(ii=0; ii<p->nWrite; ii++){ u8 aKey[6]; u32 iKey; int nVal; iKey = (testPrngValue(iSeq++) % p->nRange) + 1; nVal = (testPrngValue(iSeq++) % (p->nValMax - p->nValMin)) + p->nValMin; testPrngString(testPrngValue(iSeq++), aVal, nVal); dt3PutKey(aKey, iKey); testWrite(pDb, aKey, sizeof(aKey)-1, aVal, nVal, &rc); abPresent[iKey] = 1; } /* Perform nDelete deletes */ for(ii=0; ii<p->nDelete; ii++){ u8 aKey1[6]; u8 aKey2[6]; u32 iKey; iKey = (testPrngValue(iSeq++) % p->nRange) + 1; dt3PutKey(aKey1, iKey-1); dt3PutKey(aKey2, iKey+1); testDeleteRange(pDb, aKey1, sizeof(aKey1)-1, aKey2, sizeof(aKey2)-1, &rc); abPresent[iKey] = 0; } testReopen(&pDb, &rc); for(ii=1; rc==0 && ii<=p->nRange; ii++){ int nDbVal; void *pDbVal; u8 aKey[6]; int dbrc; dt3PutKey(aKey, ii); dbrc = tdb_fetch(pDb, aKey, sizeof(aKey)-1, &pDbVal, &nDbVal); testCompareInt(0, dbrc, &rc); if( abPresent[ii] ){ testCompareInt(1, (nDbVal>0), &rc); }else{ testCompareInt(1, (nDbVal<0), &rc); } } } testClose(&pDb); testCaseFinish(rc); *pRc = rc; } static char *getName3(const char *zSystem, Datatest3 *p){ return testMallocPrintf("data3.%s.%d.%d.%d.%d.(%d..%d)", zSystem, p->nRange, p->nIter, p->nWrite, p->nDelete, p->nValMin, p->nValMax ); } void test_data_3( const char *zSystem, /* Database system name */ const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ Datatest3 aTest[] = { /* nRange, nIter, nWrite, nDelete, nValMin, nValMax */ { 100, 1000, 5, 5, 50, 100 }, { 100, 1000, 2, 2, 5, 10 }, }; int i; for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){ char *zName = getName3(zSystem, &aTest[i]); if( testCaseBegin(pRc, zPattern, "%s", zName) ){ doDataTest3(zSystem, &aTest[i], pRc); } testFree(zName); } } |
Added ext/lsm1/lsm-test/lsmtest2.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 | /* ** This file contains tests related to recovery following application ** and system crashes (power failures) while writing to the database. */ #include "lsmtest.h" /* ** Structure used by testCksumDatabase() to accumulate checksum values in. */ typedef struct Cksum Cksum; struct Cksum { int nRow; int cksum1; int cksum2; }; /* ** tdb_scan() callback used by testCksumDatabase() */ static void scanCksumDb( void *pCtx, void *pKey, int nKey, void *pVal, int nVal ){ Cksum *p = (Cksum *)pCtx; int i; p->nRow++; for(i=0; i<nKey; i++){ p->cksum1 += ((u8 *)pKey)[i]; p->cksum2 += p->cksum1; } for(i=0; i<nVal; i++){ p->cksum1 += ((u8 *)pVal)[i]; p->cksum2 += p->cksum1; } } /* ** tdb_scan() callback used by testCountDatabase() */ static void scanCountDb( void *pCtx, void *pKey, int nKey, void *pVal, int nVal ){ Cksum *p = (Cksum *)pCtx; p->nRow++; unused_parameter(pKey); unused_parameter(nKey); unused_parameter(pVal); unused_parameter(nVal); } /* ** Iterate through the entire contents of database pDb. Write a checksum ** string based on the db contents into buffer zOut before returning. A ** checksum string is at most 29 (TEST_CKSUM_BYTES) bytes in size: ** ** * 32-bit integer (10 bytes) ** * 1 space (1 byte) ** * 32-bit hex (8 bytes) ** * 1 space (1 byte) ** * 32-bit hex (8 bytes) ** * nul-terminator (1 byte) ** ** The number of entries in the database is returned. */ int testCksumDatabase( TestDb *pDb, /* Database handle */ char *zOut /* Buffer to write checksum to */ ){ Cksum cksum; memset(&cksum, 0, sizeof(Cksum)); tdb_scan(pDb, (void *)&cksum, 0, 0, 0, 0, 0, scanCksumDb); sprintf(zOut, "%d %x %x", cksum.nRow, (u32)cksum.cksum1, (u32)cksum.cksum2 ); assert( strlen(zOut)<TEST_CKSUM_BYTES ); return cksum.nRow; } int testCountDatabase(TestDb *pDb){ Cksum cksum; memset(&cksum, 0, sizeof(Cksum)); tdb_scan(pDb, (void *)&cksum, 0, 0, 0, 0, 0, scanCountDb); return cksum.nRow; } /* ** This function is a no-op if *pRc is not 0 when it is called. ** ** Otherwise, the two nul-terminated strings z1 and z1 are compared. If ** they are the same, the function returns without doing anything. Otherwise, ** an error message is printed, *pRc is set to 1 and the test_failed() ** function called. */ void testCompareStr(const char *z1, const char *z2, int *pRc){ if( *pRc==0 ){ if( strcmp(z1, z2) ){ testPrintError("testCompareStr: \"%s\" != \"%s\"\n", z1, z2); *pRc = 1; test_failed(); } } } /* ** This function is a no-op if *pRc is not 0 when it is called. ** ** Otherwise, the two integers i1 and i2 are compared. If they are equal, ** the function returns without doing anything. Otherwise, an error message ** is printed, *pRc is set to 1 and the test_failed() function called. */ void testCompareInt(int i1, int i2, int *pRc){ if( *pRc==0 && i1!=i2 ){ testPrintError("testCompareInt: %d != %d\n", i1, i2); *pRc = 1; test_failed(); } } void testCaseStart(int *pRc, char *zFmt, ...){ va_list ap; va_start(ap, zFmt); vprintf(zFmt, ap); printf(" ..."); va_end(ap); *pRc = 0; fflush(stdout); } /* ** This function is a no-op if *pRc is non-zero when it is called. Zero ** is returned in this case. ** ** Otherwise, the zFmt (a printf style format string) and following arguments ** are used to create a test case name. If zPattern is NULL or a glob pattern ** that matches the test case name, 1 is returned and the test case started. ** Otherwise, zero is returned and the test case does not start. */ int testCaseBegin(int *pRc, const char *zPattern, const char *zFmt, ...){ int res = 0; if( *pRc==0 ){ char *zTest; va_list ap; va_start(ap, zFmt); zTest = testMallocVPrintf(zFmt, ap); va_end(ap); if( zPattern==0 || testGlobMatch(zPattern, zTest) ){ printf("%-50s ...", zTest); res = 1; } testFree(zTest); fflush(stdout); } return res; } void testCaseFinish(int rc){ if( rc==0 ){ printf("Ok\n"); }else{ printf("FAILED\n"); } fflush(stdout); } void testCaseSkip(){ printf("Skipped\n"); } void testSetupSavedLsmdb( const char *zCfg, const char *zFile, Datasource *pData, int nRow, int *pRc ){ if( *pRc==0 ){ int rc; TestDb *pDb; rc = tdb_lsm_open(zCfg, zFile, 1, &pDb); if( rc==0 ){ testWriteDatasourceRange(pDb, pData, 0, nRow, &rc); testClose(&pDb); if( rc==0 ) testSaveDb(zFile, "log"); } *pRc = rc; } } /* ** This function is a no-op if *pRc is non-zero when it is called. ** ** Open the LSM database identified by zFile and compute its checksum ** (a string, as returned by testCksumDatabase()). If the checksum is ** identical to zExpect1 or, if it is not NULL, zExpect2, the test passes. ** Otherwise, print an error message and set *pRc to 1. */ static void testCompareCksumLsmdb( const char *zFile, /* Path to LSM database */ int bCompress, /* True if db is compressed */ const char *zExpect1, /* Expected checksum 1 */ const char *zExpect2, /* Expected checksum 2 (or NULL) */ int *pRc /* IN/OUT: Test case error code */ ){ if( *pRc==0 ){ char zCksum[TEST_CKSUM_BYTES]; TestDb *pDb; *pRc = tdb_lsm_open((bCompress?"compression=1 mmap=0":""), zFile, 0, &pDb); testCksumDatabase(pDb, zCksum); testClose(&pDb); if( *pRc==0 ){ int r1 = 0; int r2 = -1; r1 = strcmp(zCksum, zExpect1); if( zExpect2 ) r2 = strcmp(zCksum, zExpect2); if( r1 && r2 ){ if( zExpect2 ){ testPrintError("testCompareCksumLsmdb: \"%s\" != (\"%s\" OR \"%s\")", zCksum, zExpect1, zExpect2 ); }else{ testPrintError("testCompareCksumLsmdb: \"%s\" != \"%s\"", zCksum, zExpect1 ); } *pRc = 1; test_failed(); } } } } #if 0 /* not used */ static void testCompareCksumBtdb( const char *zFile, /* Path to LSM database */ const char *zExpect1, /* Expected checksum 1 */ const char *zExpect2, /* Expected checksum 2 (or NULL) */ int *pRc /* IN/OUT: Test case error code */ ){ if( *pRc==0 ){ char zCksum[TEST_CKSUM_BYTES]; TestDb *pDb; *pRc = tdb_open("bt", zFile, 0, &pDb); testCksumDatabase(pDb, zCksum); testClose(&pDb); if( *pRc==0 ){ int r1 = 0; int r2 = -1; r1 = strcmp(zCksum, zExpect1); if( zExpect2 ) r2 = strcmp(zCksum, zExpect2); if( r1 && r2 ){ if( zExpect2 ){ testPrintError("testCompareCksumLsmdb: \"%s\" != (\"%s\" OR \"%s\")", zCksum, zExpect1, zExpect2 ); }else{ testPrintError("testCompareCksumLsmdb: \"%s\" != \"%s\"", zCksum, zExpect1 ); } *pRc = 1; test_failed(); } } } } #endif /* not used */ /* Above this point are reusable test routines. Not clear that they ** should really be in this file. *************************************************************************/ /* ** This test verifies that if a system crash occurs while doing merge work ** on the db, no data is lost. */ static void crash_test1(int bCompress, int *pRc){ const char *DBNAME = "testdb.lsm"; const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 200, 200}; const int nRow = 5000; /* Database size */ const int nIter = 200; /* Number of test iterations */ const int nWork = 20; /* Maximum lsm_work() calls per iteration */ const int nPage = 15; /* Pages per lsm_work call */ int i; int iDot = 0; Datasource *pData; CksumDb *pCksumDb; TestDb *pDb; char *zCfg; const char *azConfig[2] = { "page_size=1024 block_size=65536 autoflush=16384 safety=2 mmap=0", "page_size=1024 block_size=65536 autoflush=16384 safety=2 " " compression=1 mmap=0" }; assert( bCompress==0 || bCompress==1 ); /* Allocate datasource. And calculate the expected checksums. */ pData = testDatasourceNew(&defn); pCksumDb = testCksumArrayNew(pData, nRow, nRow, 1); /* Setup and save the initial database. */ zCfg = testMallocPrintf("%s automerge=7", azConfig[bCompress]); testSetupSavedLsmdb(zCfg, DBNAME, pData, 5000, pRc); testFree(zCfg); for(i=0; i<nIter && *pRc==0; i++){ int iWork; int testrc = 0; testCaseProgress(i, nIter, testCaseNDot(), &iDot); /* Restore and open the database. */ testRestoreDb(DBNAME, "log"); testrc = tdb_lsm_open(azConfig[bCompress], DBNAME, 0, &pDb); assert( testrc==0 ); /* Call lsm_work() on the db */ tdb_lsm_prepare_sync_crash(pDb, 1 + (i%(nWork*2))); for(iWork=0; testrc==0 && iWork<nWork; iWork++){ int nWrite = 0; lsm_db *db = tdb_lsm(pDb); testrc = lsm_work(db, 0, nPage, &nWrite); /* assert( testrc!=0 || nWrite>0 ); */ if( testrc==0 ) testrc = lsm_checkpoint(db, 0); } tdb_close(pDb); /* Check that the database content is still correct */ testCompareCksumLsmdb(DBNAME, bCompress, testCksumArrayGet(pCksumDb, nRow), 0, pRc); } testCksumArrayFree(pCksumDb); testDatasourceFree(pData); } /* ** This test verifies that if a system crash occurs while committing a ** transaction to the log file, no earlier transactions are lost or damaged. */ static void crash_test2(int bCompress, int *pRc){ const char *DBNAME = "testdb.lsm"; const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 1000, 1000}; const int nIter = 200; const int nInsert = 20; int i; int iDot = 0; Datasource *pData; CksumDb *pCksumDb; TestDb *pDb; /* Allocate datasource. And calculate the expected checksums. */ pData = testDatasourceNew(&defn); pCksumDb = testCksumArrayNew(pData, 100, 100+nInsert, 1); /* Setup and save the initial database. */ testSetupSavedLsmdb("", DBNAME, pData, 100, pRc); for(i=0; i<nIter && *pRc==0; i++){ int iIns; int testrc = 0; testCaseProgress(i, nIter, testCaseNDot(), &iDot); /* Restore and open the database. */ testRestoreDb(DBNAME, "log"); testrc = tdb_lsm_open("safety=2", DBNAME, 0, &pDb); assert( testrc==0 ); /* Insert nInsert records into the database. Crash midway through. */ tdb_lsm_prepare_sync_crash(pDb, 1 + (i%(nInsert+2))); for(iIns=0; iIns<nInsert; iIns++){ void *pKey; int nKey; void *pVal; int nVal; testDatasourceEntry(pData, 100+iIns, &pKey, &nKey, &pVal, &nVal); testrc = tdb_write(pDb, pKey, nKey, pVal, nVal); if( testrc ) break; } tdb_close(pDb); /* Check that no data was lost when the system crashed. */ testCompareCksumLsmdb(DBNAME, bCompress, testCksumArrayGet(pCksumDb, 100 + iIns), testCksumArrayGet(pCksumDb, 100 + iIns + 1), pRc ); } testDatasourceFree(pData); testCksumArrayFree(pCksumDb); } /* ** This test verifies that if a system crash occurs when checkpointing ** the database, data is not lost (assuming that any writes not synced ** to the db have been synced into the log file). */ static void crash_test3(int bCompress, int *pRc){ const char *DBNAME = "testdb.lsm"; const int nIter = 100; const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 1000, 1000}; int i; int iDot = 0; Datasource *pData; CksumDb *pCksumDb; TestDb *pDb; /* Allocate datasource. And calculate the expected checksums. */ pData = testDatasourceNew(&defn); pCksumDb = testCksumArrayNew(pData, 110, 150, 10); /* Setup and save the initial database. */ testSetupSavedLsmdb("", DBNAME, pData, 100, pRc); for(i=0; i<nIter && *pRc==0; i++){ int iOpen; testCaseProgress(i, nIter, testCaseNDot(), &iDot); testRestoreDb(DBNAME, "log"); for(iOpen=0; iOpen<5; iOpen++){ /* Open the database. Insert 10 more records. */ pDb = testOpen("lsm", 0, pRc); testWriteDatasourceRange(pDb, pData, 100+iOpen*10, 10, pRc); /* Schedule a crash simulation then close the db. */ tdb_lsm_prepare_sync_crash(pDb, 1 + (i%2)); tdb_close(pDb); /* Open the database and check that the crash did not cause any ** data loss. */ testCompareCksumLsmdb(DBNAME, bCompress, testCksumArrayGet(pCksumDb, 110 + iOpen*10), 0, pRc ); } } testDatasourceFree(pData); testCksumArrayFree(pCksumDb); } void do_crash_test(const char *zPattern, int *pRc){ struct Test { const char *zTest; void (*x)(int, int *); int bCompress; } aTest [] = { { "crash.lsm.1", crash_test1, 0 }, #ifdef HAVE_ZLIB { "crash.lsm_zip.1", crash_test1, 1 }, #endif { "crash.lsm.2", crash_test2, 0 }, { "crash.lsm.3", crash_test3, 0 }, }; int i; for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){ struct Test *p = &aTest[i]; if( testCaseBegin(pRc, zPattern, "%s", p->zTest) ){ p->x(p->bCompress, pRc); testCaseFinish(*pRc); } } } |
Added ext/lsm1/lsm-test/lsmtest3.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 | /* ** This file contains tests related to the explicit rollback of database ** transactions and sub-transactions. */ /* ** Repeat 2000 times (until the db contains 100,000 entries): ** ** 1. Open a transaction and insert 500 rows, opening a nested ** sub-transaction each 100 rows. ** ** 2. Roll back to each sub-transaction savepoint. Check the database ** checksum looks Ok. ** ** 3. Every second iteration, roll back the main transaction. Check the ** db checksum is correct. Every other iteration, commit the main ** transaction (increasing the size of the db by 100 rows). */ #include "lsmtest.h" struct CksumDb { int nFirst; int nLast; int nStep; char **azCksum; }; CksumDb *testCksumArrayNew( Datasource *pData, int nFirst, int nLast, int nStep ){ TestDb *pDb; CksumDb *pRet; int i; int nEntry; int rc = 0; assert( nLast>=nFirst && ((nLast-nFirst)%nStep)==0 ); pRet = malloc(sizeof(CksumDb)); memset(pRet, 0, sizeof(CksumDb)); pRet->nFirst = nFirst; pRet->nLast = nLast; pRet->nStep = nStep; nEntry = 1 + ((nLast - nFirst) / nStep); /* Allocate space so that azCksum is an array of nEntry pointers to ** buffers each TEST_CKSUM_BYTES in size. */ pRet->azCksum = (char **)malloc(nEntry * (sizeof(char *) + TEST_CKSUM_BYTES)); for(i=0; i<nEntry; i++){ char *pStart = (char *)(&pRet->azCksum[nEntry]); pRet->azCksum[i] = &pStart[i * TEST_CKSUM_BYTES]; } tdb_open("lsm", "tempdb.lsm", 1, &pDb); testWriteDatasourceRange(pDb, pData, 0, nFirst, &rc); for(i=0; i<nEntry; i++){ testCksumDatabase(pDb, pRet->azCksum[i]); if( i==nEntry ) break; testWriteDatasourceRange(pDb, pData, nFirst+i*nStep, nStep, &rc); } tdb_close(pDb); return pRet; } char *testCksumArrayGet(CksumDb *p, int nRow){ int i; assert( nRow>=p->nFirst ); assert( nRow<=p->nLast ); assert( ((nRow-p->nFirst) % p->nStep)==0 ); i = (nRow - p->nFirst) / p->nStep; return p->azCksum[i]; } void testCksumArrayFree(CksumDb *p){ free(p->azCksum); memset(p, 0x55, sizeof(*p)); free(p); } /* End of CksumDb code. **************************************************************************/ /* ** Test utility function. Write key-value pair $i from datasource pData ** into database pDb. */ void testWriteDatasource(TestDb *pDb, Datasource *pData, int i, int *pRc){ void *pKey; int nKey; void *pVal; int nVal; testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal); testWrite(pDb, pKey, nKey, pVal, nVal, pRc); } /* ** Test utility function. Delete datasource pData key $i from database pDb. */ void testDeleteDatasource(TestDb *pDb, Datasource *pData, int i, int *pRc){ void *pKey; int nKey; testDatasourceEntry(pData, i, &pKey, &nKey, 0, 0); testDelete(pDb, pKey, nKey, pRc); } /* ** This function inserts nWrite key/value pairs into database pDb - the ** nWrite key value pairs starting at iFirst from data source pData. */ void testWriteDatasourceRange( TestDb *pDb, /* Database to write to */ Datasource *pData, /* Data source to read values from */ int iFirst, /* Index of first key/value pair */ int nWrite, /* Number of key/value pairs to write */ int *pRc /* IN/OUT: Error code */ ){ int i; for(i=0; i<nWrite; i++){ testWriteDatasource(pDb, pData, iFirst+i, pRc); } } void testDeleteDatasourceRange( TestDb *pDb, /* Database to write to */ Datasource *pData, /* Data source to read keys from */ int iFirst, /* Index of first key */ int nWrite, /* Number of keys to delete */ int *pRc /* IN/OUT: Error code */ ){ int i; for(i=0; i<nWrite; i++){ testDeleteDatasource(pDb, pData, iFirst+i, pRc); } } static char *getName(const char *zSystem){ char *zRet; zRet = testMallocPrintf("rollback.%s", zSystem); return zRet; } static int rollback_test_1( const char *zSystem, Datasource *pData ){ const int nRepeat = 100; TestDb *pDb; int rc; int i; CksumDb *pCksum; char *zName; zName = getName(zSystem); testCaseStart(&rc, zName); testFree(zName); pCksum = testCksumArrayNew(pData, 0, nRepeat*100, 100); pDb = 0; rc = tdb_open(zSystem, 0, 1, &pDb); if( pDb && tdb_transaction_support(pDb)==0 ){ testCaseSkip(); goto skip_rollback_test; } for(i=0; i<nRepeat && rc==0; i++){ char zCksum[TEST_CKSUM_BYTES]; int nCurrent = (((i+1)/2) * 100); int nDbRow; int iTrans; /* Check that the database is the expected size. */ nDbRow = testCountDatabase(pDb); testCompareInt(nCurrent, nDbRow, &rc); for(iTrans=2; iTrans<=6 && rc==0; iTrans++){ tdb_begin(pDb, iTrans); testWriteDatasourceRange(pDb, pData, nCurrent, 100, &rc); nCurrent += 100; } testCksumDatabase(pDb, zCksum); testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc); for(iTrans=6; iTrans>2 && rc==0; iTrans--){ tdb_rollback(pDb, iTrans); nCurrent -= 100; testCksumDatabase(pDb, zCksum); testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc); } if( i%2 ){ tdb_rollback(pDb, 0); nCurrent -= 100; testCksumDatabase(pDb, zCksum); testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc); }else{ tdb_commit(pDb, 0); } } testCaseFinish(rc); skip_rollback_test: tdb_close(pDb); testCksumArrayFree(pCksum); return rc; } void test_rollback( const char *zSystem, const char *zPattern, int *pRc ){ if( *pRc==0 ){ int bRun = 1; if( zPattern ){ char *zName = getName(zSystem); bRun = testGlobMatch(zPattern, zName); testFree(zName); } if( bRun ){ DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 10, 15, 50, 100 }; Datasource *pData = testDatasourceNew(&defn); *pRc = rollback_test_1(zSystem, pData); testDatasourceFree(pData); } } } |
Added ext/lsm1/lsm-test/lsmtest4.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 | /* ** This file contains test cases involving multiple database clients. */ #include "lsmtest.h" /* ** The following code implements test cases "mc1.*". ** ** This test case uses one writer and $nReader readers. All connections ** are driven by a single thread. All connections are opened at the start ** of the test and remain open until the test is finished. ** ** The test consists of $nStep steps. Each step the following is performed: ** ** 1. The writer inserts $nWriteStep records into the db. ** ** 2. The writer checks that the contents of the db are as expected. ** ** 3. Each reader that currently has an open read transaction also checks ** that the contents of the db are as expected (according to the snapshot ** the read transaction is reading - see below). ** ** After step 1, reader 1 opens a read transaction. After step 2, reader ** 2 opens a read transaction, and so on. At step ($nReader+1), reader 1 ** closes the current read transaction and opens a new one. And so on. ** The result is that at step N (for N > $nReader), there exists a reader ** with an open read transaction reading the snapshot committed following ** steps (N-$nReader-1) to N. */ typedef struct Mctest Mctest; struct Mctest { DatasourceDefn defn; /* Datasource to use */ int nStep; /* Total number of steps in test */ int nWriteStep; /* Number of rows to insert each step */ int nReader; /* Number of read connections */ }; static void do_mc_test( const char *zSystem, /* Database system to test */ Mctest *pTest, int *pRc /* IN/OUT: return code */ ){ const int nDomain = pTest->nStep * pTest->nWriteStep; Datasource *pData; /* Source of data */ TestDb *pDb; /* First database connection (writer) */ int iReader; /* Used to iterate through aReader */ int iStep; /* Current step in test */ int iDot = 0; /* Current step in test */ /* Array of reader connections */ struct Reader { TestDb *pDb; /* Connection handle */ int iLast; /* Current snapshot contains keys 0..iLast */ } *aReader; /* Create a data source */ pData = testDatasourceNew(&pTest->defn); /* Open the writer connection */ pDb = testOpen(zSystem, 1, pRc); /* Allocate aReader */ aReader = (struct Reader *)testMalloc(sizeof(aReader[0]) * pTest->nReader); for(iReader=0; iReader<pTest->nReader; iReader++){ aReader[iReader].pDb = testOpen(zSystem, 0, pRc); } for(iStep=0; iStep<pTest->nStep; iStep++){ int iLast; int iBegin; /* Start read trans using aReader[iBegin] */ /* Insert nWriteStep more records into the database */ int iFirst = iStep*pTest->nWriteStep; testWriteDatasourceRange(pDb, pData, iFirst, pTest->nWriteStep, pRc); /* Check that the db is Ok according to the writer */ iLast = (iStep+1) * pTest->nWriteStep - 1; testDbContents(pDb, pData, nDomain, 0, iLast, iLast, 1, pRc); /* Have reader (iStep % nReader) open a read transaction here. */ iBegin = (iStep % pTest->nReader); if( iBegin<iStep ) tdb_commit(aReader[iBegin].pDb, 0); tdb_begin(aReader[iBegin].pDb, 1); aReader[iBegin].iLast = iLast; /* Check that the db is Ok for each open reader */ for(iReader=0; iReader<pTest->nReader && aReader[iReader].iLast; iReader++){ iLast = aReader[iReader].iLast; testDbContents( aReader[iReader].pDb, pData, nDomain, 0, iLast, iLast, 1, pRc ); } /* Report progress */ testCaseProgress(iStep, pTest->nStep, testCaseNDot(), &iDot); } /* Close all readers */ for(iReader=0; iReader<pTest->nReader; iReader++){ testClose(&aReader[iReader].pDb); } testFree(aReader); /* Close the writer-connection and free the datasource */ testClose(&pDb); testDatasourceFree(pData); } void test_mc( const char *zSystem, /* Database system name */ const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ int i; Mctest aTest[] = { { { TEST_DATASOURCE_RANDOM, 10,10, 100,100 }, 100, 10, 5 }, }; for(i=0; i<ArraySize(aTest); i++){ if( testCaseBegin(pRc, zPattern, "mc1.%s.%d", zSystem, i) ){ do_mc_test(zSystem, &aTest[i], pRc); testCaseFinish(*pRc); } } } |
Added ext/lsm1/lsm-test/lsmtest5.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 | /* ** This file is broken into three semi-autonomous parts: ** ** 1. The database functions. ** 2. The thread wrappers. ** 3. The implementation of the mt1.* tests. */ /************************************************************************* ** DATABASE CONTENTS: ** ** The database contains up to N key/value pairs, where N is some large ** number (say 10,000,000). Keys are integer values between 0 and (N-1). ** The value associated with each key is a pseudo-random blob of data. ** ** Key/value pair keys are encoded as the two bytes "k." followed by a ** 10-digit decimal number. i.e. key 45 -> "k.0000000045". ** ** As well as the key/value pairs, the database also contains checksum ** entries. The checksums form a hierarchy - for every F key/value ** entries there is one level 1 checksum. And for each F level 1 checksums ** there is one level 2 checksum. And so on. ** ** Checksum keys are encoded as the two byte "c." followed by the ** checksum level, followed by a 10 digit decimal number containing ** the value of the first key that contributes to the checksum value. ** For example, assuming F==10, the level 1 checksum that spans keys ** 10 to 19 is "c.1.0000000010". ** ** Clients may perform one of two operations on the database: a read ** or a write. ** ** READ OPERATIONS: ** ** A read operation scans a range of F key/value pairs. It computes ** the expected checksum and then compares the computed value to the ** actual value stored in the level 1 checksum entry. It then scans ** the group of F level 1 checksums, and compares the computed checksum ** to the associated level 2 checksum value, and so on until the ** highest level checksum value has been verified. ** ** If a checksum ever fails to match the expected value, the test ** has failed. ** ** WRITE OPERATIONS: ** ** A write operation involves writing (possibly clobbering) a single ** key/value pair. The associated level 1 checksum is then recalculated ** updated. Then the level 2 checksum, and so on until the highest ** level checksum has been modified. ** ** All updates occur inside a single transaction. ** ** INTERFACE: ** ** The interface used by test cases to read and write the db consists ** of type DbParameters and the following functions: ** ** dbReadOperation() ** dbWriteOperation() */ #include "lsmtest.h" typedef struct DbParameters DbParameters; struct DbParameters { int nFanout; /* Checksum fanout (F) */ int nKey; /* Size of key space (N) */ }; #define DB_KEY_BYTES (2+5+10+1) /* ** Argument aBuf[] must point to a buffer at least DB_KEY_BYTES in size. ** This function populates the buffer with a nul-terminated key string ** corresponding to key iKey. */ static void dbFormatKey( DbParameters *pParam, int iLevel, int iKey, /* Key value */ char *aBuf /* Write key string here */ ){ if( iLevel==0 ){ snprintf(aBuf, DB_KEY_BYTES, "k.%.10d", iKey); }else{ int f = 1; int i; for(i=0; i<iLevel; i++) f = f * pParam->nFanout; snprintf(aBuf, DB_KEY_BYTES, "c.%d.%.10d", iLevel, f*(iKey/f)); } } /* ** Argument aBuf[] must point to a buffer at least DB_KEY_BYTES in size. ** This function populates the buffer with the string representation of ** checksum value iVal. */ static void dbFormatCksumValue(u32 iVal, char *aBuf){ snprintf(aBuf, DB_KEY_BYTES, "%.10u", iVal); } /* ** Return the highest level of checksum in the database described ** by *pParam. */ static int dbMaxLevel(DbParameters *pParam){ int iMax; int n = 1; for(iMax=0; n<pParam->nKey; iMax++){ n = n * pParam->nFanout; } return iMax; } static void dbCksum( void *pCtx, /* IN/OUT: Pointer to u32 containing cksum */ void *pKey, int nKey, /* Database key. Unused. */ void *pVal, int nVal /* Database value. Checksum this. */ ){ u8 *aVal = (u8 *)pVal; u32 *pCksum = (u32 *)pCtx; u32 cksum = *pCksum; int i; unused_parameter(pKey); unused_parameter(nKey); for(i=0; i<nVal; i++){ cksum += (cksum<<3) + (int)aVal[i]; } *pCksum = cksum; } /* ** Compute the value of the checksum stored on level iLevel that contains ** data from key iKey by scanning the pParam->nFanout entries at level ** iLevel-1. */ static u32 dbComputeCksum( DbParameters *pParam, /* Database parameters */ TestDb *pDb, /* Database connection handle */ int iLevel, /* Level of checksum to compute */ int iKey, /* Compute checksum for this key */ int *pRc /* IN/OUT: Error code */ ){ u32 cksum = 0; if( *pRc==0 ){ int nFirst; int nLast; int iFirst = 0; int iLast = 0; int i; int f = 1; char zFirst[DB_KEY_BYTES]; char zLast[DB_KEY_BYTES]; assert( iLevel>=1 ); for(i=0; i<iLevel; i++) f = f * pParam->nFanout; iFirst = f*(iKey/f); iLast = iFirst + f - 1; dbFormatKey(pParam, iLevel-1, iFirst, zFirst); dbFormatKey(pParam, iLevel-1, iLast, zLast); nFirst = strlen(zFirst); nLast = strlen(zLast); *pRc = tdb_scan(pDb, (u32*)&cksum, 0, zFirst, nFirst, zLast, nLast,dbCksum); } return cksum; } static void dbReadOperation( DbParameters *pParam, /* Database parameters */ TestDb *pDb, /* Database connection handle */ void (*xDelay)(void *), void *pDelayCtx, int iKey, /* Key to read */ int *pRc /* IN/OUT: Error code */ ){ const int iMax = dbMaxLevel(pParam); int i; if( tdb_transaction_support(pDb) ) testBegin(pDb, 1, pRc); for(i=1; *pRc==0 && i<=iMax; i++){ char zCksum[DB_KEY_BYTES]; char zKey[DB_KEY_BYTES]; u32 iCksum = 0; iCksum = dbComputeCksum(pParam, pDb, i, iKey, pRc); if( iCksum ){ if( xDelay && i==1 ) xDelay(pDelayCtx); dbFormatCksumValue(iCksum, zCksum); dbFormatKey(pParam, i, iKey, zKey); testFetchStr(pDb, zKey, zCksum, pRc); } } if( tdb_transaction_support(pDb) ) testCommit(pDb, 0, pRc); } static int dbWriteOperation( DbParameters *pParam, /* Database parameters */ TestDb *pDb, /* Database connection handle */ int iKey, /* Key to write to */ const char *zValue, /* Nul-terminated value to write */ int *pRc /* IN/OUT: Error code */ ){ const int iMax = dbMaxLevel(pParam); char zKey[DB_KEY_BYTES]; int i; int rc; assert( iKey>=0 && iKey<pParam->nKey ); dbFormatKey(pParam, 0, iKey, zKey); /* Open a write transaction. This may fail - SQLITE4_BUSY */ if( *pRc==0 && tdb_transaction_support(pDb) ){ rc = tdb_begin(pDb, 2); if( rc==5 ) return 0; *pRc = rc; } testWriteStr(pDb, zKey, zValue, pRc); for(i=1; i<=iMax; i++){ char zCksum[DB_KEY_BYTES]; u32 iCksum = 0; iCksum = dbComputeCksum(pParam, pDb, i, iKey, pRc); dbFormatCksumValue(iCksum, zCksum); dbFormatKey(pParam, i, iKey, zKey); testWriteStr(pDb, zKey, zCksum, pRc); } if( tdb_transaction_support(pDb) ) testCommit(pDb, 0, pRc); return 1; } /************************************************************************* ** The following block contains testXXX() functions that implement a ** wrapper around the systems native multi-thread support. There are no ** synchronization primitives - just functions to launch and join ** threads. Wrapper functions are: ** ** testThreadSupport() ** ** testThreadInit() ** testThreadShutdown() ** testThreadLaunch() ** testThreadWait() ** ** testThreadSetHalt() ** testThreadGetHalt() ** testThreadSetResult() ** testThreadGetResult() ** ** testThreadEnterMutex() ** testThreadLeaveMutex() */ typedef struct ThreadSet ThreadSet; #ifdef LSM_MUTEX_PTHREADS #include <pthread.h> #include <unistd.h> typedef struct Thread Thread; struct Thread { int rc; char *zMsg; pthread_t id; void (*xMain)(ThreadSet *, int, void *); void *pCtx; ThreadSet *pThreadSet; }; struct ThreadSet { int bHalt; /* Halt flag */ int nThread; /* Number of threads */ Thread *aThread; /* Array of Thread structures */ pthread_mutex_t mutex; /* Mutex used for cheating */ }; /* ** Return true if this build supports threads, or false otherwise. If ** this function returns false, no other testThreadXXX() functions should ** be called. */ static int testThreadSupport(){ return 1; } /* ** Allocate and return a thread-set handle with enough space allocated ** to handle up to nMax threads. Each call to this function should be ** matched by a call to testThreadShutdown() to delete the object. */ static ThreadSet *testThreadInit(int nMax){ int nByte; /* Total space to allocate */ ThreadSet *p; /* Return value */ nByte = sizeof(ThreadSet) + sizeof(struct Thread) * nMax; p = (ThreadSet *)testMalloc(nByte); p->nThread = nMax; p->aThread = (Thread *)&p[1]; pthread_mutex_init(&p->mutex, 0); return p; } /* ** Delete a thread-set object and release all resources held by it. */ static void testThreadShutdown(ThreadSet *p){ int i; for(i=0; i<p->nThread; i++){ testFree(p->aThread[i].zMsg); } pthread_mutex_destroy(&p->mutex); testFree(p); } static void *ttMain(void *pArg){ Thread *pThread = (Thread *)pArg; int iThread; iThread = (pThread - pThread->pThreadSet->aThread); pThread->xMain(pThread->pThreadSet, iThread, pThread->pCtx); return 0; } /* ** Launch a new thread. */ static int testThreadLaunch( ThreadSet *p, int iThread, void (*xMain)(ThreadSet *, int, void *), void *pCtx ){ int rc; Thread *pThread; assert( iThread>=0 && iThread<p->nThread ); pThread = &p->aThread[iThread]; assert( pThread->pThreadSet==0 ); pThread->xMain = xMain; pThread->pCtx = pCtx; pThread->pThreadSet = p; rc = pthread_create(&pThread->id, 0, ttMain, (void *)pThread); return rc; } /* ** Set the thread-set "halt" flag. */ static void testThreadSetHalt(ThreadSet *pThreadSet){ pThreadSet->bHalt = 1; } /* ** Return the current value of the thread-set "halt" flag. */ static int testThreadGetHalt(ThreadSet *pThreadSet){ return pThreadSet->bHalt; } static void testThreadSleep(ThreadSet *pThreadSet, int nMs){ int nRem = nMs; while( nRem>0 && testThreadGetHalt(pThreadSet)==0 ){ usleep(50000); nRem -= 50; } } /* ** Wait for all threads launched to finish before returning. If nMs ** is greater than zero, set the "halt" flag to tell all threads ** to halt after waiting nMs milliseconds. */ static void testThreadWait(ThreadSet *pThreadSet, int nMs){ int i; testThreadSleep(pThreadSet, nMs); testThreadSetHalt(pThreadSet); for(i=0; i<pThreadSet->nThread; i++){ Thread *pThread = &pThreadSet->aThread[i]; if( pThread->xMain ){ pthread_join(pThread->id, 0); } } } /* ** Set the result for thread iThread. */ static void testThreadSetResult( ThreadSet *pThreadSet, /* Thread-set handle */ int iThread, /* Set result for this thread */ int rc, /* Result error code */ char *zFmt, /* Result string format */ ... /* Result string formatting args... */ ){ va_list ap; testFree(pThreadSet->aThread[iThread].zMsg); pThreadSet->aThread[iThread].rc = rc; pThreadSet->aThread[iThread].zMsg = 0; if( zFmt ){ va_start(ap, zFmt); pThreadSet->aThread[iThread].zMsg = testMallocVPrintf(zFmt, ap); va_end(ap); } } /* ** Retrieve the result for thread iThread. */ static int testThreadGetResult( ThreadSet *pThreadSet, /* Thread-set handle */ int iThread, /* Get result for this thread */ const char **pzRes /* OUT: Pointer to result string */ ){ if( pzRes ) *pzRes = pThreadSet->aThread[iThread].zMsg; return pThreadSet->aThread[iThread].rc; } /* ** Enter and leave the test case mutex. */ #if 0 static void testThreadEnterMutex(ThreadSet *p){ pthread_mutex_lock(&p->mutex); } static void testThreadLeaveMutex(ThreadSet *p){ pthread_mutex_unlock(&p->mutex); } #endif #endif #if !defined(LSM_MUTEX_PTHREADS) static int testThreadSupport(){ return 0; } #define testThreadInit(a) 0 #define testThreadShutdown(a) #define testThreadLaunch(a,b,c,d) 0 #define testThreadWait(a,b) #define testThreadSetHalt(a) #define testThreadGetHalt(a) 0 #define testThreadGetResult(a,b,c) 0 #define testThreadSleep(a,b) 0 static void testThreadSetResult(ThreadSet *a, int b, int c, char *d, ...){ unused_parameter(a); unused_parameter(b); unused_parameter(c); unused_parameter(d); } #endif /* End of threads wrapper. *************************************************************************/ /************************************************************************* ** Below this point is the third part of this file - the implementation ** of the mt1.* tests. */ typedef struct Mt1Test Mt1Test; struct Mt1Test { DbParameters param; /* Description of database to read/write */ int nReadwrite; /* Number of read/write threads */ int nFastReader; /* Number of fast reader threads */ int nSlowReader; /* Number of slow reader threads */ int nMs; /* How long to run for */ const char *zSystem; /* Database system to test */ }; typedef struct Mt1DelayCtx Mt1DelayCtx; struct Mt1DelayCtx { ThreadSet *pSet; /* Threadset to sleep within */ int nMs; /* Sleep in ms */ }; static void xMt1Delay(void *pCtx){ Mt1DelayCtx *p = (Mt1DelayCtx *)pCtx; testThreadSleep(p->pSet, p->nMs); } #define MT1_THREAD_RDWR 0 #define MT1_THREAD_SLOW 1 #define MT1_THREAD_FAST 2 static void xMt1Work(lsm_db *pDb, void *pCtx){ #if 0 char *z = 0; lsm_info(pDb, LSM_INFO_DB_STRUCTURE, &z); printf("%s\n", z); fflush(stdout); #endif } /* ** This is the main() proc for all threads in test case "mt1". */ static void mt1Main(ThreadSet *pThreadSet, int iThread, void *pCtx){ Mt1Test *p = (Mt1Test *)pCtx; /* Test parameters */ Mt1DelayCtx delay; int nRead = 0; /* Number of calls to dbReadOperation() */ int nWrite = 0; /* Number of completed database writes */ int rc = 0; /* Error code */ int iPrng; /* Prng argument variable */ TestDb *pDb; /* Database handle */ int eType; delay.pSet = pThreadSet; delay.nMs = 0; if( iThread<p->nReadwrite ){ eType = MT1_THREAD_RDWR; }else if( iThread<(p->nReadwrite+p->nFastReader) ){ eType = MT1_THREAD_FAST; }else{ eType = MT1_THREAD_SLOW; delay.nMs = (p->nMs / 20); } /* Open a new database connection. Initialize the pseudo-random number ** argument based on the thread number. */ iPrng = testPrngValue(iThread); pDb = testOpen(p->zSystem, 0, &rc); if( rc==0 ){ tdb_lsm_config_work_hook(pDb, xMt1Work, 0); } /* Loop until either an error occurs or some other thread sets the ** halt flag. */ while( rc==0 && testThreadGetHalt(pThreadSet)==0 ){ int iKey; /* Perform a read operation on an arbitrarily selected key. */ iKey = (testPrngValue(iPrng++) % p->param.nKey); dbReadOperation(&p->param, pDb, xMt1Delay, (void *)&delay, iKey, &rc); if( rc ) continue; nRead++; /* Attempt to write an arbitrary key value pair (and update the associated ** checksum entries). dbWriteOperation() returns 1 if the write is ** successful, or 0 if it failed with an LSM_BUSY error. */ if( eType==MT1_THREAD_RDWR ){ char aValue[50]; char aRnd[25]; iKey = (testPrngValue(iPrng++) % p->param.nKey); testPrngString(iPrng, aRnd, sizeof(aRnd)); iPrng += sizeof(aRnd); snprintf(aValue, sizeof(aValue), "%d.%s", iThread, aRnd); nWrite += dbWriteOperation(&p->param, pDb, iKey, aValue, &rc); } } testClose(&pDb); /* If an error has occured, set the thread error code and the threadset ** halt flag to tell the other test threads to halt. Otherwise, set the ** thread error code to 0 and post a message with the number of read ** and write operations completed. */ if( rc ){ testThreadSetResult(pThreadSet, iThread, rc, 0); testThreadSetHalt(pThreadSet); }else{ testThreadSetResult(pThreadSet, iThread, 0, "r/w: %d/%d", nRead, nWrite); } } static void do_test_mt1( const char *zSystem, /* Database system name */ const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ Mt1Test aTest[] = { /* param, nReadwrite, nFastReader, nSlowReader, nMs, zSystem */ { {10, 1000}, 4, 0, 0, 10000, 0 }, { {10, 1000}, 4, 4, 2, 100000, 0 }, { {10, 100000}, 4, 0, 0, 10000, 0 }, { {10, 100000}, 4, 4, 2, 100000, 0 }, }; int i; for(i=0; *pRc==0 && i<ArraySize(aTest); i++){ Mt1Test *p = &aTest[i]; int bRun = testCaseBegin(pRc, zPattern, "mt1.%s.db=%d,%d.ms=%d.rdwr=%d.fast=%d.slow=%d", zSystem, p->param.nFanout, p->param.nKey, p->nMs, p->nReadwrite, p->nFastReader, p->nSlowReader ); if( bRun ){ TestDb *pDb; ThreadSet *pSet; int iThread; int nThread; p->zSystem = zSystem; pDb = testOpen(zSystem, 1, pRc); nThread = p->nReadwrite + p->nFastReader + p->nSlowReader; pSet = testThreadInit(nThread); for(iThread=0; *pRc==0 && iThread<nThread; iThread++){ testThreadLaunch(pSet, iThread, mt1Main, (void *)p); } testThreadWait(pSet, p->nMs); for(iThread=0; *pRc==0 && iThread<nThread; iThread++){ *pRc = testThreadGetResult(pSet, iThread, 0); } testCaseFinish(*pRc); for(iThread=0; *pRc==0 && iThread<nThread; iThread++){ const char *zMsg = 0; *pRc = testThreadGetResult(pSet, iThread, &zMsg); printf(" Info: thread %d (%d): %s\n", iThread, *pRc, zMsg); } testThreadShutdown(pSet); testClose(&pDb); } } } void test_mt( const char *zSystem, /* Database system name */ const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ if( testThreadSupport()==0 ) return; do_test_mt1(zSystem, zPattern, pRc); } |
Added ext/lsm1/lsm-test/lsmtest6.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 | #include "lsmtest.h" typedef struct OomTest OomTest; struct OomTest { lsm_env *pEnv; int iNext; /* Next value to pass to testMallocOom() */ int nFail; /* Number of OOM events injected */ int bEnable; int rc; /* Test case error code */ }; static void testOomStart(OomTest *p){ memset(p, 0, sizeof(OomTest)); p->iNext = 1; p->bEnable = 1; p->nFail = 1; p->pEnv = tdb_lsm_env(); } static void xOomHook(OomTest *p){ p->nFail++; } static int testOomContinue(OomTest *p){ if( p->rc!=0 || (p->iNext>1 && p->nFail==0) ){ return 0; } p->nFail = 0; testMallocOom(p->pEnv, p->iNext, 0, (void (*)(void*))xOomHook, (void *)p); return 1; } static void testOomEnable(OomTest *p, int bEnable){ p->bEnable = bEnable; testMallocOomEnable(p->pEnv, bEnable); } static void testOomNext(OomTest *p){ p->iNext++; } static int testOomHit(OomTest *p){ return (p->nFail>0); } static int testOomFinish(OomTest *p){ return p->rc; } static void testOomAssert(OomTest *p, int bVal){ if( bVal==0 ){ test_failed(); p->rc = 1; } } /* ** Test that the error code matches the state of the OomTest object passed ** as the first argument. Specifically, check that rc is LSM_NOMEM if an ** OOM error has already been injected, or LSM_OK if not. */ static void testOomAssertRc(OomTest *p, int rc){ testOomAssert(p, rc==LSM_OK || rc==LSM_NOMEM); testOomAssert(p, testOomHit(p)==(rc==LSM_NOMEM) || p->bEnable==0 ); } static void testOomOpen( OomTest *pOom, const char *zName, lsm_db **ppDb, int *pRc ){ if( *pRc==LSM_OK ){ int rc; rc = lsm_new(tdb_lsm_env(), ppDb); if( rc==LSM_OK ) rc = lsm_open(*ppDb, zName); testOomAssertRc(pOom, rc); *pRc = rc; } } static void testOomFetch( OomTest *pOom, lsm_db *pDb, void *pKey, int nKey, void *pVal, int nVal, int *pRc ){ testOomAssertRc(pOom, *pRc); if( *pRc==LSM_OK ){ lsm_cursor *pCsr; int rc; rc = lsm_csr_open(pDb, &pCsr); if( rc==LSM_OK ) rc = lsm_csr_seek(pCsr, pKey, nKey, 0); testOomAssertRc(pOom, rc); if( rc==LSM_OK ){ const void *p; int n; testOomAssert(pOom, lsm_csr_valid(pCsr)); rc = lsm_csr_key(pCsr, &p, &n); testOomAssertRc(pOom, rc); testOomAssert(pOom, rc!=LSM_OK || (n==nKey && memcmp(pKey, p, nKey)==0) ); } if( rc==LSM_OK ){ const void *p; int n; testOomAssert(pOom, lsm_csr_valid(pCsr)); rc = lsm_csr_value(pCsr, &p, &n); testOomAssertRc(pOom, rc); testOomAssert(pOom, rc!=LSM_OK || (n==nVal && memcmp(pVal, p, nVal)==0) ); } lsm_csr_close(pCsr); *pRc = rc; } } static void testOomWrite( OomTest *pOom, lsm_db *pDb, void *pKey, int nKey, void *pVal, int nVal, int *pRc ){ testOomAssertRc(pOom, *pRc); if( *pRc==LSM_OK ){ int rc; rc = lsm_insert(pDb, pKey, nKey, pVal, nVal); testOomAssertRc(pOom, rc); *pRc = rc; } } static void testOomFetchStr( OomTest *pOom, lsm_db *pDb, const char *zKey, const char *zVal, int *pRc ){ int nKey = strlen(zKey); int nVal = strlen(zVal); testOomFetch(pOom, pDb, (void *)zKey, nKey, (void *)zVal, nVal, pRc); } static void testOomFetchData( OomTest *pOom, lsm_db *pDb, Datasource *pData, int iKey, int *pRc ){ void *pKey; int nKey; void *pVal; int nVal; testDatasourceEntry(pData, iKey, &pKey, &nKey, &pVal, &nVal); testOomFetch(pOom, pDb, pKey, nKey, pVal, nVal, pRc); } static void testOomWriteStr( OomTest *pOom, lsm_db *pDb, const char *zKey, const char *zVal, int *pRc ){ int nKey = strlen(zKey); int nVal = strlen(zVal); testOomWrite(pOom, pDb, (void *)zKey, nKey, (void *)zVal, nVal, pRc); } static void testOomWriteData( OomTest *pOom, lsm_db *pDb, Datasource *pData, int iKey, int *pRc ){ void *pKey; int nKey; void *pVal; int nVal; testDatasourceEntry(pData, iKey, &pKey, &nKey, &pVal, &nVal); testOomWrite(pOom, pDb, pKey, nKey, pVal, nVal, pRc); } static void testOomScan( OomTest *pOom, lsm_db *pDb, int bReverse, const void *pKey, int nKey, int nScan, int *pRc ){ if( *pRc==0 ){ int rc; int iScan = 0; lsm_cursor *pCsr; int (*xAdvance)(lsm_cursor *) = 0; rc = lsm_csr_open(pDb, &pCsr); testOomAssertRc(pOom, rc); if( rc==LSM_OK ){ if( bReverse ){ rc = lsm_csr_seek(pCsr, pKey, nKey, LSM_SEEK_LE); xAdvance = lsm_csr_prev; }else{ rc = lsm_csr_seek(pCsr, pKey, nKey, LSM_SEEK_GE); xAdvance = lsm_csr_next; } } testOomAssertRc(pOom, rc); while( rc==LSM_OK && lsm_csr_valid(pCsr) && iScan<nScan ){ const void *p; int n; rc = lsm_csr_key(pCsr, &p, &n); testOomAssertRc(pOom, rc); if( rc==LSM_OK ){ rc = lsm_csr_value(pCsr, &p, &n); testOomAssertRc(pOom, rc); } if( rc==LSM_OK ){ rc = xAdvance(pCsr); testOomAssertRc(pOom, rc); } iScan++; } lsm_csr_close(pCsr); *pRc = rc; } } #define LSMTEST6_TESTDB "testdb.lsm" void testDeleteLsmdb(const char *zFile){ char *zLog = testMallocPrintf("%s-log", zFile); char *zShm = testMallocPrintf("%s-shm", zFile); unlink(zFile); unlink(zLog); unlink(zShm); testFree(zLog); testFree(zShm); } static void copy_file(const char *zFrom, const char *zTo, int isDatabase){ if( access(zFrom, F_OK) ){ unlink(zTo); }else{ int fd1; int fd2; off_t sz; off_t i; struct stat buf; u8 *aBuf; fd1 = open(zFrom, O_RDONLY | _O_BINARY, 0644); fd2 = open(zTo, O_RDWR | O_CREAT | _O_BINARY, 0644); fstat(fd1, &buf); sz = buf.st_size; ftruncate(fd2, sz); aBuf = testMalloc(4096); for(i=0; i<sz; i+=4096){ int bLockPage = isDatabase && i == 0; int nByte = MIN((bLockPage ? 4066 : 4096), sz - i); memset(aBuf, 0, 4096); read(fd1, aBuf, nByte); write(fd2, aBuf, nByte); if( bLockPage ){ lseek(fd1, 4096, SEEK_SET); lseek(fd2, 4096, SEEK_SET); } } testFree(aBuf); close(fd1); close(fd2); } } void testCopyLsmdb(const char *zFrom, const char *zTo){ char *zLog1 = testMallocPrintf("%s-log", zFrom); char *zLog2 = testMallocPrintf("%s-log", zTo); char *zShm1 = testMallocPrintf("%s-shm", zFrom); char *zShm2 = testMallocPrintf("%s-shm", zTo); unlink(zShm2); unlink(zLog2); unlink(zTo); copy_file(zFrom, zTo, 1); copy_file(zLog1, zLog2, 0); copy_file(zShm1, zShm2, 0); testFree(zLog1); testFree(zLog2); testFree(zShm1); testFree(zShm2); } /* ** File zFile is the path to a database. This function makes backups ** of the database file and its log as follows: ** ** cp $(zFile) $(zFile)-save ** cp $(zFile)-$(zAux) $(zFile)-save-$(zAux) ** ** Function testRestoreDb() can be used to copy the files back in the ** other direction. */ void testSaveDb(const char *zFile, const char *zAux){ char *zLog = testMallocPrintf("%s-%s", zFile, zAux); char *zFileSave = testMallocPrintf("%s-save", zFile); char *zLogSave = testMallocPrintf("%s-%s-save", zFile, zAux); unlink(zFileSave); unlink(zLogSave); copy_file(zFile, zFileSave, 1); copy_file(zLog, zLogSave, 0); testFree(zLog); testFree(zFileSave); testFree(zLogSave); } /* ** File zFile is the path to a database. This function restores ** a backup of the database made by a previous call to testSaveDb(). ** Specifically, it does the equivalent of: ** ** cp $(zFile)-save $(zFile) ** cp $(zFile)-save-$(zAux) $(zFile)-$(zAux) */ void testRestoreDb(const char *zFile, const char *zAux){ char *zLog = testMallocPrintf("%s-%s", zFile, zAux); char *zFileSave = testMallocPrintf("%s-save", zFile); char *zLogSave = testMallocPrintf("%s-%s-save", zFile, zAux); copy_file(zFileSave, zFile, 1); copy_file(zLogSave, zLog, 0); testFree(zLog); testFree(zFileSave); testFree(zLogSave); } static int lsmWriteStr(lsm_db *pDb, const char *zKey, const char *zVal){ int nKey = strlen(zKey); int nVal = strlen(zVal); return lsm_insert(pDb, (void *)zKey, nKey, (void *)zVal, nVal); } static void setup_delete_db(void){ testDeleteLsmdb(LSMTEST6_TESTDB); } /* ** Create a small database. With the following content: ** ** "one" -> "one" ** "two" -> "four" ** "three" -> "nine" ** "four" -> "sixteen" ** "five" -> "twentyfive" ** "six" -> "thirtysix" ** "seven" -> "fourtynine" ** "eight" -> "sixtyfour" */ static void setup_populate_db(void){ const char *azStr[] = { "one", "one", "two", "four", "three", "nine", "four", "sixteen", "five", "twentyfive", "six", "thirtysix", "seven", "fourtynine", "eight", "sixtyfour", }; int rc; int ii; lsm_db *pDb; testDeleteLsmdb(LSMTEST6_TESTDB); rc = lsm_new(tdb_lsm_env(), &pDb); if( rc==LSM_OK ) rc = lsm_open(pDb, LSMTEST6_TESTDB); for(ii=0; rc==LSM_OK && ii<ArraySize(azStr); ii+=2){ rc = lsmWriteStr(pDb, azStr[ii], azStr[ii+1]); } lsm_close(pDb); testSaveDb(LSMTEST6_TESTDB, "log"); assert( rc==LSM_OK ); } static Datasource *getDatasource(void){ const DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 10, 15, 200, 250 }; return testDatasourceNew(&defn); } /* ** Set up a database file with the following properties: ** ** * Page size is 1024 bytes. ** * Block size is 64 KB. ** * Contains 5000 key-value pairs starting at 0 from the ** datasource returned getDatasource(). */ static void setup_populate_db2(void){ Datasource *pData; int ii; int rc; int nBlocksize = 64*1024; int nPagesize = 1024; int nWritebuffer = 4*1024; lsm_db *pDb; testDeleteLsmdb(LSMTEST6_TESTDB); rc = lsm_new(tdb_lsm_env(), &pDb); if( rc==LSM_OK ) rc = lsm_open(pDb, LSMTEST6_TESTDB); lsm_config(pDb, LSM_CONFIG_BLOCK_SIZE, &nBlocksize); lsm_config(pDb, LSM_CONFIG_PAGE_SIZE, &nPagesize); lsm_config(pDb, LSM_CONFIG_AUTOFLUSH, &nWritebuffer); pData = getDatasource(); for(ii=0; rc==LSM_OK && ii<5000; ii++){ void *pKey; int nKey; void *pVal; int nVal; testDatasourceEntry(pData, ii, &pKey, &nKey, &pVal, &nVal); lsm_insert(pDb, pKey, nKey, pVal, nVal); } testDatasourceFree(pData); lsm_close(pDb); testSaveDb(LSMTEST6_TESTDB, "log"); assert( rc==LSM_OK ); } /* ** Test the results of OOM conditions in lsm_new(). */ static void simple_oom_1(OomTest *pOom){ int rc; lsm_db *pDb; rc = lsm_new(tdb_lsm_env(), &pDb); testOomAssertRc(pOom, rc); lsm_close(pDb); } /* ** Test the results of OOM conditions in lsm_open(). */ static void simple_oom_2(OomTest *pOom){ int rc; lsm_db *pDb; rc = lsm_new(tdb_lsm_env(), &pDb); if( rc==LSM_OK ){ rc = lsm_open(pDb, "testdb.lsm"); } testOomAssertRc(pOom, rc); lsm_close(pDb); } /* ** Test the results of OOM conditions in simple fetch operations. */ static void simple_oom_3(OomTest *pOom){ int rc = LSM_OK; lsm_db *pDb; testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc); testOomFetchStr(pOom, pDb, "four", "sixteen", &rc); testOomFetchStr(pOom, pDb, "seven", "fourtynine", &rc); testOomFetchStr(pOom, pDb, "one", "one", &rc); testOomFetchStr(pOom, pDb, "eight", "sixtyfour", &rc); lsm_close(pDb); } /* ** Test the results of OOM conditions in simple write operations. */ static void simple_oom_4(OomTest *pOom){ int rc = LSM_OK; lsm_db *pDb; testDeleteLsmdb(LSMTEST6_TESTDB); testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc); testOomWriteStr(pOom, pDb, "123", "onetwothree", &rc); testOomWriteStr(pOom, pDb, "456", "fourfivesix", &rc); testOomWriteStr(pOom, pDb, "789", "seveneightnine", &rc); testOomWriteStr(pOom, pDb, "123", "teneleventwelve", &rc); testOomWriteStr(pOom, pDb, "456", "fourteenfifteensixteen", &rc); lsm_close(pDb); } static void simple_oom_5(OomTest *pOom){ Datasource *pData = getDatasource(); int rc = LSM_OK; lsm_db *pDb; testRestoreDb(LSMTEST6_TESTDB, "log"); testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc); testOomFetchData(pOom, pDb, pData, 3333, &rc); testOomFetchData(pOom, pDb, pData, 0, &rc); testOomFetchData(pOom, pDb, pData, 4999, &rc); lsm_close(pDb); testDatasourceFree(pData); } static void simple_oom_6(OomTest *pOom){ Datasource *pData = getDatasource(); int rc = LSM_OK; lsm_db *pDb; testRestoreDb(LSMTEST6_TESTDB, "log"); testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc); testOomWriteData(pOom, pDb, pData, 5000, &rc); testOomWriteData(pOom, pDb, pData, 5001, &rc); testOomWriteData(pOom, pDb, pData, 5002, &rc); testOomFetchData(pOom, pDb, pData, 5001, &rc); testOomFetchData(pOom, pDb, pData, 1234, &rc); lsm_close(pDb); testDatasourceFree(pData); } static void simple_oom_7(OomTest *pOom){ Datasource *pData = getDatasource(); int rc = LSM_OK; lsm_db *pDb; testRestoreDb(LSMTEST6_TESTDB, "log"); testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc); testOomScan(pOom, pDb, 0, "abc", 3, 20, &rc); lsm_close(pDb); testDatasourceFree(pData); } static void simple_oom_8(OomTest *pOom){ Datasource *pData = getDatasource(); int rc = LSM_OK; lsm_db *pDb; testRestoreDb(LSMTEST6_TESTDB, "log"); testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc); testOomScan(pOom, pDb, 1, "xyz", 3, 20, &rc); lsm_close(pDb); testDatasourceFree(pData); } /* ** This test case has two clients connected to a database. The first client ** hits an OOM while writing to the database. Check that the second ** connection is still able to query the db following the OOM. */ static void simple_oom2_1(OomTest *pOom){ const int nRecord = 100; /* Number of records initially in db */ const int nIns = 10; /* Number of records inserted with OOM */ Datasource *pData = getDatasource(); int rc = LSM_OK; lsm_db *pDb1; lsm_db *pDb2; int i; testDeleteLsmdb(LSMTEST6_TESTDB); /* Open the two connections. Initialize the in-memory tree so that it ** contains 100 records. Do all this with OOM injection disabled. */ testOomEnable(pOom, 0); testOomOpen(pOom, LSMTEST6_TESTDB, &pDb1, &rc); testOomOpen(pOom, LSMTEST6_TESTDB, &pDb2, &rc); for(i=0; i<nRecord; i++){ testOomWriteData(pOom, pDb1, pData, i, &rc); } testOomEnable(pOom, 1); assert( rc==0 ); /* Insert 10 more records using pDb1. Stop when an OOM is encountered. */ for(i=nRecord; i<nRecord+nIns; i++){ testOomWriteData(pOom, pDb1, pData, i, &rc); if( rc ) break; } testOomAssertRc(pOom, rc); /* Switch off OOM injection. Write a few rows using pDb2. Then check ** that the database may be successfully queried. */ testOomEnable(pOom, 0); rc = 0; for(; i<nRecord+nIns && rc==0; i++){ testOomWriteData(pOom, pDb2, pData, i, &rc); } for(i=0; i<nRecord+nIns; i++) testOomFetchData(pOom, pDb2, pData, i, &rc); testOomEnable(pOom, 1); lsm_close(pDb1); lsm_close(pDb2); testDatasourceFree(pData); } static void do_test_oom1(const char *zPattern, int *pRc){ struct SimpleOom { const char *zName; void (*xSetup)(void); void (*xFunc)(OomTest *); } aSimple[] = { { "oom1.lsm.1", setup_delete_db, simple_oom_1 }, { "oom1.lsm.2", setup_delete_db, simple_oom_2 }, { "oom1.lsm.3", setup_populate_db, simple_oom_3 }, { "oom1.lsm.4", setup_delete_db, simple_oom_4 }, { "oom1.lsm.5", setup_populate_db2, simple_oom_5 }, { "oom1.lsm.6", setup_populate_db2, simple_oom_6 }, { "oom1.lsm.7", setup_populate_db2, simple_oom_7 }, { "oom1.lsm.8", setup_populate_db2, simple_oom_8 }, { "oom2.lsm.1", setup_delete_db, simple_oom2_1 }, }; int i; for(i=0; i<ArraySize(aSimple); i++){ if( *pRc==0 && testCaseBegin(pRc, zPattern, "%s", aSimple[i].zName) ){ OomTest t; if( aSimple[i].xSetup ){ aSimple[i].xSetup(); } for(testOomStart(&t); testOomContinue(&t); testOomNext(&t)){ aSimple[i].xFunc(&t); } printf("(%d injections).", t.iNext-2); testCaseFinish( (*pRc = testOomFinish(&t)) ); testMallocOom(tdb_lsm_env(), 0, 0, 0, 0); } } } void test_oom( const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ do_test_oom1(zPattern, pRc); } |
Added ext/lsm1/lsm-test/lsmtest7.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 | #include "lsmtest.h" /* ** Test that the rules for when lsm_csr_next() and lsm_csr_prev() are ** enforced. Specifically: ** ** * Both functions always return LSM_MISUSE if the cursor is at EOF ** when they are called. ** ** * lsm_csr_next() may only be used after lsm_csr_seek(LSM_SEEK_GE) or ** lsm_csr_first(). ** ** * lsm_csr_prev() may only be used after lsm_csr_seek(LSM_SEEK_LE) or ** lsm_csr_last(). */ static void do_test_api1_lsm(lsm_db *pDb, int *pRc){ int ret; lsm_cursor *pCsr; lsm_cursor *pCsr2; int nKey; const void *pKey; ret = lsm_csr_open(pDb, &pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_next(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_prev(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_seek(pCsr, "jjj", 3, LSM_SEEK_GE); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_next(pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_prev(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_seek(pCsr, "jjj", 3, LSM_SEEK_LE); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_next(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_prev(pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_seek(pCsr, "jjj", 3, LSM_SEEK_LEFAST); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_next(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_prev(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_key(pCsr, &pKey, &nKey); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_open(pDb, &pCsr2); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_seek(pCsr2, pKey, nKey, LSM_SEEK_EQ); testCompareInt(LSM_OK, ret, pRc); testCompareInt(1, lsm_csr_valid(pCsr2), pRc); ret = lsm_csr_next(pCsr2); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_prev(pCsr2); testCompareInt(LSM_MISUSE, ret, pRc); lsm_csr_close(pCsr2); ret = lsm_csr_first(pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_next(pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_prev(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_last(pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_prev(pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_next(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_first(pCsr); while( lsm_csr_valid(pCsr) ){ ret = lsm_csr_next(pCsr); testCompareInt(LSM_OK, ret, pRc); } ret = lsm_csr_next(pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_prev(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); ret = lsm_csr_last(pCsr); while( lsm_csr_valid(pCsr) ){ ret = lsm_csr_prev(pCsr); testCompareInt(LSM_OK, ret, pRc); } ret = lsm_csr_prev(pCsr); testCompareInt(LSM_OK, ret, pRc); ret = lsm_csr_next(pCsr); testCompareInt(LSM_MISUSE, ret, pRc); lsm_csr_close(pCsr); } static void do_test_api1(const char *zPattern, int *pRc){ if( testCaseBegin(pRc, zPattern, "api1.lsm") ){ const DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 10, 15, 200, 250 }; Datasource *pData; TestDb *pDb; int rc = 0; pDb = testOpen("lsm_lomem", 1, &rc); pData = testDatasourceNew(&defn); testWriteDatasourceRange(pDb, pData, 0, 1000, pRc); do_test_api1_lsm(tdb_lsm(pDb), pRc); testDatasourceFree(pData); testClose(&pDb); testCaseFinish(*pRc); } } static lsm_db *newLsmConnection( const char *zDb, int nPgsz, int nBlksz, int *pRc ){ lsm_db *db = 0; if( *pRc==0 ){ int n1 = nPgsz; int n2 = nBlksz; *pRc = lsm_new(tdb_lsm_env(), &db); if( *pRc==0 ){ if( n1 ) lsm_config(db, LSM_CONFIG_PAGE_SIZE, &n1); if( n2 ) lsm_config(db, LSM_CONFIG_BLOCK_SIZE, &n2); *pRc = lsm_open(db, "testdb.lsm"); } } return db; } static void testPagesize(lsm_db *db, int nPgsz, int nBlksz, int *pRc){ if( *pRc==0 ){ int n1 = 0; int n2 = 0; lsm_config(db, LSM_CONFIG_PAGE_SIZE, &n1); lsm_config(db, LSM_CONFIG_BLOCK_SIZE, &n2); testCompareInt(n1, nPgsz, pRc); testCompareInt(n2, nBlksz, pRc); } } /* ** Test case "api2" tests that the default page and block sizes of a ** database may only be modified before lsm_open() is called. And that ** after lsm_open() is called lsm_config() may be used to read the ** actual page and block size of the db. */ static void do_test_api2(const char *zPattern, int *pRc){ if( *pRc==0 && testCaseBegin(pRc, zPattern, "api2.lsm") ){ lsm_db *db1 = 0; lsm_db *db2 = 0; testDeleteLsmdb("testdb.lsm"); db1 = newLsmConnection("testdb.lsm", 0, 0, pRc); testPagesize(db1, 4096, 1024, pRc); db2 = newLsmConnection("testdb.lsm", 1024, 64*1024, pRc); testPagesize(db2, 4096, 1024, pRc); lsm_close(db1); lsm_close(db2); testDeleteLsmdb("testdb.lsm"); db1 = newLsmConnection("testdb.lsm", 1024, 64*1024, pRc); testPagesize(db1, 1024, 64*1024, pRc); db2 = newLsmConnection("testdb.lsm", 0, 0, pRc); testPagesize(db2, 1024, 64*1024, pRc); lsm_close(db1); lsm_close(db2); testDeleteLsmdb("testdb.lsm"); db1 = newLsmConnection("testdb.lsm", 8192, 2*1024, pRc); testPagesize(db1, 8192, 2*1024, pRc); db2 = newLsmConnection("testdb.lsm", 1024, 64*1024, pRc); testPagesize(db2, 8192, 2*1024, pRc); lsm_close(db1); lsm_close(db2); testCaseFinish(*pRc); } } void test_api( const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ do_test_api1(zPattern, pRc); do_test_api2(zPattern, pRc); } |
Added ext/lsm1/lsm-test/lsmtest8.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 | /* ** This file contains test cases to verify that "live-recovery" following ** a mid-transaction failure of a writer process. */ /* ** This test file includes lsmInt.h to get access to the definition of the ** ShmHeader structure. This is required to cause strategic damage to the ** shared memory header as part of recovery testing. */ #include "lsmInt.h" #include "lsmtest.h" typedef struct SetupStep SetupStep; struct SetupStep { int bFlush; /* Flush to disk and checkpoint */ int iInsStart; /* First key-value from ds to insert */ int nIns; /* Number of rows to insert */ int iDelStart; /* First key from ds to delete */ int nDel; /* Number of rows to delete */ }; static void doSetupStep( TestDb *pDb, Datasource *pData, const SetupStep *pStep, int *pRc ){ testWriteDatasourceRange(pDb, pData, pStep->iInsStart, pStep->nIns, pRc); testDeleteDatasourceRange(pDb, pData, pStep->iDelStart, pStep->nDel, pRc); if( *pRc==0 ){ int nSave = -1; int nBuf = 64; lsm_db *db = tdb_lsm(pDb); lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave); lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf); lsm_begin(db, 1); lsm_commit(db, 0); lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave); *pRc = lsm_work(db, 0, 0, 0); if( *pRc==0 ){ *pRc = lsm_checkpoint(db, 0); } } } static void doSetupStepArray( TestDb *pDb, Datasource *pData, const SetupStep *aStep, int nStep ){ int i; for(i=0; i<nStep; i++){ int rc = 0; doSetupStep(pDb, pData, &aStep[i], &rc); assert( rc==0 ); } } static void setupDatabase1(TestDb *pDb, Datasource **ppData){ const SetupStep aStep[] = { { 0, 1, 2000, 0, 0 }, { 1, 0, 0, 0, 0 }, { 0, 10001, 1000, 0, 0 }, }; const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 100, 500}; Datasource *pData; pData = testDatasourceNew(&defn); doSetupStepArray(pDb, pData, aStep, ArraySize(aStep)); if( ppData ){ *ppData = pData; }else{ testDatasourceFree(pData); } } #include <stdio.h> void testReadFile(const char *zFile, int iOff, void *pOut, int nByte, int *pRc){ if( *pRc==0 ){ FILE *fd; fd = fopen(zFile, "rb"); if( fd==0 ){ *pRc = 1; }else{ if( 0!=fseek(fd, iOff, SEEK_SET) ){ *pRc = 1; }else{ assert( nByte>=0 ); if( (size_t)nByte!=fread(pOut, 1, nByte, fd) ){ *pRc = 1; } } fclose(fd); } } } void testWriteFile( const char *zFile, int iOff, void *pOut, int nByte, int *pRc ){ if( *pRc==0 ){ FILE *fd; fd = fopen(zFile, "r+b"); if( fd==0 ){ *pRc = 1; }else{ if( 0!=fseek(fd, iOff, SEEK_SET) ){ *pRc = 1; }else{ assert( nByte>=0 ); if( (size_t)nByte!=fwrite(pOut, 1, nByte, fd) ){ *pRc = 1; } } fclose(fd); } } } static ShmHeader *getShmHeader(const char *zDb){ int rc = 0; char *zShm = testMallocPrintf("%s-shm", zDb); ShmHeader *pHdr; pHdr = testMalloc(sizeof(ShmHeader)); testReadFile(zShm, 0, (void *)pHdr, sizeof(ShmHeader), &rc); assert( rc==0 ); return pHdr; } /* ** This function makes a copy of the three files associated with LSM ** database zDb (i.e. if zDb is "test.db", it makes copies of "test.db", ** "test.db-log" and "test.db-shm"). ** ** It then opens a new database connection to the copy with the xLock() call ** instrumented so that it appears that some other process already connected ** to the db (holding a shared lock on DMS2). This prevents recovery from ** running. Then: ** ** 1) Check that the checksum of the database is zCksum. ** 2) Write a few keys to the database. Then delete the same keys. ** 3) Check that the checksum is zCksum. ** 4) Flush the db to disk and run a checkpoint. ** 5) Check once more that the checksum is still zCksum. */ static void doLiveRecovery(const char *zDb, const char *zCksum, int *pRc){ if( *pRc==LSM_OK ){ const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 20, 25, 100, 500}; Datasource *pData; const char *zCopy = "testcopy.lsm"; char zCksum2[TEST_CKSUM_BYTES]; TestDb *pDb = 0; int rc; pData = testDatasourceNew(&defn); testCopyLsmdb(zDb, zCopy); rc = tdb_lsm_open("test_no_recovery=1", zCopy, 0, &pDb); if( rc==0 ){ ShmHeader *pHdr; lsm_db *db; testCksumDatabase(pDb, zCksum2); testCompareStr(zCksum, zCksum2, &rc); testWriteDatasourceRange(pDb, pData, 1, 10, &rc); testDeleteDatasourceRange(pDb, pData, 1, 10, &rc); /* Test that the two tree-headers are now consistent. */ pHdr = getShmHeader(zCopy); if( rc==0 && memcmp(&pHdr->hdr1, &pHdr->hdr2, sizeof(pHdr->hdr1)) ){ rc = 1; } testFree(pHdr); if( rc==0 ){ int nBuf = 64; db = tdb_lsm(pDb); lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf); lsm_begin(db, 1); lsm_commit(db, 0); rc = lsm_work(db, 0, 0, 0); } testCksumDatabase(pDb, zCksum2); testCompareStr(zCksum, zCksum2, &rc); } testDatasourceFree(pData); testClose(&pDb); testDeleteLsmdb(zCopy); *pRc = rc; } } static void doWriterCrash1(int *pRc){ const int nWrite = 2000; const int nStep = 10; const int iWriteStart = 20000; int rc = 0; TestDb *pDb = 0; Datasource *pData = 0; rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb); if( rc==0 ){ int iDot = 0; char zCksum[TEST_CKSUM_BYTES]; int i; setupDatabase1(pDb, &pData); testCksumDatabase(pDb, zCksum); testBegin(pDb, 2, &rc); for(i=0; rc==0 && i<nWrite; i+=nStep){ testCaseProgress(i, nWrite, testCaseNDot(), &iDot); testWriteDatasourceRange(pDb, pData, iWriteStart+i, nStep, &rc); doLiveRecovery("testdb.lsm", zCksum, &rc); } } testCommit(pDb, 0, &rc); testClose(&pDb); testDatasourceFree(pData); *pRc = rc; } /* ** This test case verifies that inconsistent tree-headers in shared-memory ** are resolved correctly. */ static void doWriterCrash2(int *pRc){ int rc = 0; TestDb *pDb = 0; Datasource *pData = 0; rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb); if( rc==0 ){ ShmHeader *pHdr1; ShmHeader *pHdr2; char zCksum1[TEST_CKSUM_BYTES]; char zCksum2[TEST_CKSUM_BYTES]; pHdr1 = testMalloc(sizeof(ShmHeader)); pHdr2 = testMalloc(sizeof(ShmHeader)); setupDatabase1(pDb, &pData); /* Grab a copy of the shared-memory header. And the db checksum */ testReadFile("testdb.lsm-shm", 0, (void *)pHdr1, sizeof(ShmHeader), &rc); testCksumDatabase(pDb, zCksum1); /* Modify the database */ testBegin(pDb, 2, &rc); testWriteDatasourceRange(pDb, pData, 30000, 200, &rc); testCommit(pDb, 0, &rc); /* Grab a second copy of the shared-memory header. And the db checksum */ testReadFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); testCksumDatabase(pDb, zCksum2); doLiveRecovery("testdb.lsm", zCksum2, &rc); /* If both tree-headers are valid, tree-header-1 is used. */ memcpy(&pHdr2->hdr1, &pHdr1->hdr1, sizeof(pHdr1->hdr1)); pHdr2->bWriter = 1; testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); doLiveRecovery("testdb.lsm", zCksum1, &rc); /* If both tree-headers are valid, tree-header-1 is used. */ memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1)); memcpy(&pHdr2->hdr2, &pHdr1->hdr1, sizeof(pHdr1->hdr1)); pHdr2->bWriter = 1; testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); doLiveRecovery("testdb.lsm", zCksum2, &rc); /* If tree-header 1 is invalid, tree-header-2 is used */ memcpy(&pHdr2->hdr2, &pHdr2->hdr1, sizeof(pHdr1->hdr1)); pHdr2->hdr1.aCksum[0] = 5; pHdr2->hdr1.aCksum[0] = 6; pHdr2->bWriter = 1; testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); doLiveRecovery("testdb.lsm", zCksum2, &rc); /* If tree-header 2 is invalid, tree-header-1 is used */ memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1)); pHdr2->hdr2.aCksum[0] = 5; pHdr2->hdr2.aCksum[0] = 6; pHdr2->bWriter = 1; testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); doLiveRecovery("testdb.lsm", zCksum2, &rc); testFree(pHdr1); testFree(pHdr2); testClose(&pDb); } *pRc = rc; } void do_writer_crash_test(const char *zPattern, int *pRc){ struct Test { const char *zName; void (*xFunc)(int *); } aTest[] = { { "writercrash1.lsm", doWriterCrash1 }, { "writercrash2.lsm", doWriterCrash2 }, }; int i; for(i=0; i<ArraySize(aTest); i++){ struct Test *p = &aTest[i]; if( testCaseBegin(pRc, zPattern, p->zName) ){ p->xFunc(pRc); testCaseFinish(*pRc); } } } |
Added ext/lsm1/lsm-test/lsmtest9.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 | #include "lsmtest.h" #define DATA_SEQUENTIAL TEST_DATASOURCE_SEQUENCE #define DATA_RANDOM TEST_DATASOURCE_RANDOM typedef struct Datatest4 Datatest4; /* ** Test overview: ** ** 1. Insert (Datatest4.nRec) records into a database. ** ** 2. Repeat (Datatest4.nRepeat) times: ** ** 2a. Delete 2/3 of the records in the database. ** ** 2b. Run lsm_work(nMerge=1). ** ** 2c. Insert as many records as were deleted in 2a. ** ** 2d. Check database content is as expected. ** ** 2e. If (Datatest4.bReopen) is true, close and reopen the database. */ struct Datatest4 { /* Datasource definition */ DatasourceDefn defn; int nRec; int nRepeat; int bReopen; }; static void doDataTest4( const char *zSystem, /* Database system to test */ Datatest4 *p, /* Structure containing test parameters */ int *pRc /* OUT: Error code */ ){ lsm_db *db = 0; TestDb *pDb; TestDb *pControl; Datasource *pData; int i; int rc = 0; int iDot = 0; int bMultiThreaded = 0; /* True for MT LSM database */ int nRecOn3 = (p->nRec / 3); int iData = 0; /* Start the test case, open a database and allocate the datasource. */ rc = testControlDb(&pControl); pDb = testOpen(zSystem, 1, &rc); pData = testDatasourceNew(&p->defn); if( rc==0 ){ db = tdb_lsm(pDb); bMultiThreaded = tdb_lsm_multithread(pDb); } testWriteDatasourceRange(pControl, pData, iData, nRecOn3*3, &rc); testWriteDatasourceRange(pDb, pData, iData, nRecOn3*3, &rc); for(i=0; rc==0 && i<p->nRepeat; i++){ testDeleteDatasourceRange(pControl, pData, iData, nRecOn3*2, &rc); testDeleteDatasourceRange(pDb, pData, iData, nRecOn3*2, &rc); if( db ){ int nDone; #if 0 fprintf(stderr, "lsm_work() start...\n"); fflush(stderr); #endif do { nDone = 0; rc = lsm_work(db, 1, (1<<30), &nDone); }while( rc==0 && nDone>0 ); if( bMultiThreaded && rc==LSM_BUSY ) rc = LSM_OK; #if 0 fprintf(stderr, "lsm_work() done...\n"); fflush(stderr); #endif } if( i+1<p->nRepeat ){ iData += (nRecOn3*2); testWriteDatasourceRange(pControl, pData, iData+nRecOn3, nRecOn3*2, &rc); testWriteDatasourceRange(pDb, pData, iData+nRecOn3, nRecOn3*2, &rc); testCompareDb(pData, nRecOn3*3, iData, pControl, pDb, &rc); /* If Datatest4.bReopen is true, close and reopen the database */ if( p->bReopen ){ testReopen(&pDb, &rc); if( rc==0 ) db = tdb_lsm(pDb); } } /* Update the progress dots... */ testCaseProgress(i, p->nRepeat, testCaseNDot(), &iDot); } testClose(&pDb); testClose(&pControl); testDatasourceFree(pData); testCaseFinish(rc); *pRc = rc; } static char *getName4(const char *zSystem, Datatest4 *pTest){ char *zRet; char *zData; zData = testDatasourceName(&pTest->defn); zRet = testMallocPrintf("data4.%s.%s.%d.%d.%d", zSystem, zData, pTest->nRec, pTest->nRepeat, pTest->bReopen ); testFree(zData); return zRet; } void test_data_4( const char *zSystem, /* Database system name */ const char *zPattern, /* Run test cases that match this pattern */ int *pRc /* IN/OUT: Error code */ ){ Datatest4 aTest[] = { /* defn, nRec, nRepeat, bReopen */ { {DATA_RANDOM, 20,25, 500,600}, 10000, 10, 0 }, { {DATA_RANDOM, 20,25, 500,600}, 10000, 10, 1 }, }; int i; for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){ char *zName = getName4(zSystem, &aTest[i]); if( testCaseBegin(pRc, zPattern, "%s", zName) ){ doDataTest4(zSystem, &aTest[i], pRc); } testFree(zName); } } |
Added ext/lsm1/lsm-test/lsmtest_bt.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 | #include "lsmtest.h" #include "bt.h" int do_bt(int nArg, char **azArg){ struct Option { const char *zName; int bPgno; int eOpt; } aOpt [] = { { "dbhdr", 0, BT_INFO_HDRDUMP }, { "filename", 0, BT_INFO_FILENAME }, { "block_freelist", 0, BT_INFO_BLOCK_FREELIST }, { "page_freelist", 0, BT_INFO_PAGE_FREELIST }, { "filename", 0, BT_INFO_FILENAME }, { "page", 1, BT_INFO_PAGEDUMP }, { "page_ascii", 1, BT_INFO_PAGEDUMP_ASCII }, { "leaks", 0, BT_INFO_PAGE_LEAKS }, { 0, 0 } }; int iOpt; int rc; bt_info buf; char *zOpt; char *zFile; bt_db *db = 0; if( nArg<2 ){ testPrintUsage("FILENAME OPTION ..."); return -1; } zFile = azArg[0]; zOpt = azArg[1]; rc = testArgSelect(aOpt, "option", zOpt, &iOpt); if( rc!=0 ) return rc; if( nArg!=2+aOpt[iOpt].bPgno ){ testPrintFUsage("FILENAME %s %s", zOpt, aOpt[iOpt].bPgno ? "PGNO" : ""); return -4; } rc = sqlite4BtNew(sqlite4_env_default(), 0, &db); if( rc!=SQLITE4_OK ){ testPrintError("sqlite4BtNew() failed: %d", rc); return -2; } rc = sqlite4BtOpen(db, zFile); if( rc!=SQLITE4_OK ){ testPrintError("sqlite4BtOpen() failed: %d", rc); return -3; } buf.eType = aOpt[iOpt].eOpt; buf.pgno = 0; sqlite4_buffer_init(&buf.output, 0); if( aOpt[iOpt].bPgno ){ buf.pgno = (u32)atoi(azArg[2]); } rc = sqlite4BtControl(db, BT_CONTROL_INFO, &buf); if( rc!=SQLITE4_OK ){ testPrintError("sqlite4BtControl() failed: %d\n", rc); return -4; } printf("%s\n", (char*)buf.output.p); sqlite4_buffer_clear(&buf.output); return 0; } |
Added ext/lsm1/lsm-test/lsmtest_datasource.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 | #include "lsmtest.h" struct Datasource { int eType; int nMinKey; int nMaxKey; int nMinVal; int nMaxVal; char *aKey; char *aVal; }; void testDatasourceEntry( Datasource *p, int iData, void **ppKey, int *pnKey, void **ppVal, int *pnVal ){ assert( (ppKey==0)==(pnKey==0) ); assert( (ppVal==0)==(pnVal==0) ); if( ppKey ){ int nKey = 0; switch( p->eType ){ case TEST_DATASOURCE_RANDOM: { int nRange = (1 + p->nMaxKey - p->nMinKey); nKey = (int)( testPrngValue((u32)iData) % nRange ) + p->nMinKey; testPrngString((u32)iData, p->aKey, nKey); break; } case TEST_DATASOURCE_SEQUENCE: nKey = sprintf(p->aKey, "%012d", iData); break; } *ppKey = p->aKey; *pnKey = nKey; } if( ppVal ){ u32 nVal = testPrngValue((u32)iData)%(1+p->nMaxVal-p->nMinVal)+p->nMinVal; testPrngString((u32)~iData, p->aVal, (int)nVal); *ppVal = p->aVal; *pnVal = (int)nVal; } } void testDatasourceFree(Datasource *p){ testFree(p); } /* ** Return a pointer to a nul-terminated string that corresponds to the ** contents of the datasource-definition passed as the first argument. ** The caller should eventually free the returned pointer using testFree(). */ char *testDatasourceName(const DatasourceDefn *p){ char *zRet; zRet = testMallocPrintf("%s.(%d-%d).(%d-%d)", (p->eType==TEST_DATASOURCE_SEQUENCE ? "seq" : "rnd"), p->nMinKey, p->nMaxKey, p->nMinVal, p->nMaxVal ); return zRet; } Datasource *testDatasourceNew(const DatasourceDefn *pDefn){ Datasource *p; int nMinKey; int nMaxKey; int nMinVal; int nMaxVal; if( pDefn->eType==TEST_DATASOURCE_SEQUENCE ){ nMinKey = 128; nMaxKey = 128; }else{ nMinKey = MAX(0, pDefn->nMinKey); nMaxKey = MAX(nMinKey, pDefn->nMaxKey); } nMinVal = MAX(0, pDefn->nMinVal); nMaxVal = MAX(nMinVal, pDefn->nMaxVal); p = (Datasource *)testMalloc(sizeof(Datasource) + nMaxKey + nMaxVal + 1); p->eType = pDefn->eType; p->nMinKey = nMinKey; p->nMinVal = nMinVal; p->nMaxKey = nMaxKey; p->nMaxVal = nMaxVal; p->aKey = (char *)&p[1]; p->aVal = &p->aKey[nMaxKey]; return p; }; |
Added ext/lsm1/lsm-test/lsmtest_func.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 | #include "lsmtest.h" int do_work(int nArg, char **azArg){ struct Option { const char *zName; } aOpt [] = { { "-nmerge" }, { "-nkb" }, { 0 } }; lsm_db *pDb; int rc; int i; const char *zDb; int nMerge = 1; int nKB = (1<<30); if( nArg==0 ) goto usage; zDb = azArg[nArg-1]; for(i=0; i<(nArg-1); i++){ int iSel; rc = testArgSelect(aOpt, "option", azArg[i], &iSel); if( rc ) return rc; switch( iSel ){ case 0: i++; if( i==(nArg-1) ) goto usage; nMerge = atoi(azArg[i]); break; case 1: i++; if( i==(nArg-1) ) goto usage; nKB = atoi(azArg[i]); break; } } rc = lsm_new(0, &pDb); if( rc!=LSM_OK ){ testPrintError("lsm_open(): rc=%d\n", rc); }else{ rc = lsm_open(pDb, zDb); if( rc!=LSM_OK ){ testPrintError("lsm_open(): rc=%d\n", rc); }else{ int n = -1; lsm_config(pDb, LSM_CONFIG_BLOCK_SIZE, &n); n = n*2; lsm_config(pDb, LSM_CONFIG_AUTOCHECKPOINT, &n); rc = lsm_work(pDb, nMerge, nKB, 0); if( rc!=LSM_OK ){ testPrintError("lsm_work(): rc=%d\n", rc); } } } if( rc==LSM_OK ){ rc = lsm_checkpoint(pDb, 0); } lsm_close(pDb); return rc; usage: testPrintUsage("?-optimize? ?-n N? DATABASE"); return -1; } /* ** lsmtest show ?-config LSM-CONFIG? DATABASE ?COMMAND ?PGNO?? */ int do_show(int nArg, char **azArg){ lsm_db *pDb; int rc; const char *zDb; int eOpt = LSM_INFO_DB_STRUCTURE; unsigned int iPg = 0; int bConfig = 0; const char *zConfig = ""; struct Option { const char *zName; int bConfig; int eOpt; } aOpt [] = { { "array", 0, LSM_INFO_ARRAY_STRUCTURE }, { "array-pages", 0, LSM_INFO_ARRAY_PAGES }, { "blocksize", 1, LSM_CONFIG_BLOCK_SIZE }, { "pagesize", 1, LSM_CONFIG_PAGE_SIZE }, { "freelist", 0, LSM_INFO_FREELIST }, { "page-ascii", 0, LSM_INFO_PAGE_ASCII_DUMP }, { "page-hex", 0, LSM_INFO_PAGE_HEX_DUMP }, { 0, 0 } }; char *z = 0; int iDb = 0; /* Index of DATABASE in azArg[] */ /* Check if there is a "-config" option: */ if( nArg>2 && strlen(azArg[0])>1 && memcmp(azArg[0], "-config", strlen(azArg[0]))==0 ){ zConfig = azArg[1]; iDb = 2; } if( nArg<(iDb+1) ) goto usage; if( nArg>(iDb+1) ){ rc = testArgSelect(aOpt, "option", azArg[iDb+1], &eOpt); if( rc!=0 ) return rc; bConfig = aOpt[eOpt].bConfig; eOpt = aOpt[eOpt].eOpt; if( (bConfig==0 && eOpt==LSM_INFO_FREELIST) || (bConfig==1 && eOpt==LSM_CONFIG_BLOCK_SIZE) || (bConfig==1 && eOpt==LSM_CONFIG_PAGE_SIZE) ){ if( nArg!=(iDb+2) ) goto usage; }else{ if( nArg!=(iDb+3) ) goto usage; iPg = atoi(azArg[iDb+2]); } } zDb = azArg[iDb]; rc = lsm_new(0, &pDb); tdb_lsm_configure(pDb, zConfig); if( rc!=LSM_OK ){ testPrintError("lsm_new(): rc=%d\n", rc); }else{ rc = lsm_open(pDb, zDb); if( rc!=LSM_OK ){ testPrintError("lsm_open(): rc=%d\n", rc); } } if( rc==LSM_OK ){ if( bConfig==0 ){ switch( eOpt ){ case LSM_INFO_DB_STRUCTURE: case LSM_INFO_FREELIST: rc = lsm_info(pDb, eOpt, &z); break; case LSM_INFO_ARRAY_STRUCTURE: case LSM_INFO_ARRAY_PAGES: case LSM_INFO_PAGE_ASCII_DUMP: case LSM_INFO_PAGE_HEX_DUMP: rc = lsm_info(pDb, eOpt, iPg, &z); break; default: assert( !"no chance" ); } if( rc==LSM_OK ){ printf("%s\n", z ? z : ""); fflush(stdout); } lsm_free(lsm_get_env(pDb), z); }else{ int iRes = -1; lsm_config(pDb, eOpt, &iRes); printf("%d\n", iRes); fflush(stdout); } } lsm_close(pDb); return rc; usage: testPrintUsage("DATABASE ?array|page-ascii|page-hex PGNO?"); return -1; } |
Added ext/lsm1/lsm-test/lsmtest_io.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 | /* ** SUMMARY ** ** This file implements the 'io' subcommand of the test program. It is used ** for testing the performance of various combinations of write() and fsync() ** system calls. All operations occur on a single file, which may or may not ** exist when a test is started. ** ** A test consists of a series of commands. Each command is either a write ** or an fsync. A write is specified as "<amount>@<offset>", where <amount> ** is the amount of data written, and <offset> is the offset of the file ** to write to. An <amount> or an <offset> is specified as an integer number ** of bytes. Or, if postfixed with a "K", "M" or "G", an integer number of ** KB, MB or GB, respectively. An fsync is simply "S". All commands are ** case-insensitive. ** ** Example test program: ** ** 2M@6M 1492K@4M S 4096@4K S ** ** This program writes 2 MB of data starting at the offset 6MB offset of ** the file, followed by 1492 KB of data written at the 4MB offset of the ** file, followed by a call to fsync(), a write of 4KB of data at byte ** offset 4096, and finally another call to fsync(). ** ** Commands may either be specified on the command line (one command per ** command line argument) or read from stdin. Commands read from stdin ** must be separated by white-space. ** ** COMMAND LINE INVOCATION ** ** The sub-command implemented in this file must be invoked with at least ** two arguments - the path to the file to write to and the page-size to ** use for writing. If there are more than two arguments, then each ** subsequent argument is assumed to be a test command. If there are exactly ** two arguments, the test commands are read from stdin. ** ** A write command does not result in a single call to system call write(). ** Instead, the specified region is written sequentially using one or ** more calls to write(), each of which writes not more than one page of ** data. For example, if the page-size is 4KB, the command "2M@6M" results ** in 512 calls to write(), each of which writes 4KB of data. ** ** EXAMPLES ** ** Two equivalent examples: ** ** $ lsmtest io testfile.db 4KB 2M@6M 1492K@4M S 4096@4K S ** 3544K written in 129 ms ** $ echo "2M@6M 1492K@4M S 4096@4K S" | lsmtest io testfile.db 4096 ** 3544K written in 127 ms ** */ #include "lsmtest.h" typedef struct IoContext IoContext; struct IoContext { int fd; int nWrite; }; /* ** As isspace(3) */ static int safe_isspace(char c){ if( c&0x80) return 0; return isspace(c); } /* ** As isdigit(3) */ static int safe_isdigit(char c){ if( c&0x80) return 0; return isdigit(c); } static i64 getNextSize(char *zIn, char **pzOut, int *pRc){ i64 iRet = 0; if( *pRc==0 ){ char *z = zIn; if( !safe_isdigit(*z) ){ *pRc = 1; return 0; } /* Process digits */ while( safe_isdigit(*z) ){ iRet = iRet*10 + (*z - '0'); z++; } /* Process suffix */ switch( *z ){ case 'k': case 'K': iRet = iRet * 1024; z++; break; case 'm': case 'M': iRet = iRet * 1024 * 1024; z++; break; case 'g': case 'G': iRet = iRet * 1024 * 1024 * 1024; z++; break; } if( pzOut ) *pzOut = z; } return iRet; } static int doOneCmd( IoContext *pCtx, u8 *aData, int pgsz, char *zCmd, char **pzOut ){ char c; char *z = zCmd; while( safe_isspace(*z) ) z++; c = *z; if( c==0 ){ if( pzOut ) *pzOut = z; return 0; } if( c=='s' || c=='S' ){ if( pzOut ) *pzOut = &z[1]; return fdatasync(pCtx->fd); } if( safe_isdigit(c) ){ i64 iOff = 0; int nByte = 0; int rc = 0; int nPg; int iPg; nByte = (int)getNextSize(z, &z, &rc); if( rc || *z!='@' ) goto bad_command; z++; iOff = getNextSize(z, &z, &rc); if( rc || (safe_isspace(*z)==0 && *z!='\0') ) goto bad_command; if( pzOut ) *pzOut = z; nPg = (nByte+pgsz-1) / pgsz; lseek(pCtx->fd, (off_t)iOff, SEEK_SET); for(iPg=0; iPg<nPg; iPg++){ write(pCtx->fd, aData, pgsz); } pCtx->nWrite += nByte/1024; return 0; } bad_command: testPrintError("unrecognized command: %s", zCmd); return 1; } static int readStdin(char **pzOut){ int nAlloc = 128; char *zOut = 0; int nOut = 0; while( !feof(stdin) ){ int nRead; nAlloc = nAlloc*2; zOut = realloc(zOut, nAlloc); nRead = fread(&zOut[nOut], 1, nAlloc-nOut-1, stdin); if( nRead==0 ) break; nOut += nRead; zOut[nOut] = '\0'; } *pzOut = zOut; return 0; } int do_io(int nArg, char **azArg){ IoContext ctx; int pgsz; char *zFile; char *zPgsz; int i; int rc = 0; char *zStdin = 0; char *z; u8 *aData; memset(&ctx, 0, sizeof(IoContext)); if( nArg<2 ){ testPrintUsage("FILE PGSZ ?CMD-1 ...?"); return -1; } zFile = azArg[0]; zPgsz = azArg[1]; pgsz = (int)getNextSize(zPgsz, 0, &rc); if( pgsz<=0 ){ testPrintError("Ridiculous page size: %d", pgsz); return -1; } aData = malloc(pgsz); memset(aData, 0x77, pgsz); ctx.fd = open(zFile, O_RDWR|O_CREAT|_O_BINARY, 0644); if( ctx.fd<0 ){ perror("open: "); return -1; } if( nArg==2 ){ readStdin(&zStdin); testTimeInit(); z = zStdin; while( *z && rc==0 ){ rc = doOneCmd(&ctx, aData, pgsz, z, &z); } }else{ testTimeInit(); for(i=2; i<nArg; i++){ rc = doOneCmd(&ctx, aData, pgsz, azArg[i], 0); } } printf("%dK written in %d ms\n", ctx.nWrite, testTimeGet()); free(zStdin); close(ctx.fd); return 0; } |
Added ext/lsm1/lsm-test/lsmtest_main.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 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 | #include "lsmtest.h" #include <sqlite3.h> void test_failed(){ assert( 0 ); return; } #define testSetError(rc) testSetErrorFunc(rc, pRc, __FILE__, __LINE__) static void testSetErrorFunc(int rc, int *pRc, const char *zFile, int iLine){ if( rc ){ *pRc = rc; fprintf(stderr, "FAILED (%s:%d) rc=%d ", zFile, iLine, rc); test_failed(); } } static int lsm_memcmp(u8 *a, u8 *b, int c){ int i; for(i=0; i<c; i++){ if( a[i]!=b[i] ) return a[i] - b[i]; } return 0; } /* ** A test utility function. */ void testFetch( TestDb *pDb, /* Database handle */ void *pKey, int nKey, /* Key to query database for */ void *pVal, int nVal, /* Expected value */ int *pRc /* IN/OUT: Error code */ ){ if( *pRc==0 ){ void *pDbVal; int nDbVal; int rc; static int nCall = 0; nCall++; rc = tdb_fetch(pDb, pKey, nKey, &pDbVal, &nDbVal); testSetError(rc); if( rc==0 && (nVal!=nDbVal || (nVal>0 && lsm_memcmp(pVal, pDbVal, nVal))) ){ testSetError(1); } } } void testWrite( TestDb *pDb, /* Database handle */ void *pKey, int nKey, /* Key to query database for */ void *pVal, int nVal, /* Value to write */ int *pRc /* IN/OUT: Error code */ ){ if( *pRc==0 ){ int rc; static int nCall = 0; nCall++; rc = tdb_write(pDb, pKey, nKey, pVal, nVal); testSetError(rc); } } void testDelete( TestDb *pDb, /* Database handle */ void *pKey, int nKey, /* Key to query database for */ int *pRc /* IN/OUT: Error code */ ){ if( *pRc==0 ){ int rc; *pRc = rc = tdb_delete(pDb, pKey, nKey); testSetError(rc); } } void testDeleteRange( TestDb *pDb, /* Database handle */ void *pKey1, int nKey1, void *pKey2, int nKey2, int *pRc /* IN/OUT: Error code */ ){ if( *pRc==0 ){ int rc; *pRc = rc = tdb_delete_range(pDb, pKey1, nKey1, pKey2, nKey2); testSetError(rc); } } void testBegin(TestDb *pDb, int iTrans, int *pRc){ if( *pRc==0 ){ int rc; rc = tdb_begin(pDb, iTrans); testSetError(rc); } } void testCommit(TestDb *pDb, int iTrans, int *pRc){ if( *pRc==0 ){ int rc; rc = tdb_commit(pDb, iTrans); testSetError(rc); } } #if 0 /* unused */ static void testRollback(TestDb *pDb, int iTrans, int *pRc){ if( *pRc==0 ){ int rc; rc = tdb_rollback(pDb, iTrans); testSetError(rc); } } #endif void testWriteStr( TestDb *pDb, /* Database handle */ const char *zKey, /* Key to query database for */ const char *zVal, /* Value to write */ int *pRc /* IN/OUT: Error code */ ){ int nVal = (zVal ? strlen(zVal) : 0); testWrite(pDb, (void *)zKey, strlen(zKey), (void *)zVal, nVal, pRc); } #if 0 /* unused */ static void testDeleteStr(TestDb *pDb, const char *zKey, int *pRc){ testDelete(pDb, (void *)zKey, strlen(zKey), pRc); } #endif void testFetchStr( TestDb *pDb, /* Database handle */ const char *zKey, /* Key to query database for */ const char *zVal, /* Value to write */ int *pRc /* IN/OUT: Error code */ ){ int nVal = (zVal ? strlen(zVal) : 0); testFetch(pDb, (void *)zKey, strlen(zKey), (void *)zVal, nVal, pRc); } void testFetchCompare( TestDb *pControl, TestDb *pDb, void *pKey, int nKey, int *pRc ){ int rc; void *pDbVal1; void *pDbVal2; int nDbVal1; int nDbVal2; static int nCall = 0; nCall++; rc = tdb_fetch(pControl, pKey, nKey, &pDbVal1, &nDbVal1); testSetError(rc); rc = tdb_fetch(pDb, pKey, nKey, &pDbVal2, &nDbVal2); testSetError(rc); if( *pRc==0 && (nDbVal1!=nDbVal2 || (nDbVal1>0 && memcmp(pDbVal1, pDbVal2, nDbVal1))) ){ testSetError(1); } } typedef struct ScanResult ScanResult; struct ScanResult { TestDb *pDb; int nRow; u32 cksum1; u32 cksum2; void *pKey1; int nKey1; void *pKey2; int nKey2; int bReverse; int nPrevKey; u8 aPrevKey[256]; }; static int keyCompare(void *pKey1, int nKey1, void *pKey2, int nKey2){ int res; res = memcmp(pKey1, pKey2, MIN(nKey1, nKey2)); if( res==0 ){ res = nKey1 - nKey2; } return res; } int test_scan_debug = 0; static void scanCompareCb( void *pCtx, void *pKey, int nKey, void *pVal, int nVal ){ ScanResult *p = (ScanResult *)pCtx; u8 *aKey = (u8 *)pKey; u8 *aVal = (u8 *)pVal; int i; if( test_scan_debug ){ printf("%d: %.*s\n", p->nRow, nKey, (char *)pKey); fflush(stdout); } #if 0 if( test_scan_debug ) printf("%.20s\n", (char *)pVal); #endif #if 0 /* Check tdb_fetch() matches */ int rc = 0; testFetch(p->pDb, pKey, nKey, pVal, nVal, &rc); assert( rc==0 ); #endif /* Update the checksum data */ p->nRow++; for(i=0; i<nKey; i++){ p->cksum1 += ((int)aKey[i] << (i&0x0F)); p->cksum2 += p->cksum1; } for(i=0; i<nVal; i++){ p->cksum1 += ((int)aVal[i] << (i&0x0F)); p->cksum2 += p->cksum1; } /* Check that the delivered row is not out of order. */ if( nKey<(int)sizeof(p->aPrevKey) ){ if( p->nPrevKey ){ int res = keyCompare(p->aPrevKey, p->nPrevKey, pKey, nKey); if( (res<0 && p->bReverse) || (res>0 && p->bReverse==0) ){ testPrintError("Returned key out of order at %s:%d\n", __FILE__, __LINE__ ); } } p->nPrevKey = nKey; memcpy(p->aPrevKey, pKey, MIN(p->nPrevKey, nKey)); } /* Check that the delivered row is within range. */ if( p->pKey1 && ( (memcmp(p->pKey1, pKey, MIN(p->nKey1, nKey))>0) || (memcmp(p->pKey1, pKey, MIN(p->nKey1, nKey))==0 && p->nKey1>nKey) )){ testPrintError("Returned key too small at %s:%d\n", __FILE__, __LINE__); } if( p->pKey2 && ( (memcmp(p->pKey2, pKey, MIN(p->nKey2, nKey))<0) || (memcmp(p->pKey2, pKey, MIN(p->nKey2, nKey))==0 && p->nKey2<nKey) )){ testPrintError("Returned key too large at %s:%d\n", __FILE__, __LINE__); } } /* ** Scan the contents of the two databases. Check that they match. */ void testScanCompare( TestDb *pDb1, /* Control (trusted) database */ TestDb *pDb2, /* Database being tested */ int bReverse, void *pKey1, int nKey1, void *pKey2, int nKey2, int *pRc ){ static int nCall = 0; nCall++; if( *pRc==0 ){ ScanResult res1; ScanResult res2; void *pRes1 = (void *)&res1; void *pRes2 = (void *)&res2; memset(&res1, 0, sizeof(ScanResult)); memset(&res2, 0, sizeof(ScanResult)); res1.pDb = pDb1; res1.nKey1 = nKey1; res1.pKey1 = pKey1; res1.nKey2 = nKey2; res1.pKey2 = pKey2; res1.bReverse = bReverse; res2.pDb = pDb2; res2.nKey1 = nKey1; res2.pKey1 = pKey1; res2.nKey2 = nKey2; res2.pKey2 = pKey2; res2.bReverse = bReverse; tdb_scan(pDb1, pRes1, bReverse, pKey1, nKey1, pKey2, nKey2, scanCompareCb); if( test_scan_debug ) printf("\n\n\n"); tdb_scan(pDb2, pRes2, bReverse, pKey1, nKey1, pKey2, nKey2, scanCompareCb); if( test_scan_debug ) printf("\n\n\n"); if( res1.nRow!=res2.nRow || res1.cksum1!=res2.cksum1 || res1.cksum2!=res2.cksum2 ){ printf("expected: %d %X %X\n", res1.nRow, res1.cksum1, res1.cksum2); printf("got: %d %X %X\n", res2.nRow, res2.cksum1, res2.cksum2); testSetError(1); *pRc = 1; } } } void testClose(TestDb **ppDb){ tdb_close(*ppDb); *ppDb = 0; } TestDb *testOpen(const char *zSystem, int bClear, int *pRc){ TestDb *pDb = 0; if( *pRc==0 ){ int rc; rc = tdb_open(zSystem, 0, bClear, &pDb); if( rc!=0 ){ testSetError(rc); *pRc = rc; } } return pDb; } void testReopen(TestDb **ppDb, int *pRc){ if( *pRc==0 ){ const char *zLib; zLib = tdb_library_name(*ppDb); testClose(ppDb); *pRc = tdb_open(zLib, 0, 0, ppDb); } } #if 0 /* unused */ static void testSystemSelect(const char *zSys, int *piSel, int *pRc){ if( *pRc==0 ){ struct SysName { const char *zName; } *aName; int nSys; int i; for(nSys=0; tdb_system_name(nSys); nSys++); aName = malloc(sizeof(struct SysName) * (nSys+1)); for(i=0; i<=nSys; i++){ aName[i].zName = tdb_system_name(i); } *pRc = testArgSelect(aName, "db", zSys, piSel); free(aName); } } #endif char *testMallocVPrintf(const char *zFormat, va_list ap){ int nByte; va_list copy; char *zRet; __va_copy(copy, ap); nByte = vsnprintf(0, 0, zFormat, copy); va_end(copy); assert( nByte>=0 ); zRet = (char *)testMalloc(nByte+1); vsnprintf(zRet, nByte+1, zFormat, ap); return zRet; } char *testMallocPrintf(const char *zFormat, ...){ va_list ap; char *zRet; va_start(ap, zFormat); zRet = testMallocVPrintf(zFormat, ap); va_end(ap); return zRet; } /* ** A wrapper around malloc(3). ** ** This function should be used for all allocations made by test procedures. ** It has the following properties: ** ** * Test code may assume that allocations may not fail. ** * Returned memory is always zeroed. ** ** Allocations made using testMalloc() should be freed using testFree(). */ void *testMalloc(int n){ u8 *p = (u8*)malloc(n + 8); memset(p, 0, n+8); *(int*)p = n; return (void*)&p[8]; } void *testMallocCopy(void *pCopy, int nByte){ void *pRet = testMalloc(nByte); memcpy(pRet, pCopy, nByte); return pRet; } void *testRealloc(void *ptr, int n){ if( ptr ){ u8 *p = (u8*)ptr - 8; int nOrig = *(int*)p; p = (u8*)realloc(p, n+8); if( nOrig<n ){ memset(&p[8+nOrig], 0, n-nOrig); } *(int*)p = n; return (void*)&p[8]; } return testMalloc(n); } /* ** Free an allocation made by an earlier call to testMalloc(). */ void testFree(void *ptr){ if( ptr ){ u8 *p = (u8*)ptr - 8; memset(p, 0x55, *(int*)p + 8); free(p); } } /* ** String zPattern contains a glob pattern. Return true if zStr matches ** the pattern, or false if it does not. */ int testGlobMatch(const char *zPattern, const char *zStr){ int i = 0; int j = 0; while( zPattern[i] ){ char p = zPattern[i]; if( p=='*' || p=='%' ){ do { if( testGlobMatch(&zPattern[i+1], &zStr[j]) ) return 1; }while( zStr[j++] ); return 0; } if( zStr[j]==0 || (p!='?' && p!=zStr[j]) ){ /* Match failed. */ return 0; } j++; i++; } return (zPattern[i]==0 && zStr[j]==0); } /* ** End of test utilities **************************************************************************/ int do_test(int nArg, char **azArg){ int j; int rc; int nFail = 0; const char *zPattern = 0; if( nArg>1 ){ testPrintError("Usage: test ?PATTERN?\n"); return 1; } if( nArg==1 ){ zPattern = azArg[0]; } for(j=0; tdb_system_name(j); j++){ rc = 0; test_data_1(tdb_system_name(j), zPattern, &rc); test_data_2(tdb_system_name(j), zPattern, &rc); test_data_3(tdb_system_name(j), zPattern, &rc); test_data_4(tdb_system_name(j), zPattern, &rc); test_rollback(tdb_system_name(j), zPattern, &rc); test_mc(tdb_system_name(j), zPattern, &rc); test_mt(tdb_system_name(j), zPattern, &rc); if( rc ) nFail++; } rc = 0; test_oom(zPattern, &rc); if( rc ) nFail++; rc = 0; test_api(zPattern, &rc); if( rc ) nFail++; rc = 0; do_crash_test(zPattern, &rc); if( rc ) nFail++; rc = 0; do_writer_crash_test(zPattern, &rc); if( rc ) nFail++; return (nFail!=0); } static lsm_db *configure_lsm_db(TestDb *pDb){ lsm_db *pLsm; pLsm = tdb_lsm(pDb); if( pLsm ){ tdb_lsm_config_str(pDb, "mmap=1 autowork=1 automerge=4 worker_automerge=4"); } return pLsm; } typedef struct WriteHookEvent WriteHookEvent; struct WriteHookEvent { i64 iOff; int nData; int nUs; }; WriteHookEvent prev = {0, 0, 0}; static void flushPrev(FILE *pOut){ if( prev.nData ){ fprintf(pOut, "w %s %lld %d %d\n", "d", prev.iOff, prev.nData, prev.nUs); prev.nData = 0; } } #if 0 /* unused */ static void do_speed_write_hook2( void *pCtx, int bLog, i64 iOff, int nData, int nUs ){ FILE *pOut = (FILE *)pCtx; if( bLog ) return; if( prev.nData && nData && iOff==prev.iOff+prev.nData ){ prev.nData += nData; prev.nUs += nUs; }else{ flushPrev(pOut); if( nData==0 ){ fprintf(pOut, "s %s 0 0 %d\n", (bLog ? "l" : "d"), nUs); }else{ prev.iOff = iOff; prev.nData = nData; prev.nUs = nUs; } } } #endif #define ST_REPEAT 0 #define ST_WRITE 1 #define ST_PAUSE 2 #define ST_FETCH 3 #define ST_SCAN 4 #define ST_NSCAN 5 #define ST_KEYSIZE 6 #define ST_VALSIZE 7 #define ST_TRANS 8 static void print_speed_test_help(){ printf( "\n" "Repeat the following $repeat times:\n" " 1. Insert $write key-value pairs. One transaction for each write op.\n" " 2. Pause for $pause ms.\n" " 3. Perform $fetch queries on the database.\n" "\n" " Keys are $keysize bytes in size. Values are $valsize bytes in size\n" " Both keys and values are pseudo-randomly generated\n" "\n" "Options are:\n" " -repeat $repeat (default value 10)\n" " -write $write (default value 10000)\n" " -pause $pause (default value 0)\n" " -fetch $fetch (default value 0)\n" " -keysize $keysize (default value 12)\n" " -valsize $valsize (default value 100)\n" " -system $system (default value \"lsm\")\n" " -trans $trans (default value 0)\n" "\n" ); } int do_speed_test2(int nArg, char **azArg){ struct Option { const char *zOpt; int eVal; int iDefault; } aOpt[] = { { "-repeat", ST_REPEAT, 10}, { "-write", ST_WRITE, 10000}, { "-pause", ST_PAUSE, 0}, { "-fetch", ST_FETCH, 0}, { "-scan", ST_SCAN, 0}, { "-nscan", ST_NSCAN, 0}, { "-keysize", ST_KEYSIZE, 12}, { "-valsize", ST_VALSIZE, 100}, { "-trans", ST_TRANS, 0}, { "-system", -1, 0}, { "help", -2, 0}, {0, 0, 0} }; int i; int aParam[9]; int rc = 0; int bReadonly = 0; int nContent = 0; TestDb *pDb; Datasource *pData; DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 0, 0, 0, 0 }; char *zSystem = ""; int bLsm = 1; FILE *pLog = 0; #ifdef NDEBUG /* If NDEBUG is defined, disable the dynamic memory related checks in ** lsmtest_mem.c. They slow things down. */ testMallocUninstall(tdb_lsm_env()); #endif /* Initialize aParam[] with default values. */ for(i=0; i<ArraySize(aOpt); i++){ if( aOpt[i].zOpt ) aParam[aOpt[i].eVal] = aOpt[i].iDefault; } /* Process the command line switches. */ for(i=0; i<nArg; i+=2){ int iSel; rc = testArgSelect(aOpt, "switch", azArg[i], &iSel); if( rc ){ return rc; } if( aOpt[iSel].eVal==-2 ){ print_speed_test_help(); return 0; } if( i+1==nArg ){ testPrintError("option %s requires an argument\n", aOpt[iSel].zOpt); return 1; } if( aOpt[iSel].eVal>=0 ){ aParam[aOpt[iSel].eVal] = atoi(azArg[i+1]); }else{ zSystem = azArg[i+1]; bLsm = 0; #if 0 for(j=0; zSystem[j]; j++){ if( zSystem[j]=='=' ) bLsm = 1; } #endif } } printf("#"); for(i=0; i<ArraySize(aOpt); i++){ if( aOpt[i].zOpt ){ if( aOpt[i].eVal>=0 ){ printf(" %s=%d", &aOpt[i].zOpt[1], aParam[aOpt[i].eVal]); }else if( aOpt[i].eVal==-1 ){ printf(" %s=\"%s\"", &aOpt[i].zOpt[1], zSystem); } } } printf("\n"); defn.nMinKey = defn.nMaxKey = aParam[ST_KEYSIZE]; defn.nMinVal = defn.nMaxVal = aParam[ST_VALSIZE]; pData = testDatasourceNew(&defn); if( aParam[ST_WRITE]==0 ){ bReadonly = 1; } if( bLsm ){ rc = tdb_lsm_open(zSystem, "testdb.lsm", !bReadonly, &pDb); }else{ pDb = testOpen(zSystem, !bReadonly, &rc); } if( rc!=0 ) return rc; if( bReadonly ){ nContent = testCountDatabase(pDb); } #if 0 pLog = fopen("/tmp/speed.log", "w"); tdb_lsm_write_hook(pDb, do_speed_write_hook2, (void *)pLog); #endif for(i=0; i<aParam[ST_REPEAT] && rc==0; i++){ int msWrite, msFetch; int iFetch; int nWrite = aParam[ST_WRITE]; if( bReadonly ){ msWrite = 0; }else{ testTimeInit(); if( aParam[ST_TRANS] ) testBegin(pDb, 2, &rc); testWriteDatasourceRange(pDb, pData, i*nWrite, nWrite, &rc); if( aParam[ST_TRANS] ) testCommit(pDb, 0, &rc); msWrite = testTimeGet(); nContent += nWrite; } if( aParam[ST_PAUSE] ){ if( aParam[ST_PAUSE]/1000 ) sleep(aParam[ST_PAUSE]/1000); if( aParam[ST_PAUSE]%1000 ) usleep(1000 * (aParam[ST_PAUSE]%1000)); } if( aParam[ST_FETCH] ){ testTimeInit(); if( aParam[ST_TRANS] ) testBegin(pDb, 1, &rc); for(iFetch=0; iFetch<aParam[ST_FETCH]; iFetch++){ int iKey = testPrngValue(i*nWrite+iFetch) % nContent; #ifndef NDEBUG testDatasourceFetch(pDb, pData, iKey, &rc); #else void *pKey; int nKey; /* Database key to query for */ void *pVal; int nVal; /* Result of query */ testDatasourceEntry(pData, iKey, &pKey, &nKey, 0, 0); rc = tdb_fetch(pDb, pKey, nKey, &pVal, &nVal); if( rc==0 && nVal<0 ) rc = 1; if( rc ) break; #endif } if( aParam[ST_TRANS] ) testCommit(pDb, 0, &rc); msFetch = testTimeGet(); }else{ msFetch = 0; } if( i==(aParam[ST_REPEAT]-1) ){ testTimeInit(); testClose(&pDb); msWrite += testTimeGet(); } printf("%d %d %d\n", i, msWrite, msFetch); fflush(stdout); } testClose(&pDb); testDatasourceFree(pData); if( pLog ){ flushPrev(pLog); fclose(pLog); } return rc; } int do_speed_tests(int nArg, char **azArg){ struct DbSystem { const char *zLibrary; const char *zColor; } aSys[] = { { "sqlite3", "black" }, { "leveldb", "blue" }, { "lsm", "red" }, { "lsm_mt2", "orange" }, { "lsm_mt3", "purple" }, { "kyotocabinet", "green" }, {0, 0} }; int i; int j; int rc; int nSleep = 0; /* ms of rest allowed between INSERT tests */ int nRow = 0; /* Number of rows to insert into database */ int nStep; /* Measure INSERT time after this many rows */ int nSelStep; /* Measure SELECT time after this many rows */ int nSelTest; /* Number of SELECTs to run for timing */ int doReadTest = 1; int doWriteTest = 1; int *aTime; /* INSERT timing data */ int *aWrite; /* Writes per nStep inserts */ int *aSelTime; /* SELECT timing data */ int isFirst = 1; int bSleep = 0; /* File to write gnuplot script to. */ const char *zOut = "lsmtest_speed.gnuplot"; u32 sys_mask = 0; testMallocUninstall(tdb_lsm_env()); for(i=0; i<nArg; i++){ struct Opt { const char *zOpt; int isSwitch; } aOpt[] = { { "sqlite3" , 0}, { "leveldb" , 0}, { "lsm" , 0}, { "lsm_mt2" , 0}, { "lsm_mt3" , 0}, { "kyotocabinet" , 0}, { "-rows" , 1}, { "-sleep" , 2}, { "-testmode" , 3}, { "-out" , 4}, { 0, 0} }; int iSel; rc = testArgSelect(aOpt, "argument", azArg[i], &iSel); if( rc ) return rc; if( aOpt[iSel].isSwitch ){ i++; if( i>=nArg ){ testPrintError("option %s requires an argument\n", aOpt[iSel].zOpt); return 1; } if( aOpt[iSel].isSwitch==1 ){ nRow = atoi(azArg[i]); } if( aOpt[iSel].isSwitch==2 ){ nSleep = atoi(azArg[i]); } if( aOpt[iSel].isSwitch==3 ){ struct Mode { const char *zMode; int doReadTest; int doWriteTest; } aMode[] = {{"ro", 1, 0} , {"rw", 1, 1}, {"wo", 0, 1}, {0, 0, 0}}; int iMode; rc = testArgSelect(aMode, "option", azArg[i], &iMode); if( rc ) return rc; doReadTest = aMode[iMode].doReadTest; doWriteTest = aMode[iMode].doWriteTest; } if( aOpt[iSel].isSwitch==4 ){ /* The "-out FILE" switch. This option is used to specify a file to ** write the gnuplot script to. */ zOut = azArg[i]; } }else{ /* A db name */ rc = testArgSelect(aOpt, "system", azArg[i], &iSel); if( rc ) return rc; sys_mask |= (1<<iSel); } } if( sys_mask==0 ) sys_mask = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3); nRow = MAX(nRow, 100000); nStep = nRow/100; nSelStep = nRow/10; nSelTest = (nSelStep > 100000) ? 100000 : nSelStep; aTime = malloc(sizeof(int) * ArraySize(aSys) * nRow/nStep); aWrite = malloc(sizeof(int) * nRow/nStep); aSelTime = malloc(sizeof(int) * ArraySize(aSys) * nRow/nSelStep); /* This loop collects the INSERT speed data. */ if( doWriteTest ){ printf("Writing output to file \"%s\".\n", zOut); for(j=0; aSys[j].zLibrary; j++){ FILE *pLog = 0; TestDb *pDb; /* Database being tested */ lsm_db *pLsm; int iDot = 0; if( ((1<<j)&sys_mask)==0 ) continue; if( bSleep && nSleep ) sqlite3_sleep(nSleep); bSleep = 1; testCaseBegin(&rc, 0, "speed.insert.%s", aSys[j].zLibrary); rc = tdb_open(aSys[j].zLibrary, 0, 1, &pDb); if( rc ) return rc; pLsm = configure_lsm_db(pDb); #if 0 pLog = fopen("/tmp/speed.log", "w"); tdb_lsm_write_hook(pDb, do_speed_write_hook2, (void *)pLog); #endif testTimeInit(); for(i=0; i<nRow; i+=nStep){ int iStep; int nWrite1 = 0, nWrite2 = 0; testCaseProgress(i, nRow, testCaseNDot(), &iDot); if( pLsm ) lsm_info(pLsm, LSM_INFO_NWRITE, &nWrite1); for(iStep=0; iStep<nStep; iStep++){ u32 aKey[4]; /* 16-byte key */ u32 aVal[25]; /* 100 byte value */ testPrngArray(i+iStep, aKey, ArraySize(aKey)); testPrngArray(i+iStep, aVal, ArraySize(aVal)); rc = tdb_write(pDb, aKey, sizeof(aKey), aVal, sizeof(aVal)); } aTime[(j*nRow+i)/nStep] = testTimeGet(); if( pLsm ) lsm_info(pLsm, LSM_INFO_NWRITE, &nWrite2); aWrite[i/nStep] = nWrite2 - nWrite1; } tdb_close(pDb); if( pLog ) fclose(pLog); testCaseFinish(rc); } } /* This loop collects the SELECT speed data. */ if( doReadTest ){ for(j=0; aSys[j].zLibrary; j++){ int iDot = 0; TestDb *pDb; /* Database being tested */ if( ((1<<j)&sys_mask)==0 ) continue; if( bSleep && nSleep ) sqlite3_sleep(nSleep); bSleep = 1; testCaseBegin(&rc, 0, "speed.select.%s", aSys[j].zLibrary); if( doWriteTest ){ rc = tdb_open(aSys[j].zLibrary, 0, 1, &pDb); if( rc ) return rc; configure_lsm_db(pDb); for(i=0; i<nRow; i+=nSelStep){ int iStep; int iSel; testCaseProgress(i, nRow, testCaseNDot(), &iDot); for(iStep=0; iStep<nSelStep; iStep++){ u32 aKey[4]; /* 16-byte key */ u32 aVal[25]; /* 100 byte value */ testPrngArray(i+iStep, aKey, ArraySize(aKey)); testPrngArray(i+iStep, aVal, ArraySize(aVal)); rc = tdb_write(pDb, aKey, sizeof(aKey), aVal, sizeof(aVal)); } testTimeInit(); for(iSel=0; iSel<nSelTest; iSel++){ void *pDummy; int nDummy; u32 iKey; u32 aKey[4]; /* 16-byte key */ iKey = testPrngValue(iSel) % (i+nSelStep); testPrngArray(iKey, aKey, ArraySize(aKey)); rc = tdb_fetch(pDb, aKey, sizeof(aKey), &pDummy, &nDummy); } aSelTime[(j*nRow+i)/nSelStep] = testTimeGet(); tdb_fetch(pDb, 0, 0, 0, 0); } }else{ int t; int iSel; rc = tdb_open(aSys[j].zLibrary, 0, 0, &pDb); configure_lsm_db(pDb); testTimeInit(); for(iSel=0; rc==LSM_OK && iSel<nSelTest; iSel++){ void *pDummy; int nDummy; u32 iKey; u32 aKey[4]; /* 16-byte key */ #ifndef NDEBUG u32 aVal[25]; /* 100 byte value */ #endif testCaseProgress(iSel, nSelTest, testCaseNDot(), &iDot); iKey = testPrngValue(iSel) % nRow; testPrngArray(iKey, aKey, ArraySize(aKey)); rc = tdb_fetch(pDb, aKey, sizeof(aKey), &pDummy, &nDummy); #ifndef NDEBUG testPrngArray(iKey, aVal, ArraySize(aVal)); assert( nDummy==100 && memcmp(aVal, pDummy, 100)==0 ); #endif } if( rc!=LSM_OK ) return rc; t = testTimeGet(); tdb_fetch(pDb, 0, 0, 0, 0); printf("%s: %d selects/second\n", aSys[j].zLibrary, (int)((double)nSelTest*1000.0/t) ); } tdb_close(pDb); testCaseFinish(rc); } } if( doWriteTest ){ FILE *pOut = fopen(zOut, "w"); if( !pOut ){ printf("fopen(\"%s\", \"w\"): %s\n", zOut, strerror(errno)); return 1; } fprintf(pOut, "set xlabel \"Rows Inserted\"\n"); fprintf(pOut, "set ylabel \"Inserts per second\"\n"); if( doReadTest ){ fprintf(pOut, "set y2label \"Selects per second\"\n"); }else if( sys_mask==(1<<2) ){ fprintf(pOut, "set y2label \"Page writes per insert\"\n"); } fprintf(pOut, "set yrange [0:*]\n"); fprintf(pOut, "set y2range [0:*]\n"); fprintf(pOut, "set xrange [%d:*]\n", MAX(nStep, nRow/20) ); fprintf(pOut, "set ytics nomirror\n"); fprintf(pOut, "set y2tics nomirror\n"); fprintf(pOut, "set key box lw 0.01\n"); fprintf(pOut, "plot "); for(j=0; aSys[j].zLibrary; j++){ if( (1<<j)&sys_mask ){ const char *zLib = aSys[j].zLibrary; fprintf(pOut, "%s\"-\" ti \"%s INSERT\" with lines lc rgb \"%s\" ", (isFirst?"":", "), zLib, aSys[j].zColor ); if( doReadTest ){ fprintf(pOut, ", \"-\" ti \"%s SELECT\" " "axis x1y2 with points lw 3 lc rgb \"%s\"" , zLib, aSys[j].zColor ); } isFirst = 0; } } assert( strcmp(aSys[2].zLibrary, "lsm")==0 ); if( sys_mask==(1<<2) && !doReadTest ){ fprintf(pOut, ", \"-\" ti \"lsm pages written\" " "axis x1y2 with boxes lw 1 lc rgb \"grey\"" ); } fprintf(pOut, "\n"); for(j=0; aSys[j].zLibrary; j++){ if( ((1<<j)&sys_mask)==0 ) continue; fprintf(pOut, "# Rows Inserts per second\n"); for(i=0; i<nRow; i+=nStep){ int iTime = aTime[(j*nRow+i)/nStep]; int ips = (int)((i+nStep)*1000.0 / (double)iTime); fprintf(pOut, "%d %d\n", i+nStep, ips); } fprintf(pOut, "end\n"); if( doReadTest ){ fprintf(pOut, "# Rows Selects per second\n"); for(i=0; i<nRow; i+=nSelStep){ int sps = (int)(nSelTest*1000.0/(double)aSelTime[(j*nRow+i)/nSelStep]); fprintf(pOut, "%d %d\n", i+nSelStep, sps); } fprintf(pOut, "end\n"); }else if( sys_mask==(1<<2) ){ for(i=0; i<(nRow/nStep); i++){ fprintf(pOut, "%d %f\n", i*nStep, (double)aWrite[i] / (double)nStep); } fprintf(pOut, "end\n"); } } fprintf(pOut, "pause -1\n"); fclose(pOut); } free(aTime); free(aSelTime); free(aWrite); testMallocInstall(tdb_lsm_env()); return 0; } /* ** Usage: lsmtest random ?N? ** ** This command prints a sequence of zero or more numbers from the PRNG ** system to stdout. If the "N" argument is missing, values the first 10 ** values (i=0, i=1, ... i=9) are printed. Otherwise, the first N. ** ** This was added to verify that the PRNG values do not change between ** runs of the lsmtest program. */ int do_random_tests(int nArg, char **azArg){ int i; int nRand; if( nArg==0 ){ nRand = 10; }else if( nArg==1 ){ nRand = atoi(azArg[0]); }else{ testPrintError("Usage: random ?N?\n"); return -1; } for(i=0; i<nRand; i++){ printf("0x%x\n", testPrngValue(i)); } return 0; } static int testFormatSize(char *aBuf, int nBuf, i64 nByte){ int res; if( nByte<(1<<10) ){ res = snprintf(aBuf, nBuf, "%d byte", (int)nByte); }else if( nByte<(1<<20) ){ res = snprintf(aBuf, nBuf, "%dK", (int)(nByte/(1<<10))); }else{ res = snprintf(aBuf, nBuf, "%dM", (int)(nByte/(1<<20))); } return res; } static i64 testReadSize(char *z){ int n = strlen(z); char c = z[n-1]; i64 nMul = 1; switch( c ){ case 'g': case 'G': nMul = (1<<30); break; case 'm': case 'M': nMul = (1<<20); break; case 'k': case 'K': nMul = (1<<10); break; default: nMul = 1; } return nMul * (i64)atoi(z); } /* ** Usage: lsmtest writespeed FILESIZE BLOCKSIZE SYNCSIZE */ static int do_writer_test(int nArg, char **azArg){ int nBlock; int nSize; int i; int fd; int ms; char aFilesize[32]; char aBlockSize[32]; char *aPage; int *aOrder; int nSync; i64 filesize; i64 blocksize; i64 syncsize; int nPage = 4096; /* How long to sleep before running a trial (in ms). */ #if 0 const int nSleep = 10000; #endif const int nSleep = 0; if( nArg!=3 ){ testPrintUsage("FILESIZE BLOCKSIZE SYNCSIZE"); return -1; } filesize = testReadSize(azArg[0]); blocksize = testReadSize(azArg[1]); syncsize = testReadSize(azArg[2]); nBlock = (int)(filesize / blocksize); nSize = (int)blocksize; nSync = (int)(syncsize / blocksize); aPage = (char *)malloc(4096); aOrder = (int *)malloc(nBlock * sizeof(int)); for(i=0; i<nBlock; i++) aOrder[i] = i; for(i=0; i<(nBlock*25); i++){ int tmp; u32 a = testPrngValue(i); u32 b = testPrngValue(a); a = a % nBlock; b = b % nBlock; tmp = aOrder[a]; aOrder[a] = aOrder[b]; aOrder[b] = tmp; } testFormatSize(aFilesize, sizeof(aFilesize), (i64)nBlock * (i64)nSize); testFormatSize(aBlockSize, sizeof(aFilesize), nSize); printf("Testing writing a %s file using %s blocks. ", aFilesize, aBlockSize); if( nSync==1 ){ printf("Sync after each block.\n"); }else{ printf("Sync after each %d blocks.\n", nSync); } printf("Preparing file... "); fflush(stdout); unlink("writer.out"); fd = open("writer.out", O_RDWR|O_CREAT|_O_BINARY, 0664); if( fd<0 ){ testPrintError("open(): %d - %s\n", errno, strerror(errno)); return -1; } testTimeInit(); for(i=0; i<nBlock; i++){ int iPg; memset(aPage, i&0xFF, nPage); for(iPg=0; iPg<(nSize/nPage); iPg++){ write(fd, aPage, nPage); } } fsync(fd); printf("ok (%d ms)\n", testTimeGet()); for(i=0; i<5; i++){ int j; sqlite3_sleep(nSleep); printf("Now writing sequentially... "); fflush(stdout); lseek(fd, 0, SEEK_SET); testTimeInit(); for(j=0; j<nBlock; j++){ int iPg; if( ((j+1)%nSync)==0 ) fdatasync(fd); memset(aPage, j&0xFF, nPage); for(iPg=0; iPg<(nSize/nPage); iPg++){ write(fd, aPage, nPage); } } fdatasync(fd); ms = testTimeGet(); printf("%d ms\n", ms); sqlite3_sleep(nSleep); printf("Now in an arbitrary order... "); fflush(stdout); testTimeInit(); for(j=0; j<nBlock; j++){ int iPg; if( ((j+1)%nSync)==0 ) fdatasync(fd); lseek(fd, aOrder[j]*nSize, SEEK_SET); memset(aPage, j&0xFF, nPage); for(iPg=0; iPg<(nSize/nPage); iPg++){ write(fd, aPage, nPage); } } fdatasync(fd); ms = testTimeGet(); printf("%d ms\n", ms); } close(fd); free(aPage); free(aOrder); return 0; } static void do_insert_work_hook(lsm_db *db, void *p){ char *z = 0; lsm_info(db, LSM_INFO_DB_STRUCTURE, &z); if( z ){ printf("%s\n", z); fflush(stdout); lsm_free(lsm_get_env(db), z); } unused_parameter(p); } typedef struct InsertWriteHook InsertWriteHook; struct InsertWriteHook { FILE *pOut; int bLog; i64 iOff; int nData; }; static void flushHook(InsertWriteHook *pHook){ if( pHook->nData ){ fprintf(pHook->pOut, "write %s %d %d\n", (pHook->bLog ? "log" : "db"), (int)pHook->iOff, pHook->nData ); pHook->nData = 0; fflush(pHook->pOut); } } static void do_insert_write_hook( void *pCtx, int bLog, i64 iOff, int nData, int nUs ){ InsertWriteHook *pHook = (InsertWriteHook *)pCtx; if( bLog ) return; if( nData==0 ){ flushHook(pHook); fprintf(pHook->pOut, "sync %s\n", (bLog ? "log" : "db")); }else if( pHook->nData && bLog==pHook->bLog && iOff==(pHook->iOff+pHook->nData) ){ pHook->nData += nData; }else{ flushHook(pHook); pHook->bLog = bLog; pHook->iOff = iOff; pHook->nData = nData; } } static int do_replay(int nArg, char **azArg){ char aBuf[4096]; FILE *pInput; FILE *pClose = 0; const char *zDb; lsm_env *pEnv; lsm_file *pOut; int rc; if( nArg!=2 ){ testPrintError("Usage: replay WRITELOG FILE\n"); return 1; } if( strcmp(azArg[0], "-")==0 ){ pInput = stdin; }else{ pClose = pInput = fopen(azArg[0], "r"); } zDb = azArg[1]; pEnv = tdb_lsm_env(); rc = pEnv->xOpen(pEnv, zDb, 0, &pOut); if( rc!=LSM_OK ) return rc; while( feof(pInput)==0 ){ char zLine[80]; fgets(zLine, sizeof(zLine)-1, pInput); zLine[sizeof(zLine)-1] = '\0'; if( 0==memcmp("sync db", zLine, 7) ){ rc = pEnv->xSync(pOut); if( rc!=0 ) break; }else{ int iOff; int nData; int nMatch; nMatch = sscanf(zLine, "write db %d %d", &iOff, &nData); if( nMatch==2 ){ int i; for(i=0; i<nData; i+=sizeof(aBuf)){ memset(aBuf, i&0xFF, sizeof(aBuf)); rc = pEnv->xWrite(pOut, iOff+i, aBuf, sizeof(aBuf)); if( rc!=0 ) break; } } } } if( pClose ) fclose(pClose); pEnv->xClose(pOut); return rc; } static int do_insert(int nArg, char **azArg){ const char *zDb = "lsm"; TestDb *pDb = 0; int i; int rc; const int nRow = 1 * 1000 * 1000; DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 8, 15, 80, 150 }; Datasource *pData = 0; if( nArg>1 ){ testPrintError("Usage: insert ?DATABASE?\n"); return 1; } if( nArg==1 ){ zDb = azArg[0]; } testMallocUninstall(tdb_lsm_env()); for(i=0; zDb[i] && zDb[i]!='='; i++); if( zDb[i] ){ rc = tdb_lsm_open(zDb, "testdb.lsm", 1, &pDb); }else{ rc = tdb_open(zDb, 0, 1, &pDb); } if( rc!=0 ){ testPrintError("Error opening db \"%s\": %d\n", zDb, rc); }else{ InsertWriteHook hook; memset(&hook, 0, sizeof(hook)); hook.pOut = fopen("writelog.txt", "w"); pData = testDatasourceNew(&defn); tdb_lsm_config_work_hook(pDb, do_insert_work_hook, 0); tdb_lsm_write_hook(pDb, do_insert_write_hook, (void *)&hook); if( rc==0 ){ for(i=0; i<nRow; i++){ void *pKey; int nKey; /* Database key to insert */ void *pVal; int nVal; /* Database value to insert */ testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal); tdb_write(pDb, pKey, nKey, pVal, nVal); } } testDatasourceFree(pData); tdb_close(pDb); flushHook(&hook); fclose(hook.pOut); } testMallocInstall(tdb_lsm_env()); return rc; } static int st_do_show(int a, char **b) { return do_show(a, b); } static int st_do_work(int a, char **b) { return do_work(a, b); } static int st_do_io(int a, char **b) { return do_io(a, b); } #ifdef __linux__ #include <sys/time.h> #include <sys/resource.h> static void lsmtest_rusage_report(void){ struct rusage r; memset(&r, 0, sizeof(r)); getrusage(RUSAGE_SELF, &r); printf("# getrusage: { ru_maxrss %d ru_oublock %d ru_inblock %d }\n", (int)r.ru_maxrss, (int)r.ru_oublock, (int)r.ru_inblock ); } #else static void lsmtest_rusage_report(void){ /* no-op */ } #endif int main(int argc, char **argv){ struct TestFunc { const char *zName; int bRusageReport; int (*xFunc)(int, char **); } aTest[] = { {"random", 1, do_random_tests}, {"writespeed", 1, do_writer_test}, {"io", 1, st_do_io}, {"insert", 1, do_insert}, {"replay", 1, do_replay}, {"speed", 1, do_speed_tests}, {"speed2", 1, do_speed_test2}, {"show", 0, st_do_show}, {"work", 1, st_do_work}, {"test", 1, do_test}, {0, 0} }; int rc; /* Return Code */ int iFunc; /* Index into aTest[] */ int nLeakAlloc = 0; /* Allocations leaked by lsm */ int nLeakByte = 0; /* Bytes leaked by lsm */ #ifdef LSM_DEBUG_MEM FILE *pReport = 0; /* lsm malloc() report file */ const char *zReport = "malloc.txt generated"; #else const char *zReport = "malloc.txt NOT generated"; #endif testMallocInstall(tdb_lsm_env()); if( argc<2 ){ testPrintError("Usage: %s sub-command ?args...?\n", argv[0]); return -1; } /* Initialize error reporting */ testErrorInit(argc, argv); /* Initialize PRNG system */ testPrngInit(); rc = testArgSelect(aTest, "sub-command", argv[1], &iFunc); if( rc==0 ){ rc = aTest[iFunc].xFunc(argc-2, &argv[2]); } #ifdef LSM_DEBUG_MEM pReport = fopen("malloc.txt", "w"); testMallocCheck(tdb_lsm_env(), &nLeakAlloc, &nLeakByte, pReport); fclose(pReport); #else testMallocCheck(tdb_lsm_env(), &nLeakAlloc, &nLeakByte, 0); #endif if( nLeakAlloc ){ testPrintError("Leaked %d bytes in %d allocations (%s)\n", nLeakByte, nLeakAlloc, zReport ); if( rc==0 ) rc = -1; } testMallocUninstall(tdb_lsm_env()); if( aTest[iFunc].bRusageReport ){ lsmtest_rusage_report(); } return rc; } |
Added ext/lsm1/lsm-test/lsmtest_mem.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 | #include <stdio.h> #include <assert.h> #include <string.h> #define ArraySize(x) ((int)(sizeof(x) / sizeof((x)[0]))) #define MIN(x,y) ((x)<(y) ? (x) : (y)) typedef unsigned int u32; typedef unsigned char u8; typedef long long int i64; typedef unsigned long long int u64; #if defined(__GLIBC__) && defined(LSM_DEBUG_MEM) extern int backtrace(void**,int); extern void backtrace_symbols_fd(void*const*,int,int); # define TM_BACKTRACE 12 #else # define backtrace(A,B) 1 # define backtrace_symbols_fd(A,B,C) #endif typedef struct TmBlockHdr TmBlockHdr; typedef struct TmAgg TmAgg; typedef struct TmGlobal TmGlobal; struct TmGlobal { /* Linked list of all currently outstanding allocations. And a table of ** all allocations, past and present, indexed by backtrace() info. */ TmBlockHdr *pFirst; #ifdef TM_BACKTRACE TmAgg *aHash[10000]; #endif /* Underlying malloc/realloc/free functions */ void *(*xMalloc)(int); /* underlying malloc(3) function */ void *(*xRealloc)(void *, int); /* underlying realloc(3) function */ void (*xFree)(void *); /* underlying free(3) function */ /* Mutex to protect pFirst and aHash */ void (*xEnterMutex)(TmGlobal*); /* Call this to enter the mutex */ void (*xLeaveMutex)(TmGlobal*); /* Call this to leave mutex */ void (*xDelMutex)(TmGlobal*); /* Call this to delete mutex */ void *pMutex; /* Mutex handle */ void *(*xSaveMalloc)(void *, size_t); void *(*xSaveRealloc)(void *, void *, size_t); void (*xSaveFree)(void *, void *); /* OOM injection scheduling. If nCountdown is greater than zero when a ** malloc attempt is made, it is decremented. If this means nCountdown ** transitions from 1 to 0, then the allocation fails. If bPersist is true ** when this happens, nCountdown is then incremented back to 1 (so that the ** next attempt fails too). */ int nCountdown; int bPersist; int bEnable; void (*xHook)(void *); void *pHookCtx; }; struct TmBlockHdr { TmBlockHdr *pNext; TmBlockHdr *pPrev; int nByte; #ifdef TM_BACKTRACE TmAgg *pAgg; #endif u32 iForeGuard; }; #ifdef TM_BACKTRACE struct TmAgg { int nAlloc; /* Number of allocations at this path */ int nByte; /* Total number of bytes allocated */ int nOutAlloc; /* Number of outstanding allocations */ int nOutByte; /* Number of outstanding bytes */ void *aFrame[TM_BACKTRACE]; /* backtrace() output */ TmAgg *pNext; /* Next object in hash-table collision */ }; #endif #define FOREGUARD 0x80F5E153 #define REARGUARD 0xE4676B53 static const u32 rearguard = REARGUARD; #define ROUND8(x) (((x)+7)&~7) #define BLOCK_HDR_SIZE (ROUND8( sizeof(TmBlockHdr) )) static void lsmtest_oom_error(void){ static int nErr = 0; nErr++; } static void tmEnterMutex(TmGlobal *pTm){ pTm->xEnterMutex(pTm); } static void tmLeaveMutex(TmGlobal *pTm){ pTm->xLeaveMutex(pTm); } static void *tmMalloc(TmGlobal *pTm, int nByte){ TmBlockHdr *pNew; /* New allocation header block */ u8 *pUser; /* Return value */ int nReq; /* Total number of bytes requested */ assert( sizeof(rearguard)==4 ); nReq = BLOCK_HDR_SIZE + nByte + 4; pNew = (TmBlockHdr *)pTm->xMalloc(nReq); memset(pNew, 0, sizeof(TmBlockHdr)); tmEnterMutex(pTm); assert( pTm->nCountdown>=0 ); assert( pTm->bPersist==0 || pTm->bPersist==1 ); if( pTm->bEnable && pTm->nCountdown==1 ){ /* Simulate an OOM error. */ lsmtest_oom_error(); pTm->xFree(pNew); pTm->nCountdown = pTm->bPersist; if( pTm->xHook ) pTm->xHook(pTm->pHookCtx); pUser = 0; }else{ if( pTm->bEnable && pTm->nCountdown ) pTm->nCountdown--; pNew->iForeGuard = FOREGUARD; pNew->nByte = nByte; pNew->pNext = pTm->pFirst; if( pTm->pFirst ){ pTm->pFirst->pPrev = pNew; } pTm->pFirst = pNew; pUser = &((u8 *)pNew)[BLOCK_HDR_SIZE]; memset(pUser, 0x56, nByte); memcpy(&pUser[nByte], &rearguard, 4); #ifdef TM_BACKTRACE { TmAgg *pAgg; int i; u32 iHash = 0; void *aFrame[TM_BACKTRACE]; memset(aFrame, 0, sizeof(aFrame)); backtrace(aFrame, TM_BACKTRACE); for(i=0; i<ArraySize(aFrame); i++){ iHash += (u64)(aFrame[i]) + (iHash<<3); } iHash = iHash % ArraySize(pTm->aHash); for(pAgg=pTm->aHash[iHash]; pAgg; pAgg=pAgg->pNext){ if( memcmp(pAgg->aFrame, aFrame, sizeof(aFrame))==0 ) break; } if( !pAgg ){ pAgg = (TmAgg *)pTm->xMalloc(sizeof(TmAgg)); memset(pAgg, 0, sizeof(TmAgg)); memcpy(pAgg->aFrame, aFrame, sizeof(aFrame)); pAgg->pNext = pTm->aHash[iHash]; pTm->aHash[iHash] = pAgg; } pAgg->nAlloc++; pAgg->nByte += nByte; pAgg->nOutAlloc++; pAgg->nOutByte += nByte; pNew->pAgg = pAgg; } #endif } tmLeaveMutex(pTm); return pUser; } static void tmFree(TmGlobal *pTm, void *p){ if( p ){ TmBlockHdr *pHdr; u8 *pUser = (u8 *)p; tmEnterMutex(pTm); pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE); assert( pHdr->iForeGuard==FOREGUARD ); assert( 0==memcmp(&pUser[pHdr->nByte], &rearguard, 4) ); if( pHdr->pPrev ){ assert( pHdr->pPrev->pNext==pHdr ); pHdr->pPrev->pNext = pHdr->pNext; }else{ assert( pHdr==pTm->pFirst ); pTm->pFirst = pHdr->pNext; } if( pHdr->pNext ){ assert( pHdr->pNext->pPrev==pHdr ); pHdr->pNext->pPrev = pHdr->pPrev; } #ifdef TM_BACKTRACE pHdr->pAgg->nOutAlloc--; pHdr->pAgg->nOutByte -= pHdr->nByte; #endif tmLeaveMutex(pTm); memset(pUser, 0x58, pHdr->nByte); memset(pHdr, 0x57, sizeof(TmBlockHdr)); pTm->xFree(pHdr); } } static void *tmRealloc(TmGlobal *pTm, void *p, int nByte){ void *pNew; pNew = tmMalloc(pTm, nByte); if( pNew && p ){ TmBlockHdr *pHdr; u8 *pUser = (u8 *)p; pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE); memcpy(pNew, p, MIN(nByte, pHdr->nByte)); tmFree(pTm, p); } return pNew; } static void tmMallocOom( TmGlobal *pTm, int nCountdown, int bPersist, void (*xHook)(void *), void *pHookCtx ){ assert( nCountdown>=0 ); assert( bPersist==0 || bPersist==1 ); pTm->nCountdown = nCountdown; pTm->bPersist = bPersist; pTm->xHook = xHook; pTm->pHookCtx = pHookCtx; pTm->bEnable = 1; } static void tmMallocOomEnable( TmGlobal *pTm, int bEnable ){ pTm->bEnable = bEnable; } static void tmMallocCheck( TmGlobal *pTm, int *pnLeakAlloc, int *pnLeakByte, FILE *pFile ){ TmBlockHdr *pHdr; int nLeak = 0; int nByte = 0; if( pTm==0 ) return; for(pHdr=pTm->pFirst; pHdr; pHdr=pHdr->pNext){ nLeak++; nByte += pHdr->nByte; } if( pnLeakAlloc ) *pnLeakAlloc = nLeak; if( pnLeakByte ) *pnLeakByte = nByte; #ifdef TM_BACKTRACE if( pFile ){ int i; fprintf(pFile, "LEAKS\n"); for(i=0; i<ArraySize(pTm->aHash); i++){ TmAgg *pAgg; for(pAgg=pTm->aHash[i]; pAgg; pAgg=pAgg->pNext){ if( pAgg->nOutAlloc ){ int j; fprintf(pFile, "%d %d ", pAgg->nOutByte, pAgg->nOutAlloc); for(j=0; j<TM_BACKTRACE; j++){ fprintf(pFile, "%p ", pAgg->aFrame[j]); } fprintf(pFile, "\n"); } } } fprintf(pFile, "\nALLOCATIONS\n"); for(i=0; i<ArraySize(pTm->aHash); i++){ TmAgg *pAgg; for(pAgg=pTm->aHash[i]; pAgg; pAgg=pAgg->pNext){ int j; fprintf(pFile, "%d %d ", pAgg->nByte, pAgg->nAlloc); for(j=0; j<TM_BACKTRACE; j++) fprintf(pFile, "%p ", pAgg->aFrame[j]); fprintf(pFile, "\n"); } } } #else (void)pFile; #endif } #include "lsm.h" #include "stdlib.h" typedef struct LsmMutex LsmMutex; struct LsmMutex { lsm_env *pEnv; lsm_mutex *pMutex; }; static void tmLsmMutexEnter(TmGlobal *pTm){ LsmMutex *p = (LsmMutex *)pTm->pMutex; p->pEnv->xMutexEnter(p->pMutex); } static void tmLsmMutexLeave(TmGlobal *pTm){ LsmMutex *p = (LsmMutex *)(pTm->pMutex); p->pEnv->xMutexLeave(p->pMutex); } static void tmLsmMutexDel(TmGlobal *pTm){ LsmMutex *p = (LsmMutex *)pTm->pMutex; pTm->xFree(p); } static void *tmLsmMalloc(int n){ return malloc(n); } static void tmLsmFree(void *ptr){ free(ptr); } static void *tmLsmRealloc(void *ptr, int n){ return realloc(ptr, n); } static void *tmLsmEnvMalloc(lsm_env *p, size_t n){ return tmMalloc((TmGlobal *)(p->pMemCtx), n); } static void tmLsmEnvFree(lsm_env *p, void *ptr){ tmFree((TmGlobal *)(p->pMemCtx), ptr); } static void *tmLsmEnvRealloc(lsm_env *p, void *ptr, size_t n){ return tmRealloc((TmGlobal *)(p->pMemCtx), ptr, n); } void testMallocInstall(lsm_env *pEnv){ TmGlobal *pGlobal; LsmMutex *pMutex; assert( pEnv->pMemCtx==0 ); /* Allocate and populate a TmGlobal structure. */ pGlobal = (TmGlobal *)tmLsmMalloc(sizeof(TmGlobal)); memset(pGlobal, 0, sizeof(TmGlobal)); pGlobal->xMalloc = tmLsmMalloc; pGlobal->xRealloc = tmLsmRealloc; pGlobal->xFree = tmLsmFree; pMutex = (LsmMutex *)pGlobal->xMalloc(sizeof(LsmMutex)); pMutex->pEnv = pEnv; pEnv->xMutexStatic(pEnv, LSM_MUTEX_HEAP, &pMutex->pMutex); pGlobal->xEnterMutex = tmLsmMutexEnter; pGlobal->xLeaveMutex = tmLsmMutexLeave; pGlobal->xDelMutex = tmLsmMutexDel; pGlobal->pMutex = (void *)pMutex; pGlobal->xSaveMalloc = pEnv->xMalloc; pGlobal->xSaveRealloc = pEnv->xRealloc; pGlobal->xSaveFree = pEnv->xFree; /* Set up pEnv to the use the new TmGlobal */ pEnv->pMemCtx = (void *)pGlobal; pEnv->xMalloc = tmLsmEnvMalloc; pEnv->xRealloc = tmLsmEnvRealloc; pEnv->xFree = tmLsmEnvFree; } void testMallocUninstall(lsm_env *pEnv){ TmGlobal *p = (TmGlobal *)pEnv->pMemCtx; pEnv->pMemCtx = 0; if( p ){ pEnv->xMalloc = p->xSaveMalloc; pEnv->xRealloc = p->xSaveRealloc; pEnv->xFree = p->xSaveFree; p->xDelMutex(p); tmLsmFree(p); } } void testMallocCheck( lsm_env *pEnv, int *pnLeakAlloc, int *pnLeakByte, FILE *pFile ){ if( pEnv->pMemCtx==0 ){ *pnLeakAlloc = 0; *pnLeakByte = 0; }else{ tmMallocCheck((TmGlobal *)(pEnv->pMemCtx), pnLeakAlloc, pnLeakByte, pFile); } } void testMallocOom( lsm_env *pEnv, int nCountdown, int bPersist, void (*xHook)(void *), void *pHookCtx ){ TmGlobal *pTm = (TmGlobal *)(pEnv->pMemCtx); tmMallocOom(pTm, nCountdown, bPersist, xHook, pHookCtx); } void testMallocOomEnable(lsm_env *pEnv, int bEnable){ TmGlobal *pTm = (TmGlobal *)(pEnv->pMemCtx); tmMallocOomEnable(pTm, bEnable); } |
Added ext/lsm1/lsm-test/lsmtest_tdb.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 | /* ** This program attempts to test the correctness of some facets of the ** LSM database library. Specifically, that the contents of the database ** are maintained correctly during a series of inserts and deletes. */ #include "lsmtest_tdb.h" #include "lsm.h" #include "lsmtest.h" #include <stdlib.h> #include <string.h> #include <assert.h> #ifndef _WIN32 # include <unistd.h> #endif #include <stdio.h> typedef struct SqlDb SqlDb; static int error_transaction_function(TestDb *p, int iLevel){ unused_parameter(p); unused_parameter(iLevel); return -1; } /************************************************************************* ** Begin wrapper for LevelDB. */ #ifdef HAVE_LEVELDB #include <leveldb/c.h> typedef struct LevelDb LevelDb; struct LevelDb { TestDb base; leveldb_t *db; leveldb_options_t *pOpt; leveldb_writeoptions_t *pWriteOpt; leveldb_readoptions_t *pReadOpt; char *pVal; }; static int test_leveldb_close(TestDb *pTestDb){ LevelDb *pDb = (LevelDb *)pTestDb; leveldb_close(pDb->db); leveldb_writeoptions_destroy(pDb->pWriteOpt); leveldb_readoptions_destroy(pDb->pReadOpt); leveldb_options_destroy(pDb->pOpt); free(pDb->pVal); free(pDb); return 0; } static int test_leveldb_write( TestDb *pTestDb, void *pKey, int nKey, void *pVal, int nVal ){ LevelDb *pDb = (LevelDb *)pTestDb; char *zErr = 0; leveldb_put(pDb->db, pDb->pWriteOpt, pKey, nKey, pVal, nVal, &zErr); return (zErr!=0); } static int test_leveldb_delete(TestDb *pTestDb, void *pKey, int nKey){ LevelDb *pDb = (LevelDb *)pTestDb; char *zErr = 0; leveldb_delete(pDb->db, pDb->pWriteOpt, pKey, nKey, &zErr); return (zErr!=0); } static int test_leveldb_fetch( TestDb *pTestDb, void *pKey, int nKey, void **ppVal, int *pnVal ){ LevelDb *pDb = (LevelDb *)pTestDb; char *zErr = 0; size_t nVal = 0; if( pKey==0 ) return 0; free(pDb->pVal); pDb->pVal = leveldb_get(pDb->db, pDb->pReadOpt, pKey, nKey, &nVal, &zErr); *ppVal = (void *)(pDb->pVal); if( pDb->pVal==0 ){ *pnVal = -1; }else{ *pnVal = (int)nVal; } return (zErr!=0); } static int test_leveldb_scan( TestDb *pTestDb, void *pCtx, int bReverse, void *pKey1, int nKey1, /* Start of search */ void *pKey2, int nKey2, /* End of search */ void (*xCallback)(void *, void *, int , void *, int) ){ LevelDb *pDb = (LevelDb *)pTestDb; leveldb_iterator_t *iter; iter = leveldb_create_iterator(pDb->db, pDb->pReadOpt); if( bReverse==0 ){ if( pKey1 ){ leveldb_iter_seek(iter, pKey1, nKey1); }else{ leveldb_iter_seek_to_first(iter); } }else{ if( pKey2 ){ leveldb_iter_seek(iter, pKey2, nKey2); if( leveldb_iter_valid(iter)==0 ){ leveldb_iter_seek_to_last(iter); }else{ const char *k; size_t n; int res; k = leveldb_iter_key(iter, &n); res = memcmp(k, pKey2, MIN(n, nKey2)); if( res==0 ) res = n - nKey2; assert( res>=0 ); if( res>0 ){ leveldb_iter_prev(iter); } } }else{ leveldb_iter_seek_to_last(iter); } } while( leveldb_iter_valid(iter) ){ const char *k; size_t n; const char *v; size_t n2; int res; k = leveldb_iter_key(iter, &n); if( bReverse==0 && pKey2 ){ res = memcmp(k, pKey2, MIN(n, nKey2)); if( res==0 ) res = n - nKey2; if( res>0 ) break; } if( bReverse!=0 && pKey1 ){ res = memcmp(k, pKey1, MIN(n, nKey1)); if( res==0 ) res = n - nKey1; if( res<0 ) break; } v = leveldb_iter_value(iter, &n2); xCallback(pCtx, (void *)k, n, (void *)v, n2); if( bReverse==0 ){ leveldb_iter_next(iter); }else{ leveldb_iter_prev(iter); } } leveldb_iter_destroy(iter); return 0; } static int test_leveldb_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ static const DatabaseMethods LeveldbMethods = { test_leveldb_close, test_leveldb_write, test_leveldb_delete, 0, test_leveldb_fetch, test_leveldb_scan, error_transaction_function, error_transaction_function, error_transaction_function }; LevelDb *pLevelDb; char *zErr = 0; if( bClear ){ char *zCmd = sqlite3_mprintf("rm -rf %s\n", zFilename); system(zCmd); sqlite3_free(zCmd); } pLevelDb = (LevelDb *)malloc(sizeof(LevelDb)); memset(pLevelDb, 0, sizeof(LevelDb)); pLevelDb->pOpt = leveldb_options_create(); leveldb_options_set_create_if_missing(pLevelDb->pOpt, 1); pLevelDb->pWriteOpt = leveldb_writeoptions_create(); pLevelDb->pReadOpt = leveldb_readoptions_create(); pLevelDb->db = leveldb_open(pLevelDb->pOpt, zFilename, &zErr); if( zErr ){ test_leveldb_close((TestDb *)pLevelDb); *ppDb = 0; return 1; } *ppDb = (TestDb *)pLevelDb; pLevelDb->base.pMethods = &LeveldbMethods; return 0; } #endif /* HAVE_LEVELDB */ /* ** End wrapper for LevelDB. *************************************************************************/ #ifdef HAVE_KYOTOCABINET static int kc_close(TestDb *pTestDb){ return test_kc_close(pTestDb); } static int kc_write( TestDb *pTestDb, void *pKey, int nKey, void *pVal, int nVal ){ return test_kc_write(pTestDb, pKey, nKey, pVal, nVal); } static int kc_delete(TestDb *pTestDb, void *pKey, int nKey){ return test_kc_delete(pTestDb, pKey, nKey); } static int kc_delete_range( TestDb *pTestDb, void *pKey1, int nKey1, void *pKey2, int nKey2 ){ return test_kc_delete_range(pTestDb, pKey1, nKey1, pKey2, nKey2); } static int kc_fetch( TestDb *pTestDb, void *pKey, int nKey, void **ppVal, int *pnVal ){ if( pKey==0 ) return LSM_OK; return test_kc_fetch(pTestDb, pKey, nKey, ppVal, pnVal); } static int kc_scan( TestDb *pTestDb, void *pCtx, int bReverse, void *pFirst, int nFirst, void *pLast, int nLast, void (*xCallback)(void *, void *, int , void *, int) ){ return test_kc_scan( pTestDb, pCtx, bReverse, pFirst, nFirst, pLast, nLast, xCallback ); } static int kc_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ static const DatabaseMethods KcdbMethods = { kc_close, kc_write, kc_delete, kc_delete_range, kc_fetch, kc_scan, error_transaction_function, error_transaction_function, error_transaction_function }; int rc; TestDb *pTestDb = 0; rc = test_kc_open(zFilename, bClear, &pTestDb); if( rc!=0 ){ *ppDb = 0; return rc; } pTestDb->pMethods = &KcdbMethods; *ppDb = pTestDb; return 0; } #endif /* HAVE_KYOTOCABINET */ /* ** End wrapper for Kyoto cabinet. *************************************************************************/ #ifdef HAVE_MDB static int mdb_close(TestDb *pTestDb){ return test_mdb_close(pTestDb); } static int mdb_write( TestDb *pTestDb, void *pKey, int nKey, void *pVal, int nVal ){ return test_mdb_write(pTestDb, pKey, nKey, pVal, nVal); } static int mdb_delete(TestDb *pTestDb, void *pKey, int nKey){ return test_mdb_delete(pTestDb, pKey, nKey); } static int mdb_fetch( TestDb *pTestDb, void *pKey, int nKey, void **ppVal, int *pnVal ){ if( pKey==0 ) return LSM_OK; return test_mdb_fetch(pTestDb, pKey, nKey, ppVal, pnVal); } static int mdb_scan( TestDb *pTestDb, void *pCtx, int bReverse, void *pFirst, int nFirst, void *pLast, int nLast, void (*xCallback)(void *, void *, int , void *, int) ){ return test_mdb_scan( pTestDb, pCtx, bReverse, pFirst, nFirst, pLast, nLast, xCallback ); } static int mdb_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ static const DatabaseMethods KcdbMethods = { mdb_close, mdb_write, mdb_delete, 0, mdb_fetch, mdb_scan, error_transaction_function, error_transaction_function, error_transaction_function }; int rc; TestDb *pTestDb = 0; rc = test_mdb_open(zSpec, zFilename, bClear, &pTestDb); if( rc!=0 ){ *ppDb = 0; return rc; } pTestDb->pMethods = &KcdbMethods; *ppDb = pTestDb; return 0; } #endif /* HAVE_MDB */ /************************************************************************* ** Begin wrapper for SQLite. */ /* ** nOpenTrans: ** The number of open nested transactions, in the same sense as used ** by the tdb_begin/commit/rollback and SQLite 4 KV interfaces. If this ** value is 0, there are no transactions open at all. If it is 1, then ** there is a read transaction. If it is 2 or greater, then there are ** (nOpenTrans-1) nested write transactions open. */ struct SqlDb { TestDb base; sqlite3 *db; sqlite3_stmt *pInsert; sqlite3_stmt *pDelete; sqlite3_stmt *pDeleteRange; sqlite3_stmt *pFetch; sqlite3_stmt *apScan[8]; int nOpenTrans; /* Used by sql_fetch() to allocate space for results */ int nAlloc; u8 *aAlloc; }; static int sql_close(TestDb *pTestDb){ SqlDb *pDb = (SqlDb *)pTestDb; sqlite3_finalize(pDb->pInsert); sqlite3_finalize(pDb->pDelete); sqlite3_finalize(pDb->pDeleteRange); sqlite3_finalize(pDb->pFetch); sqlite3_finalize(pDb->apScan[0]); sqlite3_finalize(pDb->apScan[1]); sqlite3_finalize(pDb->apScan[2]); sqlite3_finalize(pDb->apScan[3]); sqlite3_finalize(pDb->apScan[4]); sqlite3_finalize(pDb->apScan[5]); sqlite3_finalize(pDb->apScan[6]); sqlite3_finalize(pDb->apScan[7]); sqlite3_close(pDb->db); free((char *)pDb->aAlloc); free((char *)pDb); return SQLITE_OK; } static int sql_write( TestDb *pTestDb, void *pKey, int nKey, void *pVal, int nVal ){ SqlDb *pDb = (SqlDb *)pTestDb; sqlite3_bind_blob(pDb->pInsert, 1, pKey, nKey, SQLITE_STATIC); sqlite3_bind_blob(pDb->pInsert, 2, pVal, nVal, SQLITE_STATIC); sqlite3_step(pDb->pInsert); return sqlite3_reset(pDb->pInsert); } static int sql_delete(TestDb *pTestDb, void *pKey, int nKey){ SqlDb *pDb = (SqlDb *)pTestDb; sqlite3_bind_blob(pDb->pDelete, 1, pKey, nKey, SQLITE_STATIC); sqlite3_step(pDb->pDelete); return sqlite3_reset(pDb->pDelete); } static int sql_delete_range( TestDb *pTestDb, void *pKey1, int nKey1, void *pKey2, int nKey2 ){ SqlDb *pDb = (SqlDb *)pTestDb; sqlite3_bind_blob(pDb->pDeleteRange, 1, pKey1, nKey1, SQLITE_STATIC); sqlite3_bind_blob(pDb->pDeleteRange, 2, pKey2, nKey2, SQLITE_STATIC); sqlite3_step(pDb->pDeleteRange); return sqlite3_reset(pDb->pDeleteRange); } static int sql_fetch( TestDb *pTestDb, void *pKey, int nKey, void **ppVal, int *pnVal ){ SqlDb *pDb = (SqlDb *)pTestDb; int rc; sqlite3_reset(pDb->pFetch); if( pKey==0 ){ assert( ppVal==0 ); assert( pnVal==0 ); return LSM_OK; } sqlite3_bind_blob(pDb->pFetch, 1, pKey, nKey, SQLITE_STATIC); rc = sqlite3_step(pDb->pFetch); if( rc==SQLITE_ROW ){ int nVal = sqlite3_column_bytes(pDb->pFetch, 0); u8 *aVal = (void *)sqlite3_column_blob(pDb->pFetch, 0); if( nVal>pDb->nAlloc ){ free(pDb->aAlloc); pDb->aAlloc = (u8 *)malloc(nVal*2); pDb->nAlloc = nVal*2; } memcpy(pDb->aAlloc, aVal, nVal); *pnVal = nVal; *ppVal = (void *)pDb->aAlloc; }else{ *pnVal = -1; *ppVal = 0; } rc = sqlite3_reset(pDb->pFetch); return rc; } static int sql_scan( TestDb *pTestDb, void *pCtx, int bReverse, void *pFirst, int nFirst, void *pLast, int nLast, void (*xCallback)(void *, void *, int , void *, int) ){ SqlDb *pDb = (SqlDb *)pTestDb; sqlite3_stmt *pScan; assert( bReverse==1 || bReverse==0 ); pScan = pDb->apScan[(pFirst==0) + (pLast==0)*2 + bReverse*4]; if( pFirst ) sqlite3_bind_blob(pScan, 1, pFirst, nFirst, SQLITE_STATIC); if( pLast ) sqlite3_bind_blob(pScan, 2, pLast, nLast, SQLITE_STATIC); while( SQLITE_ROW==sqlite3_step(pScan) ){ void *pKey; int nKey; void *pVal; int nVal; nKey = sqlite3_column_bytes(pScan, 0); pKey = (void *)sqlite3_column_blob(pScan, 0); nVal = sqlite3_column_bytes(pScan, 1); pVal = (void *)sqlite3_column_blob(pScan, 1); xCallback(pCtx, pKey, nKey, pVal, nVal); } return sqlite3_reset(pScan); } static int sql_begin(TestDb *pTestDb, int iLevel){ int i; SqlDb *pDb = (SqlDb *)pTestDb; /* iLevel==0 is a no-op */ if( iLevel==0 ) return 0; /* If there are no transactions at all open, open a read transaction. */ if( pDb->nOpenTrans==0 ){ int rc = sqlite3_exec(pDb->db, "BEGIN; SELECT * FROM sqlite_master LIMIT 1;" , 0, 0, 0 ); if( rc!=0 ) return rc; pDb->nOpenTrans = 1; } /* Open any required write transactions */ for(i=pDb->nOpenTrans; i<iLevel; i++){ char *zSql = sqlite3_mprintf("SAVEPOINT x%d", i); int rc = sqlite3_exec(pDb->db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK ) return rc; } pDb->nOpenTrans = iLevel; return 0; } static int sql_commit(TestDb *pTestDb, int iLevel){ SqlDb *pDb = (SqlDb *)pTestDb; assert( iLevel>=0 ); /* Close the read transaction if requested. */ if( pDb->nOpenTrans>=1 && iLevel==0 ){ int rc = sqlite3_exec(pDb->db, "COMMIT", 0, 0, 0); if( rc!=0 ) return rc; pDb->nOpenTrans = 0; } /* Close write transactions as required */ if( pDb->nOpenTrans>iLevel ){ char *zSql = sqlite3_mprintf("RELEASE x%d", iLevel); int rc = sqlite3_exec(pDb->db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc!=0 ) return rc; } pDb->nOpenTrans = iLevel; return 0; } static int sql_rollback(TestDb *pTestDb, int iLevel){ SqlDb *pDb = (SqlDb *)pTestDb; assert( iLevel>=0 ); if( pDb->nOpenTrans>=1 && iLevel==0 ){ /* Close the read transaction if requested. */ int rc = sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0); if( rc!=0 ) return rc; }else if( pDb->nOpenTrans>1 && iLevel==1 ){ /* Or, rollback and close the top-level write transaction */ int rc = sqlite3_exec(pDb->db, "ROLLBACK TO x1; RELEASE x1;", 0, 0, 0); if( rc!=0 ) return rc; }else{ /* Or, just roll back some nested transactions */ char *zSql = sqlite3_mprintf("ROLLBACK TO x%d", iLevel-1); int rc = sqlite3_exec(pDb->db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc!=0 ) return rc; } pDb->nOpenTrans = iLevel; return 0; } static int sql_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ static const DatabaseMethods SqlMethods = { sql_close, sql_write, sql_delete, sql_delete_range, sql_fetch, sql_scan, sql_begin, sql_commit, sql_rollback }; const char *zCreate = "CREATE TABLE IF NOT EXISTS t1(k PRIMARY KEY, v)"; const char *zInsert = "REPLACE INTO t1 VALUES(?, ?)"; const char *zDelete = "DELETE FROM t1 WHERE k = ?"; const char *zRange = "DELETE FROM t1 WHERE k>? AND k<?"; const char *zFetch = "SELECT v FROM t1 WHERE k = ?"; const char *zScan0 = "SELECT * FROM t1 WHERE k BETWEEN ?1 AND ?2 ORDER BY k"; const char *zScan1 = "SELECT * FROM t1 WHERE k <= ?2 ORDER BY k"; const char *zScan2 = "SELECT * FROM t1 WHERE k >= ?1 ORDER BY k"; const char *zScan3 = "SELECT * FROM t1 ORDER BY k"; const char *zScan4 = "SELECT * FROM t1 WHERE k BETWEEN ?1 AND ?2 ORDER BY k DESC"; const char *zScan5 = "SELECT * FROM t1 WHERE k <= ?2 ORDER BY k DESC"; const char *zScan6 = "SELECT * FROM t1 WHERE k >= ?1 ORDER BY k DESC"; const char *zScan7 = "SELECT * FROM t1 ORDER BY k DESC"; int rc; SqlDb *pDb; char *zPragma; if( bClear && zFilename && zFilename[0] ){ unlink(zFilename); } pDb = (SqlDb *)malloc(sizeof(SqlDb)); memset(pDb, 0, sizeof(SqlDb)); pDb->base.pMethods = &SqlMethods; if( 0!=(rc = sqlite3_open(zFilename, &pDb->db)) || 0!=(rc = sqlite3_exec(pDb->db, zCreate, 0, 0, 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zInsert, -1, &pDb->pInsert, 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zDelete, -1, &pDb->pDelete, 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zRange, -1, &pDb->pDeleteRange, 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zFetch, -1, &pDb->pFetch, 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zScan0, -1, &pDb->apScan[0], 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zScan1, -1, &pDb->apScan[1], 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zScan2, -1, &pDb->apScan[2], 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zScan3, -1, &pDb->apScan[3], 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zScan4, -1, &pDb->apScan[4], 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zScan5, -1, &pDb->apScan[5], 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zScan6, -1, &pDb->apScan[6], 0)) || 0!=(rc = sqlite3_prepare_v2(pDb->db, zScan7, -1, &pDb->apScan[7], 0)) ){ *ppDb = 0; sql_close((TestDb *)pDb); return rc; } zPragma = sqlite3_mprintf("PRAGMA page_size=%d", TESTDB_DEFAULT_PAGE_SIZE); sqlite3_exec(pDb->db, zPragma, 0, 0, 0); sqlite3_free(zPragma); zPragma = sqlite3_mprintf("PRAGMA cache_size=%d", TESTDB_DEFAULT_CACHE_SIZE); sqlite3_exec(pDb->db, zPragma, 0, 0, 0); sqlite3_free(zPragma); /* sqlite3_exec(pDb->db, "PRAGMA locking_mode=EXCLUSIVE", 0, 0, 0); */ sqlite3_exec(pDb->db, "PRAGMA synchronous=OFF", 0, 0, 0); sqlite3_exec(pDb->db, "PRAGMA journal_mode=WAL", 0, 0, 0); sqlite3_exec(pDb->db, "PRAGMA wal_autocheckpoint=4096", 0, 0, 0); if( zSpec ){ rc = sqlite3_exec(pDb->db, zSpec, 0, 0, 0); if( rc!=SQLITE_OK ){ sql_close((TestDb *)pDb); return rc; } } *ppDb = (TestDb *)pDb; return 0; } /* ** End wrapper for SQLite. *************************************************************************/ /************************************************************************* ** Begin exported functions. */ static struct Lib { const char *zName; const char *zDefaultDb; int (*xOpen)(const char *, const char *zFilename, int bClear, TestDb **ppDb); } aLib[] = { { "sqlite3", "testdb.sqlite", sql_open }, { "lsm_small", "testdb.lsm_small", test_lsm_small_open }, { "lsm_lomem", "testdb.lsm_lomem", test_lsm_lomem_open }, #ifdef HAVE_ZLIB { "lsm_zip", "testdb.lsm_zip", test_lsm_zip_open }, #endif { "lsm", "testdb.lsm", test_lsm_open }, #ifdef LSM_MUTEX_PTHREADS { "lsm_mt2", "testdb.lsm_mt2", test_lsm_mt2 }, { "lsm_mt3", "testdb.lsm_mt3", test_lsm_mt3 }, #endif #ifdef HAVE_LEVELDB { "leveldb", "testdb.leveldb", test_leveldb_open }, #endif #ifdef HAVE_KYOTOCABINET { "kyotocabinet", "testdb.kc", kc_open }, #endif #ifdef HAVE_MDB { "mdb", "./testdb.mdb", mdb_open } #endif }; const char *tdb_system_name(int i){ if( i<0 || i>=ArraySize(aLib) ) return 0; return aLib[i].zName; } const char *tdb_default_db(const char *zSys){ int i; for(i=0; i<ArraySize(aLib); i++){ if( strcmp(aLib[i].zName, zSys)==0 ) return aLib[i].zDefaultDb; } return 0; } int tdb_open(const char *zLib, const char *zDb, int bClear, TestDb **ppDb){ int i; int rc = 1; const char *zSpec = 0; int nLib = 0; while( zLib[nLib] && zLib[nLib]!=' ' ){ nLib++; } zSpec = &zLib[nLib]; while( *zSpec==' ' ) zSpec++; if( *zSpec=='\0' ) zSpec = 0; for(i=0; i<ArraySize(aLib); i++){ if( (int)strlen(aLib[i].zName)==nLib && 0==memcmp(zLib, aLib[i].zName, nLib) ){ rc = aLib[i].xOpen(zSpec, (zDb ? zDb : aLib[i].zDefaultDb), bClear, ppDb); if( rc==0 ){ (*ppDb)->zLibrary = aLib[i].zName; } break; } } if( rc ){ /* Failed to find the requested database library. Return an error. */ *ppDb = 0; } return rc; } int tdb_close(TestDb *pDb){ if( pDb ){ return pDb->pMethods->xClose(pDb); } return 0; } int tdb_write(TestDb *pDb, void *pKey, int nKey, void *pVal, int nVal){ return pDb->pMethods->xWrite(pDb, pKey, nKey, pVal, nVal); } int tdb_delete(TestDb *pDb, void *pKey, int nKey){ return pDb->pMethods->xDelete(pDb, pKey, nKey); } int tdb_delete_range( TestDb *pDb, void *pKey1, int nKey1, void *pKey2, int nKey2 ){ return pDb->pMethods->xDeleteRange(pDb, pKey1, nKey1, pKey2, nKey2); } int tdb_fetch(TestDb *pDb, void *pKey, int nKey, void **ppVal, int *pnVal){ return pDb->pMethods->xFetch(pDb, pKey, nKey, ppVal, pnVal); } int tdb_scan( TestDb *pDb, /* Database handle */ void *pCtx, /* Context pointer to pass to xCallback */ int bReverse, /* True to scan in reverse order */ void *pKey1, int nKey1, /* Start of search */ void *pKey2, int nKey2, /* End of search */ void (*xCallback)(void *pCtx, void *pKey, int nKey, void *pVal, int nVal) ){ return pDb->pMethods->xScan( pDb, pCtx, bReverse, pKey1, nKey1, pKey2, nKey2, xCallback ); } int tdb_begin(TestDb *pDb, int iLevel){ return pDb->pMethods->xBegin(pDb, iLevel); } int tdb_commit(TestDb *pDb, int iLevel){ return pDb->pMethods->xCommit(pDb, iLevel); } int tdb_rollback(TestDb *pDb, int iLevel){ return pDb->pMethods->xRollback(pDb, iLevel); } int tdb_transaction_support(TestDb *pDb){ return (pDb->pMethods->xBegin != error_transaction_function); } const char *tdb_library_name(TestDb *pDb){ return pDb->zLibrary; } /* ** End exported functions. *************************************************************************/ |
Added ext/lsm1/lsm-test/lsmtest_tdb.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 172 173 174 | /* ** This file is the interface to a very simple database library used for ** testing. The interface is similar to that of the LSM. The main virtue ** of this library is that the same API may be used to access a key-value ** store implemented by LSM, SQLite or another database system. Which ** makes it easy to use for correctness and performance tests. */ #ifndef __WRAPPER_H_ #define __WRAPPER_H_ #ifdef __cplusplus extern "C" { #endif #include "lsm.h" typedef struct TestDb TestDb; /* ** Open a new database connection. The first argument is the name of the ** database library to use. e.g. something like: ** ** "sqlite3" ** "lsm" ** ** See function tdb_system_name() for a list of available database systems. ** ** The second argument is the name of the database to open (e.g. a filename). ** ** If the third parameter is non-zero, then any existing database by the ** name of zDb is removed before opening a new one. If it is zero, then an ** existing database may be opened. */ int tdb_open(const char *zLibrary, const char *zDb, int bClear, TestDb **ppDb); /* ** Close a database handle. */ int tdb_close(TestDb *pDb); /* ** Write a new key/value into the database. */ int tdb_write(TestDb *pDb, void *pKey, int nKey, void *pVal, int nVal); /* ** Delete a key from the database. */ int tdb_delete(TestDb *pDb, void *pKey, int nKey); /* ** Delete a range of keys from the database. */ int tdb_delete_range(TestDb *, void *pKey1, int nKey1, void *pKey2, int nKey2); /* ** Query the database for key (pKey/nKey). If no entry is found, set *ppVal ** to 0 and *pnVal to -1 before returning. Otherwise, set *ppVal and *pnVal ** to a pointer to and size of the value associated with (pKey/nKey). */ int tdb_fetch(TestDb *pDb, void *pKey, int nKey, void **ppVal, int *pnVal); /* ** Open and close nested transactions. Currently, these functions only ** work for SQLite3 and LSM systems. Use the tdb_transaction_support() ** function to determine if a given TestDb handle supports these methods. ** ** These functions and the iLevel parameter follow the same conventions as ** the SQLite 4 transaction interface. Note that this is slightly different ** from the way LSM does things. As follows: ** ** tdb_begin(): ** A successful call to tdb_begin() with (iLevel>1) guarantees that ** there are at least (iLevel-1) write transactions open. If iLevel==1, ** then it guarantees that at least a read-transaction is open. Calling ** tdb_begin() with iLevel==0 is a no-op. ** ** tdb_commit(): ** A successful call to tdb_commit() with (iLevel>1) guarantees that ** there are at most (iLevel-1) write transactions open. If iLevel==1, ** then it guarantees that there are no write transactions open (although ** a read-transaction may remain open). Calling tdb_commit() with ** iLevel==0 ensures that all transactions, read or write, have been ** closed and committed. ** ** tdb_rollback(): ** This call is similar to tdb_commit(), except that instead of committing ** transactions, it reverts them. For example, calling tdb_rollback() with ** iLevel==2 ensures that there is at most one write transaction open, and ** restores the database to the state that it was in when that transaction ** was opened. ** ** In other words, tdb_commit() just closes transactions - tdb_rollback() ** closes transactions and then restores the database to the state it ** was in before those transactions were even opened. */ int tdb_begin(TestDb *pDb, int iLevel); int tdb_commit(TestDb *pDb, int iLevel); int tdb_rollback(TestDb *pDb, int iLevel); /* ** Return true if transactions are supported, or false otherwise. */ int tdb_transaction_support(TestDb *pDb); /* ** Return the name of the database library (as passed to tdb_open()) used ** by the handled passed as the first argument. */ const char *tdb_library_name(TestDb *pDb); /* ** Scan a range of database keys. Invoke the callback function for each ** key visited. */ int tdb_scan( TestDb *pDb, /* Database handle */ void *pCtx, /* Context pointer to pass to xCallback */ int bReverse, /* True to scan in reverse order */ void *pKey1, int nKey1, /* Start of search */ void *pKey2, int nKey2, /* End of search */ void (*xCallback)(void *pCtx, void *pKey, int nKey, void *pVal, int nVal) ); const char *tdb_system_name(int i); const char *tdb_default_db(const char *zSys); int tdb_lsm_open(const char *zCfg, const char *zDb, int bClear, TestDb **ppDb); /* ** If the TestDb handle passed as an argument is a wrapper around an LSM ** database, return the LSM handle. Otherwise, if the argument is some other ** database system, return NULL. */ lsm_db *tdb_lsm(TestDb *pDb); /* ** Return true if the db passed as an argument is a multi-threaded LSM ** connection. */ int tdb_lsm_multithread(TestDb *pDb); /* ** Return a pointer to the lsm_env object used by all lsm database ** connections initialized as a copy of the object returned by ** lsm_default_env(). It may be modified (e.g. to override functions) ** if the caller can guarantee that it is not already in use. */ lsm_env *tdb_lsm_env(void); /* ** The following functions only work with LSM database handles. It is ** illegal to call them with any other type of database handle specified ** as an argument. */ void tdb_lsm_enable_log(TestDb *pDb, int bEnable); void tdb_lsm_application_crash(TestDb *pDb); void tdb_lsm_prepare_system_crash(TestDb *pDb); void tdb_lsm_system_crash(TestDb *pDb); void tdb_lsm_prepare_sync_crash(TestDb *pDb, int iSync); void tdb_lsm_safety(TestDb *pDb, int eMode); void tdb_lsm_config_work_hook(TestDb *pDb, void (*)(lsm_db *, void *), void *); void tdb_lsm_write_hook(TestDb *, void(*)(void*,int,lsm_i64,int,int), void*); int tdb_lsm_config_str(TestDb *pDb, const char *zStr); #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif |
Added ext/lsm1/lsm-test/lsmtest_tdb2.cc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include "lsmtest.h" #include <stdlib.h> #ifdef HAVE_KYOTOCABINET #include "kcpolydb.h" extern "C" { struct KcDb { TestDb base; kyotocabinet::TreeDB* db; char *pVal; }; } int test_kc_open(const char *zFilename, int bClear, TestDb **ppDb){ KcDb *pKcDb; int ok; int rc = 0; if( bClear ){ char *zCmd = sqlite3_mprintf("rm -rf %s\n", zFilename); system(zCmd); sqlite3_free(zCmd); } pKcDb = (KcDb *)malloc(sizeof(KcDb)); memset(pKcDb, 0, sizeof(KcDb)); pKcDb->db = new kyotocabinet::TreeDB(); pKcDb->db->tune_page(TESTDB_DEFAULT_PAGE_SIZE); pKcDb->db->tune_page_cache( TESTDB_DEFAULT_PAGE_SIZE * TESTDB_DEFAULT_CACHE_SIZE ); ok = pKcDb->db->open(zFilename, kyotocabinet::PolyDB::OWRITER | kyotocabinet::PolyDB::OCREATE ); if( ok==0 ){ free(pKcDb); pKcDb = 0; rc = 1; } *ppDb = (TestDb *)pKcDb; return rc; } int test_kc_close(TestDb *pDb){ KcDb *pKcDb = (KcDb *)pDb; if( pKcDb->pVal ){ delete [] pKcDb->pVal; } pKcDb->db->close(); delete pKcDb->db; free(pKcDb); return 0; } int test_kc_write(TestDb *pDb, void *pKey, int nKey, void *pVal, int nVal){ KcDb *pKcDb = (KcDb *)pDb; int ok; ok = pKcDb->db->set((const char *)pKey, nKey, (const char *)pVal, nVal); return (ok ? 0 : 1); } int test_kc_delete(TestDb *pDb, void *pKey, int nKey){ KcDb *pKcDb = (KcDb *)pDb; int ok; ok = pKcDb->db->remove((const char *)pKey, nKey); return (ok ? 0 : 1); } int test_kc_delete_range( TestDb *pDb, void *pKey1, int nKey1, void *pKey2, int nKey2 ){ int res; KcDb *pKcDb = (KcDb *)pDb; kyotocabinet::DB::Cursor* pCur = pKcDb->db->cursor(); if( pKey1 ){ res = pCur->jump((const char *)pKey1, nKey1); }else{ res = pCur->jump(); } while( 1 ){ const char *pKey; size_t nKey; const char *pVal; size_t nVal; pKey = pCur->get(&nKey, &pVal, &nVal); if( pKey==0 ) break; #ifndef NDEBUG if( pKey1 ){ res = memcmp(pKey, pKey1, MIN((size_t)nKey1, nKey)); assert( res>0 || (res==0 && nKey>nKey1) ); } #endif if( pKey2 ){ res = memcmp(pKey, pKey2, MIN((size_t)nKey2, nKey)); if( res>0 || (res==0 && (size_t)nKey2<nKey) ){ delete [] pKey; break; } } pCur->remove(); delete [] pKey; } delete pCur; return 0; } int test_kc_fetch( TestDb *pDb, void *pKey, int nKey, void **ppVal, int *pnVal ){ KcDb *pKcDb = (KcDb *)pDb; size_t nVal; if( pKcDb->pVal ){ delete [] pKcDb->pVal; pKcDb->pVal = 0; } pKcDb->pVal = pKcDb->db->get((const char *)pKey, nKey, &nVal); if( pKcDb->pVal ){ *ppVal = pKcDb->pVal; *pnVal = nVal; }else{ *ppVal = 0; *pnVal = -1; } return 0; } int test_kc_scan( TestDb *pDb, /* Database handle */ void *pCtx, /* Context pointer to pass to xCallback */ int bReverse, /* True for a reverse order scan */ void *pKey1, int nKey1, /* Start of search */ void *pKey2, int nKey2, /* End of search */ void (*xCallback)(void *pCtx, void *pKey, int nKey, void *pVal, int nVal) ){ KcDb *pKcDb = (KcDb *)pDb; kyotocabinet::DB::Cursor* pCur = pKcDb->db->cursor(); int res; if( bReverse==0 ){ if( pKey1 ){ res = pCur->jump((const char *)pKey1, nKey1); }else{ res = pCur->jump(); } }else{ if( pKey2 ){ res = pCur->jump_back((const char *)pKey2, nKey2); }else{ res = pCur->jump_back(); } } while( res ){ const char *pKey; size_t nKey; const char *pVal; size_t nVal; pKey = pCur->get(&nKey, &pVal, &nVal); if( bReverse==0 && pKey2 ){ res = memcmp(pKey, pKey2, MIN((size_t)nKey2, nKey)); if( res>0 || (res==0 && (size_t)nKey2<nKey) ){ delete [] pKey; break; } }else if( bReverse!=0 && pKey1 ){ res = memcmp(pKey, pKey1, MIN((size_t)nKey1, nKey)); if( res<0 || (res==0 && (size_t)nKey1>nKey) ){ delete [] pKey; break; } } xCallback(pCtx, (void *)pKey, (int)nKey, (void *)pVal, (int)nVal); delete [] pKey; if( bReverse ){ res = pCur->step_back(); }else{ res = pCur->step(); } } delete pCur; return 0; } #endif /* HAVE_KYOTOCABINET */ #ifdef HAVE_MDB #include "lmdb.h" extern "C" { struct MdbDb { TestDb base; MDB_env *env; MDB_dbi dbi; }; } int test_mdb_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ MDB_txn *txn; MdbDb *pMdb; int rc; if( bClear ){ char *zCmd = sqlite3_mprintf("rm -rf %s\n", zFilename); system(zCmd); sqlite3_free(zCmd); } pMdb = (MdbDb *)malloc(sizeof(MdbDb)); memset(pMdb, 0, sizeof(MdbDb)); rc = mdb_env_create(&pMdb->env); if( rc==0 ) rc = mdb_env_set_mapsize(pMdb->env, 1*1024*1024*1024); if( rc==0 ) rc = mdb_env_open(pMdb->env, zFilename, MDB_NOSYNC|MDB_NOSUBDIR, 0600); if( rc==0 ) rc = mdb_txn_begin(pMdb->env, NULL, 0, &txn); if( rc==0 ){ rc = mdb_open(txn, NULL, 0, &pMdb->dbi); mdb_txn_commit(txn); } *ppDb = (TestDb *)pMdb; return rc; } int test_mdb_close(TestDb *pDb){ MdbDb *pMdb = (MdbDb *)pDb; mdb_close(pMdb->env, pMdb->dbi); mdb_env_close(pMdb->env); free(pMdb); return 0; } int test_mdb_write(TestDb *pDb, void *pKey, int nKey, void *pVal, int nVal){ int rc; MdbDb *pMdb = (MdbDb *)pDb; MDB_val val; MDB_val key; MDB_txn *txn; val.mv_size = nVal; val.mv_data = pVal; key.mv_size = nKey; key.mv_data = pKey; rc = mdb_txn_begin(pMdb->env, NULL, 0, &txn); if( rc==0 ){ rc = mdb_put(txn, pMdb->dbi, &key, &val, 0); if( rc==0 ){ rc = mdb_txn_commit(txn); }else{ mdb_txn_abort(txn); } } return rc; } int test_mdb_delete(TestDb *pDb, void *pKey, int nKey){ int rc; MdbDb *pMdb = (MdbDb *)pDb; MDB_val key; MDB_txn *txn; key.mv_size = nKey; key.mv_data = pKey; rc = mdb_txn_begin(pMdb->env, NULL, 0, &txn); if( rc==0 ){ rc = mdb_del(txn, pMdb->dbi, &key, 0); if( rc==0 ){ rc = mdb_txn_commit(txn); }else{ mdb_txn_abort(txn); } } return rc; } int test_mdb_fetch( TestDb *pDb, void *pKey, int nKey, void **ppVal, int *pnVal ){ int rc; MdbDb *pMdb = (MdbDb *)pDb; MDB_val key; MDB_txn *txn; key.mv_size = nKey; key.mv_data = pKey; rc = mdb_txn_begin(pMdb->env, NULL, MDB_RDONLY, &txn); if( rc==0 ){ MDB_val val = {0, 0}; rc = mdb_get(txn, pMdb->dbi, &key, &val); if( rc==MDB_NOTFOUND ){ rc = 0; *ppVal = 0; *pnVal = -1; }else{ *ppVal = val.mv_data; *pnVal = val.mv_size; } mdb_txn_commit(txn); } return rc; } int test_mdb_scan( TestDb *pDb, /* Database handle */ void *pCtx, /* Context pointer to pass to xCallback */ int bReverse, /* True for a reverse order scan */ void *pKey1, int nKey1, /* Start of search */ void *pKey2, int nKey2, /* End of search */ void (*xCallback)(void *pCtx, void *pKey, int nKey, void *pVal, int nVal) ){ MdbDb *pMdb = (MdbDb *)pDb; int rc; MDB_cursor_op op = bReverse ? MDB_PREV : MDB_NEXT; MDB_txn *txn; rc = mdb_txn_begin(pMdb->env, NULL, MDB_RDONLY, &txn); if( rc==0 ){ MDB_cursor *csr; MDB_val key = {0, 0}; MDB_val val = {0, 0}; rc = mdb_cursor_open(txn, pMdb->dbi, &csr); if( rc==0 ){ while( mdb_cursor_get(csr, &key, &val, op)==0 ){ xCallback(pCtx, key.mv_data, key.mv_size, val.mv_data, val.mv_size); } mdb_cursor_close(csr); } } return rc; } #endif /* HAVE_MDB */ |
Added ext/lsm1/lsm-test/lsmtest_tdb3.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 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 | #include "lsmtest_tdb.h" #include "lsm.h" #include "lsmtest.h" #include <stdlib.h> #include <string.h> #include <assert.h> #ifndef _WIN32 # include <unistd.h> #endif #include <stdio.h> #ifndef _WIN32 # include <sys/time.h> #endif typedef struct LsmDb LsmDb; typedef struct LsmWorker LsmWorker; typedef struct LsmFile LsmFile; #define LSMTEST_DFLT_MT_MAX_CKPT (8*1024) #define LSMTEST_DFLT_MT_MIN_CKPT (2*1024) #ifdef LSM_MUTEX_PTHREADS #include <pthread.h> #define LSMTEST_THREAD_CKPT 1 #define LSMTEST_THREAD_WORKER 2 #define LSMTEST_THREAD_WORKER_AC 3 /* ** There are several different types of worker threads that run in different ** test configurations, depending on the value of LsmWorker.eType. ** ** 1. Checkpointer. ** 2. Worker with auto-checkpoint. ** 3. Worker without auto-checkpoint. */ struct LsmWorker { LsmDb *pDb; /* Main database structure */ lsm_db *pWorker; /* Worker database handle */ pthread_t worker_thread; /* Worker thread */ pthread_cond_t worker_cond; /* Condition var the worker waits on */ pthread_mutex_t worker_mutex; /* Mutex used with worker_cond */ int bDoWork; /* Set to true by client when there is work */ int worker_rc; /* Store error code here */ int eType; /* LSMTEST_THREAD_XXX constant */ int bBlock; }; #else struct LsmWorker { int worker_rc; int bBlock; }; #endif static void mt_shutdown(LsmDb *); lsm_env *tdb_lsm_env(void){ static int bInit = 0; static lsm_env env; if( bInit==0 ){ memcpy(&env, lsm_default_env(), sizeof(env)); bInit = 1; } return &env; } typedef struct FileSector FileSector; typedef struct FileData FileData; struct FileSector { u8 *aOld; /* Old data for this sector */ }; struct FileData { int nSector; /* Allocated size of apSector[] array */ FileSector *aSector; /* Array of file sectors */ }; /* ** bPrepareCrash: ** If non-zero, the file wrappers maintain enough in-memory data to ** simulate the effect of a power-failure on the file-system (i.e. that ** unsynced sectors may be written, not written, or overwritten with ** arbitrary data when the crash occurs). ** ** bCrashed: ** Set to true after a crash is simulated. Once this variable is true, all ** VFS methods other than xClose() return LSM_IOERR as soon as they are ** called (without affecting the contents of the file-system). ** ** env: ** The environment object used by all lsm_db* handles opened by this ** object (i.e. LsmDb.db plus any worker connections). Variable env.pVfsCtx ** always points to the containing LsmDb structure. */ struct LsmDb { TestDb base; /* Base class - methods table */ lsm_env env; /* Environment used by connection db */ char *zName; /* Database file name */ lsm_db *db; /* LSM database handle */ lsm_cursor *pCsr; /* Cursor held open during read transaction */ void *pBuf; /* Buffer for tdb_fetch() output */ int nBuf; /* Allocated (not used) size of pBuf */ /* Crash testing related state */ int bCrashed; /* True once a crash has occurred */ int nAutoCrash; /* Number of syncs until a crash */ int bPrepareCrash; /* True to store writes in memory */ /* Unsynced data (while crash testing) */ int szSector; /* Assumed size of disk sectors (512B) */ FileData aFile[2]; /* Database and log file data */ /* Other test instrumentation */ int bNoRecovery; /* If true, assume DMS2 is locked */ /* Work hook redirection */ void (*xWork)(lsm_db *, void *); void *pWorkCtx; /* IO logging hook */ void (*xWriteHook)(void *, int, lsm_i64, int, int); void *pWriteCtx; /* Worker threads (for lsm_mt) */ int nMtMinCkpt; int nMtMaxCkpt; int eMode; int nWorker; LsmWorker *aWorker; }; #define LSMTEST_MODE_SINGLETHREAD 1 #define LSMTEST_MODE_BACKGROUND_CKPT 2 #define LSMTEST_MODE_BACKGROUND_WORK 3 #define LSMTEST_MODE_BACKGROUND_BOTH 4 /************************************************************************* ************************************************************************** ** Begin test VFS code. */ struct LsmFile { lsm_file *pReal; /* Real underlying file */ int bLog; /* True for log file. False for db file */ LsmDb *pDb; /* Database handle that uses this file */ }; static int testEnvFullpath( lsm_env *pEnv, /* Environment for current LsmDb */ const char *zFile, /* Relative path name */ char *zOut, /* Output buffer */ int *pnOut /* IN/OUT: Size of output buffer */ ){ lsm_env *pRealEnv = tdb_lsm_env(); return pRealEnv->xFullpath(pRealEnv, zFile, zOut, pnOut); } static int testEnvOpen( lsm_env *pEnv, /* Environment for current LsmDb */ const char *zFile, /* Name of file to open */ int flags, lsm_file **ppFile /* OUT: New file handle object */ ){ lsm_env *pRealEnv = tdb_lsm_env(); LsmDb *pDb = (LsmDb *)pEnv->pVfsCtx; int rc; /* Return Code */ LsmFile *pRet; /* The new file handle */ int nFile; /* Length of string zFile in bytes */ nFile = strlen(zFile); pRet = (LsmFile *)testMalloc(sizeof(LsmFile)); pRet->pDb = pDb; pRet->bLog = (nFile > 4 && 0==memcmp("-log", &zFile[nFile-4], 4)); rc = pRealEnv->xOpen(pRealEnv, zFile, flags, &pRet->pReal); if( rc!=LSM_OK ){ testFree(pRet); pRet = 0; } *ppFile = (lsm_file *)pRet; return rc; } static int testEnvRead(lsm_file *pFile, lsm_i64 iOff, void *pData, int nData){ lsm_env *pRealEnv = tdb_lsm_env(); LsmFile *p = (LsmFile *)pFile; if( p->pDb->bCrashed ) return LSM_IOERR; return pRealEnv->xRead(p->pReal, iOff, pData, nData); } static int testEnvWrite(lsm_file *pFile, lsm_i64 iOff, void *pData, int nData){ lsm_env *pRealEnv = tdb_lsm_env(); LsmFile *p = (LsmFile *)pFile; LsmDb *pDb = p->pDb; if( pDb->bCrashed ) return LSM_IOERR; if( pDb->bPrepareCrash ){ FileData *pData2 = &pDb->aFile[p->bLog]; int iFirst; int iLast; int iSector; iFirst = (int)(iOff / pDb->szSector); iLast = (int)((iOff + nData - 1) / pDb->szSector); if( pData2->nSector<(iLast+1) ){ int nNew = ( ((iLast + 1) + 63) / 64 ) * 64; assert( nNew>iLast ); pData2->aSector = (FileSector *)testRealloc( pData2->aSector, nNew*sizeof(FileSector) ); memset(&pData2->aSector[pData2->nSector], 0, (nNew - pData2->nSector) * sizeof(FileSector) ); pData2->nSector = nNew; } for(iSector=iFirst; iSector<=iLast; iSector++){ if( pData2->aSector[iSector].aOld==0 ){ u8 *aOld = (u8 *)testMalloc(pDb->szSector); pRealEnv->xRead( p->pReal, (lsm_i64)iSector*pDb->szSector, aOld, pDb->szSector ); pData2->aSector[iSector].aOld = aOld; } } } if( pDb->xWriteHook ){ int rc; int nUs; struct timeval t1; struct timeval t2; gettimeofday(&t1, 0); assert( nData>0 ); rc = pRealEnv->xWrite(p->pReal, iOff, pData, nData); gettimeofday(&t2, 0); nUs = (t2.tv_sec - t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec); pDb->xWriteHook(pDb->pWriteCtx, p->bLog, iOff, nData, nUs); return rc; } return pRealEnv->xWrite(p->pReal, iOff, pData, nData); } static void doSystemCrash(LsmDb *pDb); static int testEnvSync(lsm_file *pFile){ lsm_env *pRealEnv = tdb_lsm_env(); LsmFile *p = (LsmFile *)pFile; LsmDb *pDb = p->pDb; FileData *pData = &pDb->aFile[p->bLog]; int i; if( pDb->bCrashed ) return LSM_IOERR; if( pDb->nAutoCrash ){ pDb->nAutoCrash--; if( pDb->nAutoCrash==0 ){ doSystemCrash(pDb); pDb->bCrashed = 1; return LSM_IOERR; } } if( pDb->bPrepareCrash ){ for(i=0; i<pData->nSector; i++){ testFree(pData->aSector[i].aOld); pData->aSector[i].aOld = 0; } } if( pDb->xWriteHook ){ int rc; int nUs; struct timeval t1; struct timeval t2; gettimeofday(&t1, 0); rc = pRealEnv->xSync(p->pReal); gettimeofday(&t2, 0); nUs = (t2.tv_sec - t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec); pDb->xWriteHook(pDb->pWriteCtx, p->bLog, 0, 0, nUs); return rc; } return pRealEnv->xSync(p->pReal); } static int testEnvTruncate(lsm_file *pFile, lsm_i64 iOff){ lsm_env *pRealEnv = tdb_lsm_env(); LsmFile *p = (LsmFile *)pFile; if( p->pDb->bCrashed ) return LSM_IOERR; return pRealEnv->xTruncate(p->pReal, iOff); } static int testEnvSectorSize(lsm_file *pFile){ lsm_env *pRealEnv = tdb_lsm_env(); LsmFile *p = (LsmFile *)pFile; return pRealEnv->xSectorSize(p->pReal); } static int testEnvRemap( lsm_file *pFile, lsm_i64 iMin, void **ppOut, lsm_i64 *pnOut ){ lsm_env *pRealEnv = tdb_lsm_env(); LsmFile *p = (LsmFile *)pFile; return pRealEnv->xRemap(p->pReal, iMin, ppOut, pnOut); } static int testEnvFileid( lsm_file *pFile, void *ppOut, int *pnOut ){ lsm_env *pRealEnv = tdb_lsm_env(); LsmFile *p = (LsmFile *)pFile; return pRealEnv->xFileid(p->pReal, ppOut, pnOut); } static int testEnvClose(lsm_file *pFile){ lsm_env *pRealEnv = tdb_lsm_env(); LsmFile *p = (LsmFile *)pFile; pRealEnv->xClose(p->pReal); testFree(p); return LSM_OK; } static int testEnvUnlink(lsm_env *pEnv, const char *zFile){ lsm_env *pRealEnv = tdb_lsm_env(); unused_parameter(pEnv); return pRealEnv->xUnlink(pRealEnv, zFile); } static int testEnvLock(lsm_file *pFile, int iLock, int eType){ LsmFile *p = (LsmFile *)pFile; lsm_env *pRealEnv = tdb_lsm_env(); if( iLock==2 && eType==LSM_LOCK_EXCL && p->pDb->bNoRecovery ){ return LSM_BUSY; } return pRealEnv->xLock(p->pReal, iLock, eType); } static int testEnvTestLock(lsm_file *pFile, int iLock, int nLock, int eType){ LsmFile *p = (LsmFile *)pFile; lsm_env *pRealEnv = tdb_lsm_env(); if( iLock==2 && eType==LSM_LOCK_EXCL && p->pDb->bNoRecovery ){ return LSM_BUSY; } return pRealEnv->xTestLock(p->pReal, iLock, nLock, eType); } static int testEnvShmMap(lsm_file *pFile, int iRegion, int sz, void **pp){ LsmFile *p = (LsmFile *)pFile; lsm_env *pRealEnv = tdb_lsm_env(); return pRealEnv->xShmMap(p->pReal, iRegion, sz, pp); } static void testEnvShmBarrier(void){ } static int testEnvShmUnmap(lsm_file *pFile, int bDel){ LsmFile *p = (LsmFile *)pFile; lsm_env *pRealEnv = tdb_lsm_env(); return pRealEnv->xShmUnmap(p->pReal, bDel); } static int testEnvSleep(lsm_env *pEnv, int us){ lsm_env *pRealEnv = tdb_lsm_env(); return pRealEnv->xSleep(pRealEnv, us); } static void doSystemCrash(LsmDb *pDb){ lsm_env *pEnv = tdb_lsm_env(); int iFile; int iSeed = pDb->aFile[0].nSector + pDb->aFile[1].nSector; char *zFile = pDb->zName; char *zFree = 0; for(iFile=0; iFile<2; iFile++){ lsm_file *pFile = 0; int i; pEnv->xOpen(pEnv, zFile, 0, &pFile); for(i=0; i<pDb->aFile[iFile].nSector; i++){ u8 *aOld = pDb->aFile[iFile].aSector[i].aOld; if( aOld ){ int iOpt = testPrngValue(iSeed++) % 3; switch( iOpt ){ case 0: break; case 1: testPrngArray(iSeed++, (u32 *)aOld, pDb->szSector/4); /* Fall-through */ case 2: pEnv->xWrite( pFile, (lsm_i64)i * pDb->szSector, aOld, pDb->szSector ); break; } testFree(aOld); pDb->aFile[iFile].aSector[i].aOld = 0; } } pEnv->xClose(pFile); zFree = zFile = sqlite3_mprintf("%s-log", pDb->zName); } sqlite3_free(zFree); } /* ** End test VFS code. ************************************************************************** *************************************************************************/ /************************************************************************* ************************************************************************** ** Begin test compression hooks. */ #ifdef HAVE_ZLIB #include <zlib.h> static int testZipBound(void *pCtx, int nSrc){ return compressBound(nSrc); } static int testZipCompress( void *pCtx, /* Context pointer */ char *aOut, int *pnOut, /* OUT: Buffer containing compressed data */ const char *aIn, int nIn /* Buffer containing input data */ ){ uLongf n = *pnOut; /* In/out buffer size for compress() */ int rc; /* compress() return code */ rc = compress((Bytef*)aOut, &n, (Bytef*)aIn, nIn); *pnOut = n; return (rc==Z_OK ? 0 : LSM_ERROR); } static int testZipUncompress( void *pCtx, /* Context pointer */ char *aOut, int *pnOut, /* OUT: Buffer containing uncompressed data */ const char *aIn, int nIn /* Buffer containing input data */ ){ uLongf n = *pnOut; /* In/out buffer size for uncompress() */ int rc; /* uncompress() return code */ rc = uncompress((Bytef*)aOut, &n, (Bytef*)aIn, nIn); *pnOut = n; return (rc==Z_OK ? 0 : LSM_ERROR); } static int testConfigureCompression(lsm_db *pDb){ static lsm_compress zip = { 0, /* Context pointer (unused) */ 1, /* Id value */ testZipBound, /* xBound method */ testZipCompress, /* xCompress method */ testZipUncompress /* xUncompress method */ }; return lsm_config(pDb, LSM_CONFIG_SET_COMPRESSION, &zip); } #endif /* ifdef HAVE_ZLIB */ /* ** End test compression hooks. ************************************************************************** *************************************************************************/ static int test_lsm_close(TestDb *pTestDb){ int i; int rc = LSM_OK; LsmDb *pDb = (LsmDb *)pTestDb; lsm_csr_close(pDb->pCsr); lsm_close(pDb->db); /* If this is a multi-threaded database, wait on the worker threads. */ mt_shutdown(pDb); for(i=0; i<pDb->nWorker && rc==LSM_OK; i++){ rc = pDb->aWorker[i].worker_rc; } for(i=0; i<pDb->aFile[0].nSector; i++){ testFree(pDb->aFile[0].aSector[i].aOld); } testFree(pDb->aFile[0].aSector); for(i=0; i<pDb->aFile[1].nSector; i++){ testFree(pDb->aFile[1].aSector[i].aOld); } testFree(pDb->aFile[1].aSector); memset(pDb, sizeof(LsmDb), 0x11); testFree((char *)pDb->pBuf); testFree((char *)pDb); return rc; } static void mt_signal_worker(LsmDb*, int); static int waitOnCheckpointer(LsmDb *pDb, lsm_db *db){ int nSleep = 0; int nKB; int rc; do { nKB = 0; rc = lsm_info(db, LSM_INFO_CHECKPOINT_SIZE, &nKB); if( rc!=LSM_OK || nKB<pDb->nMtMaxCkpt ) break; #ifdef LSM_MUTEX_PTHREADS mt_signal_worker(pDb, (pDb->eMode==LSMTEST_MODE_BACKGROUND_CKPT ? 0 : 1) ); #endif usleep(5000); nSleep += 5; }while( 1 ); #if 0 if( nSleep ) printf("# waitOnCheckpointer(): nSleep=%d\n", nSleep); #endif return rc; } static int waitOnWorker(LsmDb *pDb){ int rc; int nLimit = -1; int nSleep = 0; rc = lsm_config(pDb->db, LSM_CONFIG_AUTOFLUSH, &nLimit); do { int nOld, nNew, rc2; rc2 = lsm_info(pDb->db, LSM_INFO_TREE_SIZE, &nOld, &nNew); if( rc2!=LSM_OK ) return rc2; if( nOld==0 || nNew<(nLimit/2) ) break; #ifdef LSM_MUTEX_PTHREADS mt_signal_worker(pDb, 0); #endif usleep(5000); nSleep += 5; }while( 1 ); #if 0 if( nSleep ) printf("# waitOnWorker(): nSleep=%d\n", nSleep); #endif return rc; } static int test_lsm_write( TestDb *pTestDb, void *pKey, int nKey, void *pVal, int nVal ){ LsmDb *pDb = (LsmDb *)pTestDb; int rc = LSM_OK; if( pDb->eMode==LSMTEST_MODE_BACKGROUND_CKPT ){ rc = waitOnCheckpointer(pDb, pDb->db); }else if( pDb->eMode==LSMTEST_MODE_BACKGROUND_WORK || pDb->eMode==LSMTEST_MODE_BACKGROUND_BOTH ){ rc = waitOnWorker(pDb); } if( rc==LSM_OK ){ rc = lsm_insert(pDb->db, pKey, nKey, pVal, nVal); } return rc; } static int test_lsm_delete(TestDb *pTestDb, void *pKey, int nKey){ LsmDb *pDb = (LsmDb *)pTestDb; return lsm_delete(pDb->db, pKey, nKey); } static int test_lsm_delete_range( TestDb *pTestDb, void *pKey1, int nKey1, void *pKey2, int nKey2 ){ LsmDb *pDb = (LsmDb *)pTestDb; return lsm_delete_range(pDb->db, pKey1, nKey1, pKey2, nKey2); } static int test_lsm_fetch( TestDb *pTestDb, void *pKey, int nKey, void **ppVal, int *pnVal ){ int rc; LsmDb *pDb = (LsmDb *)pTestDb; lsm_cursor *csr; if( pKey==0 ) return LSM_OK; rc = lsm_csr_open(pDb->db, &csr); if( rc!=LSM_OK ) return rc; rc = lsm_csr_seek(csr, pKey, nKey, LSM_SEEK_EQ); if( rc==LSM_OK ){ if( lsm_csr_valid(csr) ){ const void *pVal; int nVal; rc = lsm_csr_value(csr, &pVal, &nVal); if( nVal>pDb->nBuf ){ testFree(pDb->pBuf); pDb->pBuf = testMalloc(nVal*2); pDb->nBuf = nVal*2; } memcpy(pDb->pBuf, pVal, nVal); *ppVal = pDb->pBuf; *pnVal = nVal; }else{ *ppVal = 0; *pnVal = -1; } } lsm_csr_close(csr); return rc; } static int test_lsm_scan( TestDb *pTestDb, void *pCtx, int bReverse, void *pFirst, int nFirst, void *pLast, int nLast, void (*xCallback)(void *, void *, int , void *, int) ){ LsmDb *pDb = (LsmDb *)pTestDb; lsm_cursor *csr; int rc; rc = lsm_csr_open(pDb->db, &csr); if( rc!=LSM_OK ) return rc; if( bReverse ){ if( pLast ){ rc = lsm_csr_seek(csr, pLast, nLast, LSM_SEEK_LE); }else{ rc = lsm_csr_last(csr); } }else{ if( pFirst ){ rc = lsm_csr_seek(csr, pFirst, nFirst, LSM_SEEK_GE); }else{ rc = lsm_csr_first(csr); } } while( rc==LSM_OK && lsm_csr_valid(csr) ){ const void *pKey; int nKey; const void *pVal; int nVal; int cmp; lsm_csr_key(csr, &pKey, &nKey); lsm_csr_value(csr, &pVal, &nVal); if( bReverse && pFirst ){ cmp = memcmp(pFirst, pKey, MIN(nKey, nFirst)); if( cmp>0 || (cmp==0 && nFirst>nKey) ) break; }else if( bReverse==0 && pLast ){ cmp = memcmp(pLast, pKey, MIN(nKey, nLast)); if( cmp<0 || (cmp==0 && nLast<nKey) ) break; } xCallback(pCtx, (void *)pKey, nKey, (void *)pVal, nVal); if( bReverse ){ rc = lsm_csr_prev(csr); }else{ rc = lsm_csr_next(csr); } } lsm_csr_close(csr); return rc; } static int test_lsm_begin(TestDb *pTestDb, int iLevel){ int rc = LSM_OK; LsmDb *pDb = (LsmDb *)pTestDb; /* iLevel==0 is a no-op. */ if( iLevel==0 ) return 0; if( pDb->pCsr==0 ) rc = lsm_csr_open(pDb->db, &pDb->pCsr); if( rc==LSM_OK && iLevel>1 ){ rc = lsm_begin(pDb->db, iLevel-1); } return rc; } static int test_lsm_commit(TestDb *pTestDb, int iLevel){ LsmDb *pDb = (LsmDb *)pTestDb; /* If iLevel==0, close any open read transaction */ if( iLevel==0 && pDb->pCsr ){ lsm_csr_close(pDb->pCsr); pDb->pCsr = 0; } /* If iLevel==0, close any open read transaction */ return lsm_commit(pDb->db, MAX(0, iLevel-1)); } static int test_lsm_rollback(TestDb *pTestDb, int iLevel){ LsmDb *pDb = (LsmDb *)pTestDb; /* If iLevel==0, close any open read transaction */ if( iLevel==0 && pDb->pCsr ){ lsm_csr_close(pDb->pCsr); pDb->pCsr = 0; } return lsm_rollback(pDb->db, MAX(0, iLevel-1)); } /* ** A log message callback registered with lsm connections. Prints all ** messages to stderr. */ static void xLog(void *pCtx, int rc, const char *z){ unused_parameter(rc); /* fprintf(stderr, "lsm: rc=%d \"%s\"\n", rc, z); */ if( pCtx ) fprintf(stderr, "%s: ", (char *)pCtx); fprintf(stderr, "%s\n", z); fflush(stderr); } static void xWorkHook(lsm_db *db, void *pArg){ LsmDb *p = (LsmDb *)pArg; if( p->xWork ) p->xWork(db, p->pWorkCtx); } #define TEST_NO_RECOVERY -1 #define TEST_COMPRESSION -3 #define TEST_MT_MODE -2 #define TEST_MT_MIN_CKPT -4 #define TEST_MT_MAX_CKPT -5 int test_lsm_config_str( LsmDb *pLsm, lsm_db *db, int bWorker, const char *zStr, int *pnThread ){ struct CfgParam { const char *zParam; int bWorker; int eParam; } aParam[] = { { "autoflush", 0, LSM_CONFIG_AUTOFLUSH }, { "page_size", 0, LSM_CONFIG_PAGE_SIZE }, { "block_size", 0, LSM_CONFIG_BLOCK_SIZE }, { "safety", 0, LSM_CONFIG_SAFETY }, { "autowork", 0, LSM_CONFIG_AUTOWORK }, { "autocheckpoint", 0, LSM_CONFIG_AUTOCHECKPOINT }, { "mmap", 0, LSM_CONFIG_MMAP }, { "use_log", 0, LSM_CONFIG_USE_LOG }, { "automerge", 0, LSM_CONFIG_AUTOMERGE }, { "max_freelist", 0, LSM_CONFIG_MAX_FREELIST }, { "multi_proc", 0, LSM_CONFIG_MULTIPLE_PROCESSES }, { "worker_automerge", 1, LSM_CONFIG_AUTOMERGE }, { "test_no_recovery", 0, TEST_NO_RECOVERY }, { "bg_min_ckpt", 0, TEST_NO_RECOVERY }, { "mt_mode", 0, TEST_MT_MODE }, { "mt_min_ckpt", 0, TEST_MT_MIN_CKPT }, { "mt_max_ckpt", 0, TEST_MT_MAX_CKPT }, #ifdef HAVE_ZLIB { "compression", 0, TEST_COMPRESSION }, #endif { 0, 0 } }; const char *z = zStr; int nThread = 1; if( zStr==0 ) return 0; assert( db ); while( z[0] ){ const char *zStart; /* Skip whitespace */ while( *z==' ' ) z++; zStart = z; while( *z && *z!='=' ) z++; if( *z ){ int eParam; int i; int iVal; int iMul = 1; int rc; char zParam[32]; int nParam = z-zStart; if( nParam==0 || nParam>sizeof(zParam)-1 ) goto syntax_error; memcpy(zParam, zStart, nParam); zParam[nParam] = '\0'; rc = testArgSelect(aParam, "param", zParam, &i); if( rc!=0 ) return rc; eParam = aParam[i].eParam; z++; zStart = z; while( *z>='0' && *z<='9' ) z++; if( *z=='k' || *z=='K' ){ iMul = 1; z++; }else if( *z=='M' || *z=='M' ){ iMul = 1024; z++; } nParam = z-zStart; if( nParam==0 || nParam>sizeof(zParam)-1 ) goto syntax_error; memcpy(zParam, zStart, nParam); zParam[nParam] = '\0'; iVal = atoi(zParam) * iMul; if( eParam>0 ){ if( bWorker || aParam[i].bWorker==0 ){ lsm_config(db, eParam, &iVal); } }else{ switch( eParam ){ case TEST_NO_RECOVERY: if( pLsm ) pLsm->bNoRecovery = iVal; break; case TEST_MT_MODE: if( pLsm ) nThread = iVal; break; case TEST_MT_MIN_CKPT: if( pLsm && iVal>0 ) pLsm->nMtMinCkpt = iVal*1024; break; case TEST_MT_MAX_CKPT: if( pLsm && iVal>0 ) pLsm->nMtMaxCkpt = iVal*1024; break; #ifdef HAVE_ZLIB case TEST_COMPRESSION: testConfigureCompression(db); break; #endif } } }else if( z!=zStart ){ goto syntax_error; } } if( pnThread ) *pnThread = nThread; if( pLsm && pLsm->nMtMaxCkpt < pLsm->nMtMinCkpt ){ pLsm->nMtMinCkpt = pLsm->nMtMaxCkpt; } return 0; syntax_error: testPrintError("syntax error at: \"%s\"\n", z); return 1; } int tdb_lsm_config_str(TestDb *pDb, const char *zStr){ int rc = 0; if( tdb_lsm(pDb) ){ #ifdef LSM_MUTEX_PTHREADS int i; #endif LsmDb *pLsm = (LsmDb *)pDb; rc = test_lsm_config_str(pLsm, pLsm->db, 0, zStr, 0); #ifdef LSM_MUTEX_PTHREADS for(i=0; rc==0 && i<pLsm->nWorker; i++){ rc = test_lsm_config_str(0, pLsm->aWorker[i].pWorker, 1, zStr, 0); } #endif } return rc; } int tdb_lsm_configure(lsm_db *db, const char *zConfig){ return test_lsm_config_str(0, db, 0, zConfig, 0); } static int testLsmStartWorkers(LsmDb *, int, const char *, const char *); static int testLsmOpen( const char *zCfg, const char *zFilename, int bClear, TestDb **ppDb ){ static const DatabaseMethods LsmMethods = { test_lsm_close, test_lsm_write, test_lsm_delete, test_lsm_delete_range, test_lsm_fetch, test_lsm_scan, test_lsm_begin, test_lsm_commit, test_lsm_rollback }; int rc; int nFilename; LsmDb *pDb; /* If the bClear flag is set, delete any existing database. */ assert( zFilename); if( bClear ) testDeleteLsmdb(zFilename); nFilename = strlen(zFilename); pDb = (LsmDb *)testMalloc(sizeof(LsmDb) + nFilename + 1); memset(pDb, 0, sizeof(LsmDb)); pDb->base.pMethods = &LsmMethods; pDb->zName = (char *)&pDb[1]; memcpy(pDb->zName, zFilename, nFilename + 1); /* Default the sector size used for crash simulation to 512 bytes. ** Todo: There should be an OS method to obtain this value - just as ** there is in SQLite. For now, LSM assumes that it is smaller than ** the page size (default 4KB). */ pDb->szSector = 256; /* Default values for the mt_min_ckpt and mt_max_ckpt parameters. */ pDb->nMtMinCkpt = LSMTEST_DFLT_MT_MIN_CKPT; pDb->nMtMaxCkpt = LSMTEST_DFLT_MT_MAX_CKPT; memcpy(&pDb->env, tdb_lsm_env(), sizeof(lsm_env)); pDb->env.pVfsCtx = (void *)pDb; pDb->env.xFullpath = testEnvFullpath; pDb->env.xOpen = testEnvOpen; pDb->env.xRead = testEnvRead; pDb->env.xWrite = testEnvWrite; pDb->env.xTruncate = testEnvTruncate; pDb->env.xSync = testEnvSync; pDb->env.xSectorSize = testEnvSectorSize; pDb->env.xRemap = testEnvRemap; pDb->env.xFileid = testEnvFileid; pDb->env.xClose = testEnvClose; pDb->env.xUnlink = testEnvUnlink; pDb->env.xLock = testEnvLock; pDb->env.xTestLock = testEnvTestLock; pDb->env.xShmBarrier = testEnvShmBarrier; pDb->env.xShmMap = testEnvShmMap; pDb->env.xShmUnmap = testEnvShmUnmap; pDb->env.xSleep = testEnvSleep; rc = lsm_new(&pDb->env, &pDb->db); if( rc==LSM_OK ){ int nThread = 1; lsm_config_log(pDb->db, xLog, 0); lsm_config_work_hook(pDb->db, xWorkHook, (void *)pDb); rc = test_lsm_config_str(pDb, pDb->db, 0, zCfg, &nThread); if( rc==LSM_OK ) rc = lsm_open(pDb->db, zFilename); pDb->eMode = nThread; #ifdef LSM_MUTEX_PTHREADS if( rc==LSM_OK && nThread>1 ){ testLsmStartWorkers(pDb, nThread, zFilename, zCfg); } #endif if( rc!=LSM_OK ){ test_lsm_close((TestDb *)pDb); pDb = 0; } } *ppDb = (TestDb *)pDb; return rc; } int test_lsm_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ return testLsmOpen(zSpec, zFilename, bClear, ppDb); } int test_lsm_small_open( const char *zSpec, const char *zFile, int bClear, TestDb **ppDb ){ const char *zCfg = "page_size=256 block_size=64 mmap=1024"; return testLsmOpen(zCfg, zFile, bClear, ppDb); } int test_lsm_lomem_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ /* "max_freelist=4 autocheckpoint=32" */ const char *zCfg = "page_size=256 block_size=64 autoflush=16 " "autocheckpoint=32" "mmap=0 " ; return testLsmOpen(zCfg, zFilename, bClear, ppDb); } int test_lsm_zip_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ const char *zCfg = "page_size=256 block_size=64 autoflush=16 " "autocheckpoint=32 compression=1 mmap=0 " ; return testLsmOpen(zCfg, zFilename, bClear, ppDb); } lsm_db *tdb_lsm(TestDb *pDb){ if( pDb->pMethods->xClose==test_lsm_close ){ return ((LsmDb *)pDb)->db; } return 0; } int tdb_lsm_multithread(TestDb *pDb){ int ret = 0; if( tdb_lsm(pDb) ){ ret = ((LsmDb*)pDb)->eMode!=LSMTEST_MODE_SINGLETHREAD; } return ret; } void tdb_lsm_enable_log(TestDb *pDb, int bEnable){ lsm_db *db = tdb_lsm(pDb); if( db ){ lsm_config_log(db, (bEnable ? xLog : 0), (void *)"client"); } } void tdb_lsm_application_crash(TestDb *pDb){ if( tdb_lsm(pDb) ){ LsmDb *p = (LsmDb *)pDb; p->bCrashed = 1; } } void tdb_lsm_prepare_system_crash(TestDb *pDb){ if( tdb_lsm(pDb) ){ LsmDb *p = (LsmDb *)pDb; p->bPrepareCrash = 1; } } void tdb_lsm_system_crash(TestDb *pDb){ if( tdb_lsm(pDb) ){ LsmDb *p = (LsmDb *)pDb; p->bCrashed = 1; doSystemCrash(p); } } void tdb_lsm_safety(TestDb *pDb, int eMode){ assert( eMode==LSM_SAFETY_OFF || eMode==LSM_SAFETY_NORMAL || eMode==LSM_SAFETY_FULL ); if( tdb_lsm(pDb) ){ int iParam = eMode; LsmDb *p = (LsmDb *)pDb; lsm_config(p->db, LSM_CONFIG_SAFETY, &iParam); } } void tdb_lsm_prepare_sync_crash(TestDb *pDb, int iSync){ assert( iSync>0 ); if( tdb_lsm(pDb) ){ LsmDb *p = (LsmDb *)pDb; p->nAutoCrash = iSync; p->bPrepareCrash = 1; } } void tdb_lsm_config_work_hook( TestDb *pDb, void (*xWork)(lsm_db *, void *), void *pWorkCtx ){ if( tdb_lsm(pDb) ){ LsmDb *p = (LsmDb *)pDb; p->xWork = xWork; p->pWorkCtx = pWorkCtx; } } void tdb_lsm_write_hook( TestDb *pDb, void (*xWrite)(void *, int, lsm_i64, int, int), void *pWriteCtx ){ if( tdb_lsm(pDb) ){ LsmDb *p = (LsmDb *)pDb; p->xWriteHook = xWrite; p->pWriteCtx = pWriteCtx; } } int tdb_lsm_open(const char *zCfg, const char *zDb, int bClear, TestDb **ppDb){ return testLsmOpen(zCfg, zDb, bClear, ppDb); } #ifdef LSM_MUTEX_PTHREADS /* ** Signal worker thread iWorker that there may be work to do. */ static void mt_signal_worker(LsmDb *pDb, int iWorker){ LsmWorker *p = &pDb->aWorker[iWorker]; pthread_mutex_lock(&p->worker_mutex); p->bDoWork = 1; pthread_cond_signal(&p->worker_cond); pthread_mutex_unlock(&p->worker_mutex); } /* ** This routine is used as the main() for all worker threads. */ static void *worker_main(void *pArg){ LsmWorker *p = (LsmWorker *)pArg; lsm_db *pWorker; /* Connection to access db through */ pthread_mutex_lock(&p->worker_mutex); while( (pWorker = p->pWorker) ){ int rc = LSM_OK; /* Do some work. If an error occurs, exit. */ pthread_mutex_unlock(&p->worker_mutex); if( p->eType==LSMTEST_THREAD_CKPT ){ int nKB = 0; rc = lsm_info(pWorker, LSM_INFO_CHECKPOINT_SIZE, &nKB); if( rc==LSM_OK && nKB>=p->pDb->nMtMinCkpt ){ rc = lsm_checkpoint(pWorker, 0); } }else{ int nWrite; do { if( p->eType==LSMTEST_THREAD_WORKER ){ waitOnCheckpointer(p->pDb, pWorker); } nWrite = 0; rc = lsm_work(pWorker, 0, 256, &nWrite); if( p->eType==LSMTEST_THREAD_WORKER && nWrite ){ mt_signal_worker(p->pDb, 1); } }while( nWrite && p->pWorker ); } pthread_mutex_lock(&p->worker_mutex); if( rc!=LSM_OK && rc!=LSM_BUSY ){ p->worker_rc = rc; break; } /* The thread will wake up when it is signaled either because another ** thread has created some work for this one or because the connection ** is being closed. */ if( p->pWorker && p->bDoWork==0 ){ pthread_cond_wait(&p->worker_cond, &p->worker_mutex); } p->bDoWork = 0; } pthread_mutex_unlock(&p->worker_mutex); return 0; } static void mt_stop_worker(LsmDb *pDb, int iWorker){ LsmWorker *p = &pDb->aWorker[iWorker]; if( p->pWorker ){ void *pDummy; lsm_db *pWorker; /* Signal the worker to stop */ pthread_mutex_lock(&p->worker_mutex); pWorker = p->pWorker; p->pWorker = 0; pthread_cond_signal(&p->worker_cond); pthread_mutex_unlock(&p->worker_mutex); /* Join the worker thread. */ pthread_join(p->worker_thread, &pDummy); /* Free resources allocated in mt_start_worker() */ pthread_cond_destroy(&p->worker_cond); pthread_mutex_destroy(&p->worker_mutex); lsm_close(pWorker); } } static void mt_shutdown(LsmDb *pDb){ int i; for(i=0; i<pDb->nWorker; i++){ mt_stop_worker(pDb, i); } } /* ** This callback is invoked by LSM when the client database writes to ** the database file (i.e. to flush the contents of the in-memory tree). ** This implies there may be work to do on the database, so signal ** the worker threads. */ static void mt_client_work_hook(lsm_db *db, void *pArg){ LsmDb *pDb = (LsmDb *)pArg; /* LsmDb database handle */ /* Invoke the user level work-hook, if any. */ if( pDb->xWork ) pDb->xWork(db, pDb->pWorkCtx); /* Wake up worker thread 0. */ mt_signal_worker(pDb, 0); } static void mt_worker_work_hook(lsm_db *db, void *pArg){ LsmDb *pDb = (LsmDb *)pArg; /* LsmDb database handle */ /* Invoke the user level work-hook, if any. */ if( pDb->xWork ) pDb->xWork(db, pDb->pWorkCtx); } /* ** Launch worker thread iWorker for database connection pDb. */ static int mt_start_worker( LsmDb *pDb, /* Main database structure */ int iWorker, /* Worker number to start */ const char *zFilename, /* File name of database to open */ const char *zCfg, /* Connection configuration string */ int eType /* Type of worker thread */ ){ int rc = 0; /* Return code */ LsmWorker *p; /* Object to initialize */ assert( iWorker<pDb->nWorker ); assert( eType==LSMTEST_THREAD_CKPT || eType==LSMTEST_THREAD_WORKER || eType==LSMTEST_THREAD_WORKER_AC ); p = &pDb->aWorker[iWorker]; p->eType = eType; p->pDb = pDb; /* Open the worker connection */ if( rc==0 ) rc = lsm_new(&pDb->env, &p->pWorker); if( zCfg ){ test_lsm_config_str(pDb, p->pWorker, 1, zCfg, 0); } if( rc==0 ) rc = lsm_open(p->pWorker, zFilename); lsm_config_log(p->pWorker, xLog, (void *)"worker"); /* Configure the work-hook */ if( rc==0 ){ lsm_config_work_hook(p->pWorker, mt_worker_work_hook, (void *)pDb); } if( eType==LSMTEST_THREAD_WORKER ){ test_lsm_config_str(0, p->pWorker, 1, "autocheckpoint=0", 0); } /* Kick off the worker thread. */ if( rc==0 ) rc = pthread_cond_init(&p->worker_cond, 0); if( rc==0 ) rc = pthread_mutex_init(&p->worker_mutex, 0); if( rc==0 ) rc = pthread_create(&p->worker_thread, 0, worker_main, (void *)p); return rc; } static int testLsmStartWorkers( LsmDb *pDb, int eModel, const char *zFilename, const char *zCfg ){ int rc; if( eModel<1 || eModel>4 ) return 1; if( eModel==1 ) return 0; /* Configure a work-hook for the client connection. Worker 0 is signalled ** every time the users connection writes to the database. */ lsm_config_work_hook(pDb->db, mt_client_work_hook, (void *)pDb); /* Allocate space for two worker connections. They may not both be ** used, but both are allocated. */ pDb->aWorker = (LsmWorker *)testMalloc(sizeof(LsmWorker) * 2); memset(pDb->aWorker, 0, sizeof(LsmWorker) * 2); switch( eModel ){ case LSMTEST_MODE_BACKGROUND_CKPT: pDb->nWorker = 1; test_lsm_config_str(0, pDb->db, 0, "autocheckpoint=0", 0); rc = mt_start_worker(pDb, 0, zFilename, zCfg, LSMTEST_THREAD_CKPT); break; case LSMTEST_MODE_BACKGROUND_WORK: pDb->nWorker = 1; test_lsm_config_str(0, pDb->db, 0, "autowork=0", 0); rc = mt_start_worker(pDb, 0, zFilename, zCfg, LSMTEST_THREAD_WORKER_AC); break; case LSMTEST_MODE_BACKGROUND_BOTH: pDb->nWorker = 2; test_lsm_config_str(0, pDb->db, 0, "autowork=0", 0); rc = mt_start_worker(pDb, 0, zFilename, zCfg, LSMTEST_THREAD_WORKER); if( rc==0 ){ rc = mt_start_worker(pDb, 1, zFilename, zCfg, LSMTEST_THREAD_CKPT); } break; } return rc; } int test_lsm_mt2( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ const char *zCfg = "mt_mode=2"; return testLsmOpen(zCfg, zFilename, bClear, ppDb); } int test_lsm_mt3( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ const char *zCfg = "mt_mode=4"; return testLsmOpen(zCfg, zFilename, bClear, ppDb); } #else static void mt_shutdown(LsmDb *pDb) { unused_parameter(pDb); } int test_lsm_mt(const char *zFilename, int bClear, TestDb **ppDb){ unused_parameter(zFilename); unused_parameter(bClear); unused_parameter(ppDb); testPrintError("threads unavailable - recompile with LSM_MUTEX_PTHREADS\n"); return 1; } #endif |
Added ext/lsm1/lsm-test/lsmtest_tdb4.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 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 | /* ** This file contains the TestDb bt wrapper. */ #include "lsmtest_tdb.h" #include "lsmtest.h" #include <unistd.h> #include "bt.h" #include <pthread.h> typedef struct BtDb BtDb; typedef struct BtFile BtFile; /* Background checkpointer interface (see implementations below). */ typedef struct bt_ckpter bt_ckpter; static int bgc_attach(BtDb *pDb, const char*); static int bgc_detach(BtDb *pDb); /* ** Each database or log file opened by a database handle is wrapped by ** an object of the following type. */ struct BtFile { BtDb *pBt; /* Database handle that opened this file */ bt_env *pVfs; /* Underlying VFS */ bt_file *pFile; /* File handle belonging to underlying VFS */ int nSectorSize; /* Size of sectors in bytes */ int nSector; /* Allocated size of nSector array */ u8 **apSector; /* Original sector data */ }; /* ** nCrashSync: ** If this value is non-zero, then a "crash-test" is running. If ** nCrashSync==1, then the crash is simulated during the very next ** call to the xSync() VFS method (on either the db or log file). ** If nCrashSync==2, the following call to xSync(), and so on. ** ** bCrash: ** After a crash is simulated, this variable is set. Any subsequent ** attempts to write to a file or modify the file system in any way ** fail once this is set. All the caller can do is close the connection. ** ** bFastInsert: ** If this variable is set to true, then a BT_CONTROL_FAST_INSERT_OP ** control is issued before each callto BtReplace() or BtCsrOpen(). */ struct BtDb { TestDb base; /* Base class */ bt_db *pBt; /* bt database handle */ sqlite4_env *pEnv; /* SQLite environment (for malloc/free) */ bt_env *pVfs; /* Underlying VFS */ int bFastInsert; /* True to use fast-insert */ /* Space for bt_fetch() results */ u8 *aBuffer; /* Space to store results */ int nBuffer; /* Allocated size of aBuffer[] in bytes */ int nRef; /* Background checkpointer used by mt connections */ bt_ckpter *pCkpter; /* Stuff used for crash test simulation */ BtFile *apFile[2]; /* Database and log files used by pBt */ bt_env env; /* Private VFS for this object */ int nCrashSync; /* Number of syncs until crash (see above) */ int bCrash; /* True once a crash has been simulated */ }; static int btVfsFullpath( sqlite4_env *pEnv, bt_env *pVfs, const char *z, char **pzOut ){ BtDb *pBt = (BtDb*)pVfs->pVfsCtx; if( pBt->bCrash ) return SQLITE4_IOERR; return pBt->pVfs->xFullpath(pEnv, pBt->pVfs, z, pzOut); } static int btVfsOpen( sqlite4_env *pEnv, bt_env *pVfs, const char *zFile, int flags, bt_file **ppFile ){ BtFile *p; BtDb *pBt = (BtDb*)pVfs->pVfsCtx; int rc; if( pBt->bCrash ) return SQLITE4_IOERR; p = (BtFile*)testMalloc(sizeof(BtFile)); if( !p ) return SQLITE4_NOMEM; if( flags & BT_OPEN_DATABASE ){ pBt->apFile[0] = p; }else if( flags & BT_OPEN_LOG ){ pBt->apFile[1] = p; } if( (flags & BT_OPEN_SHARED)==0 ){ p->pBt = pBt; } p->pVfs = pBt->pVfs; rc = pBt->pVfs->xOpen(pEnv, pVfs, zFile, flags, &p->pFile); if( rc!=SQLITE4_OK ){ testFree(p); p = 0; }else{ pBt->nRef++; } *ppFile = (bt_file*)p; return rc; } static int btVfsSize(bt_file *pFile, sqlite4_int64 *piRes){ BtFile *p = (BtFile*)pFile; if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR; return p->pVfs->xSize(p->pFile, piRes); } static int btVfsRead(bt_file *pFile, sqlite4_int64 iOff, void *pBuf, int nBuf){ BtFile *p = (BtFile*)pFile; if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR; return p->pVfs->xRead(p->pFile, iOff, pBuf, nBuf); } static int btFlushSectors(BtFile *p, int iFile){ sqlite4_int64 iSz; int rc; int i; u8 *aTmp = 0; rc = p->pBt->pVfs->xSize(p->pFile, &iSz); for(i=0; rc==SQLITE4_OK && i<p->nSector; i++){ if( p->pBt->bCrash && p->apSector[i] ){ /* The system is simulating a crash. There are three choices for ** this sector: ** ** 1) Leave it as it is (simulating a successful write), ** 2) Restore the original data (simulating a lost write), ** 3) Populate the disk sector with garbage data. */ sqlite4_int64 iSOff = p->nSectorSize*i; int nWrite = MIN(p->nSectorSize, iSz - iSOff); if( nWrite ){ u8 *aWrite = 0; int iOpt = (testPrngValue(i) % 3) + 1; if( iOpt==1 ){ aWrite = p->apSector[i]; }else if( iOpt==3 ){ if( aTmp==0 ) aTmp = testMalloc(p->nSectorSize); aWrite = aTmp; testPrngArray(i*13, (u32*)aWrite, nWrite/sizeof(u32)); } #if 0 fprintf(stderr, "handle sector %d of %s with %s\n", i, iFile==0 ? "db" : "log", iOpt==1 ? "rollback" : iOpt==2 ? "write" : "omit" ); fflush(stderr); #endif if( aWrite ){ rc = p->pBt->pVfs->xWrite(p->pFile, iSOff, aWrite, nWrite); } } } testFree(p->apSector[i]); p->apSector[i] = 0; } testFree(aTmp); return rc; } static int btSaveSectors(BtFile *p, sqlite4_int64 iOff, int nBuf){ int rc; sqlite4_int64 iSz; /* Size of file on disk */ int iFirst; /* First sector affected */ int iSector; /* Current sector */ int iLast; /* Last sector affected */ if( p->nSectorSize==0 ){ p->nSectorSize = p->pBt->pVfs->xSectorSize(p->pFile); if( p->nSectorSize<512 ) p->nSectorSize = 512; } iLast = (iOff+nBuf-1) / p->nSectorSize; iFirst = iOff / p->nSectorSize; rc = p->pBt->pVfs->xSize(p->pFile, &iSz); for(iSector=iFirst; rc==SQLITE4_OK && iSector<=iLast; iSector++){ int nRead; sqlite4_int64 iSOff = iSector * p->nSectorSize; u8 *aBuf = testMalloc(p->nSectorSize); nRead = MIN(p->nSectorSize, (iSz - iSOff)); if( nRead>0 ){ rc = p->pBt->pVfs->xRead(p->pFile, iSOff, aBuf, nRead); } while( rc==SQLITE4_OK && iSector>=p->nSector ){ int nNew = p->nSector + 32; u8 **apNew = (u8**)testMalloc(nNew * sizeof(u8*)); memcpy(apNew, p->apSector, p->nSector*sizeof(u8*)); testFree(p->apSector); p->apSector = apNew; p->nSector = nNew; } p->apSector[iSector] = aBuf; } return rc; } static int btVfsWrite(bt_file *pFile, sqlite4_int64 iOff, void *pBuf, int nBuf){ BtFile *p = (BtFile*)pFile; if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR; if( p->pBt && p->pBt->nCrashSync ){ btSaveSectors(p, iOff, nBuf); } return p->pVfs->xWrite(p->pFile, iOff, pBuf, nBuf); } static int btVfsTruncate(bt_file *pFile, sqlite4_int64 iOff){ BtFile *p = (BtFile*)pFile; if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR; return p->pVfs->xTruncate(p->pFile, iOff); } static int btVfsSync(bt_file *pFile){ int rc = SQLITE4_OK; BtFile *p = (BtFile*)pFile; BtDb *pBt = p->pBt; if( pBt ){ if( pBt->bCrash ) return SQLITE4_IOERR; if( pBt->nCrashSync ){ pBt->nCrashSync--; pBt->bCrash = (pBt->nCrashSync==0); if( pBt->bCrash ){ btFlushSectors(pBt->apFile[0], 0); btFlushSectors(pBt->apFile[1], 1); rc = SQLITE4_IOERR; }else{ btFlushSectors(p, 0); } } } if( rc==SQLITE4_OK ){ rc = p->pVfs->xSync(p->pFile); } return rc; } static int btVfsSectorSize(bt_file *pFile){ BtFile *p = (BtFile*)pFile; return p->pVfs->xSectorSize(p->pFile); } static void btDeref(BtDb *p){ p->nRef--; assert( p->nRef>=0 ); if( p->nRef<=0 ) testFree(p); } static int btVfsClose(bt_file *pFile){ BtFile *p = (BtFile*)pFile; BtDb *pBt = p->pBt; int rc; if( pBt ){ btFlushSectors(p, 0); if( p==pBt->apFile[0] ) pBt->apFile[0] = 0; if( p==pBt->apFile[1] ) pBt->apFile[1] = 0; } testFree(p->apSector); rc = p->pVfs->xClose(p->pFile); #if 0 btDeref(p->pBt); #endif testFree(p); return rc; } static int btVfsUnlink(sqlite4_env *pEnv, bt_env *pVfs, const char *zFile){ BtDb *pBt = (BtDb*)pVfs->pVfsCtx; if( pBt->bCrash ) return SQLITE4_IOERR; return pBt->pVfs->xUnlink(pEnv, pBt->pVfs, zFile); } static int btVfsLock(bt_file *pFile, int iLock, int eType){ BtFile *p = (BtFile*)pFile; if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR; return p->pVfs->xLock(p->pFile, iLock, eType); } static int btVfsTestLock(bt_file *pFile, int iLock, int nLock, int eType){ BtFile *p = (BtFile*)pFile; if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR; return p->pVfs->xTestLock(p->pFile, iLock, nLock, eType); } static int btVfsShmMap(bt_file *pFile, int iChunk, int sz, void **ppOut){ BtFile *p = (BtFile*)pFile; if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR; return p->pVfs->xShmMap(p->pFile, iChunk, sz, ppOut); } static void btVfsShmBarrier(bt_file *pFile){ BtFile *p = (BtFile*)pFile; return p->pVfs->xShmBarrier(p->pFile); } static int btVfsShmUnmap(bt_file *pFile, int bDelete){ BtFile *p = (BtFile*)pFile; if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR; return p->pVfs->xShmUnmap(p->pFile, bDelete); } static int bt_close(TestDb *pTestDb){ BtDb *p = (BtDb*)pTestDb; int rc = sqlite4BtClose(p->pBt); free(p->aBuffer); if( p->apFile[0] ) p->apFile[0]->pBt = 0; if( p->apFile[1] ) p->apFile[1]->pBt = 0; bgc_detach(p); testFree(p); return rc; } static int btMinTransaction(BtDb *p, int iMin, int *piLevel){ int iLevel; int rc = SQLITE4_OK; iLevel = sqlite4BtTransactionLevel(p->pBt); if( iLevel<iMin ){ rc = sqlite4BtBegin(p->pBt, iMin); *piLevel = iLevel; }else{ *piLevel = -1; } return rc; } static int btRestoreTransaction(BtDb *p, int iLevel, int rcin){ int rc = rcin; if( iLevel>=0 ){ if( rc==SQLITE4_OK ){ rc = sqlite4BtCommit(p->pBt, iLevel); }else{ sqlite4BtRollback(p->pBt, iLevel); } assert( iLevel==sqlite4BtTransactionLevel(p->pBt) ); } return rc; } static int bt_write(TestDb *pTestDb, void *pK, int nK, void *pV, int nV){ BtDb *p = (BtDb*)pTestDb; int iLevel; int rc; rc = btMinTransaction(p, 2, &iLevel); if( rc==SQLITE4_OK ){ if( p->bFastInsert ) sqlite4BtControl(p->pBt, BT_CONTROL_FAST_INSERT_OP, 0); rc = sqlite4BtReplace(p->pBt, pK, nK, pV, nV); rc = btRestoreTransaction(p, iLevel, rc); } return rc; } static int bt_delete(TestDb *pTestDb, void *pK, int nK){ return bt_write(pTestDb, pK, nK, 0, -1); } static int bt_delete_range( TestDb *pTestDb, void *pKey1, int nKey1, void *pKey2, int nKey2 ){ BtDb *p = (BtDb*)pTestDb; bt_cursor *pCsr = 0; int rc = SQLITE4_OK; int iLevel; rc = btMinTransaction(p, 2, &iLevel); if( rc==SQLITE4_OK ){ if( p->bFastInsert ) sqlite4BtControl(p->pBt, BT_CONTROL_FAST_INSERT_OP, 0); rc = sqlite4BtCsrOpen(p->pBt, 0, &pCsr); } while( rc==SQLITE4_OK ){ const void *pK; int n; int nCmp; int res; rc = sqlite4BtCsrSeek(pCsr, pKey1, nKey1, BT_SEEK_GE); if( rc==SQLITE4_INEXACT ) rc = SQLITE4_OK; if( rc!=SQLITE4_OK ) break; rc = sqlite4BtCsrKey(pCsr, &pK, &n); if( rc!=SQLITE4_OK ) break; nCmp = MIN(n, nKey1); res = memcmp(pKey1, pK, nCmp); assert( res<0 || (res==0 && nKey1<=n) ); if( res==0 && nKey1==n ){ rc = sqlite4BtCsrNext(pCsr); if( rc!=SQLITE4_OK ) break; rc = sqlite4BtCsrKey(pCsr, &pK, &n); if( rc!=SQLITE4_OK ) break; } nCmp = MIN(n, nKey2); res = memcmp(pKey2, pK, nCmp); if( res<0 || (res==0 && nKey2<=n) ) break; rc = sqlite4BtDelete(pCsr); } if( rc==SQLITE4_NOTFOUND ) rc = SQLITE4_OK; sqlite4BtCsrClose(pCsr); rc = btRestoreTransaction(p, iLevel, rc); return rc; } static int bt_fetch( TestDb *pTestDb, void *pK, int nK, void **ppVal, int *pnVal ){ BtDb *p = (BtDb*)pTestDb; bt_cursor *pCsr = 0; int iLevel; int rc = SQLITE4_OK; iLevel = sqlite4BtTransactionLevel(p->pBt); if( iLevel==0 ){ rc = sqlite4BtBegin(p->pBt, 1); if( rc!=SQLITE4_OK ) return rc; } if( p->bFastInsert ) sqlite4BtControl(p->pBt, BT_CONTROL_FAST_INSERT_OP, 0); rc = sqlite4BtCsrOpen(p->pBt, 0, &pCsr); if( rc==SQLITE4_OK ){ rc = sqlite4BtCsrSeek(pCsr, pK, nK, BT_SEEK_EQ); if( rc==SQLITE4_OK ){ const void *pV = 0; int nV = 0; rc = sqlite4BtCsrData(pCsr, 0, -1, &pV, &nV); if( rc==SQLITE4_OK ){ if( nV>p->nBuffer ){ free(p->aBuffer); p->aBuffer = (u8*)malloc(nV*2); p->nBuffer = nV*2; } memcpy(p->aBuffer, pV, nV); *pnVal = nV; *ppVal = (void*)(p->aBuffer); } }else if( rc==SQLITE4_INEXACT || rc==SQLITE4_NOTFOUND ){ *ppVal = 0; *pnVal = -1; rc = SQLITE4_OK; } sqlite4BtCsrClose(pCsr); } if( iLevel==0 ) sqlite4BtCommit(p->pBt, 0); return rc; } static int bt_scan( TestDb *pTestDb, void *pCtx, int bReverse, void *pFirst, int nFirst, void *pLast, int nLast, void (*xCallback)(void *, void *, int , void *, int) ){ BtDb *p = (BtDb*)pTestDb; bt_cursor *pCsr = 0; int rc; int iLevel; rc = btMinTransaction(p, 1, &iLevel); if( rc==SQLITE4_OK ){ if( p->bFastInsert ) sqlite4BtControl(p->pBt, BT_CONTROL_FAST_INSERT_OP, 0); rc = sqlite4BtCsrOpen(p->pBt, 0, &pCsr); } if( rc==SQLITE4_OK ){ if( bReverse ){ if( pLast ){ rc = sqlite4BtCsrSeek(pCsr, pLast, nLast, BT_SEEK_LE); }else{ rc = sqlite4BtCsrLast(pCsr); } }else{ rc = sqlite4BtCsrSeek(pCsr, pFirst, nFirst, BT_SEEK_GE); } if( rc==SQLITE4_INEXACT ) rc = SQLITE4_OK; while( rc==SQLITE4_OK ){ const void *pK = 0; int nK = 0; const void *pV = 0; int nV = 0; rc = sqlite4BtCsrKey(pCsr, &pK, &nK); if( rc==SQLITE4_OK ){ rc = sqlite4BtCsrData(pCsr, 0, -1, &pV, &nV); } if( rc!=SQLITE4_OK ) break; if( bReverse ){ if( pFirst ){ int res; int nCmp = MIN(nK, nFirst); res = memcmp(pFirst, pK, nCmp); if( res>0 || (res==0 && nK<nFirst) ) break; } }else{ if( pLast ){ int res; int nCmp = MIN(nK, nLast); res = memcmp(pLast, pK, nCmp); if( res<0 || (res==0 && nK>nLast) ) break; } } xCallback(pCtx, (void*)pK, nK, (void*)pV, nV); if( bReverse ){ rc = sqlite4BtCsrPrev(pCsr); }else{ rc = sqlite4BtCsrNext(pCsr); } } if( rc==SQLITE4_NOTFOUND ) rc = SQLITE4_OK; sqlite4BtCsrClose(pCsr); } rc = btRestoreTransaction(p, iLevel, rc); return rc; } static int bt_begin(TestDb *pTestDb, int iLvl){ BtDb *p = (BtDb*)pTestDb; int rc = sqlite4BtBegin(p->pBt, iLvl); return rc; } static int bt_commit(TestDb *pTestDb, int iLvl){ BtDb *p = (BtDb*)pTestDb; int rc = sqlite4BtCommit(p->pBt, iLvl); return rc; } static int bt_rollback(TestDb *pTestDb, int iLvl){ BtDb *p = (BtDb*)pTestDb; int rc = sqlite4BtRollback(p->pBt, iLvl); return rc; } static int testParseOption( const char **pzIn, /* IN/OUT: pointer to next option */ const char **pzOpt, /* OUT: nul-terminated option name */ const char **pzArg, /* OUT: nul-terminated option argument */ char *pSpace /* Temporary space for output params */ ){ const char *p = *pzIn; const char *pStart; int n; char *pOut = pSpace; while( *p==' ' ) p++; pStart = p; while( *p && *p!='=' ) p++; if( *p==0 ) return 1; n = (p - pStart); memcpy(pOut, pStart, n); *pzOpt = pOut; pOut += n; *pOut++ = '\0'; p++; pStart = p; while( *p && *p!=' ' ) p++; n = (p - pStart); memcpy(pOut, pStart, n); *pzArg = pOut; pOut += n; *pOut++ = '\0'; *pzIn = p; return 0; } static int testParseInt(const char *z, int *piVal){ int i = 0; const char *p = z; while( *p>='0' && *p<='9' ){ i = i*10 + (*p - '0'); p++; } if( *p=='K' || *p=='k' ){ i = i * 1024; p++; }else if( *p=='M' || *p=='m' ){ i = i * 1024 * 1024; p++; } if( *p ) return SQLITE4_ERROR; *piVal = i; return SQLITE4_OK; } static int testBtConfigure(BtDb *pDb, const char *zCfg, int *pbMt){ int rc = SQLITE4_OK; if( zCfg ){ struct CfgParam { const char *zParam; int eParam; } aParam[] = { { "safety", BT_CONTROL_SAFETY }, { "autockpt", BT_CONTROL_AUTOCKPT }, { "multiproc", BT_CONTROL_MULTIPROC }, { "blksz", BT_CONTROL_BLKSZ }, { "pagesz", BT_CONTROL_PAGESZ }, { "mt", -1 }, { "fastinsert", -2 }, { 0, 0 } }; const char *z = zCfg; int n = strlen(z); char *aSpace; const char *zOpt; const char *zArg; aSpace = (char*)testMalloc(n+2); while( rc==SQLITE4_OK && 0==testParseOption(&z, &zOpt, &zArg, aSpace) ){ int i; int iVal; rc = testArgSelect(aParam, "param", zOpt, &i); if( rc!=SQLITE4_OK ) break; rc = testParseInt(zArg, &iVal); if( rc!=SQLITE4_OK ) break; switch( aParam[i].eParam ){ case -1: *pbMt = iVal; break; case -2: pDb->bFastInsert = 1; break; default: rc = sqlite4BtControl(pDb->pBt, aParam[i].eParam, (void*)&iVal); break; } } testFree(aSpace); } return rc; } int test_bt_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ static const DatabaseMethods SqlMethods = { bt_close, bt_write, bt_delete, bt_delete_range, bt_fetch, bt_scan, bt_begin, bt_commit, bt_rollback }; BtDb *p = 0; bt_db *pBt = 0; int rc; sqlite4_env *pEnv = sqlite4_env_default(); if( bClear && zFilename && zFilename[0] ){ char *zLog = sqlite3_mprintf("%s-wal", zFilename); unlink(zFilename); unlink(zLog); sqlite3_free(zLog); } rc = sqlite4BtNew(pEnv, 0, &pBt); if( rc==SQLITE4_OK ){ int mt = 0; /* True for multi-threaded connection */ p = (BtDb*)testMalloc(sizeof(BtDb)); p->base.pMethods = &SqlMethods; p->pBt = pBt; p->pEnv = pEnv; p->nRef = 1; p->env.pVfsCtx = (void*)p; p->env.xFullpath = btVfsFullpath; p->env.xOpen = btVfsOpen; p->env.xSize = btVfsSize; p->env.xRead = btVfsRead; p->env.xWrite = btVfsWrite; p->env.xTruncate = btVfsTruncate; p->env.xSync = btVfsSync; p->env.xSectorSize = btVfsSectorSize; p->env.xClose = btVfsClose; p->env.xUnlink = btVfsUnlink; p->env.xLock = btVfsLock; p->env.xTestLock = btVfsTestLock; p->env.xShmMap = btVfsShmMap; p->env.xShmBarrier = btVfsShmBarrier; p->env.xShmUnmap = btVfsShmUnmap; sqlite4BtControl(pBt, BT_CONTROL_GETVFS, (void*)&p->pVfs); sqlite4BtControl(pBt, BT_CONTROL_SETVFS, (void*)&p->env); rc = testBtConfigure(p, zSpec, &mt); if( rc==SQLITE4_OK ){ rc = sqlite4BtOpen(pBt, zFilename); } if( rc==SQLITE4_OK && mt ){ int nAuto = 0; rc = bgc_attach(p, zSpec); sqlite4BtControl(pBt, BT_CONTROL_AUTOCKPT, (void*)&nAuto); } } if( rc!=SQLITE4_OK && p ){ bt_close(&p->base); } *ppDb = &p->base; return rc; } int test_fbt_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ return test_bt_open("fast=1", zFilename, bClear, ppDb); } int test_fbts_open( const char *zSpec, const char *zFilename, int bClear, TestDb **ppDb ){ return test_bt_open("fast=1 blksz=32K pagesz=512", zFilename, bClear, ppDb); } void tdb_bt_prepare_sync_crash(TestDb *pTestDb, int iSync){ BtDb *p = (BtDb*)pTestDb; assert( pTestDb->pMethods->xClose==bt_close ); assert( p->bCrash==0 ); p->nCrashSync = iSync; } bt_db *tdb_bt(TestDb *pDb){ if( pDb->pMethods->xClose==bt_close ){ return ((BtDb *)pDb)->pBt; } return 0; } /************************************************************************* ** Beginning of code for background checkpointer. */ struct bt_ckpter { sqlite4_buffer file; /* File name */ sqlite4_buffer spec; /* Options */ int nLogsize; /* Minimum log size to checkpoint */ int nRef; /* Number of clients */ int bDoWork; /* Set by client threads */ pthread_t ckpter_thread; /* Checkpointer thread */ pthread_cond_t ckpter_cond; /* Condition var the ckpter waits on */ pthread_mutex_t ckpter_mutex; /* Mutex used with ckpter_cond */ bt_ckpter *pNext; /* Next object in list at gBgc.pCkpter */ }; static struct GlobalBackgroundCheckpointer { bt_ckpter *pCkpter; /* Linked list of checkpointers */ } gBgc; static void *bgc_main(void *pArg){ BtDb *pDb = 0; int rc; int mt; bt_ckpter *pCkpter = (bt_ckpter*)pArg; rc = test_bt_open("", (char*)pCkpter->file.p, 0, (TestDb**)&pDb); assert( rc==SQLITE4_OK ); rc = testBtConfigure(pDb, (char*)pCkpter->spec.p, &mt); while( pCkpter->nRef>0 ){ bt_db *db = pDb->pBt; int nLog = 0; sqlite4BtBegin(db, 1); sqlite4BtCommit(db, 0); sqlite4BtControl(db, BT_CONTROL_LOGSIZE, (void*)&nLog); if( nLog>=pCkpter->nLogsize ){ int rc; bt_checkpoint ckpt; memset(&ckpt, 0, sizeof(bt_checkpoint)); ckpt.nFrameBuffer = nLog/2; rc = sqlite4BtControl(db, BT_CONTROL_CHECKPOINT, (void*)&ckpt); assert( rc==SQLITE4_OK ); sqlite4BtControl(db, BT_CONTROL_LOGSIZE, (void*)&nLog); } /* The thread will wake up when it is signaled either because another ** thread has created some work for this one or because the connection ** is being closed. */ pthread_mutex_lock(&pCkpter->ckpter_mutex); if( pCkpter->bDoWork==0 ){ pthread_cond_wait(&pCkpter->ckpter_cond, &pCkpter->ckpter_mutex); } pCkpter->bDoWork = 0; pthread_mutex_unlock(&pCkpter->ckpter_mutex); } if( pDb ) bt_close((TestDb*)pDb); return 0; } static void bgc_logsize_cb(void *pCtx, int nLogsize){ bt_ckpter *p = (bt_ckpter*)pCtx; if( nLogsize>=p->nLogsize ){ pthread_mutex_lock(&p->ckpter_mutex); p->bDoWork = 1; pthread_cond_signal(&p->ckpter_cond); pthread_mutex_unlock(&p->ckpter_mutex); } } static int bgc_attach(BtDb *pDb, const char *zSpec){ int rc; int n; bt_info info; bt_ckpter *pCkpter; /* Figure out the full path to the database opened by handle pDb. */ info.eType = BT_INFO_FILENAME; info.pgno = 0; sqlite4_buffer_init(&info.output, 0); rc = sqlite4BtControl(pDb->pBt, BT_CONTROL_INFO, (void*)&info); if( rc!=SQLITE4_OK ) return rc; sqlite4_mutex_enter(sqlite4_mutex_alloc(pDb->pEnv, SQLITE4_MUTEX_STATIC_KV)); /* Search for an existing bt_ckpter object. */ n = info.output.n; for(pCkpter=gBgc.pCkpter; pCkpter; pCkpter=pCkpter->pNext){ if( n==pCkpter->file.n && 0==memcmp(info.output.p, pCkpter->file.p, n) ){ break; } } /* Failed to find a suitable checkpointer. Create a new one. */ if( pCkpter==0 ){ bt_logsizecb cb; pCkpter = testMalloc(sizeof(bt_ckpter)); memcpy(&pCkpter->file, &info.output, sizeof(sqlite4_buffer)); info.output.p = 0; pCkpter->pNext = gBgc.pCkpter; pCkpter->nLogsize = 1000; gBgc.pCkpter = pCkpter; pCkpter->nRef = 1; sqlite4_buffer_init(&pCkpter->spec, 0); rc = sqlite4_buffer_set(&pCkpter->spec, zSpec, strlen(zSpec)+1); assert( rc==SQLITE4_OK ); /* Kick off the checkpointer thread. */ if( rc==0 ) rc = pthread_cond_init(&pCkpter->ckpter_cond, 0); if( rc==0 ) rc = pthread_mutex_init(&pCkpter->ckpter_mutex, 0); if( rc==0 ){ rc = pthread_create(&pCkpter->ckpter_thread, 0, bgc_main, (void*)pCkpter); } assert( rc==0 ); /* todo: Fix this */ /* Set up the logsize callback for the client thread */ cb.pCtx = (void*)pCkpter; cb.xLogsize = bgc_logsize_cb; sqlite4BtControl(pDb->pBt, BT_CONTROL_LOGSIZECB, (void*)&cb); }else{ pCkpter->nRef++; } /* Assuming a checkpointer was encountered or effected, attach the ** connection to it. */ if( pCkpter ){ pDb->pCkpter = pCkpter; } sqlite4_mutex_leave(sqlite4_mutex_alloc(pDb->pEnv, SQLITE4_MUTEX_STATIC_KV)); sqlite4_buffer_clear(&info.output); return rc; } static int bgc_detach(BtDb *pDb){ int rc = SQLITE4_OK; bt_ckpter *pCkpter = pDb->pCkpter; if( pCkpter ){ int bShutdown = 0; /* True if this is the last reference */ sqlite4_mutex_enter(sqlite4_mutex_alloc(pDb->pEnv,SQLITE4_MUTEX_STATIC_KV)); pCkpter->nRef--; if( pCkpter->nRef==0 ){ bt_ckpter **pp; *pp = pCkpter->pNext; for(pp=&gBgc.pCkpter; *pp!=pCkpter; pp=&((*pp)->pNext)); bShutdown = 1; } sqlite4_mutex_leave(sqlite4_mutex_alloc(pDb->pEnv,SQLITE4_MUTEX_STATIC_KV)); if( bShutdown ){ void *pDummy; /* Signal the checkpointer thread. */ pthread_mutex_lock(&pCkpter->ckpter_mutex); pCkpter->bDoWork = 1; pthread_cond_signal(&pCkpter->ckpter_cond); pthread_mutex_unlock(&pCkpter->ckpter_mutex); /* Join the checkpointer thread. */ pthread_join(pCkpter->ckpter_thread, &pDummy); pthread_cond_destroy(&pCkpter->ckpter_cond); pthread_mutex_destroy(&pCkpter->ckpter_mutex); sqlite4_buffer_clear(&pCkpter->file); sqlite4_buffer_clear(&pCkpter->spec); testFree(pCkpter); } pDb->pCkpter = 0; } return rc; } /* ** End of background checkpointer. *************************************************************************/ |
Added ext/lsm1/lsm-test/lsmtest_util.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 | #include "lsmtest.h" #include <stdarg.h> #include <stdio.h> #include <string.h> #ifndef _WIN32 # include <sys/time.h> #endif /* ** Global variables used within this module. */ static struct TestutilGlobal { char **argv; int argc; } g = {0, 0}; static struct TestutilRnd { unsigned int aRand1[2048]; /* Bits 0..10 */ unsigned int aRand2[2048]; /* Bits 11..21 */ unsigned int aRand3[1024]; /* Bits 22..31 */ } r; /************************************************************************* ** The following block is a copy of the implementation of SQLite function ** sqlite3_randomness. This version has two important differences: ** ** 1. It always uses the same seed. So the sequence of random data output ** is the same for every run of the program. ** ** 2. It is not threadsafe. */ static struct sqlite3PrngType { unsigned char i, j; /* State variables */ unsigned char s[256]; /* State variables */ } sqlite3Prng = { 0xAF, 0x28, { 0x71, 0xF5, 0xB4, 0x6E, 0x80, 0xAB, 0x1D, 0xB8, 0xFB, 0xB7, 0x49, 0xBF, 0xFF, 0x72, 0x2D, 0x14, 0x79, 0x09, 0xE3, 0x78, 0x76, 0xB0, 0x2C, 0x0A, 0x8E, 0x23, 0xEE, 0xDF, 0xE0, 0x9A, 0x2F, 0x67, 0xE1, 0xBE, 0x0E, 0xA7, 0x08, 0x97, 0xEB, 0x77, 0x78, 0xBA, 0x9D, 0xCA, 0x49, 0x4C, 0x60, 0x9A, 0xF6, 0xBD, 0xDA, 0x7F, 0xBC, 0x48, 0x58, 0x52, 0xE5, 0xCD, 0x83, 0x72, 0x23, 0x52, 0xFF, 0x6D, 0xEF, 0x0F, 0x82, 0x29, 0xA0, 0x83, 0x3F, 0x7D, 0xA4, 0x88, 0x31, 0xE7, 0x88, 0x92, 0x3B, 0x9B, 0x3B, 0x2C, 0xC2, 0x4C, 0x71, 0xA2, 0xB0, 0xEA, 0x36, 0xD0, 0x00, 0xF1, 0xD3, 0x39, 0x17, 0x5D, 0x2A, 0x7A, 0xE4, 0xAD, 0xE1, 0x64, 0xCE, 0x0F, 0x9C, 0xD9, 0xF5, 0xED, 0xB0, 0x22, 0x5E, 0x62, 0x97, 0x02, 0xA3, 0x8C, 0x67, 0x80, 0xFC, 0x88, 0x14, 0x0B, 0x15, 0x10, 0x0F, 0xC7, 0x40, 0xD4, 0xF1, 0xF9, 0x0E, 0x1A, 0xCE, 0xB9, 0x1E, 0xA1, 0x72, 0x8E, 0xD7, 0x78, 0x39, 0xCD, 0xF4, 0x5D, 0x2A, 0x59, 0x26, 0x34, 0xF2, 0x73, 0x0B, 0xA0, 0x02, 0x51, 0x2C, 0x03, 0xA3, 0xA7, 0x43, 0x13, 0xE8, 0x98, 0x2B, 0xD2, 0x53, 0xF8, 0xEE, 0x91, 0x7D, 0xE7, 0xE3, 0xDA, 0xD5, 0xBB, 0xC0, 0x92, 0x9D, 0x98, 0x01, 0x2C, 0xF9, 0xB9, 0xA0, 0xEB, 0xCF, 0x32, 0xFA, 0x01, 0x49, 0xA5, 0x1D, 0x9A, 0x76, 0x86, 0x3F, 0x40, 0xD4, 0x89, 0x8F, 0x9C, 0xE2, 0xE3, 0x11, 0x31, 0x37, 0xB2, 0x49, 0x28, 0x35, 0xC0, 0x99, 0xB6, 0xD0, 0xBC, 0x66, 0x35, 0xF7, 0x83, 0x5B, 0xD7, 0x37, 0x1A, 0x2B, 0x18, 0xA6, 0xFF, 0x8D, 0x7C, 0x81, 0xA8, 0xFC, 0x9E, 0xC4, 0xEC, 0x80, 0xD0, 0x98, 0xA7, 0x76, 0xCC, 0x9C, 0x2F, 0x7B, 0xFF, 0x8E, 0x0E, 0xBB, 0x90, 0xAE, 0x13, 0x06, 0xF5, 0x1C, 0x4E, 0x52, 0xF7 } }; /* Generate and return single random byte */ static unsigned char randomByte(void){ unsigned char t; sqlite3Prng.i++; t = sqlite3Prng.s[sqlite3Prng.i]; sqlite3Prng.j += t; sqlite3Prng.s[sqlite3Prng.i] = sqlite3Prng.s[sqlite3Prng.j]; sqlite3Prng.s[sqlite3Prng.j] = t; t += sqlite3Prng.s[sqlite3Prng.i]; return sqlite3Prng.s[t]; } /* ** Return N random bytes. */ static void randomBlob(int nBuf, unsigned char *zBuf){ int i; for(i=0; i<nBuf; i++){ zBuf[i] = randomByte(); } } /* ** End of code copied from SQLite. *************************************************************************/ int testPrngInit(void){ randomBlob(sizeof(r.aRand1), (unsigned char *)r.aRand1); randomBlob(sizeof(r.aRand2), (unsigned char *)r.aRand2); randomBlob(sizeof(r.aRand3), (unsigned char *)r.aRand3); return 0; } unsigned int testPrngValue(unsigned int iVal){ return r.aRand1[iVal & 0x000007FF] ^ r.aRand2[(iVal>>11) & 0x000007FF] ^ r.aRand3[(iVal>>22) & 0x000003FF] ; } void testPrngArray(unsigned int iVal, unsigned int *aOut, int nOut){ int i; for(i=0; i<nOut; i++){ aOut[i] = testPrngValue(iVal+i); } } void testPrngString(unsigned int iVal, char *aOut, int nOut){ int i; for(i=0; i<(nOut-1); i++){ aOut[i] = 'a' + (testPrngValue(iVal+i) % 26); } aOut[i] = '\0'; } void testErrorInit(int argc, char **argv){ g.argc = argc; g.argv = argv; } void testPrintError(const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); vfprintf(stderr, zFormat, ap); va_end(ap); } void testPrintFUsage(const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); fprintf(stderr, "Usage: %s %s ", g.argv[0], g.argv[1]); vfprintf(stderr, zFormat, ap); fprintf(stderr, "\n"); va_end(ap); } void testPrintUsage(const char *zArgs){ testPrintError("Usage: %s %s %s\n", g.argv[0], g.argv[1], zArgs); } static void argError(void *aData, const char *zType, int sz, const char *zArg){ struct Entry { const char *zName; }; struct Entry *pEntry; const char *zPrev = 0; testPrintError("unrecognized %s \"%s\": must be ", zType, zArg); for(pEntry=(struct Entry *)aData; pEntry->zName; pEntry=(struct Entry *)&((unsigned char *)pEntry)[sz] ){ if( zPrev ){ testPrintError("%s, ", zPrev); } zPrev = pEntry->zName; } testPrintError("or %s\n", zPrev); } int testArgSelectX( void *aData, const char *zType, int sz, const char *zArg, int *piOut ){ struct Entry { const char *zName; }; struct Entry *pEntry; int nArg = strlen(zArg); int i = 0; int iOut = -1; int nOut = 0; for(pEntry=(struct Entry *)aData; pEntry->zName; pEntry=(struct Entry *)&((unsigned char *)pEntry)[sz] ){ int nName = strlen(pEntry->zName); if( nArg<=nName && memcmp(pEntry->zName, zArg, nArg)==0 ){ iOut = i; if( nName==nArg ){ nOut = 1; break; } nOut++; } i++; } if( nOut!=1 ){ argError(aData, zType, sz, zArg); }else{ *piOut = iOut; } return (nOut!=1); } struct timeval zero_time; void testTimeInit(void){ gettimeofday(&zero_time, 0); } int testTimeGet(void){ struct timeval now; gettimeofday(&now, 0); return (((int)now.tv_sec - (int)zero_time.tv_sec)*1000) + (((int)now.tv_usec - (int)zero_time.tv_usec)/1000); } |
Added ext/lsm1/lsm-test/lsmtest_win32.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 | #include "lsmtest.h" #ifdef _WIN32 #define TICKS_PER_SECOND (10000000) #define TICKS_PER_MICROSECOND (10) #define TICKS_UNIX_EPOCH (116444736000000000LL) int win32GetTimeOfDay( struct timeval *tp, void *tzp ){ FILETIME fileTime; ULONGLONG ticks; ULONGLONG unixTicks; unused_parameter(tzp); memset(&fileTime, 0, sizeof(FILETIME)); GetSystemTimeAsFileTime(&fileTime); ticks = (ULONGLONG)fileTime.dwHighDateTime << 32; ticks |= (ULONGLONG)fileTime.dwLowDateTime; unixTicks = ticks - TICKS_UNIX_EPOCH; tp->tv_sec = (long)(unixTicks / TICKS_PER_SECOND); unixTicks -= ((ULONGLONG)tp->tv_sec * TICKS_PER_SECOND); tp->tv_usec = (long)(unixTicks / TICKS_PER_MICROSECOND); return 0; } #endif |
Added ext/lsm1/lsm.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 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 | /* ** 2011-08-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 defines the LSM API. */ #ifndef _LSM_H #define _LSM_H #include <stddef.h> #ifdef __cplusplus extern "C" { #endif /* ** Opaque handle types. */ typedef struct lsm_compress lsm_compress; /* Compression library functions */ typedef struct lsm_compress_factory lsm_compress_factory; typedef struct lsm_cursor lsm_cursor; /* Database cursor handle */ typedef struct lsm_db lsm_db; /* Database connection handle */ typedef struct lsm_env lsm_env; /* Runtime environment */ typedef struct lsm_file lsm_file; /* OS file handle */ typedef struct lsm_mutex lsm_mutex; /* Mutex handle */ /* 64-bit integer type used for file offsets. */ typedef long long int lsm_i64; /* 64-bit signed integer type */ /* Candidate values for the 3rd argument to lsm_env.xLock() */ #define LSM_LOCK_UNLOCK 0 #define LSM_LOCK_SHARED 1 #define LSM_LOCK_EXCL 2 /* Flags for lsm_env.xOpen() */ #define LSM_OPEN_READONLY 0x0001 /* ** CAPI: Database Runtime Environment ** ** Run-time environment used by LSM */ struct lsm_env { int nByte; /* Size of this structure in bytes */ int iVersion; /* Version number of this structure (1) */ /****** file i/o ***********************************************/ void *pVfsCtx; int (*xFullpath)(lsm_env*, const char *, char *, int *); int (*xOpen)(lsm_env*, const char *, int flags, lsm_file **); int (*xRead)(lsm_file *, lsm_i64, void *, int); int (*xWrite)(lsm_file *, lsm_i64, void *, int); int (*xTruncate)(lsm_file *, lsm_i64); int (*xSync)(lsm_file *); int (*xSectorSize)(lsm_file *); int (*xRemap)(lsm_file *, lsm_i64, void **, lsm_i64*); int (*xFileid)(lsm_file *, void *pBuf, int *pnBuf); int (*xClose)(lsm_file *); int (*xUnlink)(lsm_env*, const char *); int (*xLock)(lsm_file*, int, int); int (*xTestLock)(lsm_file*, int, int, int); int (*xShmMap)(lsm_file*, int, int, void **); void (*xShmBarrier)(void); int (*xShmUnmap)(lsm_file*, int); /****** memory allocation ****************************************/ void *pMemCtx; void *(*xMalloc)(lsm_env*, size_t); /* malloc(3) function */ void *(*xRealloc)(lsm_env*, void *, size_t); /* realloc(3) function */ void (*xFree)(lsm_env*, void *); /* free(3) function */ size_t (*xSize)(lsm_env*, void *); /* xSize function */ /****** mutexes ****************************************************/ void *pMutexCtx; int (*xMutexStatic)(lsm_env*,int,lsm_mutex**); /* Obtain a static mutex */ int (*xMutexNew)(lsm_env*, lsm_mutex**); /* Get a new dynamic mutex */ void (*xMutexDel)(lsm_mutex *); /* Delete an allocated mutex */ void (*xMutexEnter)(lsm_mutex *); /* Grab a mutex */ int (*xMutexTry)(lsm_mutex *); /* Attempt to obtain a mutex */ void (*xMutexLeave)(lsm_mutex *); /* Leave a mutex */ int (*xMutexHeld)(lsm_mutex *); /* Return true if mutex is held */ int (*xMutexNotHeld)(lsm_mutex *); /* Return true if mutex not held */ /****** other ****************************************************/ int (*xSleep)(lsm_env*, int microseconds); /* New fields may be added in future releases, in which case the ** iVersion value will increase. */ }; /* ** Values that may be passed as the second argument to xMutexStatic. */ #define LSM_MUTEX_GLOBAL 1 #define LSM_MUTEX_HEAP 2 /* ** CAPI: LSM Error Codes */ #define LSM_OK 0 #define LSM_ERROR 1 #define LSM_BUSY 5 #define LSM_NOMEM 7 #define LSM_READONLY 8 #define LSM_IOERR 10 #define LSM_CORRUPT 11 #define LSM_FULL 13 #define LSM_CANTOPEN 14 #define LSM_PROTOCOL 15 #define LSM_MISUSE 21 #define LSM_MISMATCH 50 #define LSM_IOERR_NOENT (LSM_IOERR | (1<<8)) /* ** CAPI: Creating and Destroying Database Connection Handles ** ** Open and close a database connection handle. */ int lsm_new(lsm_env*, lsm_db **ppDb); int lsm_close(lsm_db *pDb); /* ** CAPI: Connecting to a Database */ int lsm_open(lsm_db *pDb, const char *zFilename); /* ** CAPI: Obtaining pointers to database environments ** ** Return a pointer to the environment used by the database connection ** passed as the first argument. Assuming the argument is valid, this ** function always returns a valid environment pointer - it cannot fail. */ lsm_env *lsm_get_env(lsm_db *pDb); /* ** The lsm_default_env() function returns a pointer to the default LSM ** environment for the current platform. */ lsm_env *lsm_default_env(void); /* ** CAPI: Configuring a database connection. ** ** The lsm_config() function is used to configure a database connection. */ int lsm_config(lsm_db *, int, ...); /* ** The following values may be passed as the second argument to lsm_config(). ** ** LSM_CONFIG_AUTOFLUSH: ** A read/write integer parameter. ** ** This value determines the amount of data allowed to accumulate in a ** live in-memory tree before it is marked as old. After committing a ** transaction, a connection checks if the size of the live in-memory tree, ** including data structure overhead, is greater than the value of this ** option in KB. If it is, and there is not already an old in-memory tree, ** the live in-memory tree is marked as old. ** ** The maximum allowable value is 1048576 (1GB). There is no minimum ** value. If this parameter is set to zero, then an attempt is made to ** mark the live in-memory tree as old after each transaction is committed. ** ** The default value is 1024 (1MB). ** ** LSM_CONFIG_PAGE_SIZE: ** A read/write integer parameter. This parameter may only be set before ** lsm_open() has been called. ** ** LSM_CONFIG_BLOCK_SIZE: ** A read/write integer parameter. ** ** This parameter may only be set before lsm_open() has been called. It ** must be set to a power of two between 64 and 65536, inclusive (block ** sizes between 64KB and 64MB). ** ** If the connection creates a new database, the block size of the new ** database is set to the value of this option in KB. After lsm_open() ** has been called, querying this parameter returns the actual block ** size of the opened database. ** ** The default value is 1024 (1MB blocks). ** ** LSM_CONFIG_SAFETY: ** A read/write integer parameter. Valid values are 0, 1 (the default) ** and 2. This parameter determines how robust the database is in the ** face of a system crash (e.g. a power failure or operating system ** crash). As follows: ** ** 0 (off): No robustness. A system crash may corrupt the database. ** ** 1 (normal): Some robustness. A system crash may not corrupt the ** database file, but recently committed transactions may ** be lost following recovery. ** ** 2 (full): Full robustness. A system crash may not corrupt the ** database file. Following recovery the database file ** contains all successfully committed transactions. ** ** LSM_CONFIG_AUTOWORK: ** A read/write integer parameter. ** ** LSM_CONFIG_AUTOCHECKPOINT: ** A read/write integer parameter. ** ** If this option is set to non-zero value N, then a checkpoint is ** automatically attempted after each N KB of data have been written to ** the database file. ** ** The amount of uncheckpointed data already written to the database file ** is a global parameter. After performing database work (writing to the ** database file), the process checks if the total amount of uncheckpointed ** data exceeds the value of this paramter. If so, a checkpoint is performed. ** This means that this option may cause the connection to perform a ** checkpoint even if the current connection has itself written very little ** data into the database file. ** ** The default value is 2048 (checkpoint every 2MB). ** ** LSM_CONFIG_MMAP: ** A read/write integer parameter. If this value is set to 0, then the ** database file is accessed using ordinary read/write IO functions. Or, ** if it is set to 1, then the database file is memory mapped and accessed ** that way. If this parameter is set to any value N greater than 1, then ** up to the first N KB of the file are memory mapped, and any remainder ** accessed using read/write IO. ** ** The default value is 1 on 64-bit platforms and 32768 on 32-bit platforms. ** ** ** LSM_CONFIG_USE_LOG: ** A read/write boolean parameter. True (the default) to use the log ** file normally. False otherwise. ** ** LSM_CONFIG_AUTOMERGE: ** A read/write integer parameter. The minimum number of segments to ** merge together at a time. Default value 4. ** ** LSM_CONFIG_MAX_FREELIST: ** A read/write integer parameter. The maximum number of free-list ** entries that are stored in a database checkpoint (the others are ** stored elsewhere in the database). ** ** There is no reason for an application to configure or query this ** parameter. It is only present because configuring a small value ** makes certain parts of the lsm code easier to test. ** ** LSM_CONFIG_MULTIPLE_PROCESSES: ** A read/write boolean parameter. This parameter may only be set before ** lsm_open() has been called. If true, the library uses shared-memory ** and posix advisory locks to co-ordinate access by clients from within ** multiple processes. Otherwise, if false, all database clients must be ** located in the same process. The default value is true. ** ** LSM_CONFIG_SET_COMPRESSION: ** Set the compression methods used to compress and decompress database ** content. The argument to this option should be a pointer to a structure ** of type lsm_compress. The lsm_config() method takes a copy of the ** structures contents. ** ** This option may only be used before lsm_open() is called. Invoking it ** after lsm_open() has been called results in an LSM_MISUSE error. ** ** LSM_CONFIG_GET_COMPRESSION: ** Query the compression methods used to compress and decompress database ** content. ** ** LSM_CONFIG_SET_COMPRESSION_FACTORY: ** Configure a factory method to be invoked in case of an LSM_MISMATCH ** error. ** ** LSM_CONFIG_READONLY: ** A read/write boolean parameter. This parameter may only be set before ** lsm_open() is called. */ #define LSM_CONFIG_AUTOFLUSH 1 #define LSM_CONFIG_PAGE_SIZE 2 #define LSM_CONFIG_SAFETY 3 #define LSM_CONFIG_BLOCK_SIZE 4 #define LSM_CONFIG_AUTOWORK 5 #define LSM_CONFIG_MMAP 7 #define LSM_CONFIG_USE_LOG 8 #define LSM_CONFIG_AUTOMERGE 9 #define LSM_CONFIG_MAX_FREELIST 10 #define LSM_CONFIG_MULTIPLE_PROCESSES 11 #define LSM_CONFIG_AUTOCHECKPOINT 12 #define LSM_CONFIG_SET_COMPRESSION 13 #define LSM_CONFIG_GET_COMPRESSION 14 #define LSM_CONFIG_SET_COMPRESSION_FACTORY 15 #define LSM_CONFIG_READONLY 16 #define LSM_SAFETY_OFF 0 #define LSM_SAFETY_NORMAL 1 #define LSM_SAFETY_FULL 2 /* ** CAPI: Compression and/or Encryption Hooks */ struct lsm_compress { void *pCtx; unsigned int iId; int (*xBound)(void *, int nSrc); int (*xCompress)(void *, char *, int *, const char *, int); int (*xUncompress)(void *, char *, int *, const char *, int); void (*xFree)(void *pCtx); }; struct lsm_compress_factory { void *pCtx; int (*xFactory)(void *, lsm_db *, unsigned int); void (*xFree)(void *pCtx); }; #define LSM_COMPRESSION_EMPTY 0 #define LSM_COMPRESSION_NONE 1 /* ** CAPI: Allocating and Freeing Memory ** ** Invoke the memory allocation functions that belong to environment ** pEnv. Or the system defaults if no memory allocation functions have ** been registered. */ void *lsm_malloc(lsm_env*, size_t); void *lsm_realloc(lsm_env*, void *, size_t); void lsm_free(lsm_env*, void *); /* ** CAPI: Querying a Connection For Operational Data ** ** Query a database connection for operational statistics or data. */ int lsm_info(lsm_db *, int, ...); int lsm_get_user_version(lsm_db *, unsigned int *); int lsm_set_user_version(lsm_db *, unsigned int); /* ** The following values may be passed as the second argument to lsm_info(). ** ** LSM_INFO_NWRITE: ** The third parameter should be of type (int *). The location pointed ** to by the third parameter is set to the number of 4KB pages written to ** the database file during the lifetime of this connection. ** ** LSM_INFO_NREAD: ** The third parameter should be of type (int *). The location pointed ** to by the third parameter is set to the number of 4KB pages read from ** the database file during the lifetime of this connection. ** ** LSM_INFO_DB_STRUCTURE: ** The third argument should be of type (char **). The location pointed ** to is populated with a pointer to a nul-terminated string containing ** the string representation of a Tcl data-structure reflecting the ** current structure of the database file. Specifically, the current state ** of the worker snapshot. The returned string should be eventually freed ** by the caller using lsm_free(). ** ** The returned list contains one element for each level in the database, ** in order from most to least recent. Each element contains a ** single element for each segment comprising the corresponding level, ** starting with the lhs segment, then each of the rhs segments (if any) ** in order from most to least recent. ** ** Each segment element is itself a list of 4 integer values, as follows: ** ** <ol><li> First page of segment ** <li> Last page of segment ** <li> Root page of segment (if applicable) ** <li> Total number of pages in segment ** </ol> ** ** LSM_INFO_ARRAY_STRUCTURE: ** There should be two arguments passed following this option (i.e. a ** total of four arguments passed to lsm_info()). The first argument ** should be the page number of the first page in a database array ** (perhaps obtained from an earlier INFO_DB_STRUCTURE call). The second ** trailing argument should be of type (char **). The location pointed ** to is populated with a pointer to a nul-terminated string that must ** be eventually freed using lsm_free() by the caller. ** ** The output string contains the text representation of a Tcl list of ** integers. Each pair of integers represent a range of pages used by ** the identified array. For example, if the array occupies database ** pages 993 to 1024, then pages 2048 to 2777, then the returned string ** will be "993 1024 2048 2777". ** ** If the specified integer argument does not correspond to the first ** page of any database array, LSM_ERROR is returned and the output ** pointer is set to a NULL value. ** ** LSM_INFO_LOG_STRUCTURE: ** The third argument should be of type (char **). The location pointed ** to is populated with a pointer to a nul-terminated string containing ** the string representation of a Tcl data-structure. The returned ** string should be eventually freed by the caller using lsm_free(). ** ** The Tcl structure returned is a list of six integers that describe ** the current structure of the log file. ** ** LSM_INFO_ARRAY_PAGES: ** ** LSM_INFO_PAGE_ASCII_DUMP: ** As with LSM_INFO_ARRAY_STRUCTURE, there should be two arguments passed ** with calls that specify this option - an integer page number and a ** (char **) used to return a nul-terminated string that must be later ** freed using lsm_free(). In this case the output string is populated ** with a human-readable description of the page content. ** ** If the page cannot be decoded, it is not an error. In this case the ** human-readable output message will report the systems failure to ** interpret the page data. ** ** LSM_INFO_PAGE_HEX_DUMP: ** This argument is similar to PAGE_ASCII_DUMP, except that keys and ** values are represented using hexadecimal notation instead of ascii. ** ** LSM_INFO_FREELIST: ** The third argument should be of type (char **). The location pointed ** to is populated with a pointer to a nul-terminated string containing ** the string representation of a Tcl data-structure. The returned ** string should be eventually freed by the caller using lsm_free(). ** ** The Tcl structure returned is a list containing one element for each ** free block in the database. The element itself consists of two ** integers - the block number and the id of the snapshot that freed it. ** ** LSM_INFO_CHECKPOINT_SIZE: ** The third argument should be of type (int *). The location pointed to ** by this argument is populated with the number of KB written to the ** database file since the most recent checkpoint. ** ** LSM_INFO_TREE_SIZE: ** If this value is passed as the second argument to an lsm_info() call, it ** should be followed by two arguments of type (int *) (for a total of four ** arguments). ** ** At any time, there are either one or two tree structures held in shared ** memory that new database clients will access (there may also be additional ** tree structures being used by older clients - this API does not provide ** information on them). One tree structure - the current tree - is used to ** accumulate new data written to the database. The other tree structure - ** the old tree - is a read-only tree holding older data and may be flushed ** to disk at any time. ** ** Assuming no error occurs, the location pointed to by the first of the two ** (int *) arguments is set to the size of the old in-memory tree in KB. ** The second is set to the size of the current, or live in-memory tree. ** ** LSM_INFO_COMPRESSION_ID: ** This value should be followed by a single argument of type ** (unsigned int *). If successful, the location pointed to is populated ** with the database compression id before returning. */ #define LSM_INFO_NWRITE 1 #define LSM_INFO_NREAD 2 #define LSM_INFO_DB_STRUCTURE 3 #define LSM_INFO_LOG_STRUCTURE 4 #define LSM_INFO_ARRAY_STRUCTURE 5 #define LSM_INFO_PAGE_ASCII_DUMP 6 #define LSM_INFO_PAGE_HEX_DUMP 7 #define LSM_INFO_FREELIST 8 #define LSM_INFO_ARRAY_PAGES 9 #define LSM_INFO_CHECKPOINT_SIZE 10 #define LSM_INFO_TREE_SIZE 11 #define LSM_INFO_FREELIST_SIZE 12 #define LSM_INFO_COMPRESSION_ID 13 /* ** CAPI: Opening and Closing Write Transactions ** ** These functions are used to open and close transactions and nested ** sub-transactions. ** ** The lsm_begin() function is used to open transactions and sub-transactions. ** A successful call to lsm_begin() ensures that there are at least iLevel ** nested transactions open. To open a top-level transaction, pass iLevel=1. ** To open a sub-transaction within the top-level transaction, iLevel=2. ** Passing iLevel=0 is a no-op. ** ** lsm_commit() is used to commit transactions and sub-transactions. A ** successful call to lsm_commit() ensures that there are at most iLevel ** nested transactions open. To commit a top-level transaction, pass iLevel=0. ** To commit all sub-transactions inside the main transaction, pass iLevel=1. ** ** Function lsm_rollback() is used to roll back transactions and ** sub-transactions. A successful call to lsm_rollback() restores the database ** to the state it was in when the iLevel'th nested sub-transaction (if any) ** was first opened. And then closes transactions to ensure that there are ** at most iLevel nested transactions open. Passing iLevel=0 rolls back and ** closes the top-level transaction. iLevel=1 also rolls back the top-level ** transaction, but leaves it open. iLevel=2 rolls back the sub-transaction ** nested directly inside the top-level transaction (and leaves it open). */ int lsm_begin(lsm_db *pDb, int iLevel); int lsm_commit(lsm_db *pDb, int iLevel); int lsm_rollback(lsm_db *pDb, int iLevel); /* ** CAPI: Writing to a Database ** ** Write a new value into the database. If a value with a duplicate key ** already exists it is replaced. */ int lsm_insert(lsm_db*, const void *pKey, int nKey, const void *pVal, int nVal); /* ** Delete a value from the database. No error is returned if the specified ** key value does not exist in the database. */ int lsm_delete(lsm_db *, const void *pKey, int nKey); /* ** Delete all database entries with keys that are greater than (pKey1/nKey1) ** and smaller than (pKey2/nKey2). Note that keys (pKey1/nKey1) and ** (pKey2/nKey2) themselves, if they exist in the database, are not deleted. ** ** Return LSM_OK if successful, or an LSM error code otherwise. */ int lsm_delete_range(lsm_db *, const void *pKey1, int nKey1, const void *pKey2, int nKey2 ); /* ** CAPI: Explicit Database Work and Checkpointing ** ** This function is called by a thread to work on the database structure. */ int lsm_work(lsm_db *pDb, int nMerge, int nKB, int *pnWrite); int lsm_flush(lsm_db *pDb); /* ** Attempt to checkpoint the current database snapshot. Return an LSM ** error code if an error occurs or LSM_OK otherwise. ** ** If the current snapshot has already been checkpointed, calling this ** function is a no-op. In this case if pnKB is not NULL, *pnKB is ** set to 0. Or, if the current snapshot is successfully checkpointed ** by this function and pbKB is not NULL, *pnKB is set to the number ** of bytes written to the database file since the previous checkpoint ** (the same measure as returned by the LSM_INFO_CHECKPOINT_SIZE query). */ int lsm_checkpoint(lsm_db *pDb, int *pnKB); /* ** CAPI: Opening and Closing Database Cursors ** ** Open and close a database cursor. */ int lsm_csr_open(lsm_db *pDb, lsm_cursor **ppCsr); int lsm_csr_close(lsm_cursor *pCsr); /* ** CAPI: Positioning Database Cursors ** ** If the fourth parameter is LSM_SEEK_EQ, LSM_SEEK_GE or LSM_SEEK_LE, ** this function searches the database for an entry with key (pKey/nKey). ** If an error occurs, an LSM error code is returned. Otherwise, LSM_OK. ** ** If no error occurs and the requested key is present in the database, the ** cursor is left pointing to the entry with the specified key. Or, if the ** specified key is not present in the database the state of the cursor ** depends on the value passed as the final parameter, as follows: ** ** LSM_SEEK_EQ: ** The cursor is left at EOF (invalidated). A call to lsm_csr_valid() ** returns non-zero. ** ** LSM_SEEK_LE: ** The cursor is left pointing to the largest key in the database that ** is smaller than (pKey/nKey). If the database contains no keys smaller ** than (pKey/nKey), the cursor is left at EOF. ** ** LSM_SEEK_GE: ** The cursor is left pointing to the smallest key in the database that ** is larger than (pKey/nKey). If the database contains no keys larger ** than (pKey/nKey), the cursor is left at EOF. ** ** If the fourth parameter is LSM_SEEK_LEFAST, this function searches the ** database in a similar manner to LSM_SEEK_LE, with two differences: ** ** <ol><li>Even if a key can be found (the cursor is not left at EOF), the ** lsm_csr_value() function may not be used (attempts to do so return ** LSM_MISUSE). ** ** <li>The key that the cursor is left pointing to may be one that has ** been recently deleted from the database. In this case it is ** guaranteed that the returned key is larger than any key currently ** in the database that is less than or equal to (pKey/nKey). ** </ol> ** ** LSM_SEEK_LEFAST requests are intended to be used to allocate database ** keys. */ int lsm_csr_seek(lsm_cursor *pCsr, const void *pKey, int nKey, int eSeek); int lsm_csr_first(lsm_cursor *pCsr); int lsm_csr_last(lsm_cursor *pCsr); /* ** Advance the specified cursor to the next or previous key in the database. ** Return LSM_OK if successful, or an LSM error code otherwise. ** ** Functions lsm_csr_seek(), lsm_csr_first() and lsm_csr_last() are "seek" ** functions. Whether or not lsm_csr_next and lsm_csr_prev may be called ** successfully also depends on the most recent seek function called on ** the cursor. Specifically: ** ** <ul> ** <li> At least one seek function must have been called on the cursor. ** <li> To call lsm_csr_next(), the most recent call to a seek function must ** have been either lsm_csr_first() or a call to lsm_csr_seek() specifying ** LSM_SEEK_GE. ** <li> To call lsm_csr_prev(), the most recent call to a seek function must ** have been either lsm_csr_last() or a call to lsm_csr_seek() specifying ** LSM_SEEK_LE. ** </ul> ** ** Otherwise, if the above conditions are not met when lsm_csr_next or ** lsm_csr_prev is called, LSM_MISUSE is returned and the cursor position ** remains unchanged. */ int lsm_csr_next(lsm_cursor *pCsr); int lsm_csr_prev(lsm_cursor *pCsr); /* ** Values that may be passed as the fourth argument to lsm_csr_seek(). */ #define LSM_SEEK_LEFAST -2 #define LSM_SEEK_LE -1 #define LSM_SEEK_EQ 0 #define LSM_SEEK_GE 1 /* ** CAPI: Extracting Data From Database Cursors ** ** Retrieve data from a database cursor. */ int lsm_csr_valid(lsm_cursor *pCsr); int lsm_csr_key(lsm_cursor *pCsr, const void **ppKey, int *pnKey); int lsm_csr_value(lsm_cursor *pCsr, const void **ppVal, int *pnVal); /* ** If no error occurs, this function compares the database key passed via ** the pKey/nKey arguments with the key that the cursor passed as the first ** argument currently points to. If the cursors key is less than, equal to ** or greater than pKey/nKey, *piRes is set to less than, equal to or greater ** than zero before returning. LSM_OK is returned in this case. ** ** Or, if an error occurs, an LSM error code is returned and the final ** value of *piRes is undefined. If the cursor does not point to a valid ** key when this function is called, LSM_MISUSE is returned. */ int lsm_csr_cmp(lsm_cursor *pCsr, const void *pKey, int nKey, int *piRes); /* ** CAPI: Change these!! ** ** Configure a callback to which debugging and other messages should ** be directed. Only useful for debugging lsm. */ void lsm_config_log(lsm_db *, void (*)(void *, int, const char *), void *); /* ** Configure a callback that is invoked if the database connection ever ** writes to the database file. */ void lsm_config_work_hook(lsm_db *, void (*)(lsm_db *, void *), void *); /* ENDOFAPI */ #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif /* ifndef _LSM_H */ |
Added ext/lsm1/lsmInt.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 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 | /* ** 2011-08-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. ** ************************************************************************* ** Internal structure definitions for the LSM module. */ #ifndef _LSM_INT_H #define _LSM_INT_H #include "lsm.h" #include <assert.h> #include <string.h> #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <ctype.h> #ifdef _WIN32 # ifdef _MSC_VER # define snprintf _snprintf # endif #else # include <unistd.h> #endif #ifdef NDEBUG # ifdef LSM_DEBUG_EXPENSIVE # undef LSM_DEBUG_EXPENSIVE # endif # ifdef LSM_DEBUG # undef LSM_DEBUG # endif #else # ifndef LSM_DEBUG # define LSM_DEBUG # endif #endif /* ** Default values for various data structure parameters. These may be ** overridden by calls to lsm_config(). */ #define LSM_DFLT_PAGE_SIZE (4 * 1024) #define LSM_DFLT_BLOCK_SIZE (1 * 1024 * 1024) #define LSM_DFLT_AUTOFLUSH (1 * 1024 * 1024) #define LSM_DFLT_AUTOCHECKPOINT (i64)(2 * 1024 * 1024) #define LSM_DFLT_AUTOWORK 1 #define LSM_DFLT_LOG_SIZE (128*1024) #define LSM_DFLT_AUTOMERGE 4 #define LSM_DFLT_SAFETY LSM_SAFETY_NORMAL #define LSM_DFLT_MMAP (LSM_IS_64_BIT ? 1 : 32768) #define LSM_DFLT_MULTIPLE_PROCESSES 1 #define LSM_DFLT_USE_LOG 1 /* Initial values for log file checksums. These are only used if the ** database file does not contain a valid checkpoint. */ #define LSM_CKSUM0_INIT 42 #define LSM_CKSUM1_INIT 42 /* "mmap" mode is currently only used in environments with 64-bit address ** spaces. The following macro is used to test for this. */ #define LSM_IS_64_BIT (sizeof(void*)==8) #define LSM_AUTOWORK_QUANT 32 typedef struct Database Database; typedef struct DbLog DbLog; typedef struct FileSystem FileSystem; typedef struct Freelist Freelist; typedef struct FreelistEntry FreelistEntry; typedef struct Level Level; typedef struct LogMark LogMark; typedef struct LogRegion LogRegion; typedef struct LogWriter LogWriter; typedef struct LsmString LsmString; typedef struct Mempool Mempool; typedef struct Merge Merge; typedef struct MergeInput MergeInput; typedef struct MetaPage MetaPage; typedef struct MultiCursor MultiCursor; typedef struct Page Page; typedef struct Redirect Redirect; typedef struct Segment Segment; typedef struct SegmentMerger SegmentMerger; typedef struct ShmChunk ShmChunk; typedef struct ShmHeader ShmHeader; typedef struct ShmReader ShmReader; typedef struct Snapshot Snapshot; typedef struct TransMark TransMark; typedef struct Tree Tree; typedef struct TreeCursor TreeCursor; typedef struct TreeHeader TreeHeader; typedef struct TreeMark TreeMark; typedef struct TreeRoot TreeRoot; #ifndef _SQLITEINT_H_ typedef unsigned char u8; typedef unsigned short int u16; typedef unsigned int u32; typedef lsm_i64 i64; typedef unsigned long long int u64; #endif /* A page number is a 64-bit integer. */ typedef i64 Pgno; #ifdef LSM_DEBUG int lsmErrorBkpt(int); #else # define lsmErrorBkpt(x) (x) #endif #define LSM_PROTOCOL_BKPT lsmErrorBkpt(LSM_PROTOCOL) #define LSM_IOERR_BKPT lsmErrorBkpt(LSM_IOERR) #define LSM_NOMEM_BKPT lsmErrorBkpt(LSM_NOMEM) #define LSM_CORRUPT_BKPT lsmErrorBkpt(LSM_CORRUPT) #define LSM_MISUSE_BKPT lsmErrorBkpt(LSM_MISUSE) #define unused_parameter(x) (void)(x) #define array_size(x) (sizeof(x)/sizeof(x[0])) /* The size of each shared-memory chunk */ #define LSM_SHM_CHUNK_SIZE (32*1024) /* The number of bytes reserved at the start of each shm chunk for MM. */ #define LSM_SHM_CHUNK_HDR (sizeof(ShmChunk)) /* The number of available read locks. */ #define LSM_LOCK_NREADER 6 /* The number of available read-write client locks. */ #define LSM_LOCK_NRWCLIENT 16 /* Lock definitions. */ #define LSM_LOCK_DMS1 1 /* Serialize connect/disconnect ops */ #define LSM_LOCK_DMS2 2 /* Read-write connections */ #define LSM_LOCK_DMS3 3 /* Read-only connections */ #define LSM_LOCK_WRITER 4 #define LSM_LOCK_WORKER 5 #define LSM_LOCK_CHECKPOINTER 6 #define LSM_LOCK_ROTRANS 7 #define LSM_LOCK_READER(i) ((i) + LSM_LOCK_ROTRANS + 1) #define LSM_LOCK_RWCLIENT(i) ((i) + LSM_LOCK_READER(LSM_LOCK_NREADER)) #define LSM_N_LOCK LSM_LOCK_RWCLIENT(LSM_LOCK_NRWCLIENT) /* ** Meta-page size and usable size. */ #define LSM_META_PAGE_SIZE 4096 #define LSM_META_RW_PAGE_SIZE (LSM_META_PAGE_SIZE - LSM_N_LOCK) /* ** Hard limit on the number of free-list entries that may be stored in ** a checkpoint (the remainder are stored as a system record in the LSM). ** See also LSM_CONFIG_MAX_FREELIST. */ #define LSM_MAX_FREELIST_ENTRIES 24 #define LSM_MAX_BLOCK_REDIRECTS 16 #define LSM_ATTEMPTS_BEFORE_PROTOCOL 10000 /* ** Each entry stored in the LSM (or in-memory tree structure) has an ** associated mask of the following flags. */ #define LSM_START_DELETE 0x01 /* Start of open-ended delete range */ #define LSM_END_DELETE 0x02 /* End of open-ended delete range */ #define LSM_POINT_DELETE 0x04 /* Delete this key */ #define LSM_INSERT 0x08 /* Insert this key and value */ #define LSM_SEPARATOR 0x10 /* True if entry is separator key only */ #define LSM_SYSTEMKEY 0x20 /* True if entry is a system key (FREELIST) */ #define LSM_CONTIGUOUS 0x40 /* Used in lsm_tree.c */ /* ** A string that can grow by appending. */ struct LsmString { lsm_env *pEnv; /* Run-time environment */ int n; /* Size of string. -1 indicates error */ int nAlloc; /* Space allocated for z[] */ char *z; /* The string content */ }; typedef struct LsmFile LsmFile; struct LsmFile { lsm_file *pFile; LsmFile *pNext; }; /* ** An instance of the following type is used to store an ordered list of ** u32 values. ** ** Note: This is a place-holder implementation. It should be replaced by ** a version that avoids making a single large allocation when the array ** contains a large number of values. For this reason, the internals of ** this object should only manipulated by the intArrayXXX() functions in ** lsm_tree.c. */ typedef struct IntArray IntArray; struct IntArray { int nAlloc; int nArray; u32 *aArray; }; struct Redirect { int n; /* Number of redirects */ struct RedirectEntry { int iFrom; int iTo; } *a; }; /* ** An instance of this structure represents a point in the history of the ** tree structure to roll back to. Refer to comments in lsm_tree.c for ** details. */ struct TreeMark { u32 iRoot; /* Offset of root node in shm file */ u32 nHeight; /* Current height of tree structure */ u32 iWrite; /* Write offset in shm file */ u32 nChunk; /* Number of chunks in shared-memory file */ u32 iFirst; /* First chunk in linked list */ u32 iNextShmid; /* Next id to allocate */ int iRollback; /* Index in lsm->rollback to revert to */ }; /* ** An instance of this structure represents a point in the database log. */ struct LogMark { i64 iOff; /* Offset into log (see lsm_log.c) */ int nBuf; /* Size of in-memory buffer here */ u8 aBuf[8]; /* Bytes of content in aBuf[] */ u32 cksum0; /* Checksum 0 at offset (iOff-nBuf) */ u32 cksum1; /* Checksum 1 at offset (iOff-nBuf) */ }; struct TransMark { TreeMark tree; LogMark log; }; /* ** A structure that defines the start and end offsets of a region in the ** log file. The size of the region in bytes is (iEnd - iStart), so if ** iEnd==iStart the region is zero bytes in size. */ struct LogRegion { i64 iStart; /* Start of region in log file */ i64 iEnd; /* End of region in log file */ }; struct DbLog { u32 cksum0; /* Checksum 0 at offset iOff */ u32 cksum1; /* Checksum 1 at offset iOff */ i64 iSnapshotId; /* Log space has been reclaimed to this ss */ LogRegion aRegion[3]; /* Log file regions (see docs in lsm_log.c) */ }; struct TreeRoot { u32 iRoot; u32 nHeight; u32 nByte; /* Total size of this tree in bytes */ u32 iTransId; }; /* ** Tree header structure. */ struct TreeHeader { u32 iUsedShmid; /* Id of first shm chunk used by this tree */ u32 iNextShmid; /* Shm-id of next chunk allocated */ u32 iFirst; /* Chunk number of smallest shm-id */ u32 nChunk; /* Number of chunks in shared-memory file */ TreeRoot root; /* Root and height of current tree */ u32 iWrite; /* Write offset in shm file */ TreeRoot oldroot; /* Root and height of the previous tree */ u32 iOldShmid; /* Last shm-id used by previous tree */ u32 iUsrVersion; /* get/set_user_version() value */ i64 iOldLog; /* Log offset associated with old tree */ u32 oldcksum0; u32 oldcksum1; DbLog log; /* Current layout of log file */ u32 aCksum[2]; /* Checksums 1 and 2. */ }; /* ** Database handle structure. ** ** mLock: ** A bitmask representing the locks currently held by the connection. ** An LSM database supports N distinct locks, where N is some number less ** than or equal to 32. Locks are numbered starting from 1 (see the ** definitions for LSM_LOCK_WRITER and co.). ** ** The least significant 32-bits in mLock represent EXCLUSIVE locks. The ** most significant are SHARED locks. So, if a connection holds a SHARED ** lock on lock region iLock, then the following is true: ** ** (mLock & ((iLock+32-1) << 1)) ** ** Or for an EXCLUSIVE lock: ** ** (mLock & ((iLock-1) << 1)) ** ** pCsr: ** Points to the head of a linked list that contains all currently open ** cursors. Once this list becomes empty, the user has no outstanding ** cursors and the database handle can be successfully closed. ** ** pCsrCache: ** This list contains cursor objects that have been closed using ** lsm_csr_close(). Each time a cursor is closed, it is shifted from ** the pCsr list to this list. When a new cursor is opened, this list ** is inspected to see if there exists a cursor object that can be ** reused. This is an optimization only. */ struct lsm_db { /* Database handle configuration */ lsm_env *pEnv; /* runtime environment */ int (*xCmp)(void *, int, void *, int); /* Compare function */ /* Values configured by calls to lsm_config */ int eSafety; /* LSM_SAFETY_OFF, NORMAL or FULL */ int bAutowork; /* Configured by LSM_CONFIG_AUTOWORK */ int nTreeLimit; /* Configured by LSM_CONFIG_AUTOFLUSH */ int nMerge; /* Configured by LSM_CONFIG_AUTOMERGE */ int bUseLog; /* Configured by LSM_CONFIG_USE_LOG */ int nDfltPgsz; /* Configured by LSM_CONFIG_PAGE_SIZE */ int nDfltBlksz; /* Configured by LSM_CONFIG_BLOCK_SIZE */ int nMaxFreelist; /* Configured by LSM_CONFIG_MAX_FREELIST */ int iMmap; /* Configured by LSM_CONFIG_MMAP */ i64 nAutockpt; /* Configured by LSM_CONFIG_AUTOCHECKPOINT */ int bMultiProc; /* Configured by L_C_MULTIPLE_PROCESSES */ int bReadonly; /* Configured by LSM_CONFIG_READONLY */ lsm_compress compress; /* Compression callbacks */ lsm_compress_factory factory; /* Compression callback factory */ /* Sub-system handles */ FileSystem *pFS; /* On-disk portion of database */ Database *pDatabase; /* Database shared data */ int iRwclient; /* Read-write client lock held (-1 == none) */ /* Client transaction context */ Snapshot *pClient; /* Client snapshot */ int iReader; /* Read lock held (-1 == unlocked) */ int bRoTrans; /* True if a read-only db trans is open */ MultiCursor *pCsr; /* List of all open cursors */ LogWriter *pLogWriter; /* Context for writing to the log file */ int nTransOpen; /* Number of opened write transactions */ int nTransAlloc; /* Allocated size of aTrans[] array */ TransMark *aTrans; /* Array of marks for transaction rollback */ IntArray rollback; /* List of tree-nodes to roll back */ int bDiscardOld; /* True if lsmTreeDiscardOld() was called */ MultiCursor *pCsrCache; /* List of all closed cursors */ /* Worker context */ Snapshot *pWorker; /* Worker snapshot (or NULL) */ Freelist *pFreelist; /* See sortedNewToplevel() */ int bUseFreelist; /* True to use pFreelist */ int bIncrMerge; /* True if currently doing a merge */ int bInFactory; /* True if within factory.xFactory() */ /* Debugging message callback */ void (*xLog)(void *, int, const char *); void *pLogCtx; /* Work done notification callback */ void (*xWork)(lsm_db *, void *); void *pWorkCtx; u64 mLock; /* Mask of current locks. See lsmShmLock(). */ lsm_db *pNext; /* Next connection to same database */ int nShm; /* Size of apShm[] array */ void **apShm; /* Shared memory chunks */ ShmHeader *pShmhdr; /* Live shared-memory header */ TreeHeader treehdr; /* Local copy of tree-header */ u32 aSnapshot[LSM_META_PAGE_SIZE / sizeof(u32)]; }; struct Segment { Pgno iFirst; /* First page of this run */ Pgno iLastPg; /* Last page of this run */ Pgno iRoot; /* Root page number (if any) */ int nSize; /* Size of this run in pages */ Redirect *pRedirect; /* Block redirects (or NULL) */ }; /* ** iSplitTopic/pSplitKey/nSplitKey: ** If nRight>0, this buffer contains a copy of the largest key that has ** already been written to the left-hand-side of the level. */ struct Level { Segment lhs; /* Left-hand (main) segment */ int nRight; /* Size of apRight[] array */ Segment *aRhs; /* Old segments being merged into this */ int iSplitTopic; /* Split key topic (if nRight>0) */ void *pSplitKey; /* Pointer to split-key (if nRight>0) */ int nSplitKey; /* Number of bytes in split-key */ u16 iAge; /* Number of times data has been written */ u16 flags; /* Mask of LEVEL_XXX bits */ Merge *pMerge; /* Merge operation currently underway */ Level *pNext; /* Next level in tree */ }; /* ** The Level.flags field is set to a combination of the following bits. ** ** LEVEL_FREELIST_ONLY: ** Set if the level consists entirely of free-list entries. ** ** LEVEL_INCOMPLETE: ** This is set while a new toplevel level is being constructed. It is ** never set for any level other than a new toplevel. */ #define LEVEL_FREELIST_ONLY 0x0001 #define LEVEL_INCOMPLETE 0x0002 /* ** A structure describing an ongoing merge. There is an instance of this ** structure for every Level currently undergoing a merge in the worker ** snapshot. ** ** It is assumed that code that uses an instance of this structure has ** access to the associated Level struct. ** ** iOutputOff: ** The byte offset to write to next within the last page of the ** output segment. */ struct MergeInput { Pgno iPg; /* Page on which next input is stored */ int iCell; /* Cell containing next input to merge */ }; struct Merge { int nInput; /* Number of input runs being merged */ MergeInput *aInput; /* Array nInput entries in size */ MergeInput splitkey; /* Location in file of current splitkey */ int nSkip; /* Number of separators entries to skip */ int iOutputOff; /* Write offset on output page */ Pgno iCurrentPtr; /* Current pointer value */ }; /* ** The first argument to this macro is a pointer to a Segment structure. ** Returns true if the structure instance indicates that the separators ** array is valid. */ #define segmentHasSeparators(pSegment) ((pSegment)->sep.iFirst>0) /* ** The values that accompany the lock held by a database reader. */ struct ShmReader { u32 iTreeId; i64 iLsmId; }; /* ** An instance of this structure is stored in the first shared-memory ** page. The shared-memory header. ** ** bWriter: ** Immediately after opening a write transaction taking the WRITER lock, ** each writer client sets this flag. It is cleared right before the ** WRITER lock is relinquished. If a subsequent writer finds that this ** flag is already set when a write transaction is opened, this indicates ** that a previous writer failed mid-transaction. ** ** iMetaPage: ** If the database file does not contain a valid, synced, checkpoint, this ** value is set to 0. Otherwise, it is set to the meta-page number that ** contains the most recently written checkpoint (either 1 or 2). ** ** hdr1, hdr2: ** The two copies of the in-memory tree header. Two copies are required ** in case a writer fails while updating one of them. */ struct ShmHeader { u32 aSnap1[LSM_META_PAGE_SIZE / 4]; u32 aSnap2[LSM_META_PAGE_SIZE / 4]; u32 bWriter; u32 iMetaPage; TreeHeader hdr1; TreeHeader hdr2; ShmReader aReader[LSM_LOCK_NREADER]; }; /* ** An instance of this structure is stored at the start of each shared-memory ** chunk except the first (which is the header chunk - see above). */ struct ShmChunk { u32 iShmid; u32 iNext; }; /* ** Maximum number of shared-memory chunks allowed in the *-shm file. Since ** each shared-memory chunk is 32KB in size, this is a theoretical limit only. */ #define LSM_MAX_SHMCHUNKS (1<<30) /* Return true if shm-sequence "a" is larger than or equal to "b" */ #define shm_sequence_ge(a, b) (((u32)a-(u32)b) < LSM_MAX_SHMCHUNKS) #define LSM_APPLIST_SZ 4 /* ** An instance of the following structure stores the in-memory part of ** the current free block list. This structure is to the free block list ** as the in-memory tree is to the users database content. The contents ** of the free block list is found by merging the in-memory components ** with those stored in the LSM, just as the contents of the database is ** found by merging the in-memory tree with the user data entries in the ** LSM. ** ** Each FreelistEntry structure in the array represents either an insert ** or delete operation on the free-list. For deletes, the FreelistEntry.iId ** field is set to -1. For inserts, it is set to zero or greater. ** ** The array of FreelistEntry structures is always sorted in order of ** block number (ascending). ** ** When the in-memory free block list is written into the LSM, each insert ** operation is written separately. The entry key is the bitwise inverse ** of the block number as a 32-bit big-endian integer. This is done so that ** the entries in the LSM are sorted in descending order of block id. ** The associated value is the snapshot id, formated as a varint. */ struct Freelist { FreelistEntry *aEntry; /* Free list entries */ int nEntry; /* Number of valid slots in aEntry[] */ int nAlloc; /* Allocated size of aEntry[] */ }; struct FreelistEntry { u32 iBlk; /* Block number */ i64 iId; /* Largest snapshot id to use this block */ }; /* ** A snapshot of a database. A snapshot contains all the information required ** to read or write a database file on disk. See the description of struct ** Database below for futher details. */ struct Snapshot { Database *pDatabase; /* Database this snapshot belongs to */ u32 iCmpId; /* Id of compression scheme */ Level *pLevel; /* Pointer to level 0 of snapshot (or NULL) */ i64 iId; /* Snapshot id */ i64 iLogOff; /* Log file offset */ Redirect redirect; /* Block redirection array */ /* Used by worker snapshots only */ int nBlock; /* Number of blocks in database file */ Pgno aiAppend[LSM_APPLIST_SZ]; /* Append point list */ Freelist freelist; /* Free block list */ u32 nWrite; /* Total number of pages written to disk */ }; #define LSM_INITIAL_SNAPSHOT_ID 11 /* ** Functions from file "lsm_ckpt.c". */ int lsmCheckpointWrite(lsm_db *, u32 *); int lsmCheckpointLevels(lsm_db *, int, void **, int *); int lsmCheckpointLoadLevels(lsm_db *pDb, void *pVal, int nVal); int lsmCheckpointRecover(lsm_db *); int lsmCheckpointDeserialize(lsm_db *, int, u32 *, Snapshot **); int lsmCheckpointLoadWorker(lsm_db *pDb); int lsmCheckpointStore(lsm_db *pDb, int); int lsmCheckpointLoad(lsm_db *pDb, int *); int lsmCheckpointLoadOk(lsm_db *pDb, int); int lsmCheckpointClientCacheOk(lsm_db *); u32 lsmCheckpointNBlock(u32 *); i64 lsmCheckpointId(u32 *, int); u32 lsmCheckpointNWrite(u32 *, int); i64 lsmCheckpointLogOffset(u32 *); int lsmCheckpointPgsz(u32 *); int lsmCheckpointBlksz(u32 *); void lsmCheckpointLogoffset(u32 *aCkpt, DbLog *pLog); void lsmCheckpointZeroLogoffset(lsm_db *); int lsmCheckpointSaveWorker(lsm_db *pDb, int); int lsmDatabaseFull(lsm_db *pDb); int lsmCheckpointSynced(lsm_db *pDb, i64 *piId, i64 *piLog, u32 *pnWrite); int lsmCheckpointSize(lsm_db *db, int *pnByte); int lsmInfoCompressionId(lsm_db *db, u32 *piCmpId); /* ** Functions from file "lsm_tree.c". */ int lsmTreeNew(lsm_env *, int (*)(void *, int, void *, int), Tree **ppTree); void lsmTreeRelease(lsm_env *, Tree *); int lsmTreeInit(lsm_db *); int lsmTreeRepair(lsm_db *); void lsmTreeMakeOld(lsm_db *pDb); void lsmTreeDiscardOld(lsm_db *pDb); int lsmTreeHasOld(lsm_db *pDb); int lsmTreeSize(lsm_db *); int lsmTreeEndTransaction(lsm_db *pDb, int bCommit); int lsmTreeLoadHeader(lsm_db *pDb, int *); int lsmTreeLoadHeaderOk(lsm_db *, int); int lsmTreeInsert(lsm_db *pDb, void *pKey, int nKey, void *pVal, int nVal); int lsmTreeDelete(lsm_db *db, void *pKey1, int nKey1, void *pKey2, int nKey2); void lsmTreeRollback(lsm_db *pDb, TreeMark *pMark); void lsmTreeMark(lsm_db *pDb, TreeMark *pMark); int lsmTreeCursorNew(lsm_db *pDb, int, TreeCursor **); void lsmTreeCursorDestroy(TreeCursor *); int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes); int lsmTreeCursorNext(TreeCursor *pCsr); int lsmTreeCursorPrev(TreeCursor *pCsr); int lsmTreeCursorEnd(TreeCursor *pCsr, int bLast); void lsmTreeCursorReset(TreeCursor *pCsr); int lsmTreeCursorKey(TreeCursor *pCsr, int *pFlags, void **ppKey, int *pnKey); int lsmTreeCursorFlags(TreeCursor *pCsr); int lsmTreeCursorValue(TreeCursor *pCsr, void **ppVal, int *pnVal); int lsmTreeCursorValid(TreeCursor *pCsr); int lsmTreeCursorSave(TreeCursor *pCsr); void lsmFlagsToString(int flags, char *zFlags); /* ** Functions from file "mem.c". */ void *lsmMalloc(lsm_env*, size_t); void lsmFree(lsm_env*, void *); void *lsmRealloc(lsm_env*, void *, size_t); void *lsmReallocOrFree(lsm_env*, void *, size_t); void *lsmReallocOrFreeRc(lsm_env *, void *, size_t, int *); void *lsmMallocZeroRc(lsm_env*, size_t, int *); void *lsmMallocRc(lsm_env*, size_t, int *); void *lsmMallocZero(lsm_env *pEnv, size_t); char *lsmMallocStrdup(lsm_env *pEnv, const char *); /* ** Functions from file "lsm_mutex.c". */ int lsmMutexStatic(lsm_env*, int, lsm_mutex **); int lsmMutexNew(lsm_env*, lsm_mutex **); void lsmMutexDel(lsm_env*, lsm_mutex *); void lsmMutexEnter(lsm_env*, lsm_mutex *); int lsmMutexTry(lsm_env*, lsm_mutex *); void lsmMutexLeave(lsm_env*, lsm_mutex *); #ifndef NDEBUG int lsmMutexHeld(lsm_env *, lsm_mutex *); int lsmMutexNotHeld(lsm_env *, lsm_mutex *); #endif /************************************************************************** ** Start of functions from "lsm_file.c". */ int lsmFsOpen(lsm_db *, const char *, int); int lsmFsOpenLog(lsm_db *, int *); void lsmFsCloseLog(lsm_db *); void lsmFsClose(FileSystem *); int lsmFsUnmap(FileSystem *); int lsmFsConfigure(lsm_db *db); int lsmFsBlockSize(FileSystem *); void lsmFsSetBlockSize(FileSystem *, int); int lsmFsMoveBlock(FileSystem *pFS, Segment *pSeg, int iTo, int iFrom); int lsmFsPageSize(FileSystem *); void lsmFsSetPageSize(FileSystem *, int); int lsmFsFileid(lsm_db *pDb, void **ppId, int *pnId); /* Creating, populating, gobbling and deleting sorted runs. */ void lsmFsGobble(lsm_db *, Segment *, Pgno *, int); int lsmFsSortedDelete(FileSystem *, Snapshot *, int, Segment *); int lsmFsSortedFinish(FileSystem *, Segment *); int lsmFsSortedAppend(FileSystem *, Snapshot *, Level *, int, Page **); int lsmFsSortedPadding(FileSystem *, Snapshot *, Segment *); /* Functions to retrieve the lsm_env pointer from a FileSystem or Page object */ lsm_env *lsmFsEnv(FileSystem *); lsm_env *lsmPageEnv(Page *); FileSystem *lsmPageFS(Page *); int lsmFsSectorSize(FileSystem *); void lsmSortedSplitkey(lsm_db *, Level *, int *); /* Reading sorted run content. */ int lsmFsDbPageLast(FileSystem *pFS, Segment *pSeg, Page **ppPg); int lsmFsDbPageGet(FileSystem *, Segment *, Pgno, Page **); int lsmFsDbPageNext(Segment *, Page *, int eDir, Page **); u8 *lsmFsPageData(Page *, int *); int lsmFsPageRelease(Page *); int lsmFsPagePersist(Page *); void lsmFsPageRef(Page *); Pgno lsmFsPageNumber(Page *); int lsmFsNRead(FileSystem *); int lsmFsNWrite(FileSystem *); int lsmFsMetaPageGet(FileSystem *, int, int, MetaPage **); int lsmFsMetaPageRelease(MetaPage *); u8 *lsmFsMetaPageData(MetaPage *, int *); #ifdef LSM_DEBUG int lsmFsDbPageIsLast(Segment *pSeg, Page *pPg); int lsmFsIntegrityCheck(lsm_db *); #endif Pgno lsmFsRedirectPage(FileSystem *, Redirect *, Pgno); int lsmFsPageWritable(Page *); /* Functions to read, write and sync the log file. */ int lsmFsWriteLog(FileSystem *pFS, i64 iOff, LsmString *pStr); int lsmFsSyncLog(FileSystem *pFS); int lsmFsReadLog(FileSystem *pFS, i64 iOff, int nRead, LsmString *pStr); int lsmFsTruncateLog(FileSystem *pFS, i64 nByte); int lsmFsTruncateDb(FileSystem *pFS, i64 nByte); int lsmFsCloseAndDeleteLog(FileSystem *pFS); LsmFile *lsmFsDeferClose(FileSystem *pFS); /* And to sync the db file */ int lsmFsSyncDb(FileSystem *, int); void lsmFsFlushWaiting(FileSystem *, int *); /* Used by lsm_info(ARRAY_STRUCTURE) and lsm_config(MMAP) */ int lsmInfoArrayStructure(lsm_db *pDb, int bBlock, Pgno iFirst, char **pzOut); int lsmInfoArrayPages(lsm_db *pDb, Pgno iFirst, char **pzOut); int lsmConfigMmap(lsm_db *pDb, int *piParam); int lsmEnvOpen(lsm_env *, const char *, int, lsm_file **); int lsmEnvClose(lsm_env *pEnv, lsm_file *pFile); int lsmEnvLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int eLock); int lsmEnvTestLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int nLock, int); int lsmEnvShmMap(lsm_env *, lsm_file *, int, int, void **); void lsmEnvShmBarrier(lsm_env *); void lsmEnvShmUnmap(lsm_env *, lsm_file *, int); void lsmEnvSleep(lsm_env *, int); int lsmFsReadSyncedId(lsm_db *db, int, i64 *piVal); int lsmFsSegmentContainsPg(FileSystem *pFS, Segment *, Pgno, int *); void lsmFsPurgeCache(FileSystem *); /* ** End of functions from "lsm_file.c". **************************************************************************/ /* ** Functions from file "lsm_sorted.c". */ int lsmInfoPageDump(lsm_db *, Pgno, int, char **); void lsmSortedCleanup(lsm_db *); int lsmSortedAutoWork(lsm_db *, int nUnit); int lsmSortedWalkFreelist(lsm_db *, int, int (*)(void *, int, i64), void *); int lsmSaveWorker(lsm_db *, int); int lsmFlushTreeToDisk(lsm_db *pDb); void lsmSortedRemap(lsm_db *pDb); void lsmSortedFreeLevel(lsm_env *pEnv, Level *); int lsmSortedAdvanceAll(lsm_db *pDb); int lsmSortedLoadMerge(lsm_db *, Level *, u32 *, int *); int lsmSortedLoadFreelist(lsm_db *pDb, void **, int *); void *lsmSortedSplitKey(Level *pLevel, int *pnByte); void lsmSortedSaveTreeCursors(lsm_db *); int lsmMCursorNew(lsm_db *, MultiCursor **); void lsmMCursorClose(MultiCursor *, int); int lsmMCursorSeek(MultiCursor *, int, void *, int , int); int lsmMCursorFirst(MultiCursor *); int lsmMCursorPrev(MultiCursor *); int lsmMCursorLast(MultiCursor *); int lsmMCursorValid(MultiCursor *); int lsmMCursorNext(MultiCursor *); int lsmMCursorKey(MultiCursor *, void **, int *); int lsmMCursorValue(MultiCursor *, void **, int *); int lsmMCursorType(MultiCursor *, int *); lsm_db *lsmMCursorDb(MultiCursor *); void lsmMCursorFreeCache(lsm_db *); int lsmSaveCursors(lsm_db *pDb); int lsmRestoreCursors(lsm_db *pDb); void lsmSortedDumpStructure(lsm_db *pDb, Snapshot *, int, int, const char *); void lsmFsDumpBlocklists(lsm_db *); void lsmSortedExpandBtreePage(Page *pPg, int nOrig); void lsmPutU32(u8 *, u32); u32 lsmGetU32(u8 *); u64 lsmGetU64(u8 *); /* ** Functions from "lsm_varint.c". */ int lsmVarintPut32(u8 *, int); int lsmVarintGet32(u8 *, int *); int lsmVarintPut64(u8 *aData, i64 iVal); int lsmVarintGet64(const u8 *aData, i64 *piVal); int lsmVarintLen32(int); int lsmVarintSize(u8 c); /* ** Functions from file "main.c". */ void lsmLogMessage(lsm_db *, int, const char *, ...); int lsmInfoFreelist(lsm_db *pDb, char **pzOut); /* ** Functions from file "lsm_log.c". */ int lsmLogBegin(lsm_db *pDb); int lsmLogWrite(lsm_db *, int, void *, int, void *, int); int lsmLogCommit(lsm_db *); void lsmLogEnd(lsm_db *pDb, int bCommit); void lsmLogTell(lsm_db *, LogMark *); void lsmLogSeek(lsm_db *, LogMark *); void lsmLogClose(lsm_db *); int lsmLogRecover(lsm_db *); int lsmInfoLogStructure(lsm_db *pDb, char **pzVal); /* Valid values for the second argument to lsmLogWrite(). */ #define LSM_WRITE 0x06 #define LSM_DELETE 0x08 #define LSM_DRANGE 0x0A /************************************************************************** ** Functions from file "lsm_shared.c". */ int lsmDbDatabaseConnect(lsm_db*, const char *); void lsmDbDatabaseRelease(lsm_db *); int lsmBeginReadTrans(lsm_db *); int lsmBeginWriteTrans(lsm_db *); int lsmBeginFlush(lsm_db *); int lsmDetectRoTrans(lsm_db *db, int *); int lsmBeginRoTrans(lsm_db *db); int lsmBeginWork(lsm_db *); void lsmFinishWork(lsm_db *, int, int *); int lsmFinishRecovery(lsm_db *); void lsmFinishReadTrans(lsm_db *); int lsmFinishWriteTrans(lsm_db *, int); int lsmFinishFlush(lsm_db *, int); int lsmSnapshotSetFreelist(lsm_db *, int *, int); Snapshot *lsmDbSnapshotClient(lsm_db *); Snapshot *lsmDbSnapshotWorker(lsm_db *); void lsmSnapshotSetCkptid(Snapshot *, i64); Level *lsmDbSnapshotLevel(Snapshot *); void lsmDbSnapshotSetLevel(Snapshot *, Level *); void lsmDbRecoveryComplete(lsm_db *, int); int lsmBlockAllocate(lsm_db *, int, int *); int lsmBlockFree(lsm_db *, int); int lsmBlockRefree(lsm_db *, int); void lsmFreelistDeltaBegin(lsm_db *); void lsmFreelistDeltaEnd(lsm_db *); int lsmFreelistDelta(lsm_db *pDb); DbLog *lsmDatabaseLog(lsm_db *pDb); #ifdef LSM_DEBUG int lsmHoldingClientMutex(lsm_db *pDb); int lsmShmAssertLock(lsm_db *db, int iLock, int eOp); int lsmShmAssertWorker(lsm_db *db); #endif void lsmFreeSnapshot(lsm_env *, Snapshot *); /* Candidate values for the 3rd argument to lsmShmLock() */ #define LSM_LOCK_UNLOCK 0 #define LSM_LOCK_SHARED 1 #define LSM_LOCK_EXCL 2 int lsmShmCacheChunks(lsm_db *db, int nChunk); int lsmShmLock(lsm_db *db, int iLock, int eOp, int bBlock); int lsmShmTestLock(lsm_db *db, int iLock, int nLock, int eOp); void lsmShmBarrier(lsm_db *db); #ifdef LSM_DEBUG void lsmShmHasLock(lsm_db *db, int iLock, int eOp); #else # define lsmShmHasLock(x,y,z) #endif int lsmReadlock(lsm_db *, i64 iLsm, u32 iShmMin, u32 iShmMax); int lsmLsmInUse(lsm_db *db, i64 iLsmId, int *pbInUse); int lsmTreeInUse(lsm_db *db, u32 iLsmId, int *pbInUse); int lsmFreelistAppend(lsm_env *pEnv, Freelist *p, int iBlk, i64 iId); int lsmDbMultiProc(lsm_db *); void lsmDbDeferredClose(lsm_db *, lsm_file *, LsmFile *); LsmFile *lsmDbRecycleFd(lsm_db *); int lsmWalkFreelist(lsm_db *, int, int (*)(void *, int, i64), void *); int lsmCheckCompressionId(lsm_db *, u32); /************************************************************************** ** functions in lsm_str.c */ void lsmStringInit(LsmString*, lsm_env *pEnv); int lsmStringExtend(LsmString*, int); int lsmStringAppend(LsmString*, const char *, int); void lsmStringVAppendf(LsmString*, const char *zFormat, va_list, va_list); void lsmStringAppendf(LsmString*, const char *zFormat, ...); void lsmStringClear(LsmString*); char *lsmMallocPrintf(lsm_env*, const char*, ...); int lsmStringBinAppend(LsmString *pStr, const u8 *a, int n); int lsmStrlen(const char *zName); /* ** Round up a number to the next larger multiple of 8. This is used ** to force 8-byte alignment on 64-bit architectures. */ #define ROUND8(x) (((x)+7)&~7) #define LSM_MIN(x,y) ((x)>(y) ? (y) : (x)) #define LSM_MAX(x,y) ((x)>(y) ? (x) : (y)) #endif |
Added ext/lsm1/lsm_ckpt.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 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 | /* ** 2011-09-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. ** ************************************************************************* ** ** This file contains code to read and write checkpoints. ** ** A checkpoint represents the database layout at a single point in time. ** It includes a log offset. When an existing database is opened, the ** current state is determined by reading the newest checkpoint and updating ** it with all committed transactions from the log that follow the specified ** offset. */ #include "lsmInt.h" /* ** CHECKPOINT BLOB FORMAT: ** ** A checkpoint blob is a series of unsigned 32-bit integers stored in ** big-endian byte order. As follows: ** ** Checkpoint header (see the CKPT_HDR_XXX #defines): ** ** 1. The checkpoint id MSW. ** 2. The checkpoint id LSW. ** 3. The number of integer values in the entire checkpoint, including ** the two checksum values. ** 4. The compression scheme id. ** 5. The total number of blocks in the database. ** 6. The block size. ** 7. The number of levels. ** 8. The nominal database page size. ** 9. The number of pages (in total) written to the database file. ** ** Log pointer: ** ** 1. The log offset MSW. ** 2. The log offset LSW. ** 3. Log checksum 0. ** 4. Log checksum 1. ** ** Note that the "log offset" is not the literal byte offset. Instead, ** it is the byte offset multiplied by 2, with least significant bit ** toggled each time the log pointer value is changed. This is to make ** sure that this field changes each time the log pointer is updated, ** even if the log file itself is disabled. See lsmTreeMakeOld(). ** ** See ckptExportLog() and ckptImportLog(). ** ** Append points: ** ** 8 integers (4 * 64-bit page numbers). See ckptExportAppendlist(). ** ** For each level in the database, a level record. Formatted as follows: ** ** 0. Age of the level (least significant 16-bits). And flags mask (most ** significant 16-bits). ** 1. The number of right-hand segments (nRight, possibly 0), ** 2. Segment record for left-hand segment (8 integers defined below), ** 3. Segment record for each right-hand segment (8 integers defined below), ** 4. If nRight>0, The number of segments involved in the merge ** 5. if nRight>0, Current nSkip value (see Merge structure defn.), ** 6. For each segment in the merge: ** 5a. Page number of next cell to read during merge (this field ** is 64-bits - 2 integers) ** 5b. Cell number of next cell to read during merge ** 7. Page containing current split-key (64-bits - 2 integers). ** 8. Cell within page containing current split-key. ** 9. Current pointer value (64-bits - 2 integers). ** ** The block redirect array: ** ** 1. Number of redirections (maximum LSM_MAX_BLOCK_REDIRECTS). ** 2. For each redirection: ** a. "from" block number ** b. "to" block number ** ** The in-memory freelist entries. Each entry is either an insert or a ** delete. The in-memory freelist is to the free-block-list as the ** in-memory tree is to the users database content. ** ** 1. Number of free-list entries stored in checkpoint header. ** 2. Number of free blocks (in total). ** 3. Total number of blocks freed during database lifetime. ** 4. For each entry: ** 2a. Block number of free block. ** 2b. A 64-bit integer (MSW followed by LSW). -1 for a delete entry, ** or the associated checkpoint id for an insert. ** ** The checksum: ** ** 1. Checksum value 1. ** 2. Checksum value 2. ** ** In the above, a segment record consists of the following four 64-bit ** fields (converted to 2 * u32 by storing the MSW followed by LSW): ** ** 1. First page of array, ** 2. Last page of array, ** 3. Root page of array (or 0), ** 4. Size of array in pages. */ /* ** LARGE NUMBERS OF LEVEL RECORDS: ** ** A limit on the number of rhs segments that may be present in the database ** file. Defining this limit ensures that all level records fit within ** the 4096 byte limit for checkpoint blobs. ** ** The number of right-hand-side segments in a database is counted as ** follows: ** ** * For each level in the database not undergoing a merge, add 1. ** ** * For each level in the database that is undergoing a merge, add ** the number of segments on the rhs of the level. ** ** A level record not undergoing a merge is 10 integers. A level record ** with nRhs rhs segments and (nRhs+1) input segments (i.e. including the ** separators from the next level) is (11*nRhs+20) integers. The maximum ** per right-hand-side level is therefore 21 integers. So the maximum ** size of all level records in a checkpoint is 21*40=820 integers. ** ** TODO: Before pointer values were changed from 32 to 64 bits, the above ** used to come to 420 bytes - leaving significant space for a free-list ** prefix. No more. To fix this, reduce the size of the level records in ** a db snapshot, and improve management of the free-list tail in ** lsm_sorted.c. */ #define LSM_MAX_RHS_SEGMENTS 40 /* ** LARGE NUMBERS OF FREELIST ENTRIES: ** ** There is also a limit (LSM_MAX_FREELIST_ENTRIES - defined in lsmInt.h) ** on the number of free-list entries stored in a checkpoint. Since each ** free-list entry consists of 3 integers, the maximum free-list size is ** 3*100=300 integers. Combined with the limit on rhs segments defined ** above, this ensures that a checkpoint always fits within a 4096 byte ** meta page. ** ** If the database contains more than 100 free blocks, the "overflow" flag ** in the checkpoint header is set and the remainder are stored in the ** system FREELIST entry in the LSM (along with user data). The value ** accompanying the FREELIST key in the LSM is, like a checkpoint, an array ** of 32-bit big-endian integers. As follows: ** ** For each entry: ** a. Block number of free block. ** b. MSW of associated checkpoint id. ** c. LSW of associated checkpoint id. ** ** The number of entries is not required - it is implied by the size of the ** value blob containing the integer array. ** ** Note that the limit defined by LSM_MAX_FREELIST_ENTRIES is a hard limit. ** The actual value used may be configured using LSM_CONFIG_MAX_FREELIST. */ /* ** The argument to this macro must be of type u32. On a little-endian ** architecture, it returns the u32 value that results from interpreting ** the 4 bytes as a big-endian value. On a big-endian architecture, it ** returns the value that would be produced by intepreting the 4 bytes ** of the input value as a little-endian integer. */ #define BYTESWAP32(x) ( \ (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \ + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ ) static const int one = 1; #define LSM_LITTLE_ENDIAN (*(u8 *)(&one)) /* Sizes, in integers, of various parts of the checkpoint. */ #define CKPT_HDR_SIZE 9 #define CKPT_LOGPTR_SIZE 4 #define CKPT_APPENDLIST_SIZE (LSM_APPLIST_SZ * 2) /* A #define to describe each integer in the checkpoint header. */ #define CKPT_HDR_ID_MSW 0 #define CKPT_HDR_ID_LSW 1 #define CKPT_HDR_NCKPT 2 #define CKPT_HDR_CMPID 3 #define CKPT_HDR_NBLOCK 4 #define CKPT_HDR_BLKSZ 5 #define CKPT_HDR_NLEVEL 6 #define CKPT_HDR_PGSZ 7 #define CKPT_HDR_NWRITE 8 #define CKPT_HDR_LO_MSW 9 #define CKPT_HDR_LO_LSW 10 #define CKPT_HDR_LO_CKSUM1 11 #define CKPT_HDR_LO_CKSUM2 12 typedef struct CkptBuffer CkptBuffer; /* ** Dynamic buffer used to accumulate data for a checkpoint. */ struct CkptBuffer { lsm_env *pEnv; int nAlloc; u32 *aCkpt; }; /* ** Calculate the checksum of the checkpoint specified by arguments aCkpt and ** nCkpt. Store the checksum in *piCksum1 and *piCksum2 before returning. ** ** The value of the nCkpt parameter includes the two checksum values at ** the end of the checkpoint. They are not used as inputs to the checksum ** calculation. The checksum is based on the array of (nCkpt-2) integers ** at aCkpt[]. */ static void ckptChecksum(u32 *aCkpt, u32 nCkpt, u32 *piCksum1, u32 *piCksum2){ u32 i; u32 cksum1 = 1; u32 cksum2 = 2; if( nCkpt % 2 ){ cksum1 += aCkpt[nCkpt-3] & 0x0000FFFF; cksum2 += aCkpt[nCkpt-3] & 0xFFFF0000; } for(i=0; (i+3)<nCkpt; i+=2){ cksum1 += cksum2 + aCkpt[i]; cksum2 += cksum1 + aCkpt[i+1]; } *piCksum1 = cksum1; *piCksum2 = cksum2; } /* ** Set integer iIdx of the checkpoint accumulating in buffer *p to iVal. */ static void ckptSetValue(CkptBuffer *p, int iIdx, u32 iVal, int *pRc){ if( *pRc ) return; if( iIdx>=p->nAlloc ){ int nNew = LSM_MAX(8, iIdx*2); p->aCkpt = (u32 *)lsmReallocOrFree(p->pEnv, p->aCkpt, nNew*sizeof(u32)); if( !p->aCkpt ){ *pRc = LSM_NOMEM_BKPT; return; } p->nAlloc = nNew; } p->aCkpt[iIdx] = iVal; } /* ** Argument aInt points to an array nInt elements in size. Switch the ** endian-ness of each element of the array. */ static void ckptChangeEndianness(u32 *aInt, int nInt){ if( LSM_LITTLE_ENDIAN ){ int i; for(i=0; i<nInt; i++) aInt[i] = BYTESWAP32(aInt[i]); } } /* ** Object *p contains a checkpoint in native byte-order. The checkpoint is ** nCkpt integers in size, not including any checksum. This function sets ** the two checksum elements of the checkpoint accordingly. */ static void ckptAddChecksum(CkptBuffer *p, int nCkpt, int *pRc){ if( *pRc==LSM_OK ){ u32 aCksum[2] = {0, 0}; ckptChecksum(p->aCkpt, nCkpt+2, &aCksum[0], &aCksum[1]); ckptSetValue(p, nCkpt, aCksum[0], pRc); ckptSetValue(p, nCkpt+1, aCksum[1], pRc); } } static void ckptAppend64(CkptBuffer *p, int *piOut, i64 iVal, int *pRc){ int iOut = *piOut; ckptSetValue(p, iOut++, (iVal >> 32) & 0xFFFFFFFF, pRc); ckptSetValue(p, iOut++, (iVal & 0xFFFFFFFF), pRc); *piOut = iOut; } static i64 ckptRead64(u32 *a){ return (((i64)a[0]) << 32) + (i64)a[1]; } static i64 ckptGobble64(u32 *a, int *piIn){ int iIn = *piIn; *piIn += 2; return ckptRead64(&a[iIn]); } /* ** Append a 6-value segment record corresponding to pSeg to the checkpoint ** buffer passed as the third argument. */ static void ckptExportSegment( Segment *pSeg, CkptBuffer *p, int *piOut, int *pRc ){ ckptAppend64(p, piOut, pSeg->iFirst, pRc); ckptAppend64(p, piOut, pSeg->iLastPg, pRc); ckptAppend64(p, piOut, pSeg->iRoot, pRc); ckptAppend64(p, piOut, pSeg->nSize, pRc); } static void ckptExportLevel( Level *pLevel, /* Level object to serialize */ CkptBuffer *p, /* Append new level record to this ckpt */ int *piOut, /* IN/OUT: Size of checkpoint so far */ int *pRc /* IN/OUT: Error code */ ){ int iOut = *piOut; Merge *pMerge; pMerge = pLevel->pMerge; ckptSetValue(p, iOut++, (u32)pLevel->iAge + (u32)(pLevel->flags<<16), pRc); ckptSetValue(p, iOut++, pLevel->nRight, pRc); ckptExportSegment(&pLevel->lhs, p, &iOut, pRc); assert( (pLevel->nRight>0)==(pMerge!=0) ); if( pMerge ){ int i; for(i=0; i<pLevel->nRight; i++){ ckptExportSegment(&pLevel->aRhs[i], p, &iOut, pRc); } assert( pMerge->nInput==pLevel->nRight || pMerge->nInput==pLevel->nRight+1 ); ckptSetValue(p, iOut++, pMerge->nInput, pRc); ckptSetValue(p, iOut++, pMerge->nSkip, pRc); for(i=0; i<pMerge->nInput; i++){ ckptAppend64(p, &iOut, pMerge->aInput[i].iPg, pRc); ckptSetValue(p, iOut++, pMerge->aInput[i].iCell, pRc); } ckptAppend64(p, &iOut, pMerge->splitkey.iPg, pRc); ckptSetValue(p, iOut++, pMerge->splitkey.iCell, pRc); ckptAppend64(p, &iOut, pMerge->iCurrentPtr, pRc); } *piOut = iOut; } /* ** Populate the log offset fields of the checkpoint buffer. 4 values. */ static void ckptExportLog( lsm_db *pDb, int bFlush, CkptBuffer *p, int *piOut, int *pRc ){ int iOut = *piOut; assert( iOut==CKPT_HDR_LO_MSW ); if( bFlush ){ i64 iOff = pDb->treehdr.iOldLog; ckptAppend64(p, &iOut, iOff, pRc); ckptSetValue(p, iOut++, pDb->treehdr.oldcksum0, pRc); ckptSetValue(p, iOut++, pDb->treehdr.oldcksum1, pRc); }else{ for(; iOut<=CKPT_HDR_LO_CKSUM2; iOut++){ ckptSetValue(p, iOut, pDb->pShmhdr->aSnap2[iOut], pRc); } } assert( *pRc || iOut==CKPT_HDR_LO_CKSUM2+1 ); *piOut = iOut; } static void ckptExportAppendlist( lsm_db *db, /* Database connection */ CkptBuffer *p, /* Checkpoint buffer to write to */ int *piOut, /* IN/OUT: Offset within checkpoint buffer */ int *pRc /* IN/OUT: Error code */ ){ int i; Pgno *aiAppend = db->pWorker->aiAppend; for(i=0; i<LSM_APPLIST_SZ; i++){ ckptAppend64(p, piOut, aiAppend[i], pRc); } }; static int ckptExportSnapshot( lsm_db *pDb, /* Connection handle */ int bLog, /* True to update log-offset fields */ i64 iId, /* Checkpoint id */ int bCksum, /* If true, include checksums */ void **ppCkpt, /* OUT: Buffer containing checkpoint */ int *pnCkpt /* OUT: Size of checkpoint in bytes */ ){ int rc = LSM_OK; /* Return Code */ FileSystem *pFS = pDb->pFS; /* File system object */ Snapshot *pSnap = pDb->pWorker; /* Worker snapshot */ int nLevel = 0; /* Number of levels in checkpoint */ int iLevel; /* Used to count out nLevel levels */ int iOut = 0; /* Current offset in aCkpt[] */ Level *pLevel; /* Level iterator */ int i; /* Iterator used while serializing freelist */ CkptBuffer ckpt; /* Initialize the output buffer */ memset(&ckpt, 0, sizeof(CkptBuffer)); ckpt.pEnv = pDb->pEnv; iOut = CKPT_HDR_SIZE; /* Write the log offset into the checkpoint. */ ckptExportLog(pDb, bLog, &ckpt, &iOut, &rc); /* Write the append-point list */ ckptExportAppendlist(pDb, &ckpt, &iOut, &rc); /* Figure out how many levels will be written to the checkpoint. */ for(pLevel=lsmDbSnapshotLevel(pSnap); pLevel; pLevel=pLevel->pNext) nLevel++; /* Serialize nLevel levels. */ iLevel = 0; for(pLevel=lsmDbSnapshotLevel(pSnap); iLevel<nLevel; pLevel=pLevel->pNext){ ckptExportLevel(pLevel, &ckpt, &iOut, &rc); iLevel++; } /* Write the block-redirect list */ ckptSetValue(&ckpt, iOut++, pSnap->redirect.n, &rc); for(i=0; i<pSnap->redirect.n; i++){ ckptSetValue(&ckpt, iOut++, pSnap->redirect.a[i].iFrom, &rc); ckptSetValue(&ckpt, iOut++, pSnap->redirect.a[i].iTo, &rc); } /* Write the freelist */ assert( pSnap->freelist.nEntry<=pDb->nMaxFreelist ); if( rc==LSM_OK ){ int nFree = pSnap->freelist.nEntry; ckptSetValue(&ckpt, iOut++, nFree, &rc); for(i=0; i<nFree; i++){ FreelistEntry *p = &pSnap->freelist.aEntry[i]; ckptSetValue(&ckpt, iOut++, p->iBlk, &rc); ckptSetValue(&ckpt, iOut++, (p->iId >> 32) & 0xFFFFFFFF, &rc); ckptSetValue(&ckpt, iOut++, p->iId & 0xFFFFFFFF, &rc); } } /* Write the checkpoint header */ assert( iId>=0 ); assert( pSnap->iCmpId==pDb->compress.iId || pSnap->iCmpId==LSM_COMPRESSION_EMPTY ); ckptSetValue(&ckpt, CKPT_HDR_ID_MSW, (u32)(iId>>32), &rc); ckptSetValue(&ckpt, CKPT_HDR_ID_LSW, (u32)(iId&0xFFFFFFFF), &rc); ckptSetValue(&ckpt, CKPT_HDR_NCKPT, iOut+2, &rc); ckptSetValue(&ckpt, CKPT_HDR_CMPID, pDb->compress.iId, &rc); ckptSetValue(&ckpt, CKPT_HDR_NBLOCK, pSnap->nBlock, &rc); ckptSetValue(&ckpt, CKPT_HDR_BLKSZ, lsmFsBlockSize(pFS), &rc); ckptSetValue(&ckpt, CKPT_HDR_NLEVEL, nLevel, &rc); ckptSetValue(&ckpt, CKPT_HDR_PGSZ, lsmFsPageSize(pFS), &rc); ckptSetValue(&ckpt, CKPT_HDR_NWRITE, pSnap->nWrite, &rc); if( bCksum ){ ckptAddChecksum(&ckpt, iOut, &rc); }else{ ckptSetValue(&ckpt, iOut, 0, &rc); ckptSetValue(&ckpt, iOut+1, 0, &rc); } iOut += 2; assert( iOut<=1024 ); #ifdef LSM_LOG_FREELIST lsmLogMessage(pDb, rc, "ckptExportSnapshot(): id=%lld freelist: %d", iId, pSnap->freelist.nEntry ); for(i=0; i<pSnap->freelist.nEntry; i++){ lsmLogMessage(pDb, rc, "ckptExportSnapshot(): iBlk=%d id=%lld", pSnap->freelist.aEntry[i].iBlk, pSnap->freelist.aEntry[i].iId ); } #endif *ppCkpt = (void *)ckpt.aCkpt; if( pnCkpt ) *pnCkpt = sizeof(u32)*iOut; return rc; } /* ** Helper function for ckptImport(). */ static void ckptNewSegment( u32 *aIn, int *piIn, Segment *pSegment /* Populate this structure */ ){ assert( pSegment->iFirst==0 && pSegment->iLastPg==0 ); assert( pSegment->nSize==0 && pSegment->iRoot==0 ); pSegment->iFirst = ckptGobble64(aIn, piIn); pSegment->iLastPg = ckptGobble64(aIn, piIn); pSegment->iRoot = ckptGobble64(aIn, piIn); pSegment->nSize = (int)ckptGobble64(aIn, piIn); assert( pSegment->iFirst ); } static int ckptSetupMerge(lsm_db *pDb, u32 *aInt, int *piIn, Level *pLevel){ Merge *pMerge; /* Allocated Merge object */ int nInput; /* Number of input segments in merge */ int iIn = *piIn; /* Next value to read from aInt[] */ int i; /* Iterator variable */ int nByte; /* Number of bytes to allocate */ /* Allocate the Merge object. If malloc() fails, return LSM_NOMEM. */ nInput = (int)aInt[iIn++]; nByte = sizeof(Merge) + sizeof(MergeInput) * nInput; pMerge = (Merge *)lsmMallocZero(pDb->pEnv, nByte); if( !pMerge ) return LSM_NOMEM_BKPT; pLevel->pMerge = pMerge; /* Populate the Merge object. */ pMerge->aInput = (MergeInput *)&pMerge[1]; pMerge->nInput = nInput; pMerge->iOutputOff = -1; pMerge->nSkip = (int)aInt[iIn++]; for(i=0; i<nInput; i++){ pMerge->aInput[i].iPg = ckptGobble64(aInt, &iIn); pMerge->aInput[i].iCell = (int)aInt[iIn++]; } pMerge->splitkey.iPg = ckptGobble64(aInt, &iIn); pMerge->splitkey.iCell = (int)aInt[iIn++]; pMerge->iCurrentPtr = ckptGobble64(aInt, &iIn); /* Set *piIn and return LSM_OK. */ *piIn = iIn; return LSM_OK; } static int ckptLoadLevels( lsm_db *pDb, u32 *aIn, int *piIn, int nLevel, Level **ppLevel ){ int i; int rc = LSM_OK; Level *pRet = 0; Level **ppNext; int iIn = *piIn; ppNext = &pRet; for(i=0; rc==LSM_OK && i<nLevel; i++){ int iRight; Level *pLevel; /* Allocate space for the Level structure and Level.apRight[] array */ pLevel = (Level *)lsmMallocZeroRc(pDb->pEnv, sizeof(Level), &rc); if( rc==LSM_OK ){ pLevel->iAge = (u16)(aIn[iIn] & 0x0000FFFF); pLevel->flags = (u16)((aIn[iIn]>>16) & 0x0000FFFF); iIn++; pLevel->nRight = aIn[iIn++]; if( pLevel->nRight ){ int nByte = sizeof(Segment) * pLevel->nRight; pLevel->aRhs = (Segment *)lsmMallocZeroRc(pDb->pEnv, nByte, &rc); } if( rc==LSM_OK ){ *ppNext = pLevel; ppNext = &pLevel->pNext; /* Allocate the main segment */ ckptNewSegment(aIn, &iIn, &pLevel->lhs); /* Allocate each of the right-hand segments, if any */ for(iRight=0; iRight<pLevel->nRight; iRight++){ ckptNewSegment(aIn, &iIn, &pLevel->aRhs[iRight]); } /* Set up the Merge object, if required */ if( pLevel->nRight>0 ){ rc = ckptSetupMerge(pDb, aIn, &iIn, pLevel); } } } } if( rc!=LSM_OK ){ /* An OOM must have occurred. Free any level structures allocated and ** return the error to the caller. */ lsmSortedFreeLevel(pDb->pEnv, pRet); pRet = 0; } *ppLevel = pRet; *piIn = iIn; return rc; } int lsmCheckpointLoadLevels(lsm_db *pDb, void *pVal, int nVal){ int rc = LSM_OK; if( nVal>0 ){ u32 *aIn; aIn = lsmMallocRc(pDb->pEnv, nVal, &rc); if( aIn ){ Level *pLevel = 0; Level *pParent; int nIn; int nLevel; int iIn = 1; memcpy(aIn, pVal, nVal); nIn = nVal / sizeof(u32); ckptChangeEndianness(aIn, nIn); nLevel = aIn[0]; rc = ckptLoadLevels(pDb, aIn, &iIn, nLevel, &pLevel); lsmFree(pDb->pEnv, aIn); assert( rc==LSM_OK || pLevel==0 ); if( rc==LSM_OK ){ pParent = lsmDbSnapshotLevel(pDb->pWorker); assert( pParent ); while( pParent->pNext ) pParent = pParent->pNext; pParent->pNext = pLevel; } } } return rc; } /* ** Return the data for the LEVELS record. ** ** The size of the checkpoint that can be stored in the database header ** must not exceed 1024 32-bit integers. Normally, it does not. However, ** if it does, part of the checkpoint must be stored in the LSM. This ** routine returns that part. */ int lsmCheckpointLevels( lsm_db *pDb, /* Database handle */ int nLevel, /* Number of levels to write to blob */ void **paVal, /* OUT: Pointer to LEVELS blob */ int *pnVal /* OUT: Size of LEVELS blob in bytes */ ){ Level *p; /* Used to iterate through levels */ int nAll= 0; int rc; int i; int iOut; CkptBuffer ckpt; assert( nLevel>0 ); for(p=lsmDbSnapshotLevel(pDb->pWorker); p; p=p->pNext) nAll++; assert( nAll>nLevel ); nAll -= nLevel; for(p=lsmDbSnapshotLevel(pDb->pWorker); p && nAll>0; p=p->pNext) nAll--; memset(&ckpt, 0, sizeof(CkptBuffer)); ckpt.pEnv = pDb->pEnv; ckptSetValue(&ckpt, 0, nLevel, &rc); iOut = 1; for(i=0; rc==LSM_OK && i<nLevel; i++){ ckptExportLevel(p, &ckpt, &iOut, &rc); p = p->pNext; } assert( rc!=LSM_OK || p==0 ); if( rc==LSM_OK ){ ckptChangeEndianness(ckpt.aCkpt, iOut); *paVal = (void *)ckpt.aCkpt; *pnVal = iOut * sizeof(u32); }else{ *pnVal = 0; *paVal = 0; } return rc; } /* ** Read the checkpoint id from meta-page pPg. */ static i64 ckptLoadId(MetaPage *pPg){ i64 ret = 0; if( pPg ){ int nData; u8 *aData = lsmFsMetaPageData(pPg, &nData); ret = (((i64)lsmGetU32(&aData[CKPT_HDR_ID_MSW*4])) << 32) + ((i64)lsmGetU32(&aData[CKPT_HDR_ID_LSW*4])); } return ret; } /* ** Return true if the buffer passed as an argument contains a valid ** checkpoint. */ static int ckptChecksumOk(u32 *aCkpt){ u32 nCkpt = aCkpt[CKPT_HDR_NCKPT]; u32 cksum1; u32 cksum2; if( nCkpt<CKPT_HDR_NCKPT || nCkpt>(LSM_META_RW_PAGE_SIZE)/sizeof(u32) ){ return 0; } ckptChecksum(aCkpt, nCkpt, &cksum1, &cksum2); return (cksum1==aCkpt[nCkpt-2] && cksum2==aCkpt[nCkpt-1]); } /* ** Attempt to load a checkpoint from meta page iMeta. ** ** This function is a no-op if *pRc is set to any value other than LSM_OK ** when it is called. If an error occurs, *pRc is set to an LSM error code ** before returning. ** ** If no error occurs and the checkpoint is successfully loaded, copy it to ** ShmHeader.aSnap1[] and ShmHeader.aSnap2[], and set ShmHeader.iMetaPage ** to indicate its origin. In this case return 1. Or, if the checkpoint ** cannot be loaded (because the checksum does not compute), return 0. */ static int ckptTryLoad(lsm_db *pDb, MetaPage *pPg, u32 iMeta, int *pRc){ int bLoaded = 0; /* Return value */ if( *pRc==LSM_OK ){ int rc = LSM_OK; /* Error code */ u32 *aCkpt = 0; /* Pointer to buffer containing checkpoint */ u32 nCkpt; /* Number of elements in aCkpt[] */ int nData; /* Bytes of data in aData[] */ u8 *aData; /* Meta page data */ aData = lsmFsMetaPageData(pPg, &nData); nCkpt = (u32)lsmGetU32(&aData[CKPT_HDR_NCKPT*sizeof(u32)]); if( nCkpt<=nData/sizeof(u32) && nCkpt>CKPT_HDR_NCKPT ){ aCkpt = (u32 *)lsmMallocRc(pDb->pEnv, nCkpt*sizeof(u32), &rc); } if( aCkpt ){ memcpy(aCkpt, aData, nCkpt*sizeof(u32)); ckptChangeEndianness(aCkpt, nCkpt); if( ckptChecksumOk(aCkpt) ){ ShmHeader *pShm = pDb->pShmhdr; memcpy(pShm->aSnap1, aCkpt, nCkpt*sizeof(u32)); memcpy(pShm->aSnap2, aCkpt, nCkpt*sizeof(u32)); memcpy(pDb->aSnapshot, aCkpt, nCkpt*sizeof(u32)); pShm->iMetaPage = iMeta; bLoaded = 1; } } lsmFree(pDb->pEnv, aCkpt); *pRc = rc; } return bLoaded; } /* ** Initialize the shared-memory header with an empty snapshot. This function ** is called when no valid snapshot can be found in the database header. */ static void ckptLoadEmpty(lsm_db *pDb){ u32 aCkpt[] = { 0, /* CKPT_HDR_ID_MSW */ 10, /* CKPT_HDR_ID_LSW */ 0, /* CKPT_HDR_NCKPT */ LSM_COMPRESSION_EMPTY, /* CKPT_HDR_CMPID */ 0, /* CKPT_HDR_NBLOCK */ 0, /* CKPT_HDR_BLKSZ */ 0, /* CKPT_HDR_NLEVEL */ 0, /* CKPT_HDR_PGSZ */ 0, /* CKPT_HDR_NWRITE */ 0, 0, 1234, 5678, /* The log pointer and initial checksum */ 0,0,0,0, 0,0,0,0, /* The append list */ 0, /* The redirected block list */ 0, /* The free block list */ 0, 0 /* Space for checksum values */ }; u32 nCkpt = array_size(aCkpt); ShmHeader *pShm = pDb->pShmhdr; aCkpt[CKPT_HDR_NCKPT] = nCkpt; aCkpt[CKPT_HDR_BLKSZ] = pDb->nDfltBlksz; aCkpt[CKPT_HDR_PGSZ] = pDb->nDfltPgsz; ckptChecksum(aCkpt, array_size(aCkpt), &aCkpt[nCkpt-2], &aCkpt[nCkpt-1]); memcpy(pShm->aSnap1, aCkpt, nCkpt*sizeof(u32)); memcpy(pShm->aSnap2, aCkpt, nCkpt*sizeof(u32)); memcpy(pDb->aSnapshot, aCkpt, nCkpt*sizeof(u32)); } /* ** This function is called as part of database recovery to initialize the ** ShmHeader.aSnap1[] and ShmHeader.aSnap2[] snapshots. */ int lsmCheckpointRecover(lsm_db *pDb){ int rc = LSM_OK; /* Return Code */ i64 iId1; /* Id of checkpoint on meta-page 1 */ i64 iId2; /* Id of checkpoint on meta-page 2 */ int bLoaded = 0; /* True once checkpoint has been loaded */ int cmp; /* True if (iId2>iId1) */ MetaPage *apPg[2] = {0, 0}; /* Meta-pages 1 and 2 */ rc = lsmFsMetaPageGet(pDb->pFS, 0, 1, &apPg[0]); if( rc==LSM_OK ) rc = lsmFsMetaPageGet(pDb->pFS, 0, 2, &apPg[1]); iId1 = ckptLoadId(apPg[0]); iId2 = ckptLoadId(apPg[1]); cmp = (iId2 > iId1); bLoaded = ckptTryLoad(pDb, apPg[cmp?1:0], (cmp?2:1), &rc); if( bLoaded==0 ){ bLoaded = ckptTryLoad(pDb, apPg[cmp?0:1], (cmp?1:2), &rc); } /* The database does not contain a valid checkpoint. Initialize the shared ** memory header with an empty checkpoint. */ if( bLoaded==0 ){ ckptLoadEmpty(pDb); } lsmFsMetaPageRelease(apPg[0]); lsmFsMetaPageRelease(apPg[1]); return rc; } /* ** Store the snapshot in pDb->aSnapshot[] in meta-page iMeta. */ int lsmCheckpointStore(lsm_db *pDb, int iMeta){ MetaPage *pPg = 0; int rc; assert( iMeta==1 || iMeta==2 ); rc = lsmFsMetaPageGet(pDb->pFS, 1, iMeta, &pPg); if( rc==LSM_OK ){ u8 *aData; int nData; int nCkpt; nCkpt = (int)pDb->aSnapshot[CKPT_HDR_NCKPT]; aData = lsmFsMetaPageData(pPg, &nData); memcpy(aData, pDb->aSnapshot, nCkpt*sizeof(u32)); ckptChangeEndianness((u32 *)aData, nCkpt); rc = lsmFsMetaPageRelease(pPg); } return rc; } /* ** Copy the current client snapshot from shared-memory to pDb->aSnapshot[]. */ int lsmCheckpointLoad(lsm_db *pDb, int *piRead){ int nRem = LSM_ATTEMPTS_BEFORE_PROTOCOL; ShmHeader *pShm = pDb->pShmhdr; while( (nRem--)>0 ){ int nInt; nInt = pShm->aSnap1[CKPT_HDR_NCKPT]; if( nInt<=(LSM_META_RW_PAGE_SIZE / sizeof(u32)) ){ memcpy(pDb->aSnapshot, pShm->aSnap1, nInt*sizeof(u32)); if( ckptChecksumOk(pDb->aSnapshot) ){ if( piRead ) *piRead = 1; return LSM_OK; } } nInt = pShm->aSnap2[CKPT_HDR_NCKPT]; if( nInt<=(LSM_META_RW_PAGE_SIZE / sizeof(u32)) ){ memcpy(pDb->aSnapshot, pShm->aSnap2, nInt*sizeof(u32)); if( ckptChecksumOk(pDb->aSnapshot) ){ if( piRead ) *piRead = 2; return LSM_OK; } } lsmShmBarrier(pDb); } return LSM_PROTOCOL_BKPT; } int lsmInfoCompressionId(lsm_db *db, u32 *piCmpId){ int rc; assert( db->pClient==0 && db->pWorker==0 ); rc = lsmCheckpointLoad(db, 0); if( rc==LSM_OK ){ *piCmpId = db->aSnapshot[CKPT_HDR_CMPID]; } return rc; } int lsmCheckpointLoadOk(lsm_db *pDb, int iSnap){ u32 *aShm; assert( iSnap==1 || iSnap==2 ); aShm = (iSnap==1) ? pDb->pShmhdr->aSnap1 : pDb->pShmhdr->aSnap2; return (lsmCheckpointId(pDb->aSnapshot, 0)==lsmCheckpointId(aShm, 0) ); } int lsmCheckpointClientCacheOk(lsm_db *pDb){ return ( pDb->pClient && pDb->pClient->iId==lsmCheckpointId(pDb->aSnapshot, 0) && pDb->pClient->iId==lsmCheckpointId(pDb->pShmhdr->aSnap1, 0) && pDb->pClient->iId==lsmCheckpointId(pDb->pShmhdr->aSnap2, 0) ); } int lsmCheckpointLoadWorker(lsm_db *pDb){ int rc; ShmHeader *pShm = pDb->pShmhdr; int nInt1; int nInt2; /* Must be holding the WORKER lock to do this. Or DMS2. */ assert( lsmShmAssertLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_EXCL) || lsmShmAssertLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL) ); /* Check that the two snapshots match. If not, repair them. */ nInt1 = pShm->aSnap1[CKPT_HDR_NCKPT]; nInt2 = pShm->aSnap2[CKPT_HDR_NCKPT]; if( nInt1!=nInt2 || memcmp(pShm->aSnap1, pShm->aSnap2, nInt2*sizeof(u32)) ){ if( ckptChecksumOk(pShm->aSnap1) ){ memcpy(pShm->aSnap2, pShm->aSnap1, sizeof(u32)*nInt1); }else if( ckptChecksumOk(pShm->aSnap2) ){ memcpy(pShm->aSnap1, pShm->aSnap2, sizeof(u32)*nInt2); }else{ return LSM_PROTOCOL_BKPT; } } rc = lsmCheckpointDeserialize(pDb, 1, pShm->aSnap1, &pDb->pWorker); if( pDb->pWorker ) pDb->pWorker->pDatabase = pDb->pDatabase; if( rc==LSM_OK ){ rc = lsmCheckCompressionId(pDb, pDb->pWorker->iCmpId); } #if 0 assert( rc!=LSM_OK || lsmFsIntegrityCheck(pDb) ); #endif return rc; } int lsmCheckpointDeserialize( lsm_db *pDb, int bInclFreelist, /* If true, deserialize free-list */ u32 *aCkpt, Snapshot **ppSnap ){ int rc = LSM_OK; Snapshot *pNew; pNew = (Snapshot *)lsmMallocZeroRc(pDb->pEnv, sizeof(Snapshot), &rc); if( rc==LSM_OK ){ Level *pLvl; int nFree; int i; int nLevel = (int)aCkpt[CKPT_HDR_NLEVEL]; int iIn = CKPT_HDR_SIZE + CKPT_APPENDLIST_SIZE + CKPT_LOGPTR_SIZE; pNew->iId = lsmCheckpointId(aCkpt, 0); pNew->nBlock = aCkpt[CKPT_HDR_NBLOCK]; pNew->nWrite = aCkpt[CKPT_HDR_NWRITE]; rc = ckptLoadLevels(pDb, aCkpt, &iIn, nLevel, &pNew->pLevel); pNew->iLogOff = lsmCheckpointLogOffset(aCkpt); pNew->iCmpId = aCkpt[CKPT_HDR_CMPID]; /* Make a copy of the append-list */ for(i=0; i<LSM_APPLIST_SZ; i++){ u32 *a = &aCkpt[CKPT_HDR_SIZE + CKPT_LOGPTR_SIZE + i*2]; pNew->aiAppend[i] = ckptRead64(a); } /* Read the block-redirect list */ pNew->redirect.n = aCkpt[iIn++]; if( pNew->redirect.n ){ pNew->redirect.a = lsmMallocZeroRc(pDb->pEnv, (sizeof(struct RedirectEntry) * LSM_MAX_BLOCK_REDIRECTS), &rc ); if( rc==LSM_OK ){ for(i=0; i<pNew->redirect.n; i++){ pNew->redirect.a[i].iFrom = aCkpt[iIn++]; pNew->redirect.a[i].iTo = aCkpt[iIn++]; } } for(pLvl=pNew->pLevel; pLvl->pNext; pLvl=pLvl->pNext); if( pLvl->nRight ){ pLvl->aRhs[pLvl->nRight-1].pRedirect = &pNew->redirect; }else{ pLvl->lhs.pRedirect = &pNew->redirect; } } /* Copy the free-list */ if( rc==LSM_OK && bInclFreelist ){ nFree = aCkpt[iIn++]; if( nFree ){ pNew->freelist.aEntry = (FreelistEntry *)lsmMallocZeroRc( pDb->pEnv, sizeof(FreelistEntry)*nFree, &rc ); if( rc==LSM_OK ){ int j; for(j=0; j<nFree; j++){ FreelistEntry *p = &pNew->freelist.aEntry[j]; p->iBlk = aCkpt[iIn++]; p->iId = ((i64)(aCkpt[iIn])<<32) + aCkpt[iIn+1]; iIn += 2; } pNew->freelist.nEntry = pNew->freelist.nAlloc = nFree; } } } } if( rc!=LSM_OK ){ lsmFreeSnapshot(pDb->pEnv, pNew); pNew = 0; } *ppSnap = pNew; return rc; } /* ** Connection pDb must be the worker connection in order to call this ** function. It returns true if the database already contains the maximum ** number of levels or false otherwise. ** ** This is used when flushing the in-memory tree to disk. If the database ** is already full, then the caller should invoke lsm_work() or similar ** until it is not full before creating a new level by flushing the in-memory ** tree to disk. Limiting the number of levels in the database ensures that ** the records describing them always fit within the checkpoint blob. */ int lsmDatabaseFull(lsm_db *pDb){ Level *p; int nRhs = 0; assert( lsmShmAssertLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_EXCL) ); assert( pDb->pWorker ); for(p=pDb->pWorker->pLevel; p; p=p->pNext){ nRhs += (p->nRight ? p->nRight : 1); } return (nRhs >= LSM_MAX_RHS_SEGMENTS); } /* ** The connection passed as the only argument is currently the worker ** connection. Some work has been performed on the database by the connection, ** but no new snapshot has been written into shared memory. ** ** This function updates the shared-memory worker and client snapshots with ** the new snapshot produced by the work performed by pDb. ** ** If successful, LSM_OK is returned. Otherwise, if an error occurs, an LSM ** error code is returned. */ int lsmCheckpointSaveWorker(lsm_db *pDb, int bFlush){ Snapshot *pSnap = pDb->pWorker; ShmHeader *pShm = pDb->pShmhdr; void *p = 0; int n = 0; int rc; pSnap->iId++; rc = ckptExportSnapshot(pDb, bFlush, pSnap->iId, 1, &p, &n); if( rc!=LSM_OK ) return rc; assert( ckptChecksumOk((u32 *)p) ); assert( n<=LSM_META_RW_PAGE_SIZE ); memcpy(pShm->aSnap2, p, n); lsmShmBarrier(pDb); memcpy(pShm->aSnap1, p, n); lsmFree(pDb->pEnv, p); /* assert( lsmFsIntegrityCheck(pDb) ); */ return LSM_OK; } /* ** This function is used to determine the snapshot-id of the most recently ** checkpointed snapshot. Variable ShmHeader.iMetaPage indicates which of ** the two meta-pages said snapshot resides on (if any). ** ** If successful, this function loads the snapshot from the meta-page, ** verifies its checksum and sets *piId to the snapshot-id before returning ** LSM_OK. Or, if the checksum attempt fails, *piId is set to zero and ** LSM_OK returned. If an error occurs, an LSM error code is returned and ** the final value of *piId is undefined. */ int lsmCheckpointSynced(lsm_db *pDb, i64 *piId, i64 *piLog, u32 *pnWrite){ int rc = LSM_OK; MetaPage *pPg; u32 iMeta; iMeta = pDb->pShmhdr->iMetaPage; if( iMeta==1 || iMeta==2 ){ rc = lsmFsMetaPageGet(pDb->pFS, 0, iMeta, &pPg); if( rc==LSM_OK ){ int nCkpt; int nData; u8 *aData; aData = lsmFsMetaPageData(pPg, &nData); assert( nData==LSM_META_RW_PAGE_SIZE ); nCkpt = lsmGetU32(&aData[CKPT_HDR_NCKPT*sizeof(u32)]); if( nCkpt<(LSM_META_RW_PAGE_SIZE/sizeof(u32)) ){ u32 *aCopy = lsmMallocRc(pDb->pEnv, sizeof(u32) * nCkpt, &rc); if( aCopy ){ memcpy(aCopy, aData, nCkpt*sizeof(u32)); ckptChangeEndianness(aCopy, nCkpt); if( ckptChecksumOk(aCopy) ){ if( piId ) *piId = lsmCheckpointId(aCopy, 0); if( piLog ) *piLog = (lsmCheckpointLogOffset(aCopy) >> 1); if( pnWrite ) *pnWrite = aCopy[CKPT_HDR_NWRITE]; } lsmFree(pDb->pEnv, aCopy); } } lsmFsMetaPageRelease(pPg); } } if( (iMeta!=1 && iMeta!=2) || rc!=LSM_OK || pDb->pShmhdr->iMetaPage!=iMeta ){ if( piId ) *piId = 0; if( piLog ) *piLog = 0; if( pnWrite ) *pnWrite = 0; } return rc; } /* ** Return the checkpoint-id of the checkpoint array passed as the first ** argument to this function. If the second argument is true, then assume ** that the checkpoint is made up of 32-bit big-endian integers. If it ** is false, assume that the integers are in machine byte order. */ i64 lsmCheckpointId(u32 *aCkpt, int bDisk){ i64 iId; if( bDisk ){ u8 *aData = (u8 *)aCkpt; iId = (((i64)lsmGetU32(&aData[CKPT_HDR_ID_MSW*4])) << 32); iId += ((i64)lsmGetU32(&aData[CKPT_HDR_ID_LSW*4])); }else{ iId = ((i64)aCkpt[CKPT_HDR_ID_MSW] << 32) + (i64)aCkpt[CKPT_HDR_ID_LSW]; } return iId; } u32 lsmCheckpointNBlock(u32 *aCkpt){ return aCkpt[CKPT_HDR_NBLOCK]; } u32 lsmCheckpointNWrite(u32 *aCkpt, int bDisk){ if( bDisk ){ return lsmGetU32((u8 *)&aCkpt[CKPT_HDR_NWRITE]); }else{ return aCkpt[CKPT_HDR_NWRITE]; } } i64 lsmCheckpointLogOffset(u32 *aCkpt){ return ((i64)aCkpt[CKPT_HDR_LO_MSW] << 32) + (i64)aCkpt[CKPT_HDR_LO_LSW]; } int lsmCheckpointPgsz(u32 *aCkpt){ return (int)aCkpt[CKPT_HDR_PGSZ]; } int lsmCheckpointBlksz(u32 *aCkpt){ return (int)aCkpt[CKPT_HDR_BLKSZ]; } void lsmCheckpointLogoffset( u32 *aCkpt, DbLog *pLog ){ pLog->aRegion[2].iStart = (lsmCheckpointLogOffset(aCkpt) >> 1); pLog->cksum0 = aCkpt[CKPT_HDR_LO_CKSUM1]; pLog->cksum1 = aCkpt[CKPT_HDR_LO_CKSUM2]; pLog->iSnapshotId = lsmCheckpointId(aCkpt, 0); } void lsmCheckpointZeroLogoffset(lsm_db *pDb){ u32 nCkpt; nCkpt = pDb->aSnapshot[CKPT_HDR_NCKPT]; assert( nCkpt>CKPT_HDR_NCKPT ); assert( nCkpt==pDb->pShmhdr->aSnap1[CKPT_HDR_NCKPT] ); assert( 0==memcmp(pDb->aSnapshot, pDb->pShmhdr->aSnap1, nCkpt*sizeof(u32)) ); assert( 0==memcmp(pDb->aSnapshot, pDb->pShmhdr->aSnap2, nCkpt*sizeof(u32)) ); pDb->aSnapshot[CKPT_HDR_LO_MSW] = 0; pDb->aSnapshot[CKPT_HDR_LO_LSW] = 0; ckptChecksum(pDb->aSnapshot, nCkpt, &pDb->aSnapshot[nCkpt-2], &pDb->aSnapshot[nCkpt-1] ); memcpy(pDb->pShmhdr->aSnap1, pDb->aSnapshot, nCkpt*sizeof(u32)); memcpy(pDb->pShmhdr->aSnap2, pDb->aSnapshot, nCkpt*sizeof(u32)); } /* ** Set the output variable to the number of KB of data written into the ** database file since the most recent checkpoint. */ int lsmCheckpointSize(lsm_db *db, int *pnKB){ int rc = LSM_OK; u32 nSynced; /* Set nSynced to the number of pages that had been written when the ** database was last checkpointed. */ rc = lsmCheckpointSynced(db, 0, 0, &nSynced); if( rc==LSM_OK ){ u32 nPgsz = db->pShmhdr->aSnap1[CKPT_HDR_PGSZ]; u32 nWrite = db->pShmhdr->aSnap1[CKPT_HDR_NWRITE]; *pnKB = (int)(( ((i64)(nWrite - nSynced) * nPgsz) + 1023) / 1024); } return rc; } |
Added ext/lsm1/lsm_file.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 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 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 | /* ** 2011-08-26 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** NORMAL DATABASE FILE FORMAT ** ** The following database file format concepts are used by the code in ** this file to read and write the database file. ** ** Pages: ** ** A database file is divided into pages. The first 8KB of the file consists ** of two 4KB meta-pages. The meta-page size is not configurable. The ** remainder of the file is made up of database pages. The default database ** page size is 4KB. Database pages are aligned to page-size boundaries, ** so if the database page size is larger than 8KB there is a gap between ** the end of the meta pages and the start of the database pages. ** ** Database pages are numbered based on their position in the file. Page N ** begins at byte offset ((N-1)*pgsz). This means that page 1 does not ** exist - since it would always overlap with the meta pages. If the ** page-size is (say) 512 bytes, then the first usable page in the database ** is page 33. ** ** It is assumed that the first two meta pages and the data that follows ** them are located on different disk sectors. So that if a power failure ** while writing to a meta page there is no risk of damage to the other ** meta page or any other part of the database file. TODO: This may need ** to be revisited. ** ** Blocks: ** ** The database file is also divided into blocks. The default block size is ** 1MB. When writing to the database file, an attempt is made to write data ** in contiguous block-sized chunks. ** ** The first and last page on each block are special in that they are 4 ** bytes smaller than all other pages. This is because the last four bytes ** of space on the first and last pages of each block are reserved for ** pointers to other blocks (i.e. a 32-bit block number). ** ** Runs: ** ** A run is a sequence of pages that the upper layer uses to store a ** sorted array of database keys (and accompanying data - values, FC ** pointers and so on). Given a page within a run, it is possible to ** navigate to the next page in the run as follows: ** ** a) if the current page is not the last in a block, the next page ** in the run is located immediately after the current page, OR ** ** b) if the current page is the last page in a block, the next page ** in the run is the first page on the block identified by the ** block pointer stored in the last 4 bytes of the current block. ** ** It is possible to navigate to the previous page in a similar fashion, ** using the block pointer embedded in the last 4 bytes of the first page ** of each block as required. ** ** The upper layer is responsible for identifying by page number the ** first and last page of any run that it needs to navigate - there are ** no "end-of-run" markers stored or identified by this layer. This is ** necessary as clients reading different database snapshots may access ** different subsets of a run. ** ** THE LOG FILE ** ** This file opens and closes the log file. But it does not contain any ** logic related to the log file format. Instead, it exports the following ** functions that are used by the code in lsm_log.c to read and write the ** log file: ** ** lsmFsOpenLog ** lsmFsWriteLog ** lsmFsSyncLog ** lsmFsReadLog ** lsmFsTruncateLog ** lsmFsCloseAndDeleteLog ** ** COMPRESSED DATABASE FILE FORMAT ** ** The compressed database file format is very similar to the normal format. ** The file still begins with two 4KB meta-pages (which are never compressed). ** It is still divided into blocks. ** ** The first and last four bytes of each block are reserved for 32-bit ** pointer values. Similar to the way four bytes are carved from the end of ** the first and last page of each block in uncompressed databases. From ** the point of view of the upper layer, all pages are the same size - this ** is different from the uncompressed format where the first and last pages ** on each block are 4 bytes smaller than the others. ** ** Pages are stored in variable length compressed form, as follows: ** ** * 3-byte size field containing the size of the compressed page image ** in bytes. The most significant bit of each byte of the size field ** is always set. The remaining 7 bits are used to store a 21-bit ** integer value (in big-endian order - the first byte in the field ** contains the most significant 7 bits). Since the maximum allowed ** size of a compressed page image is (2^17 - 1) bytes, there are ** actually 4 unused bits in the size field. ** ** In other words, if the size of the compressed page image is nSz, ** the header can be serialized as follows: ** ** u8 aHdr[3] ** aHdr[0] = 0x80 | (u8)(nSz >> 14); ** aHdr[1] = 0x80 | (u8)(nSz >> 7); ** aHdr[2] = 0x80 | (u8)(nSz >> 0); ** ** * Compressed page image. ** ** * A second copy of the 3-byte record header. ** ** A page number is a byte offset into the database file. So the smallest ** possible page number is 8192 (immediately after the two meta-pages). ** The first and root page of a segment are identified by a page number ** corresponding to the byte offset of the first byte in the corresponding ** page record. The last page of a segment is identified by the byte offset ** of the last byte in its record. ** ** Unlike uncompressed pages, compressed page records may span blocks. ** ** Sometimes, in order to avoid touching sectors that contain synced data ** when writing, it is necessary to insert unused space between compressed ** page records. This can be done as follows: ** ** * For less than 6 bytes of empty space, the first and last byte ** of the free space contain the total number of free bytes. For ** example: ** ** Block of 4 free bytes: 0x04 0x?? 0x?? 0x04 ** Block of 2 free bytes: 0x02 0x02 ** A single free byte: 0x01 ** ** * For 6 or more bytes of empty space, a record similar to a ** compressed page record is added to the segment. A padding record ** is distinguished from a compressed page record by the most ** significant bit of the second byte of the size field, which is ** cleared instead of set. */ #include "lsmInt.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* ** File-system object. Each database connection allocates a single instance ** of the following structure. It is used for all access to the database and ** log files. ** ** The database file may be accessed via two methods - using mmap() or using ** read() and write() calls. In the general case both methods are used - a ** prefix of the file is mapped into memory and the remainder accessed using ** read() and write(). This is helpful when accessing very large files (or ** files that may grow very large during the lifetime of a database ** connection) on systems with 32-bit address spaces. However, it also requires ** that this object manage two distinct types of Page objects simultaneously - ** those that carry pointers to the mapped file and those that carry arrays ** populated by read() calls. ** ** pFree: ** The head of a singly-linked list that containing currently unused Page ** structures suitable for use as mmap-page handles. Connected by the ** Page.pFreeNext pointers. ** ** pMapped: ** The head of a singly-linked list that contains all pages that currently ** carry pointers to the mapped region. This is used if the region is ** every remapped - the pointers carried by existing pages can be adjusted ** to account for the remapping. Connected by the Page.pMappedNext pointers. ** ** pWaiting: ** When the upper layer wishes to append a new b-tree page to a segment, ** it allocates a Page object that carries a malloc'd block of memory - ** regardless of the mmap-related configuration. The page is not assigned ** a page number at first. When the upper layer has finished constructing ** the page contents, it calls lsmFsPagePersist() to assign a page number ** to it. At this point it is likely that N pages have been written to the ** segment, the (N+1)th page is still outstanding and the b-tree page is ** assigned page number (N+2). To avoid writing page (N+2) before page ** (N+1), the recently completed b-tree page is held in the singly linked ** list headed by pWaiting until page (N+1) has been written. ** ** Function lsmFsFlushWaiting() is responsible for eventually writing ** waiting pages to disk. ** ** apHash/nHash: ** Hash table used to store all Page objects that carry malloc'd arrays, ** except those b-tree pages that have not yet been assigned page numbers. ** Once they have been assigned page numbers - they are added to this ** hash table. ** ** Hash table overflow chains are connected using the Page.pHashNext ** pointers. ** ** pLruFirst, pLruLast: ** The first and last entries in a doubly-linked list of pages. This ** list contains all pages with malloc'd data that are present in the ** hash table and have a ref-count of zero. */ struct FileSystem { lsm_db *pDb; /* Database handle that owns this object */ lsm_env *pEnv; /* Environment pointer */ char *zDb; /* Database file name */ char *zLog; /* Database file name */ int nMetasize; /* Size of meta pages in bytes */ int nMetaRwSize; /* Read/written size of meta pages in bytes */ int nPagesize; /* Database page-size in bytes */ int nBlocksize; /* Database block-size in bytes */ /* r/w file descriptors for both files. */ LsmFile *pLsmFile; /* Used after lsm_close() to link into list */ lsm_file *fdDb; /* Database file */ lsm_file *fdLog; /* Log file */ int szSector; /* Database file sector size */ /* If this is a compressed database, a pointer to the compression methods. ** For an uncompressed database, a NULL pointer. */ lsm_compress *pCompress; u8 *aIBuffer; /* Buffer to compress to */ u8 *aOBuffer; /* Buffer to uncompress from */ int nBuffer; /* Allocated size of above buffers in bytes */ /* mmap() page related things */ i64 nMapLimit; /* Maximum bytes of file to map */ void *pMap; /* Current mapping of database file */ i64 nMap; /* Bytes mapped at pMap */ Page *pFree; /* Unused Page structures */ Page *pMapped; /* List of Page structs that point to pMap */ /* Page cache parameters for non-mmap() pages */ int nCacheMax; /* Configured cache size (in pages) */ int nCacheAlloc; /* Current cache size (in pages) */ Page *pLruFirst; /* Head of the LRU list */ Page *pLruLast; /* Tail of the LRU list */ int nHash; /* Number of hash slots in hash table */ Page **apHash; /* nHash Hash slots */ Page *pWaiting; /* b-tree pages waiting to be written */ /* Statistics */ int nOut; /* Number of outstanding pages */ int nWrite; /* Total number of pages written */ int nRead; /* Total number of pages read */ }; /* ** Database page handle. ** ** pSeg: ** When lsmFsSortedAppend() is called on a compressed database, the new ** page is not assigned a page number or location in the database file ** immediately. Instead, these are assigned by the lsmFsPagePersist() call ** right before it writes the compressed page image to disk. ** ** The lsmFsSortedAppend() function sets the pSeg pointer to point to the ** segment that the new page will be a part of. It is unset by ** lsmFsPagePersist() after the page is written to disk. */ struct Page { u8 *aData; /* Buffer containing page data */ int nData; /* Bytes of usable data at aData[] */ Pgno iPg; /* Page number */ int nRef; /* Number of outstanding references */ int flags; /* Combination of PAGE_XXX flags */ Page *pHashNext; /* Next page in hash table slot */ Page *pLruNext; /* Next page in LRU list */ Page *pLruPrev; /* Previous page in LRU list */ FileSystem *pFS; /* File system that owns this page */ /* Only used in compressed database mode: */ int nCompress; /* Compressed size (or 0 for uncomp. db) */ int nCompressPrev; /* Compressed size of prev page */ Segment *pSeg; /* Segment this page will be written to */ /* Pointers for singly linked lists */ Page *pWaitingNext; /* Next page in FileSystem.pWaiting list */ Page *pFreeNext; /* Next page in FileSystem.pFree list */ Page *pMappedNext; /* Next page in FileSystem.pMapped list */ }; /* ** Meta-data page handle. There are two meta-data pages at the start of ** the database file, each FileSystem.nMetasize bytes in size. */ struct MetaPage { int iPg; /* Either 1 or 2 */ int bWrite; /* Write back to db file on release */ u8 *aData; /* Pointer to buffer */ FileSystem *pFS; /* FileSystem that owns this page */ }; /* ** Values for LsmPage.flags */ #define PAGE_DIRTY 0x00000001 /* Set if page is dirty */ #define PAGE_FREE 0x00000002 /* Set if Page.aData requires lsmFree() */ #define PAGE_HASPREV 0x00000004 /* Set if page is first on uncomp. block */ /* ** Number of pgsz byte pages omitted from the start of block 1. The start ** of block 1 contains two 4096 byte meta pages (8192 bytes in total). */ #define BLOCK1_HDR_SIZE(pgsz) LSM_MAX(1, 8192/(pgsz)) /* ** If NDEBUG is not defined, set a breakpoint in function lsmIoerrBkpt() ** to catch IO errors (any error returned by a VFS method). */ #ifndef NDEBUG static void lsmIoerrBkpt(void){ static int nErr = 0; nErr++; } static int IOERR_WRAPPER(int rc){ if( rc!=LSM_OK ) lsmIoerrBkpt(); return rc; } #else # define IOERR_WRAPPER(rc) (rc) #endif #ifdef NDEBUG # define assert_lists_are_ok(x) #else static Page *fsPageFindInHash(FileSystem *pFS, Pgno iPg, int *piHash); static void assert_lists_are_ok(FileSystem *pFS){ #if 0 Page *p; assert( pFS->nMapLimit>=0 ); /* Check that all pages in the LRU list have nRef==0, pointers to buffers ** in heap memory, and corresponding entries in the hash table. */ for(p=pFS->pLruFirst; p; p=p->pLruNext){ assert( p==pFS->pLruFirst || p->pLruPrev!=0 ); assert( p==pFS->pLruLast || p->pLruNext!=0 ); assert( p->pLruPrev==0 || p->pLruPrev->pLruNext==p ); assert( p->pLruNext==0 || p->pLruNext->pLruPrev==p ); assert( p->nRef==0 ); assert( p->flags & PAGE_FREE ); assert( p==fsPageFindInHash(pFS, p->iPg, 0) ); } #endif } #endif /* ** Wrappers around the VFS methods of the lsm_env object: ** ** lsmEnvOpen() ** lsmEnvRead() ** lsmEnvWrite() ** lsmEnvSync() ** lsmEnvSectorSize() ** lsmEnvClose() ** lsmEnvTruncate() ** lsmEnvUnlink() ** lsmEnvRemap() */ int lsmEnvOpen(lsm_env *pEnv, const char *zFile, int flags, lsm_file **ppNew){ return pEnv->xOpen(pEnv, zFile, flags, ppNew); } static int lsmEnvRead( lsm_env *pEnv, lsm_file *pFile, lsm_i64 iOff, void *pRead, int nRead ){ return IOERR_WRAPPER( pEnv->xRead(pFile, iOff, pRead, nRead) ); } static int lsmEnvWrite( lsm_env *pEnv, lsm_file *pFile, lsm_i64 iOff, const void *pWrite, int nWrite ){ return IOERR_WRAPPER( pEnv->xWrite(pFile, iOff, (void *)pWrite, nWrite) ); } static int lsmEnvSync(lsm_env *pEnv, lsm_file *pFile){ return IOERR_WRAPPER( pEnv->xSync(pFile) ); } static int lsmEnvSectorSize(lsm_env *pEnv, lsm_file *pFile){ return pEnv->xSectorSize(pFile); } int lsmEnvClose(lsm_env *pEnv, lsm_file *pFile){ return IOERR_WRAPPER( pEnv->xClose(pFile) ); } static int lsmEnvTruncate(lsm_env *pEnv, lsm_file *pFile, lsm_i64 nByte){ return IOERR_WRAPPER( pEnv->xTruncate(pFile, nByte) ); } static int lsmEnvUnlink(lsm_env *pEnv, const char *zDel){ return IOERR_WRAPPER( pEnv->xUnlink(pEnv, zDel) ); } static int lsmEnvRemap( lsm_env *pEnv, lsm_file *pFile, i64 szMin, void **ppMap, i64 *pszMap ){ return pEnv->xRemap(pFile, szMin, ppMap, pszMap); } int lsmEnvLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int eLock){ if( pFile==0 ) return LSM_OK; return pEnv->xLock(pFile, iLock, eLock); } int lsmEnvTestLock( lsm_env *pEnv, lsm_file *pFile, int iLock, int nLock, int eLock ){ return pEnv->xTestLock(pFile, iLock, nLock, eLock); } int lsmEnvShmMap( lsm_env *pEnv, lsm_file *pFile, int iChunk, int sz, void **ppOut ){ return pEnv->xShmMap(pFile, iChunk, sz, ppOut); } void lsmEnvShmBarrier(lsm_env *pEnv){ pEnv->xShmBarrier(); } void lsmEnvShmUnmap(lsm_env *pEnv, lsm_file *pFile, int bDel){ pEnv->xShmUnmap(pFile, bDel); } void lsmEnvSleep(lsm_env *pEnv, int nUs){ pEnv->xSleep(pEnv, nUs); } /* ** Write the contents of string buffer pStr into the log file, starting at ** offset iOff. */ int lsmFsWriteLog(FileSystem *pFS, i64 iOff, LsmString *pStr){ assert( pFS->fdLog ); return lsmEnvWrite(pFS->pEnv, pFS->fdLog, iOff, pStr->z, pStr->n); } /* ** fsync() the log file. */ int lsmFsSyncLog(FileSystem *pFS){ assert( pFS->fdLog ); return lsmEnvSync(pFS->pEnv, pFS->fdLog); } /* ** Read nRead bytes of data starting at offset iOff of the log file. Append ** the results to string buffer pStr. */ int lsmFsReadLog(FileSystem *pFS, i64 iOff, int nRead, LsmString *pStr){ int rc; /* Return code */ assert( pFS->fdLog ); rc = lsmStringExtend(pStr, nRead); if( rc==LSM_OK ){ rc = lsmEnvRead(pFS->pEnv, pFS->fdLog, iOff, &pStr->z[pStr->n], nRead); pStr->n += nRead; } return rc; } /* ** Truncate the log file to nByte bytes in size. */ int lsmFsTruncateLog(FileSystem *pFS, i64 nByte){ if( pFS->fdLog==0 ) return LSM_OK; return lsmEnvTruncate(pFS->pEnv, pFS->fdLog, nByte); } /* ** Truncate the db file to nByte bytes in size. */ int lsmFsTruncateDb(FileSystem *pFS, i64 nByte){ if( pFS->fdDb==0 ) return LSM_OK; return lsmEnvTruncate(pFS->pEnv, pFS->fdDb, nByte); } /* ** Close the log file. Then delete it from the file-system. This function ** is called during database shutdown only. */ int lsmFsCloseAndDeleteLog(FileSystem *pFS){ char *zDel; if( pFS->fdLog ){ lsmEnvClose(pFS->pEnv, pFS->fdLog ); pFS->fdLog = 0; } zDel = lsmMallocPrintf(pFS->pEnv, "%s-log", pFS->zDb); if( zDel ){ lsmEnvUnlink(pFS->pEnv, zDel); lsmFree(pFS->pEnv, zDel); } return LSM_OK; } /* ** Return true if page iReal of the database should be accessed using mmap. ** False otherwise. */ static int fsMmapPage(FileSystem *pFS, Pgno iReal){ return ((i64)iReal*pFS->nPagesize <= pFS->nMapLimit); } /* ** Given that there are currently nHash slots in the hash table, return ** the hash key for file iFile, page iPg. */ static int fsHashKey(int nHash, Pgno iPg){ return (iPg % nHash); } /* ** This is a helper function for lsmFsOpen(). It opens a single file on ** disk (either the database or log file). */ static lsm_file *fsOpenFile( FileSystem *pFS, /* File system object */ int bReadonly, /* True to open this file read-only */ int bLog, /* True for log, false for db */ int *pRc /* IN/OUT: Error code */ ){ lsm_file *pFile = 0; if( *pRc==LSM_OK ){ int flags = (bReadonly ? LSM_OPEN_READONLY : 0); const char *zPath = (bLog ? pFS->zLog : pFS->zDb); *pRc = lsmEnvOpen(pFS->pEnv, zPath, flags, &pFile); } return pFile; } /* ** If it is not already open, this function opens the log file. It returns ** LSM_OK if successful (or if the log file was already open) or an LSM ** error code otherwise. ** ** The log file must be opened before any of the following may be called: ** ** lsmFsWriteLog ** lsmFsSyncLog ** lsmFsReadLog */ int lsmFsOpenLog(lsm_db *db, int *pbOpen){ int rc = LSM_OK; FileSystem *pFS = db->pFS; if( 0==pFS->fdLog ){ pFS->fdLog = fsOpenFile(pFS, db->bReadonly, 1, &rc); if( rc==LSM_IOERR_NOENT && db->bReadonly ){ rc = LSM_OK; } } if( pbOpen ) *pbOpen = (pFS->fdLog!=0); return rc; } /* ** Close the log file, if it is open. */ void lsmFsCloseLog(lsm_db *db){ FileSystem *pFS = db->pFS; if( pFS->fdLog ){ lsmEnvClose(pFS->pEnv, pFS->fdLog); pFS->fdLog = 0; } } /* ** Open a connection to a database stored within the file-system. ** ** If parameter bReadonly is true, then open a read-only file-descriptor ** on the database file. It is possible that bReadonly will be false even ** if the user requested that pDb be opened read-only. This is because the ** file-descriptor may later on be recycled by a read-write connection. ** If the db file can be opened for read-write access, it always is. Parameter ** bReadonly is only ever true if it has already been determined that the ** db can only be opened for read-only access. ** ** Return LSM_OK if successful or an lsm error code otherwise. */ int lsmFsOpen( lsm_db *pDb, /* Database connection to open fd for */ const char *zDb, /* Full path to database file */ int bReadonly /* True to open db file read-only */ ){ FileSystem *pFS; int rc = LSM_OK; int nDb = strlen(zDb); int nByte; assert( pDb->pFS==0 ); assert( pDb->pWorker==0 && pDb->pClient==0 ); nByte = sizeof(FileSystem) + nDb+1 + nDb+4+1; pFS = (FileSystem *)lsmMallocZeroRc(pDb->pEnv, nByte, &rc); if( pFS ){ LsmFile *pLsmFile; pFS->zDb = (char *)&pFS[1]; pFS->zLog = &pFS->zDb[nDb+1]; pFS->nPagesize = LSM_DFLT_PAGE_SIZE; pFS->nBlocksize = LSM_DFLT_BLOCK_SIZE; pFS->nMetasize = LSM_META_PAGE_SIZE; pFS->nMetaRwSize = LSM_META_RW_PAGE_SIZE; pFS->pDb = pDb; pFS->pEnv = pDb->pEnv; /* Make a copy of the database and log file names. */ memcpy(pFS->zDb, zDb, nDb+1); memcpy(pFS->zLog, zDb, nDb); memcpy(&pFS->zLog[nDb], "-log", 5); /* Allocate the hash-table here. At some point, it should be changed ** so that it can grow dynamicly. */ pFS->nCacheMax = 2048*1024 / pFS->nPagesize; pFS->nHash = 4096; pFS->apHash = lsmMallocZeroRc(pDb->pEnv, sizeof(Page *) * pFS->nHash, &rc); /* Open the database file */ pLsmFile = lsmDbRecycleFd(pDb); if( pLsmFile ){ pFS->pLsmFile = pLsmFile; pFS->fdDb = pLsmFile->pFile; memset(pLsmFile, 0, sizeof(LsmFile)); }else{ pFS->pLsmFile = lsmMallocZeroRc(pDb->pEnv, sizeof(LsmFile), &rc); if( rc==LSM_OK ){ pFS->fdDb = fsOpenFile(pFS, bReadonly, 0, &rc); } } if( rc!=LSM_OK ){ lsmFsClose(pFS); pFS = 0; }else{ pFS->szSector = lsmEnvSectorSize(pFS->pEnv, pFS->fdDb); } } pDb->pFS = pFS; return rc; } /* ** Configure the file-system object according to the current values of ** the LSM_CONFIG_MMAP and LSM_CONFIG_SET_COMPRESSION options. */ int lsmFsConfigure(lsm_db *db){ FileSystem *pFS = db->pFS; if( pFS ){ lsm_env *pEnv = pFS->pEnv; Page *pPg; assert( pFS->nOut==0 ); assert( pFS->pWaiting==0 ); assert( pFS->pMapped==0 ); /* Reset any compression/decompression buffers already allocated */ lsmFree(pEnv, pFS->aIBuffer); lsmFree(pEnv, pFS->aOBuffer); pFS->nBuffer = 0; /* Unmap the file, if it is currently mapped */ if( pFS->pMap ){ lsmEnvRemap(pEnv, pFS->fdDb, -1, &pFS->pMap, &pFS->nMap); pFS->nMapLimit = 0; } /* Free all allocated page structures */ pPg = pFS->pLruFirst; while( pPg ){ Page *pNext = pPg->pLruNext; assert( pPg->flags & PAGE_FREE ); lsmFree(pEnv, pPg->aData); lsmFree(pEnv, pPg); pPg = pNext; } pPg = pFS->pFree; while( pPg ){ Page *pNext = pPg->pFreeNext; lsmFree(pEnv, pPg); pPg = pNext; } /* Zero pointers that point to deleted page objects */ pFS->nCacheAlloc = 0; pFS->pLruFirst = 0; pFS->pLruLast = 0; pFS->pFree = 0; if( pFS->apHash ){ memset(pFS->apHash, 0, pFS->nHash*sizeof(pFS->apHash[0])); } /* Configure the FileSystem object */ if( db->compress.xCompress ){ pFS->pCompress = &db->compress; pFS->nMapLimit = 0; }else{ pFS->pCompress = 0; if( db->iMmap==1 ){ /* Unlimited */ pFS->nMapLimit = (i64)1 << 60; }else{ /* iMmap is a limit in KB. Set nMapLimit to the same value in bytes. */ pFS->nMapLimit = (i64)db->iMmap * 1024; } } } return LSM_OK; } /* ** Close and destroy a FileSystem object. */ void lsmFsClose(FileSystem *pFS){ if( pFS ){ Page *pPg; lsm_env *pEnv = pFS->pEnv; assert( pFS->nOut==0 ); pPg = pFS->pLruFirst; while( pPg ){ Page *pNext = pPg->pLruNext; if( pPg->flags & PAGE_FREE ) lsmFree(pEnv, pPg->aData); lsmFree(pEnv, pPg); pPg = pNext; } pPg = pFS->pFree; while( pPg ){ Page *pNext = pPg->pFreeNext; if( pPg->flags & PAGE_FREE ) lsmFree(pEnv, pPg->aData); lsmFree(pEnv, pPg); pPg = pNext; } if( pFS->fdDb ) lsmEnvClose(pFS->pEnv, pFS->fdDb ); if( pFS->fdLog ) lsmEnvClose(pFS->pEnv, pFS->fdLog ); lsmFree(pEnv, pFS->pLsmFile); lsmFree(pEnv, pFS->apHash); lsmFree(pEnv, pFS->aIBuffer); lsmFree(pEnv, pFS->aOBuffer); lsmFree(pEnv, pFS); } } /* ** This function is called when closing a database handle (i.e. lsm_close()) ** if there exist other connections to the same database within this process. ** In that case the file-descriptor open on the database file is not closed ** when the FileSystem object is destroyed, as this would cause any POSIX ** locks held by the other connections to be silently dropped (see "man close" ** for details). Instead, the file-descriptor is stored in a list by the ** lsm_shared.c module until it is either closed or reused. ** ** This function returns a pointer to an object that can be linked into ** the list described above. The returned object now 'owns' the database ** file descriptr, so that when the FileSystem object is destroyed, it ** will not be closed. ** ** This function may be called at most once in the life-time of a ** FileSystem object. The results of any operations involving the database ** file descriptor are undefined once this function has been called. ** ** None of this is necessary on non-POSIX systems. But we do it anyway in ** the name of using as similar code as possible on all platforms. */ LsmFile *lsmFsDeferClose(FileSystem *pFS){ LsmFile *p = pFS->pLsmFile; assert( p->pNext==0 ); p->pFile = pFS->fdDb; pFS->fdDb = 0; pFS->pLsmFile = 0; return p; } /* ** Allocate a buffer and populate it with the output of the xFileid() ** method of the database file handle. If successful, set *ppId to point ** to the buffer and *pnId to the number of bytes in the buffer and return ** LSM_OK. Otherwise, set *ppId and *pnId to zero and return an LSM ** error code. */ int lsmFsFileid(lsm_db *pDb, void **ppId, int *pnId){ lsm_env *pEnv = pDb->pEnv; FileSystem *pFS = pDb->pFS; int rc; int nId = 0; void *pId; rc = pEnv->xFileid(pFS->fdDb, 0, &nId); pId = lsmMallocZeroRc(pEnv, nId, &rc); if( rc==LSM_OK ) rc = pEnv->xFileid(pFS->fdDb, pId, &nId); if( rc!=LSM_OK ){ lsmFree(pEnv, pId); pId = 0; nId = 0; } *ppId = pId; *pnId = nId; return rc; } /* ** Return the nominal page-size used by this file-system. Actual pages ** may be smaller or larger than this value. */ int lsmFsPageSize(FileSystem *pFS){ return pFS->nPagesize; } /* ** Return the block-size used by this file-system. */ int lsmFsBlockSize(FileSystem *pFS){ return pFS->nBlocksize; } /* ** Configure the nominal page-size used by this file-system. Actual ** pages may be smaller or larger than this value. */ void lsmFsSetPageSize(FileSystem *pFS, int nPgsz){ pFS->nPagesize = nPgsz; pFS->nCacheMax = 2048*1024 / pFS->nPagesize; } /* ** Configure the block-size used by this file-system. */ void lsmFsSetBlockSize(FileSystem *pFS, int nBlocksize){ pFS->nBlocksize = nBlocksize; } /* ** Return the page number of the first page on block iBlock. Blocks are ** numbered starting from 1. ** ** For a compressed database, page numbers are byte offsets. The first ** page on each block is the byte offset immediately following the 4-byte ** "previous block" pointer at the start of each block. */ static Pgno fsFirstPageOnBlock(FileSystem *pFS, int iBlock){ Pgno iPg; if( pFS->pCompress ){ if( iBlock==1 ){ iPg = pFS->nMetasize * 2 + 4; }else{ iPg = pFS->nBlocksize * (Pgno)(iBlock-1) + 4; } }else{ const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize); if( iBlock==1 ){ iPg = 1 + ((pFS->nMetasize*2 + pFS->nPagesize - 1) / pFS->nPagesize); }else{ iPg = 1 + (iBlock-1) * nPagePerBlock; } } return iPg; } /* ** Return the page number of the last page on block iBlock. Blocks are ** numbered starting from 1. ** ** For a compressed database, page numbers are byte offsets. The first ** page on each block is the byte offset of the byte immediately before ** the 4-byte "next block" pointer at the end of each block. */ static Pgno fsLastPageOnBlock(FileSystem *pFS, int iBlock){ if( pFS->pCompress ){ return pFS->nBlocksize * (Pgno)iBlock - 1 - 4; }else{ const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize); return iBlock * nPagePerBlock; } } /* ** Return the block number of the block that page iPg is located on. ** Blocks are numbered starting from 1. */ static int fsPageToBlock(FileSystem *pFS, Pgno iPg){ if( pFS->pCompress ){ return (int)((iPg / pFS->nBlocksize) + 1); }else{ return (int)(1 + ((iPg-1) / (pFS->nBlocksize / pFS->nPagesize))); } } /* ** Return true if page iPg is the last page on its block. ** ** This function is only called in non-compressed database mode. */ static int fsIsLast(FileSystem *pFS, Pgno iPg){ const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize); assert( !pFS->pCompress ); return ( iPg && (iPg % nPagePerBlock)==0 ); } /* ** Return true if page iPg is the first page on its block. ** ** This function is only called in non-compressed database mode. */ static int fsIsFirst(FileSystem *pFS, Pgno iPg){ const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize); assert( !pFS->pCompress ); return ( (iPg % nPagePerBlock)==1 || (iPg<nPagePerBlock && iPg==fsFirstPageOnBlock(pFS, 1)) ); } /* ** Given a page reference, return a pointer to the buffer containing the ** pages contents. If parameter pnData is not NULL, set *pnData to the size ** of the buffer in bytes before returning. */ u8 *lsmFsPageData(Page *pPage, int *pnData){ if( pnData ){ *pnData = pPage->nData; } return pPage->aData; } /* ** Return the page number of a page. */ Pgno lsmFsPageNumber(Page *pPage){ /* assert( (pPage->flags & PAGE_DIRTY)==0 ); */ return pPage ? pPage->iPg : 0; } /* ** Page pPg is currently part of the LRU list belonging to pFS. Remove ** it from the list. pPg->pLruNext and pPg->pLruPrev are cleared by this ** operation. */ static void fsPageRemoveFromLru(FileSystem *pFS, Page *pPg){ assert( pPg->pLruNext || pPg==pFS->pLruLast ); assert( pPg->pLruPrev || pPg==pFS->pLruFirst ); if( pPg->pLruNext ){ pPg->pLruNext->pLruPrev = pPg->pLruPrev; }else{ pFS->pLruLast = pPg->pLruPrev; } if( pPg->pLruPrev ){ pPg->pLruPrev->pLruNext = pPg->pLruNext; }else{ pFS->pLruFirst = pPg->pLruNext; } pPg->pLruPrev = 0; pPg->pLruNext = 0; } /* ** Page pPg is not currently part of the LRU list belonging to pFS. Add it. */ static void fsPageAddToLru(FileSystem *pFS, Page *pPg){ assert( pPg->pLruNext==0 && pPg->pLruPrev==0 ); pPg->pLruPrev = pFS->pLruLast; if( pPg->pLruPrev ){ pPg->pLruPrev->pLruNext = pPg; }else{ pFS->pLruFirst = pPg; } pFS->pLruLast = pPg; } /* ** Page pPg is currently stored in the apHash/nHash hash table. Remove it. */ static void fsPageRemoveFromHash(FileSystem *pFS, Page *pPg){ int iHash; Page **pp; iHash = fsHashKey(pFS->nHash, pPg->iPg); for(pp=&pFS->apHash[iHash]; *pp!=pPg; pp=&(*pp)->pHashNext); *pp = pPg->pHashNext; pPg->pHashNext = 0; } /* ** Free a Page object allocated by fsPageBuffer(). */ static void fsPageBufferFree(Page *pPg){ pPg->pFS->nCacheAlloc--; lsmFree(pPg->pFS->pEnv, pPg->aData); lsmFree(pPg->pFS->pEnv, pPg); } /* ** Purge the cache of all non-mmap pages with nRef==0. */ void lsmFsPurgeCache(FileSystem *pFS){ Page *pPg; pPg = pFS->pLruFirst; while( pPg ){ Page *pNext = pPg->pLruNext; assert( pPg->flags & PAGE_FREE ); fsPageRemoveFromHash(pFS, pPg); fsPageBufferFree(pPg); pPg = pNext; } pFS->pLruFirst = 0; pFS->pLruLast = 0; assert( pFS->nCacheAlloc<=pFS->nOut && pFS->nCacheAlloc>=0 ); } /* ** Search the hash-table for page iPg. If an entry is round, return a pointer ** to it. Otherwise, return NULL. ** ** Either way, if argument piHash is not NULL set *piHash to the hash slot ** number that page iPg would be stored in before returning. */ static Page *fsPageFindInHash(FileSystem *pFS, Pgno iPg, int *piHash){ Page *p; /* Return value */ int iHash = fsHashKey(pFS->nHash, iPg); if( piHash ) *piHash = iHash; for(p=pFS->apHash[iHash]; p; p=p->pHashNext){ if( p->iPg==iPg) break; } return p; } /* ** Allocate and return a non-mmap Page object. If there are already ** nCacheMax such Page objects outstanding, try to recycle an existing ** Page instead. */ static int fsPageBuffer( FileSystem *pFS, Page **ppOut ){ int rc = LSM_OK; Page *pPage = 0; if( pFS->pLruFirst==0 || pFS->nCacheAlloc<pFS->nCacheMax ){ /* Allocate a new Page object */ pPage = lsmMallocZero(pFS->pEnv, sizeof(Page)); if( !pPage ){ rc = LSM_NOMEM_BKPT; }else{ pPage->aData = (u8 *)lsmMalloc(pFS->pEnv, pFS->nPagesize); if( !pPage->aData ){ lsmFree(pFS->pEnv, pPage); rc = LSM_NOMEM_BKPT; pPage = 0; }else{ pFS->nCacheAlloc++; } } }else{ /* Reuse an existing Page object */ u8 *aData; pPage = pFS->pLruFirst; aData = pPage->aData; fsPageRemoveFromLru(pFS, pPage); fsPageRemoveFromHash(pFS, pPage); memset(pPage, 0, sizeof(Page)); pPage->aData = aData; } if( pPage ){ pPage->flags = PAGE_FREE; } *ppOut = pPage; return rc; } /* ** Assuming *pRc is initially LSM_OK, attempt to ensure that the ** memory-mapped region is at least iSz bytes in size. If it is not already, ** iSz bytes in size, extend it and update the pointers associated with any ** outstanding Page objects. ** ** If *pRc is not LSM_OK when this function is called, it is a no-op. ** Otherwise, *pRc is set to an lsm error code if an error occurs, or ** left unmodified otherwise. ** ** This function is never called in compressed database mode. */ static void fsGrowMapping( FileSystem *pFS, /* File system object */ i64 iSz, /* Minimum size to extend mapping to */ int *pRc /* IN/OUT: Error code */ ){ assert( pFS->pCompress==0 ); assert( PAGE_HASPREV==4 ); if( *pRc==LSM_OK && iSz>pFS->nMap ){ int rc; u8 *aOld = pFS->pMap; rc = lsmEnvRemap(pFS->pEnv, pFS->fdDb, iSz, &pFS->pMap, &pFS->nMap); if( rc==LSM_OK && pFS->pMap!=aOld ){ Page *pFix; i64 iOff = (u8 *)pFS->pMap - aOld; for(pFix=pFS->pMapped; pFix; pFix=pFix->pMappedNext){ pFix->aData += iOff; } lsmSortedRemap(pFS->pDb); } *pRc = rc; } } /* ** If it is mapped, unmap the database file. */ int lsmFsUnmap(FileSystem *pFS){ int rc = LSM_OK; if( pFS ){ rc = lsmEnvRemap(pFS->pEnv, pFS->fdDb, -1, &pFS->pMap, &pFS->nMap); } return rc; } /* ** fsync() the database file. */ int lsmFsSyncDb(FileSystem *pFS, int nBlock){ return lsmEnvSync(pFS->pEnv, pFS->fdDb); } /* ** If block iBlk has been redirected according to the redirections in the ** object passed as the first argument, return the destination block to ** which it is redirected. Otherwise, return a copy of iBlk. */ static int fsRedirectBlock(Redirect *p, int iBlk){ if( p ){ int i; for(i=0; i<p->n; i++){ if( iBlk==p->a[i].iFrom ) return p->a[i].iTo; } } assert( iBlk!=0 ); return iBlk; } /* ** If page iPg has been redirected according to the redirections in the ** object passed as the second argument, return the destination page to ** which it is redirected. Otherwise, return a copy of iPg. */ Pgno lsmFsRedirectPage(FileSystem *pFS, Redirect *pRedir, Pgno iPg){ Pgno iReal = iPg; if( pRedir ){ const int nPagePerBlock = ( pFS->pCompress ? pFS->nBlocksize : (pFS->nBlocksize / pFS->nPagesize) ); int iBlk = fsPageToBlock(pFS, iPg); int i; for(i=0; i<pRedir->n; i++){ int iFrom = pRedir->a[i].iFrom; if( iFrom>iBlk ) break; if( iFrom==iBlk ){ int iTo = pRedir->a[i].iTo; iReal = iPg - (Pgno)(iFrom - iTo) * nPagePerBlock; if( iTo==1 ){ iReal += (fsFirstPageOnBlock(pFS, 1)-1); } break; } } } assert( iReal!=0 ); return iReal; } /* Required by the circular fsBlockNext<->fsPageGet dependency. */ static int fsPageGet(FileSystem *, Segment *, Pgno, int, Page **, int *); /* ** Parameter iBlock is a database file block. This function reads the value ** stored in the blocks "next block" pointer and stores it in *piNext. ** LSM_OK is returned if everything is successful, or an LSM error code ** otherwise. */ static int fsBlockNext( FileSystem *pFS, /* File-system object handle */ Segment *pSeg, /* Use this segment for block redirects */ int iBlock, /* Read field from this block */ int *piNext /* OUT: Next block in linked list */ ){ int rc; int iRead; /* Read block from here */ if( pSeg ){ iRead = fsRedirectBlock(pSeg->pRedirect, iBlock); }else{ iRead = iBlock; } assert( pFS->nMapLimit==0 || pFS->pCompress==0 ); if( pFS->pCompress ){ i64 iOff; /* File offset to read data from */ u8 aNext[4]; /* 4-byte pointer read from db file */ iOff = (i64)iRead * pFS->nBlocksize - sizeof(aNext); rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, aNext, sizeof(aNext)); if( rc==LSM_OK ){ *piNext = (int)lsmGetU32(aNext); } }else{ const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize); Page *pLast; rc = fsPageGet(pFS, 0, iRead*nPagePerBlock, 0, &pLast, 0); if( rc==LSM_OK ){ *piNext = lsmGetU32(&pLast->aData[pFS->nPagesize-4]); lsmFsPageRelease(pLast); } } if( pSeg ){ *piNext = fsRedirectBlock(pSeg->pRedirect, *piNext); } return rc; } /* ** Return the page number of the last page on the same block as page iPg. */ Pgno fsLastPageOnPagesBlock(FileSystem *pFS, Pgno iPg){ return fsLastPageOnBlock(pFS, fsPageToBlock(pFS, iPg)); } /* ** Read nData bytes of data from offset iOff of the database file into ** buffer aData. If this means reading past the end of a block, follow ** the block pointer to the next block and continue reading. ** ** Offset iOff is an absolute offset - not subject to any block redirection. ** However any block pointer followed is. Use pSeg->pRedirect in this case. ** ** This function is only called in compressed database mode. */ static int fsReadData( FileSystem *pFS, /* File-system handle */ Segment *pSeg, /* Block redirection */ i64 iOff, /* Read data from this offset */ u8 *aData, /* Buffer to read data into */ int nData /* Number of bytes to read */ ){ i64 iEob; /* End of block */ int nRead; int rc; assert( pFS->pCompress ); iEob = fsLastPageOnPagesBlock(pFS, iOff) + 1; nRead = (int)LSM_MIN(iEob - iOff, nData); rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, aData, nRead); if( rc==LSM_OK && nRead!=nData ){ int iBlk; rc = fsBlockNext(pFS, pSeg, fsPageToBlock(pFS, iOff), &iBlk); if( rc==LSM_OK ){ i64 iOff2 = fsFirstPageOnBlock(pFS, iBlk); rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff2, &aData[nRead], nData-nRead); } } return rc; } /* ** Parameter iBlock is a database file block. This function reads the value ** stored in the blocks "previous block" pointer and stores it in *piPrev. ** LSM_OK is returned if everything is successful, or an LSM error code ** otherwise. */ static int fsBlockPrev( FileSystem *pFS, /* File-system object handle */ Segment *pSeg, /* Use this segment for block redirects */ int iBlock, /* Read field from this block */ int *piPrev /* OUT: Previous block in linked list */ ){ int rc = LSM_OK; /* Return code */ assert( pFS->nMapLimit==0 || pFS->pCompress==0 ); assert( iBlock>0 ); if( pFS->pCompress ){ i64 iOff = fsFirstPageOnBlock(pFS, iBlock) - 4; u8 aPrev[4]; /* 4-byte pointer read from db file */ rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, aPrev, sizeof(aPrev)); if( rc==LSM_OK ){ Redirect *pRedir = (pSeg ? pSeg->pRedirect : 0); *piPrev = fsRedirectBlock(pRedir, (int)lsmGetU32(aPrev)); } }else{ assert( 0 ); } return rc; } /* ** Encode and decode routines for record size fields. */ static void putRecordSize(u8 *aBuf, int nByte, int bFree){ aBuf[0] = (u8)(nByte >> 14) | 0x80; aBuf[1] = ((u8)(nByte >> 7) & 0x7F) | (bFree ? 0x00 : 0x80); aBuf[2] = (u8)nByte | 0x80; } static int getRecordSize(u8 *aBuf, int *pbFree){ int nByte; nByte = (aBuf[0] & 0x7F) << 14; nByte += (aBuf[1] & 0x7F) << 7; nByte += (aBuf[2] & 0x7F); *pbFree = !(aBuf[1] & 0x80); return nByte; } /* ** Subtract iSub from database file offset iOff and set *piRes to the ** result. If doing so means passing the start of a block, follow the ** block pointer stored in the first 4 bytes of the block. ** ** Offset iOff is an absolute offset - not subject to any block redirection. ** However any block pointer followed is. Use pSeg->pRedirect in this case. ** ** Return LSM_OK if successful or an lsm error code if an error occurs. */ static int fsSubtractOffset( FileSystem *pFS, Segment *pSeg, i64 iOff, int iSub, i64 *piRes ){ i64 iStart; int iBlk = 0; int rc; assert( pFS->pCompress ); iStart = fsFirstPageOnBlock(pFS, fsPageToBlock(pFS, iOff)); if( (iOff-iSub)>=iStart ){ *piRes = (iOff-iSub); return LSM_OK; } rc = fsBlockPrev(pFS, pSeg, fsPageToBlock(pFS, iOff), &iBlk); *piRes = fsLastPageOnBlock(pFS, iBlk) - iSub + (iOff - iStart + 1); return rc; } /* ** Add iAdd to database file offset iOff and set *piRes to the ** result. If doing so means passing the end of a block, follow the ** block pointer stored in the last 4 bytes of the block. ** ** Offset iOff is an absolute offset - not subject to any block redirection. ** However any block pointer followed is. Use pSeg->pRedirect in this case. ** ** Return LSM_OK if successful or an lsm error code if an error occurs. */ static int fsAddOffset( FileSystem *pFS, Segment *pSeg, i64 iOff, int iAdd, i64 *piRes ){ i64 iEob; int iBlk; int rc; assert( pFS->pCompress ); iEob = fsLastPageOnPagesBlock(pFS, iOff); if( (iOff+iAdd)<=iEob ){ *piRes = (iOff+iAdd); return LSM_OK; } rc = fsBlockNext(pFS, pSeg, fsPageToBlock(pFS, iOff), &iBlk); *piRes = fsFirstPageOnBlock(pFS, iBlk) + iAdd - (iEob - iOff + 1); return rc; } /* ** If it is not already allocated, allocate either the FileSystem.aOBuffer (if ** bWrite is true) or the FileSystem.aIBuffer (if bWrite is false). Return ** LSM_OK if successful if the attempt to allocate memory fails. */ static int fsAllocateBuffer(FileSystem *pFS, int bWrite){ u8 **pp; /* Pointer to either aIBuffer or aOBuffer */ assert( pFS->pCompress ); /* If neither buffer has been allocated, figure out how large they ** should be. Store this value in FileSystem.nBuffer. */ if( pFS->nBuffer==0 ){ assert( pFS->aIBuffer==0 && pFS->aOBuffer==0 ); pFS->nBuffer = pFS->pCompress->xBound(pFS->pCompress->pCtx, pFS->nPagesize); if( pFS->nBuffer<(pFS->szSector+6) ){ pFS->nBuffer = pFS->szSector+6; } } pp = (bWrite ? &pFS->aOBuffer : &pFS->aIBuffer); if( *pp==0 ){ *pp = lsmMalloc(pFS->pEnv, LSM_MAX(pFS->nBuffer, pFS->nPagesize)); if( *pp==0 ) return LSM_NOMEM_BKPT; } return LSM_OK; } /* ** This function is only called in compressed database mode. It reads and ** uncompresses the compressed data for page pPg from the database and ** populates the pPg->aData[] buffer and pPg->nCompress field. ** ** It is possible that instead of a page record, there is free space ** at offset pPg->iPgno. In this case no data is read from the file, but ** output variable *pnSpace is set to the total number of free bytes. ** ** LSM_OK is returned if successful, or an LSM error code otherwise. */ static int fsReadPagedata( FileSystem *pFS, /* File-system handle */ Segment *pSeg, /* pPg is part of this segment */ Page *pPg, /* Page to read and uncompress data for */ int *pnSpace /* OUT: Total bytes of free space */ ){ lsm_compress *p = pFS->pCompress; i64 iOff = pPg->iPg; u8 aSz[3]; int rc; assert( p && pPg->nCompress==0 ); if( fsAllocateBuffer(pFS, 0) ) return LSM_NOMEM; rc = fsReadData(pFS, pSeg, iOff, aSz, sizeof(aSz)); if( rc==LSM_OK ){ int bFree; if( aSz[0] & 0x80 ){ pPg->nCompress = (int)getRecordSize(aSz, &bFree); }else{ pPg->nCompress = (int)aSz[0] - sizeof(aSz)*2; bFree = 1; } if( bFree ){ if( pnSpace ){ *pnSpace = pPg->nCompress + sizeof(aSz)*2; }else{ rc = LSM_CORRUPT_BKPT; } }else{ rc = fsAddOffset(pFS, pSeg, iOff, 3, &iOff); if( rc==LSM_OK ){ if( pPg->nCompress>pFS->nBuffer ){ rc = LSM_CORRUPT_BKPT; }else{ rc = fsReadData(pFS, pSeg, iOff, pFS->aIBuffer, pPg->nCompress); } if( rc==LSM_OK ){ int n = pFS->nPagesize; rc = p->xUncompress(p->pCtx, (char *)pPg->aData, &n, (const char *)pFS->aIBuffer, pPg->nCompress ); if( rc==LSM_OK && n!=pPg->pFS->nPagesize ){ rc = LSM_CORRUPT_BKPT; } } } } } return rc; } /* ** Return a handle for a database page. ** ** If this file-system object is accessing a compressed database it may be ** that there is no page record at database file offset iPg. Instead, there ** may be a free space record. In this case, set *ppPg to NULL and *pnSpace ** to the total number of free bytes before returning. ** ** If no error occurs, LSM_OK is returned. Otherwise, an lsm error code. */ static int fsPageGet( FileSystem *pFS, /* File-system handle */ Segment *pSeg, /* Block redirection to use (or NULL) */ Pgno iPg, /* Page id */ int noContent, /* True to not load content from disk */ Page **ppPg, /* OUT: New page handle */ int *pnSpace /* OUT: Bytes of free space */ ){ Page *p; int iHash; int rc = LSM_OK; /* In most cases iReal is the same as iPg. Except, if pSeg->pRedirect is ** not NULL, and the block containing iPg has been redirected, then iReal ** is the page number after redirection. */ Pgno iReal = lsmFsRedirectPage(pFS, (pSeg ? pSeg->pRedirect : 0), iPg); assert_lists_are_ok(pFS); assert( iPg>=fsFirstPageOnBlock(pFS, 1) ); assert( iReal>=fsFirstPageOnBlock(pFS, 1) ); *ppPg = 0; /* Search the hash-table for the page */ p = fsPageFindInHash(pFS, iReal, &iHash); if( p ){ assert( p->flags & PAGE_FREE ); if( p->nRef==0 ) fsPageRemoveFromLru(pFS, p); }else{ if( fsMmapPage(pFS, iReal) ){ i64 iEnd = (i64)iReal * pFS->nPagesize; fsGrowMapping(pFS, iEnd, &rc); if( rc!=LSM_OK ) return rc; if( pFS->pFree ){ p = pFS->pFree; pFS->pFree = p->pFreeNext; assert( p->nRef==0 ); }else{ p = lsmMallocZeroRc(pFS->pEnv, sizeof(Page), &rc); if( rc ) return rc; p->pFS = pFS; } p->aData = &((u8 *)pFS->pMap)[pFS->nPagesize * (iReal-1)]; p->iPg = iReal; /* This page now carries a pointer to the mapping. Link it in to ** the FileSystem.pMapped list. */ assert( p->pMappedNext==0 ); p->pMappedNext = pFS->pMapped; pFS->pMapped = p; assert( pFS->pCompress==0 ); assert( (p->flags & PAGE_FREE)==0 ); }else{ rc = fsPageBuffer(pFS, &p); if( rc==LSM_OK ){ int nSpace = 0; p->iPg = iReal; p->nRef = 0; p->pFS = pFS; assert( p->flags==0 || p->flags==PAGE_FREE ); #ifdef LSM_DEBUG memset(p->aData, 0x56, pFS->nPagesize); #endif assert( p->pLruNext==0 && p->pLruPrev==0 ); if( noContent==0 ){ if( pFS->pCompress ){ rc = fsReadPagedata(pFS, pSeg, p, &nSpace); }else{ int nByte = pFS->nPagesize; i64 iOff = (i64)(iReal-1) * pFS->nPagesize; rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, p->aData, nByte); } pFS->nRead++; } /* If the xRead() call was successful (or not attempted), link the ** page into the page-cache hash-table. Otherwise, if it failed, ** free the buffer. */ if( rc==LSM_OK && nSpace==0 ){ p->pHashNext = pFS->apHash[iHash]; pFS->apHash[iHash] = p; }else{ fsPageBufferFree(p); p = 0; if( pnSpace ) *pnSpace = nSpace; } } } assert( (rc==LSM_OK && (p || (pnSpace && *pnSpace))) || (rc!=LSM_OK && p==0) ); } if( rc==LSM_OK && p ){ if( pFS->pCompress==0 && (fsIsLast(pFS, iReal) || fsIsFirst(pFS, iReal)) ){ p->nData = pFS->nPagesize - 4; if( fsIsFirst(pFS, iReal) && p->nRef==0 ){ p->aData += 4; p->flags |= PAGE_HASPREV; } }else{ p->nData = pFS->nPagesize; } pFS->nOut += (p->nRef==0); p->nRef++; } *ppPg = p; return rc; } /* ** Read the 64-bit checkpoint id of the checkpoint currently stored on meta ** page iMeta of the database file. If no error occurs, store the id value ** in *piVal and return LSM_OK. Otherwise, return an LSM error code and leave ** *piVal unmodified. ** ** If a checkpointer connection is currently updating meta-page iMeta, or an ** earlier checkpointer crashed while doing so, the value read into *piVal ** may be garbage. It is the callers responsibility to deal with this. */ int lsmFsReadSyncedId(lsm_db *db, int iMeta, i64 *piVal){ FileSystem *pFS = db->pFS; int rc = LSM_OK; assert( iMeta==1 || iMeta==2 ); if( pFS->nMapLimit>0 ){ fsGrowMapping(pFS, iMeta*LSM_META_PAGE_SIZE, &rc); if( rc==LSM_OK ){ *piVal = (i64)lsmGetU64(&((u8 *)pFS->pMap)[(iMeta-1)*LSM_META_PAGE_SIZE]); } }else{ MetaPage *pMeta = 0; rc = lsmFsMetaPageGet(pFS, 0, iMeta, &pMeta); if( rc==LSM_OK ){ *piVal = (i64)lsmGetU64(pMeta->aData); lsmFsMetaPageRelease(pMeta); } } return rc; } /* ** Return true if the first or last page of segment pRun falls between iFirst ** and iLast, inclusive, and pRun is not equal to pIgnore. */ static int fsRunEndsBetween( Segment *pRun, Segment *pIgnore, Pgno iFirst, Pgno iLast ){ return (pRun!=pIgnore && ( (pRun->iFirst>=iFirst && pRun->iFirst<=iLast) || (pRun->iLastPg>=iFirst && pRun->iLastPg<=iLast) )); } /* ** Return true if level pLevel contains a segment other than pIgnore for ** which the first or last page is between iFirst and iLast, inclusive. */ static int fsLevelEndsBetween( Level *pLevel, Segment *pIgnore, Pgno iFirst, Pgno iLast ){ int i; if( fsRunEndsBetween(&pLevel->lhs, pIgnore, iFirst, iLast) ){ return 1; } for(i=0; i<pLevel->nRight; i++){ if( fsRunEndsBetween(&pLevel->aRhs[i], pIgnore, iFirst, iLast) ){ return 1; } } return 0; } /* ** Block iBlk is no longer in use by segment pIgnore. If it is not in use ** by any other segment, move it to the free block list. */ static int fsFreeBlock( FileSystem *pFS, /* File system object */ Snapshot *pSnapshot, /* Worker snapshot */ Segment *pIgnore, /* Ignore this run when searching */ int iBlk /* Block number of block to free */ ){ int rc = LSM_OK; /* Return code */ Pgno iFirst; /* First page on block iBlk */ Pgno iLast; /* Last page on block iBlk */ Level *pLevel; /* Used to iterate through levels */ int iIn; /* Used to iterate through append points */ int iOut = 0; /* Used to output append points */ Pgno *aApp = pSnapshot->aiAppend; iFirst = fsFirstPageOnBlock(pFS, iBlk); iLast = fsLastPageOnBlock(pFS, iBlk); /* Check if any other run in the snapshot has a start or end page ** within this block. If there is such a run, return early. */ for(pLevel=lsmDbSnapshotLevel(pSnapshot); pLevel; pLevel=pLevel->pNext){ if( fsLevelEndsBetween(pLevel, pIgnore, iFirst, iLast) ){ return LSM_OK; } } /* Remove any entries that lie on this block from the append-list. */ for(iIn=0; iIn<LSM_APPLIST_SZ; iIn++){ if( aApp[iIn]<iFirst || aApp[iIn]>iLast ){ aApp[iOut++] = aApp[iIn]; } } while( iOut<LSM_APPLIST_SZ ) aApp[iOut++] = 0; if( rc==LSM_OK ){ rc = lsmBlockFree(pFS->pDb, iBlk); } return rc; } /* ** Delete or otherwise recycle the blocks currently occupied by run pDel. */ int lsmFsSortedDelete( FileSystem *pFS, Snapshot *pSnapshot, int bZero, /* True to zero the Segment structure */ Segment *pDel ){ if( pDel->iFirst ){ int rc = LSM_OK; int iBlk; int iLastBlk; iBlk = fsPageToBlock(pFS, pDel->iFirst); iLastBlk = fsPageToBlock(pFS, pDel->iLastPg); /* Mark all blocks currently used by this sorted run as free */ while( iBlk && rc==LSM_OK ){ int iNext = 0; if( iBlk!=iLastBlk ){ rc = fsBlockNext(pFS, pDel, iBlk, &iNext); }else if( bZero==0 && pDel->iLastPg!=fsLastPageOnBlock(pFS, iLastBlk) ){ break; } rc = fsFreeBlock(pFS, pSnapshot, pDel, iBlk); iBlk = iNext; } if( pDel->pRedirect ){ assert( pDel->pRedirect==&pSnapshot->redirect ); pSnapshot->redirect.n = 0; } if( bZero ) memset(pDel, 0, sizeof(Segment)); } return LSM_OK; } /* ** aPgno is an array containing nPgno page numbers. Return the smallest page ** number from the array that falls on block iBlk. Or, if none of the pages ** in aPgno[] fall on block iBlk, return 0. */ static Pgno firstOnBlock(FileSystem *pFS, int iBlk, Pgno *aPgno, int nPgno){ Pgno iRet = 0; int i; for(i=0; i<nPgno; i++){ Pgno iPg = aPgno[i]; if( fsPageToBlock(pFS, iPg)==iBlk && (iRet==0 || iPg<iRet) ){ iRet = iPg; } } return iRet; } #ifndef NDEBUG /* ** Return true if page iPg, which is a part of segment p, lies on ** a redirected block. */ static int fsPageRedirects(FileSystem *pFS, Segment *p, Pgno iPg){ return (iPg!=0 && iPg!=lsmFsRedirectPage(pFS, p->pRedirect, iPg)); } /* ** Return true if the second argument is not NULL and any of the first ** last or root pages lie on a redirected block. */ static int fsSegmentRedirects(FileSystem *pFS, Segment *p){ return (p && ( fsPageRedirects(pFS, p, p->iFirst) || fsPageRedirects(pFS, p, p->iRoot) || fsPageRedirects(pFS, p, p->iLastPg) )); } #endif /* ** Argument aPgno is an array of nPgno page numbers. All pages belong to ** the segment pRun. This function gobbles from the start of the run to the ** first page that appears in aPgno[] (i.e. so that the aPgno[] entry is ** the new first page of the run). */ void lsmFsGobble( lsm_db *pDb, Segment *pRun, Pgno *aPgno, int nPgno ){ int rc = LSM_OK; FileSystem *pFS = pDb->pFS; Snapshot *pSnapshot = pDb->pWorker; int iBlk; assert( pRun->nSize>0 ); assert( 0==fsSegmentRedirects(pFS, pRun) ); assert( nPgno>0 && 0==fsPageRedirects(pFS, pRun, aPgno[0]) ); iBlk = fsPageToBlock(pFS, pRun->iFirst); pRun->nSize += (int)(pRun->iFirst - fsFirstPageOnBlock(pFS, iBlk)); while( rc==LSM_OK ){ int iNext = 0; Pgno iFirst = firstOnBlock(pFS, iBlk, aPgno, nPgno); if( iFirst ){ pRun->iFirst = iFirst; break; } rc = fsBlockNext(pFS, pRun, iBlk, &iNext); if( rc==LSM_OK ) rc = fsFreeBlock(pFS, pSnapshot, pRun, iBlk); pRun->nSize -= (int)( 1 + fsLastPageOnBlock(pFS, iBlk) - fsFirstPageOnBlock(pFS, iBlk) ); iBlk = iNext; } pRun->nSize -= (int)(pRun->iFirst - fsFirstPageOnBlock(pFS, iBlk)); assert( pRun->nSize>0 ); } /* ** This function is only used in compressed database mode. ** ** Argument iPg is the page number (byte offset) of a page within segment ** pSeg. The page record, including all headers, is nByte bytes in size. ** Before returning, set *piNext to the page number of the next page in ** the segment, or to zero if iPg is the last. ** ** In other words, do: ** ** *piNext = iPg + nByte; ** ** But take block overflow and redirection into account. */ static int fsNextPageOffset( FileSystem *pFS, /* File system object */ Segment *pSeg, /* Segment to move within */ Pgno iPg, /* Offset of current page */ int nByte, /* Size of current page including headers */ Pgno *piNext /* OUT: Offset of next page. Or zero (EOF) */ ){ Pgno iNext; int rc; assert( pFS->pCompress ); rc = fsAddOffset(pFS, pSeg, iPg, nByte-1, &iNext); if( pSeg && iNext==pSeg->iLastPg ){ iNext = 0; }else if( rc==LSM_OK ){ rc = fsAddOffset(pFS, pSeg, iNext, 1, &iNext); } *piNext = iNext; return rc; } /* ** This function is only used in compressed database mode. ** ** Argument iPg is the page number of a pagethat appears in segment pSeg. ** This function determines the page number of the previous page in the ** same run. *piPrev is set to the previous page number before returning. ** ** LSM_OK is returned if no error occurs. Otherwise, an lsm error code. ** If any value other than LSM_OK is returned, then the final value of ** *piPrev is undefined. */ static int fsGetPageBefore( FileSystem *pFS, Segment *pSeg, Pgno iPg, Pgno *piPrev ){ u8 aSz[3]; int rc; i64 iRead; assert( pFS->pCompress ); rc = fsSubtractOffset(pFS, pSeg, iPg, sizeof(aSz), &iRead); if( rc==LSM_OK ) rc = fsReadData(pFS, pSeg, iRead, aSz, sizeof(aSz)); if( rc==LSM_OK ){ int bFree; int nSz; if( aSz[2] & 0x80 ){ nSz = getRecordSize(aSz, &bFree) + sizeof(aSz)*2; }else{ nSz = (int)(aSz[2] & 0x7F); bFree = 1; } rc = fsSubtractOffset(pFS, pSeg, iPg, nSz, piPrev); } return rc; } /* ** The first argument to this function is a valid reference to a database ** file page that is part of a sorted run. If parameter eDir is -1, this ** function attempts to locate and load the previous page in the same run. ** Or, if eDir is +1, it attempts to find the next page in the same run. ** The results of passing an eDir value other than positive or negative one ** are undefined. ** ** If parameter pRun is not NULL then it must point to the run that page ** pPg belongs to. In this case, if pPg is the first or last page of the ** run, and the request is for the previous or next page, respectively, ** *ppNext is set to NULL before returning LSM_OK. If pRun is NULL, then it ** is assumed that the next or previous page, as requested, exists. ** ** If the previous/next page does exist and is successfully loaded, *ppNext ** is set to point to it and LSM_OK is returned. Otherwise, if an error ** occurs, *ppNext is set to NULL and and lsm error code returned. ** ** Page references returned by this function should be released by the ** caller using lsmFsPageRelease(). */ int lsmFsDbPageNext(Segment *pRun, Page *pPg, int eDir, Page **ppNext){ int rc = LSM_OK; FileSystem *pFS = pPg->pFS; Pgno iPg = pPg->iPg; assert( 0==fsSegmentRedirects(pFS, pRun) ); if( pFS->pCompress ){ int nSpace = pPg->nCompress + 2*3; do { if( eDir>0 ){ rc = fsNextPageOffset(pFS, pRun, iPg, nSpace, &iPg); }else{ if( iPg==pRun->iFirst ){ iPg = 0; }else{ rc = fsGetPageBefore(pFS, pRun, iPg, &iPg); } } nSpace = 0; if( iPg!=0 ){ rc = fsPageGet(pFS, pRun, iPg, 0, ppNext, &nSpace); assert( (*ppNext==0)==(rc!=LSM_OK || nSpace>0) ); }else{ *ppNext = 0; } }while( nSpace>0 && rc==LSM_OK ); }else{ Redirect *pRedir = pRun ? pRun->pRedirect : 0; assert( eDir==1 || eDir==-1 ); if( eDir<0 ){ if( pRun && iPg==pRun->iFirst ){ *ppNext = 0; return LSM_OK; }else if( fsIsFirst(pFS, iPg) ){ assert( pPg->flags & PAGE_HASPREV ); iPg = fsLastPageOnBlock(pFS, lsmGetU32(&pPg->aData[-4])); }else{ iPg--; } }else{ if( pRun ){ if( iPg==pRun->iLastPg ){ *ppNext = 0; return LSM_OK; } } if( fsIsLast(pFS, iPg) ){ int iBlk = fsRedirectBlock( pRedir, lsmGetU32(&pPg->aData[pFS->nPagesize-4]) ); iPg = fsFirstPageOnBlock(pFS, iBlk); }else{ iPg++; } } rc = fsPageGet(pFS, pRun, iPg, 0, ppNext, 0); } return rc; } /* ** This function is called when creating a new segment to determine if the ** first part of it can be written following an existing segment on an ** already allocated block. If it is possible, the page number of the first ** page to use for the new segment is returned. Otherwise zero. ** ** If argument pLvl is not NULL, then this function will not attempt to ** start the new segment immediately following any segment that is part ** of the right-hand-side of pLvl. */ static Pgno findAppendPoint(FileSystem *pFS, Level *pLvl){ int i; Pgno *aiAppend = pFS->pDb->pWorker->aiAppend; Pgno iRet = 0; for(i=LSM_APPLIST_SZ-1; iRet==0 && i>=0; i--){ if( (iRet = aiAppend[i]) ){ if( pLvl ){ int iBlk = fsPageToBlock(pFS, iRet); int j; for(j=0; iRet && j<pLvl->nRight; j++){ if( fsPageToBlock(pFS, pLvl->aRhs[j].iLastPg)==iBlk ){ iRet = 0; } } } if( iRet ) aiAppend[i] = 0; } } return iRet; } /* ** Append a page to the left-hand-side of pLvl. Set the ref-count to 1 and ** return a pointer to it. The page is writable until either ** lsmFsPagePersist() is called on it or the ref-count drops to zero. */ int lsmFsSortedAppend( FileSystem *pFS, Snapshot *pSnapshot, Level *pLvl, int bDefer, Page **ppOut ){ int rc = LSM_OK; Page *pPg = 0; Pgno iApp = 0; Pgno iNext = 0; Segment *p = &pLvl->lhs; Pgno iPrev = p->iLastPg; *ppOut = 0; assert( p->pRedirect==0 ); if( pFS->pCompress || bDefer ){ /* In compressed database mode the page is not assigned a page number ** or location in the database file at this point. This will be done ** by the lsmFsPagePersist() call. */ rc = fsPageBuffer(pFS, &pPg); if( rc==LSM_OK ){ pPg->pFS = pFS; pPg->pSeg = p; pPg->iPg = 0; pPg->flags |= PAGE_DIRTY; pPg->nData = pFS->nPagesize; assert( pPg->aData ); if( pFS->pCompress==0 ) pPg->nData -= 4; pPg->nRef = 1; pFS->nOut++; } }else{ if( iPrev==0 ){ iApp = findAppendPoint(pFS, pLvl); }else if( fsIsLast(pFS, iPrev) ){ int iNext2; rc = fsBlockNext(pFS, 0, fsPageToBlock(pFS, iPrev), &iNext2); if( rc!=LSM_OK ) return rc; iApp = fsFirstPageOnBlock(pFS, iNext2); }else{ iApp = iPrev + 1; } /* If this is the first page allocated, or if the page allocated is the ** last in the block, also allocate the next block here. */ if( iApp==0 || fsIsLast(pFS, iApp) ){ int iNew; /* New block number */ rc = lsmBlockAllocate(pFS->pDb, 0, &iNew); if( rc!=LSM_OK ) return rc; if( iApp==0 ){ iApp = fsFirstPageOnBlock(pFS, iNew); }else{ iNext = fsFirstPageOnBlock(pFS, iNew); } } /* Grab the new page. */ pPg = 0; rc = fsPageGet(pFS, 0, iApp, 1, &pPg, 0); assert( rc==LSM_OK || pPg==0 ); /* If this is the first or last page of a block, fill in the pointer ** value at the end of the new page. */ if( rc==LSM_OK ){ p->nSize++; p->iLastPg = iApp; if( p->iFirst==0 ) p->iFirst = iApp; pPg->flags |= PAGE_DIRTY; if( fsIsLast(pFS, iApp) ){ lsmPutU32(&pPg->aData[pFS->nPagesize-4], fsPageToBlock(pFS, iNext)); }else if( fsIsFirst(pFS, iApp) ){ lsmPutU32(&pPg->aData[-4], fsPageToBlock(pFS, iPrev)); } } } *ppOut = pPg; return rc; } /* ** Mark the segment passed as the second argument as finished. Once a segment ** is marked as finished it is not possible to append any further pages to ** it. ** ** Return LSM_OK if successful or an lsm error code if an error occurs. */ int lsmFsSortedFinish(FileSystem *pFS, Segment *p){ int rc = LSM_OK; if( p && p->iLastPg ){ assert( p->pRedirect==0 ); /* Check if the last page of this run happens to be the last of a block. ** If it is, then an extra block has already been allocated for this run. ** Shift this extra block back to the free-block list. ** ** Otherwise, add the first free page in the last block used by the run ** to the lAppend list. */ if( fsLastPageOnPagesBlock(pFS, p->iLastPg)!=p->iLastPg ){ int i; Pgno *aiAppend = pFS->pDb->pWorker->aiAppend; for(i=0; i<LSM_APPLIST_SZ; i++){ if( aiAppend[i]==0 ){ aiAppend[i] = p->iLastPg+1; break; } } }else if( pFS->pCompress==0 ){ Page *pLast; rc = fsPageGet(pFS, 0, p->iLastPg, 0, &pLast, 0); if( rc==LSM_OK ){ int iBlk = (int)lsmGetU32(&pLast->aData[pFS->nPagesize-4]); lsmBlockRefree(pFS->pDb, iBlk); lsmFsPageRelease(pLast); } }else{ int iBlk = 0; rc = fsBlockNext(pFS, p, fsPageToBlock(pFS, p->iLastPg), &iBlk); if( rc==LSM_OK ){ lsmBlockRefree(pFS->pDb, iBlk); } } } return rc; } /* ** Obtain a reference to page number iPg. ** ** Return LSM_OK if successful, or an lsm error code if an error occurs. */ int lsmFsDbPageGet(FileSystem *pFS, Segment *pSeg, Pgno iPg, Page **ppPg){ return fsPageGet(pFS, pSeg, iPg, 0, ppPg, 0); } /* ** Obtain a reference to the last page in the segment passed as the ** second argument. ** ** Return LSM_OK if successful, or an lsm error code if an error occurs. */ int lsmFsDbPageLast(FileSystem *pFS, Segment *pSeg, Page **ppPg){ int rc; Pgno iPg = pSeg->iLastPg; if( pFS->pCompress ){ int nSpace; iPg++; do { nSpace = 0; rc = fsGetPageBefore(pFS, pSeg, iPg, &iPg); if( rc==LSM_OK ){ rc = fsPageGet(pFS, pSeg, iPg, 0, ppPg, &nSpace); } }while( rc==LSM_OK && nSpace>0 ); }else{ rc = fsPageGet(pFS, pSeg, iPg, 0, ppPg, 0); } return rc; } /* ** Return a reference to meta-page iPg. If successful, LSM_OK is returned ** and *ppPg populated with the new page reference. The reference should ** be released by the caller using lsmFsPageRelease(). ** ** Otherwise, if an error occurs, *ppPg is set to NULL and an LSM error ** code is returned. */ int lsmFsMetaPageGet( FileSystem *pFS, /* File-system connection */ int bWrite, /* True for write access, false for read */ int iPg, /* Either 1 or 2 */ MetaPage **ppPg /* OUT: Pointer to MetaPage object */ ){ int rc = LSM_OK; MetaPage *pPg; assert( iPg==1 || iPg==2 ); pPg = lsmMallocZeroRc(pFS->pEnv, sizeof(Page), &rc); if( pPg ){ i64 iOff = (iPg-1) * pFS->nMetasize; if( pFS->nMapLimit>0 ){ fsGrowMapping(pFS, 2*pFS->nMetasize, &rc); pPg->aData = (u8 *)(pFS->pMap) + iOff; }else{ pPg->aData = lsmMallocRc(pFS->pEnv, pFS->nMetasize, &rc); if( rc==LSM_OK && bWrite==0 ){ rc = lsmEnvRead( pFS->pEnv, pFS->fdDb, iOff, pPg->aData, pFS->nMetaRwSize ); } #ifndef NDEBUG /* pPg->aData causes an uninitialized access via a downstreadm write(). After discussion on this list, this memory should not, for performance reasons, be memset. However, tracking down "real" misuse is more difficult with this "false" positive, so it is set when NDEBUG. */ else if( rc==LSM_OK ){ memset( pPg->aData, 0x77, pFS->nMetasize ); } #endif } if( rc!=LSM_OK ){ if( pFS->nMapLimit==0 ) lsmFree(pFS->pEnv, pPg->aData); lsmFree(pFS->pEnv, pPg); pPg = 0; }else{ pPg->iPg = iPg; pPg->bWrite = bWrite; pPg->pFS = pFS; } } *ppPg = pPg; return rc; } /* ** Release a meta-page reference obtained via a call to lsmFsMetaPageGet(). */ int lsmFsMetaPageRelease(MetaPage *pPg){ int rc = LSM_OK; if( pPg ){ FileSystem *pFS = pPg->pFS; if( pFS->nMapLimit==0 ){ if( pPg->bWrite ){ i64 iOff = (pPg->iPg==2 ? pFS->nMetasize : 0); int nWrite = pFS->nMetaRwSize; rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iOff, pPg->aData, nWrite); } lsmFree(pFS->pEnv, pPg->aData); } lsmFree(pFS->pEnv, pPg); } return rc; } /* ** Return a pointer to a buffer containing the data associated with the ** meta-page passed as the first argument. If parameter pnData is not NULL, ** set *pnData to the size of the meta-page in bytes before returning. */ u8 *lsmFsMetaPageData(MetaPage *pPg, int *pnData){ if( pnData ) *pnData = pPg->pFS->nMetaRwSize; return pPg->aData; } /* ** Return true if page is currently writable. This is used in assert() ** statements only. */ #ifndef NDEBUG int lsmFsPageWritable(Page *pPg){ return (pPg->flags & PAGE_DIRTY) ? 1 : 0; } #endif /* ** This is called when block iFrom is being redirected to iTo. If page ** number (*piPg) lies on block iFrom, then calculate the equivalent ** page on block iTo and set *piPg to this value before returning. */ static void fsMovePage( FileSystem *pFS, /* File system object */ int iTo, /* Destination block */ int iFrom, /* Source block */ Pgno *piPg /* IN/OUT: Page number */ ){ Pgno iPg = *piPg; if( iFrom==fsPageToBlock(pFS, iPg) ){ const int nPagePerBlock = ( pFS->pCompress ? pFS ->nBlocksize : (pFS->nBlocksize / pFS->nPagesize) ); *piPg = iPg - (Pgno)(iFrom - iTo) * nPagePerBlock; } } /* ** Copy the contents of block iFrom to block iTo. ** ** It is safe to assume that there are no outstanding references to pages ** on block iTo. And that block iFrom is not currently being written. In ** other words, the data can be read and written directly. */ int lsmFsMoveBlock(FileSystem *pFS, Segment *pSeg, int iTo, int iFrom){ Snapshot *p = pFS->pDb->pWorker; int rc = LSM_OK; int i; i64 nMap; i64 iFromOff = (i64)(iFrom-1) * pFS->nBlocksize; i64 iToOff = (i64)(iTo-1) * pFS->nBlocksize; assert( iTo!=1 ); assert( iFrom>iTo ); /* Grow the mapping as required. */ nMap = LSM_MIN(pFS->nMapLimit, (i64)iFrom * pFS->nBlocksize); fsGrowMapping(pFS, nMap, &rc); if( rc==LSM_OK ){ const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize); int nSz = pFS->nPagesize; u8 *aBuf = 0; u8 *aData = 0; for(i=0; rc==LSM_OK && i<nPagePerBlock; i++){ i64 iOff = iFromOff + i*nSz; /* Set aData to point to a buffer containing the from page */ if( (iOff+nSz)<=pFS->nMapLimit ){ u8 *aMap = (u8 *)(pFS->pMap); aData = &aMap[iOff]; }else{ if( aBuf==0 ){ aBuf = (u8 *)lsmMallocRc(pFS->pEnv, nSz, &rc); if( aBuf==0 ) break; } aData = aBuf; rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, aData, nSz); } /* Copy aData to the to page */ if( rc==LSM_OK ){ iOff = iToOff + i*nSz; if( (iOff+nSz)<=pFS->nMapLimit ){ u8 *aMap = (u8 *)(pFS->pMap); memcpy(&aMap[iOff], aData, nSz); }else{ rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iOff, aData, nSz); } } } lsmFree(pFS->pEnv, aBuf); lsmFsPurgeCache(pFS); } /* Update append-point list if necessary */ for(i=0; i<LSM_APPLIST_SZ; i++){ fsMovePage(pFS, iTo, iFrom, &p->aiAppend[i]); } /* Update the Segment structure itself */ fsMovePage(pFS, iTo, iFrom, &pSeg->iFirst); fsMovePage(pFS, iTo, iFrom, &pSeg->iLastPg); fsMovePage(pFS, iTo, iFrom, &pSeg->iRoot); return rc; } /* ** Append raw data to a segment. Return the database file offset that the ** data is written to (this may be used as the page number if the data ** being appended is a new page record). ** ** This function is only used in compressed database mode. */ static Pgno fsAppendData( FileSystem *pFS, /* File-system handle */ Segment *pSeg, /* Segment to append to */ const u8 *aData, /* Buffer containing data to write */ int nData, /* Size of buffer aData[] in bytes */ int *pRc /* IN/OUT: Error code */ ){ Pgno iRet = 0; int rc = *pRc; assert( pFS->pCompress ); if( rc==LSM_OK ){ int nRem = 0; int nWrite = 0; Pgno iLastOnBlock; Pgno iApp = pSeg->iLastPg+1; /* If this is the first data written into the segment, find an append-point ** or allocate a new block. */ if( iApp==1 ){ pSeg->iFirst = iApp = findAppendPoint(pFS, 0); if( iApp==0 ){ int iBlk; rc = lsmBlockAllocate(pFS->pDb, 0, &iBlk); pSeg->iFirst = iApp = fsFirstPageOnBlock(pFS, iBlk); } } iRet = iApp; /* Write as much data as is possible at iApp (usually all of it). */ iLastOnBlock = fsLastPageOnPagesBlock(pFS, iApp); if( rc==LSM_OK ){ int nSpace = (int)(iLastOnBlock - iApp + 1); nWrite = LSM_MIN(nData, nSpace); nRem = nData - nWrite; assert( nWrite>=0 ); if( nWrite!=0 ){ rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iApp, aData, nWrite); } iApp += nWrite; } /* If required, allocate a new block and write the rest of the data ** into it. Set the next and previous block pointers to link the new ** block to the old. */ assert( nRem<=0 || (iApp-1)==iLastOnBlock ); if( rc==LSM_OK && (iApp-1)==iLastOnBlock ){ u8 aPtr[4]; /* Space to serialize a u32 */ int iBlk; /* New block number */ if( nWrite>0 ){ /* Allocate a new block. */ rc = lsmBlockAllocate(pFS->pDb, 0, &iBlk); /* Set the "next" pointer on the old block */ if( rc==LSM_OK ){ assert( iApp==(fsPageToBlock(pFS, iApp)*pFS->nBlocksize)-4 ); lsmPutU32(aPtr, iBlk); rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iApp, aPtr, sizeof(aPtr)); } /* Set the "prev" pointer on the new block */ if( rc==LSM_OK ){ Pgno iWrite; lsmPutU32(aPtr, fsPageToBlock(pFS, iApp)); iWrite = fsFirstPageOnBlock(pFS, iBlk); rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iWrite-4, aPtr, sizeof(aPtr)); if( nRem>0 ) iApp = iWrite; } }else{ /* The next block is already allocated. */ assert( nRem>0 ); assert( pSeg->pRedirect==0 ); rc = fsBlockNext(pFS, 0, fsPageToBlock(pFS, iApp), &iBlk); iRet = iApp = fsFirstPageOnBlock(pFS, iBlk); } /* Write the remaining data into the new block */ if( rc==LSM_OK && nRem>0 ){ rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iApp, &aData[nWrite], nRem); iApp += nRem; } } pSeg->iLastPg = iApp-1; *pRc = rc; } return iRet; } /* ** This function is only called in compressed database mode. It ** compresses the contents of page pPg and writes the result to the ** buffer at pFS->aOBuffer. The size of the compressed data is stored in ** pPg->nCompress. ** ** If buffer pFS->aOBuffer[] has not been allocated then this function ** allocates it. If this fails, LSM_NOMEM is returned. Otherwise, LSM_OK. */ static int fsCompressIntoBuffer(FileSystem *pFS, Page *pPg){ lsm_compress *p = pFS->pCompress; if( fsAllocateBuffer(pFS, 1) ) return LSM_NOMEM; assert( pPg->nData==pFS->nPagesize ); pPg->nCompress = pFS->nBuffer; return p->xCompress(p->pCtx, (char *)pFS->aOBuffer, &pPg->nCompress, (const char *)pPg->aData, pPg->nData ); } /* ** Append a new page to segment pSeg. Set output variable *piNew to the ** page number of the new page before returning. ** ** If the new page is the last on its block, then the 'next' block that ** will be used by the segment is allocated here too. In this case output ** variable *piNext is set to the block number of the next block. ** ** If the new page is the first on its block but not the first in the ** entire segment, set output variable *piPrev to the block number of ** the previous block in the segment. ** ** LSM_OK is returned if successful, or an lsm error code otherwise. If ** any value other than LSM_OK is returned, then the final value of all ** output variables is undefined. */ static int fsAppendPage( FileSystem *pFS, Segment *pSeg, Pgno *piNew, int *piPrev, int *piNext ){ Pgno iPrev = pSeg->iLastPg; int rc; assert( iPrev!=0 ); *piPrev = 0; *piNext = 0; if( fsIsLast(pFS, iPrev) ){ /* Grab the first page on the next block (which has already be ** allocated). In this case set *piPrev to tell the caller to set ** the "previous block" pointer in the first 4 bytes of the page. */ int iNext; int iBlk = fsPageToBlock(pFS, iPrev); assert( pSeg->pRedirect==0 ); rc = fsBlockNext(pFS, 0, iBlk, &iNext); if( rc!=LSM_OK ) return rc; *piNew = fsFirstPageOnBlock(pFS, iNext); *piPrev = iBlk; }else{ *piNew = iPrev+1; if( fsIsLast(pFS, *piNew) ){ /* Allocate the next block here. */ int iBlk; rc = lsmBlockAllocate(pFS->pDb, 0, &iBlk); if( rc!=LSM_OK ) return rc; *piNext = iBlk; } } pSeg->nSize++; pSeg->iLastPg = *piNew; return LSM_OK; } /* ** Flush all pages in the FileSystem.pWaiting list to disk. */ void lsmFsFlushWaiting(FileSystem *pFS, int *pRc){ int rc = *pRc; Page *pPg; pPg = pFS->pWaiting; pFS->pWaiting = 0; while( pPg ){ Page *pNext = pPg->pWaitingNext; if( rc==LSM_OK ) rc = lsmFsPagePersist(pPg); assert( pPg->nRef==1 ); lsmFsPageRelease(pPg); pPg = pNext; } *pRc = rc; } /* ** If there exists a hash-table entry associated with page iPg, remove it. */ static void fsRemoveHashEntry(FileSystem *pFS, Pgno iPg){ Page *p; int iHash = fsHashKey(pFS->nHash, iPg); for(p=pFS->apHash[iHash]; p && p->iPg!=iPg; p=p->pHashNext); if( p ){ assert( p->nRef==0 || (p->flags & PAGE_FREE)==0 ); fsPageRemoveFromHash(pFS, p); p->iPg = 0; iHash = fsHashKey(pFS->nHash, 0); p->pHashNext = pFS->apHash[iHash]; pFS->apHash[iHash] = p; } } /* ** If the page passed as an argument is dirty, update the database file ** (or mapping of the database file) with its current contents and mark ** the page as clean. ** ** Return LSM_OK if the operation is a success, or an LSM error code ** otherwise. */ int lsmFsPagePersist(Page *pPg){ int rc = LSM_OK; if( pPg && (pPg->flags & PAGE_DIRTY) ){ FileSystem *pFS = pPg->pFS; if( pFS->pCompress ){ int iHash; /* Hash key of assigned page number */ u8 aSz[3]; /* pPg->nCompress as a 24-bit big-endian */ assert( pPg->pSeg && pPg->iPg==0 && pPg->nCompress==0 ); /* Compress the page image. */ rc = fsCompressIntoBuffer(pFS, pPg); /* Serialize the compressed size into buffer aSz[] */ putRecordSize(aSz, pPg->nCompress, 0); /* Write the serialized page record into the database file. */ pPg->iPg = fsAppendData(pFS, pPg->pSeg, aSz, sizeof(aSz), &rc); fsAppendData(pFS, pPg->pSeg, pFS->aOBuffer, pPg->nCompress, &rc); fsAppendData(pFS, pPg->pSeg, aSz, sizeof(aSz), &rc); /* Now that it has a page number, insert the page into the hash table */ iHash = fsHashKey(pFS->nHash, pPg->iPg); pPg->pHashNext = pFS->apHash[iHash]; pFS->apHash[iHash] = pPg; pPg->pSeg->nSize += (sizeof(aSz) * 2) + pPg->nCompress; pPg->flags &= ~PAGE_DIRTY; pFS->nWrite++; }else{ if( pPg->iPg==0 ){ /* No page number has been assigned yet. This occurs with pages used ** in the b-tree hierarchy. They were not assigned page numbers when ** they were created as doing so would cause this call to ** lsmFsPagePersist() to write an out-of-order page. Instead a page ** number is assigned here so that the page data will be appended ** to the current segment. */ Page **pp; int iPrev = 0; int iNext = 0; int iHash; assert( pPg->pSeg->iFirst ); assert( pPg->flags & PAGE_FREE ); assert( (pPg->flags & PAGE_HASPREV)==0 ); assert( pPg->nData==pFS->nPagesize-4 ); rc = fsAppendPage(pFS, pPg->pSeg, &pPg->iPg, &iPrev, &iNext); if( rc!=LSM_OK ) return rc; assert( pPg->flags & PAGE_FREE ); iHash = fsHashKey(pFS->nHash, pPg->iPg); fsRemoveHashEntry(pFS, pPg->iPg); pPg->pHashNext = pFS->apHash[iHash]; pFS->apHash[iHash] = pPg; assert( pPg->pHashNext==0 || pPg->pHashNext->iPg!=pPg->iPg ); if( iPrev ){ assert( iNext==0 ); memmove(&pPg->aData[4], pPg->aData, pPg->nData); lsmPutU32(pPg->aData, iPrev); pPg->flags |= PAGE_HASPREV; pPg->aData += 4; }else if( iNext ){ assert( iPrev==0 ); lsmPutU32(&pPg->aData[pPg->nData], iNext); }else{ int nData = pPg->nData; pPg->nData += 4; lsmSortedExpandBtreePage(pPg, nData); } pPg->nRef++; for(pp=&pFS->pWaiting; *pp; pp=&(*pp)->pWaitingNext); *pp = pPg; assert( pPg->pWaitingNext==0 ); }else{ i64 iOff; /* Offset to write within database file */ iOff = (i64)pFS->nPagesize * (i64)(pPg->iPg-1); if( fsMmapPage(pFS, pPg->iPg)==0 ){ u8 *aData = pPg->aData - (pPg->flags & PAGE_HASPREV); rc = lsmEnvWrite(pFS->pEnv, pFS->fdDb, iOff, aData, pFS->nPagesize); }else if( pPg->flags & PAGE_FREE ){ fsGrowMapping(pFS, iOff + pFS->nPagesize, &rc); if( rc==LSM_OK ){ u8 *aTo = &((u8 *)(pFS->pMap))[iOff]; u8 *aFrom = pPg->aData - (pPg->flags & PAGE_HASPREV); memcpy(aTo, aFrom, pFS->nPagesize); lsmFree(pFS->pEnv, aFrom); pFS->nCacheAlloc--; pPg->aData = aTo + (pPg->flags & PAGE_HASPREV); pPg->flags &= ~PAGE_FREE; fsPageRemoveFromHash(pFS, pPg); pPg->pMappedNext = pFS->pMapped; pFS->pMapped = pPg; } } lsmFsFlushWaiting(pFS, &rc); pPg->flags &= ~PAGE_DIRTY; pFS->nWrite++; } } } return rc; } /* ** For non-compressed databases, this function is a no-op. For compressed ** databases, it adds a padding record to the segment passed as the third ** argument. ** ** The size of the padding records is selected so that the last byte ** written is the last byte of a disk sector. This means that if a ** snapshot is taken and checkpointed, subsequent worker processes will ** not write to any sector that contains checkpointed data. */ int lsmFsSortedPadding( FileSystem *pFS, Snapshot *pSnapshot, Segment *pSeg ){ int rc = LSM_OK; if( pFS->pCompress ){ Pgno iLast2; Pgno iLast = pSeg->iLastPg; /* Current last page of segment */ int nPad; /* Bytes of padding required */ u8 aSz[3]; iLast2 = (1 + iLast/pFS->szSector) * pFS->szSector - 1; assert( fsPageToBlock(pFS, iLast)==fsPageToBlock(pFS, iLast2) ); nPad = (int)(iLast2 - iLast); if( iLast2>fsLastPageOnPagesBlock(pFS, iLast) ){ nPad -= 4; } assert( nPad>=0 ); if( nPad>=6 ){ pSeg->nSize += nPad; nPad -= 6; putRecordSize(aSz, nPad, 1); fsAppendData(pFS, pSeg, aSz, sizeof(aSz), &rc); memset(pFS->aOBuffer, 0, nPad); fsAppendData(pFS, pSeg, pFS->aOBuffer, nPad, &rc); fsAppendData(pFS, pSeg, aSz, sizeof(aSz), &rc); }else if( nPad>0 ){ u8 aBuf[5] = {0,0,0,0,0}; aBuf[0] = (u8)nPad; aBuf[nPad-1] = (u8)nPad; fsAppendData(pFS, pSeg, aBuf, nPad, &rc); } assert( rc!=LSM_OK || pSeg->iLastPg==fsLastPageOnPagesBlock(pFS, pSeg->iLastPg) || ((pSeg->iLastPg + 1) % pFS->szSector)==0 ); } return rc; } /* ** Increment the reference count on the page object passed as the first ** argument. */ void lsmFsPageRef(Page *pPg){ if( pPg ){ pPg->nRef++; } } /* ** Release a page-reference obtained using fsPageGet(). */ int lsmFsPageRelease(Page *pPg){ int rc = LSM_OK; if( pPg ){ assert( pPg->nRef>0 ); pPg->nRef--; if( pPg->nRef==0 ){ FileSystem *pFS = pPg->pFS; rc = lsmFsPagePersist(pPg); pFS->nOut--; assert( pPg->pFS->pCompress || fsIsFirst(pPg->pFS, pPg->iPg)==0 || (pPg->flags & PAGE_HASPREV) ); pPg->aData -= (pPg->flags & PAGE_HASPREV); pPg->flags &= ~PAGE_HASPREV; if( (pPg->flags & PAGE_FREE)==0 ){ /* Removed from mapped list */ Page **pp; for(pp=&pFS->pMapped; (*pp)!=pPg; pp=&(*pp)->pMappedNext); *pp = pPg->pMappedNext; pPg->pMappedNext = 0; /* Add to free list */ pPg->pFreeNext = pFS->pFree; pFS->pFree = pPg; }else{ fsPageAddToLru(pFS, pPg); } } } return rc; } /* ** Return the total number of pages read from the database file. */ int lsmFsNRead(FileSystem *pFS){ return pFS->nRead; } /* ** Return the total number of pages written to the database file. */ int lsmFsNWrite(FileSystem *pFS){ return pFS->nWrite; } /* ** Return a copy of the environment pointer used by the file-system object. */ lsm_env *lsmFsEnv(FileSystem *pFS){ return pFS->pEnv; } /* ** Return a copy of the environment pointer used by the file-system object ** to which this page belongs. */ lsm_env *lsmPageEnv(Page *pPg) { return pPg->pFS->pEnv; } /* ** Return a pointer to the file-system object associated with the Page ** passed as the only argument. */ FileSystem *lsmPageFS(Page *pPg){ return pPg->pFS; } /* ** Return the sector-size as reported by the log file handle. */ int lsmFsSectorSize(FileSystem *pFS){ return pFS->szSector; } /* ** Helper function for lsmInfoArrayStructure(). */ static Segment *startsWith(Segment *pRun, Pgno iFirst){ return (iFirst==pRun->iFirst) ? pRun : 0; } /* ** Return the segment that starts with page iFirst, if any. If no such segment ** can be found, return NULL. */ static Segment *findSegment(Snapshot *pWorker, Pgno iFirst){ Level *pLvl; /* Used to iterate through db levels */ Segment *pSeg = 0; /* Pointer to segment to return */ for(pLvl=lsmDbSnapshotLevel(pWorker); pLvl && pSeg==0; pLvl=pLvl->pNext){ if( 0==(pSeg = startsWith(&pLvl->lhs, iFirst)) ){ int i; for(i=0; i<pLvl->nRight; i++){ if( (pSeg = startsWith(&pLvl->aRhs[i], iFirst)) ) break; } } } return pSeg; } /* ** This function implements the lsm_info(LSM_INFO_ARRAY_STRUCTURE) request. ** If successful, *pzOut is set to point to a nul-terminated string ** containing the array structure and LSM_OK is returned. The caller should ** eventually free the string using lsmFree(). ** ** If an error occurs, *pzOut is set to NULL and an LSM error code returned. */ int lsmInfoArrayStructure( lsm_db *pDb, int bBlock, /* True for block numbers only */ Pgno iFirst, char **pzOut ){ int rc = LSM_OK; Snapshot *pWorker; /* Worker snapshot */ Segment *pArray = 0; /* Array to report on */ int bUnlock = 0; *pzOut = 0; if( iFirst==0 ) return LSM_ERROR; /* Obtain the worker snapshot */ pWorker = pDb->pWorker; if( !pWorker ){ rc = lsmBeginWork(pDb); if( rc!=LSM_OK ) return rc; pWorker = pDb->pWorker; bUnlock = 1; } /* Search for the array that starts on page iFirst */ pArray = findSegment(pWorker, iFirst); if( pArray==0 ){ /* Could not find the requested array. This is an error. */ rc = LSM_ERROR; }else{ FileSystem *pFS = pDb->pFS; LsmString str; int iBlk; int iLastBlk; iBlk = fsPageToBlock(pFS, pArray->iFirst); iLastBlk = fsPageToBlock(pFS, pArray->iLastPg); lsmStringInit(&str, pDb->pEnv); if( bBlock ){ lsmStringAppendf(&str, "%d", iBlk); while( iBlk!=iLastBlk ){ fsBlockNext(pFS, pArray, iBlk, &iBlk); lsmStringAppendf(&str, " %d", iBlk); } }else{ lsmStringAppendf(&str, "%d", pArray->iFirst); while( iBlk!=iLastBlk ){ lsmStringAppendf(&str, " %d", fsLastPageOnBlock(pFS, iBlk)); fsBlockNext(pFS, pArray, iBlk, &iBlk); lsmStringAppendf(&str, " %d", fsFirstPageOnBlock(pFS, iBlk)); } lsmStringAppendf(&str, " %d", pArray->iLastPg); } *pzOut = str.z; } if( bUnlock ){ int rcwork = LSM_BUSY; lsmFinishWork(pDb, 0, &rcwork); } return rc; } int lsmFsSegmentContainsPg( FileSystem *pFS, Segment *pSeg, Pgno iPg, int *pbRes ){ Redirect *pRedir = pSeg->pRedirect; int rc = LSM_OK; int iBlk; int iLastBlk; int iPgBlock; /* Block containing page iPg */ iPgBlock = fsPageToBlock(pFS, pSeg->iFirst); iBlk = fsRedirectBlock(pRedir, fsPageToBlock(pFS, pSeg->iFirst)); iLastBlk = fsRedirectBlock(pRedir, fsPageToBlock(pFS, pSeg->iLastPg)); while( iBlk!=iLastBlk && iBlk!=iPgBlock && rc==LSM_OK ){ rc = fsBlockNext(pFS, pSeg, iBlk, &iBlk); } *pbRes = (iBlk==iPgBlock); return rc; } /* ** This function implements the lsm_info(LSM_INFO_ARRAY_PAGES) request. ** If successful, *pzOut is set to point to a nul-terminated string ** containing the array structure and LSM_OK is returned. The caller should ** eventually free the string using lsmFree(). ** ** If an error occurs, *pzOut is set to NULL and an LSM error code returned. */ int lsmInfoArrayPages(lsm_db *pDb, Pgno iFirst, char **pzOut){ int rc = LSM_OK; Snapshot *pWorker; /* Worker snapshot */ Segment *pSeg = 0; /* Array to report on */ int bUnlock = 0; *pzOut = 0; if( iFirst==0 ) return LSM_ERROR; /* Obtain the worker snapshot */ pWorker = pDb->pWorker; if( !pWorker ){ rc = lsmBeginWork(pDb); if( rc!=LSM_OK ) return rc; pWorker = pDb->pWorker; bUnlock = 1; } /* Search for the array that starts on page iFirst */ pSeg = findSegment(pWorker, iFirst); if( pSeg==0 ){ /* Could not find the requested array. This is an error. */ rc = LSM_ERROR; }else{ Page *pPg = 0; FileSystem *pFS = pDb->pFS; LsmString str; lsmStringInit(&str, pDb->pEnv); rc = lsmFsDbPageGet(pFS, pSeg, iFirst, &pPg); while( rc==LSM_OK && pPg ){ Page *pNext = 0; lsmStringAppendf(&str, " %lld", lsmFsPageNumber(pPg)); rc = lsmFsDbPageNext(pSeg, pPg, 1, &pNext); lsmFsPageRelease(pPg); pPg = pNext; } if( rc!=LSM_OK ){ lsmFree(pDb->pEnv, str.z); }else{ *pzOut = str.z; } } if( bUnlock ){ int rcwork = LSM_BUSY; lsmFinishWork(pDb, 0, &rcwork); } return rc; } /* ** The following macros are used by the integrity-check code. Associated with ** each block in the database is an 8-bit bit mask (the entry in the aUsed[] ** array). As the integrity-check meanders through the database, it sets the ** following bits to indicate how each block is used. ** ** INTEGRITY_CHECK_FIRST_PG: ** First page of block is in use by sorted run. ** ** INTEGRITY_CHECK_LAST_PG: ** Last page of block is in use by sorted run. ** ** INTEGRITY_CHECK_USED: ** At least one page of the block is in use by a sorted run. ** ** INTEGRITY_CHECK_FREE: ** The free block list contains an entry corresponding to this block. */ #define INTEGRITY_CHECK_FIRST_PG 0x01 #define INTEGRITY_CHECK_LAST_PG 0x02 #define INTEGRITY_CHECK_USED 0x04 #define INTEGRITY_CHECK_FREE 0x08 /* ** Helper function for lsmFsIntegrityCheck() */ static void checkBlocks( FileSystem *pFS, Segment *pSeg, int bExtra, /* If true, count the "next" block if any */ int nUsed, u8 *aUsed ){ if( pSeg ){ if( pSeg && pSeg->nSize>0 ){ int rc; int iBlk; /* Current block (during iteration) */ int iLastBlk; /* Last block of segment */ int iFirstBlk; /* First block of segment */ int bLastIsLastOnBlock; /* True iLast is the last on its block */ assert( 0==fsSegmentRedirects(pFS, pSeg) ); iBlk = iFirstBlk = fsPageToBlock(pFS, pSeg->iFirst); iLastBlk = fsPageToBlock(pFS, pSeg->iLastPg); bLastIsLastOnBlock = (fsLastPageOnBlock(pFS, iLastBlk)==pSeg->iLastPg); assert( iBlk>0 ); do { /* iBlk is a part of this sorted run. */ aUsed[iBlk-1] |= INTEGRITY_CHECK_USED; /* If the first page of this block is also part of the segment, ** set the flag to indicate that the first page of iBlk is in use. */ if( fsFirstPageOnBlock(pFS, iBlk)==pSeg->iFirst || iBlk!=iFirstBlk ){ assert( (aUsed[iBlk-1] & INTEGRITY_CHECK_FIRST_PG)==0 ); aUsed[iBlk-1] |= INTEGRITY_CHECK_FIRST_PG; } /* Unless the sorted run finishes before the last page on this block, ** the last page of this block is also in use. */ if( iBlk!=iLastBlk || bLastIsLastOnBlock ){ assert( (aUsed[iBlk-1] & INTEGRITY_CHECK_LAST_PG)==0 ); aUsed[iBlk-1] |= INTEGRITY_CHECK_LAST_PG; } /* Special case. The sorted run being scanned is the output run of ** a level currently undergoing an incremental merge. The sorted ** run ends on the last page of iBlk, but the next block has already ** been allocated. So mark it as in use as well. */ if( iBlk==iLastBlk && bLastIsLastOnBlock && bExtra ){ int iExtra = 0; rc = fsBlockNext(pFS, pSeg, iBlk, &iExtra); assert( rc==LSM_OK ); assert( aUsed[iExtra-1]==0 ); aUsed[iExtra-1] |= INTEGRITY_CHECK_USED; aUsed[iExtra-1] |= INTEGRITY_CHECK_FIRST_PG; aUsed[iExtra-1] |= INTEGRITY_CHECK_LAST_PG; } /* Move on to the next block in the sorted run. Or set iBlk to zero ** in order to break out of the loop if this was the last block in ** the run. */ if( iBlk==iLastBlk ){ iBlk = 0; }else{ rc = fsBlockNext(pFS, pSeg, iBlk, &iBlk); assert( rc==LSM_OK ); } }while( iBlk ); } } } typedef struct CheckFreelistCtx CheckFreelistCtx; struct CheckFreelistCtx { u8 *aUsed; int nBlock; }; static int checkFreelistCb(void *pCtx, int iBlk, i64 iSnapshot){ CheckFreelistCtx *p = (CheckFreelistCtx *)pCtx; assert( iBlk>=1 ); assert( iBlk<=p->nBlock ); assert( p->aUsed[iBlk-1]==0 ); p->aUsed[iBlk-1] = INTEGRITY_CHECK_FREE; return 0; } /* ** This function checks that all blocks in the database file are accounted ** for. For each block, exactly one of the following must be true: ** ** + the block is part of a sorted run, or ** + the block is on the free-block list ** ** This function also checks that there are no references to blocks with ** out-of-range block numbers. ** ** If no errors are found, non-zero is returned. If an error is found, an ** assert() fails. */ int lsmFsIntegrityCheck(lsm_db *pDb){ CheckFreelistCtx ctx; FileSystem *pFS = pDb->pFS; int i; int rc; Freelist freelist = {0, 0, 0}; u8 *aUsed; Level *pLevel; Snapshot *pWorker = pDb->pWorker; int nBlock = pWorker->nBlock; #if 0 static int nCall = 0; nCall++; printf("%d calls\n", nCall); #endif aUsed = lsmMallocZero(pDb->pEnv, nBlock); if( aUsed==0 ){ /* Malloc has failed. Since this function is only called within debug ** builds, this probably means the user is running an OOM injection test. ** Regardless, it will not be possible to run the integrity-check at this ** time, so assume the database is Ok and return non-zero. */ return 1; } for(pLevel=pWorker->pLevel; pLevel; pLevel=pLevel->pNext){ int j; checkBlocks(pFS, &pLevel->lhs, (pLevel->nRight!=0), nBlock, aUsed); for(j=0; j<pLevel->nRight; j++){ checkBlocks(pFS, &pLevel->aRhs[j], 0, nBlock, aUsed); } } /* Mark all blocks in the free-list as used */ ctx.aUsed = aUsed; ctx.nBlock = nBlock; rc = lsmWalkFreelist(pDb, 0, checkFreelistCb, (void *)&ctx); if( rc==LSM_OK ){ for(i=0; i<nBlock; i++) assert( aUsed[i]!=0 ); } lsmFree(pDb->pEnv, aUsed); lsmFree(pDb->pEnv, freelist.aEntry); return 1; } #ifndef NDEBUG /* ** Return true if pPg happens to be the last page in segment pSeg. Or false ** otherwise. This function is only invoked as part of assert() conditions. */ int lsmFsDbPageIsLast(Segment *pSeg, Page *pPg){ if( pPg->pFS->pCompress ){ Pgno iNext = 0; int rc; rc = fsNextPageOffset(pPg->pFS, pSeg, pPg->iPg, pPg->nCompress+6, &iNext); return (rc!=LSM_OK || iNext==0); } return (pPg->iPg==pSeg->iLastPg); } #endif |
Added ext/lsm1/lsm_log.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 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 | /* ** 2011-08-13 ** ** 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 implementation of LSM database logging. Logging ** has one purpose in LSM - to make transactions durable. ** ** When data is written to an LSM database, it is initially stored in an ** in-memory tree structure. Since this structure is in volatile memory, ** if a power failure or application crash occurs it may be lost. To ** prevent loss of data in this case, each time a record is written to the ** in-memory tree an equivalent record is appended to the log on disk. ** If a power failure or application crash does occur, data can be recovered ** by reading the log. ** ** A log file consists of the following types of records representing data ** written into the database: ** ** LOG_WRITE: A key-value pair written to the database. ** LOG_DELETE: A delete key issued to the database. ** LOG_COMMIT: A transaction commit. ** ** And the following types of records for ancillary purposes.. ** ** LOG_EOF: A record indicating the end of a log file. ** LOG_PAD1: A single byte padding record. ** LOG_PAD2: An N byte padding record (N>1). ** LOG_JUMP: A pointer to another offset within the log file. ** ** Each transaction written to the log contains one or more LOG_WRITE and/or ** LOG_DELETE records, followed by a LOG_COMMIT record. The LOG_COMMIT record ** contains an 8-byte checksum based on all previous data written to the ** log file. ** ** LOG CHECKSUMS & RECOVERY ** ** Checksums are found in two types of log records: LOG_COMMIT and ** LOG_CKSUM records. In order to recover content from a log, a client ** reads each record from the start of the log, calculating a checksum as ** it does. Each time a LOG_COMMIT or LOG_CKSUM is encountered, the ** recovery process verifies that the checksum stored in the log ** matches the calculated checksum. If it does not, the recovery process ** can stop reading the log. ** ** If a recovery process reads records (other than COMMIT or CKSUM) ** consisting of at least LSM_CKSUM_MAXDATA bytes, then the next record in ** the log must be either a LOG_CKSUM or LOG_COMMIT record. If it is ** not, the recovery process also stops reading the log. ** ** To recover the log file, it must be read twice. The first time to ** determine the location of the last valid commit record. And the second ** time to load data into the in-memory tree. ** ** Todo: Surely there is a better way... ** ** LOG WRAPPING ** ** If the log file were never deleted or wrapped, it would be possible to ** read it from start to end each time is required recovery (i.e each time ** the number of database clients changes from 0 to 1). Effectively reading ** the entire history of the database each time. This would quickly become ** inefficient. Additionally, since the log file would grow without bound, ** it wastes storage space. ** ** Instead, part of each checkpoint written into the database file contains ** a log offset (and other information required to read the log starting at ** at this offset) at which to begin recovery. Offset $O. ** ** Once a checkpoint has been written and synced into the database file, it ** is guaranteed that no recovery process will need to read any data before ** offset $O of the log file. It is therefore safe to begin overwriting ** any data that occurs before offset $O. ** ** This implementation separates the log into three regions mapped into ** the log file - regions 0, 1 and 2. During recovery, regions are read ** in ascending order (i.e. 0, then 1, then 2). Each region is zero or ** more bytes in size. ** ** |---1---|..|--0--|.|--2--|.... ** ** New records are always appended to the end of region 2. ** ** Initially (when it is empty), all three regions are zero bytes in size. ** Each of them are located at the beginning of the file. As records are ** added to the log, region 2 grows, so that the log consists of a zero ** byte region 1, followed by a zero byte region 0, followed by an N byte ** region 2. After one or more checkpoints have been written to disk, ** the start point of region 2 is moved to $O. For example: ** ** A) ||.........|--2--|.... ** ** (both regions 0 and 1 are 0 bytes in size at offset 0). ** ** Eventually, the log wraps around to write new records into the start. ** At this point, region 2 is renamed to region 0. Region 0 is renamed ** to region 2. After appending a few records to the new region 2, the ** log file looks like this: ** ** B) ||--2--|...|--0--|.... ** ** (region 1 is still 0 bytes in size, located at offset 0). ** ** Any checkpoints made at this point may reduce the size of region 0. ** However, if they do not, and region 2 expands so that it is about to ** overwrite the start of region 0, then region 2 is renamed to region 1, ** and a new region 2 created at the end of the file following the existing ** region 0. ** ** C) |---1---|..|--0--|.|-2-| ** ** In this state records are appended to region 2 until checkpoints have ** contracted regions 0 AND 1 UNTil they are both zero bytes in size. They ** are then shifted to the start of the log file, leaving the system in ** the equivalent of state A above. ** ** Alternatively, state B may transition directly to state A if the size ** of region 0 is reduced to zero bytes before region 2 threatens to ** encroach upon it. ** ** LOG_PAD1 & LOG_PAD2 RECORDS ** ** PAD1 and PAD2 records may appear in a log file at any point. They allow ** a process writing the log file align the beginning of transactions with ** the beginning of disk sectors, which increases robustness. ** ** RECORD FORMATS: ** ** LOG_EOF: * A single 0x00 byte. ** ** LOG_PAD1: * A single 0x01 byte. ** ** LOG_PAD2: * A single 0x02 byte, followed by ** * The number of unused bytes (N) as a varint, ** * An N byte block of unused space. ** ** LOG_COMMIT: * A single 0x03 byte. ** * An 8-byte checksum. ** ** LOG_JUMP: * A single 0x04 byte. ** * Absolute file offset to jump to, encoded as a varint. ** ** LOG_WRITE: * A single 0x06 or 0x07 byte, ** * The number of bytes in the key, encoded as a varint, ** * The number of bytes in the value, encoded as a varint, ** * If the first byte was 0x07, an 8 byte checksum. ** * The key data, ** * The value data. ** ** LOG_DELETE: * A single 0x08 or 0x09 byte, ** * The number of bytes in the key, encoded as a varint, ** * If the first byte was 0x09, an 8 byte checksum. ** * The key data. ** ** Varints are as described in lsm_varint.c (SQLite 4 format). ** ** CHECKSUMS: ** ** The checksum is calculated using two 32-bit unsigned integers, s0 and ** s1. The initial value for both is 42. It is updated each time a record ** is written into the log file by treating the encoded (binary) record as ** an array of 32-bit little-endian integers. Then, if x[] is the integer ** array, updating the checksum accumulators as follows: ** ** for i from 0 to n-1 step 2: ** s0 += x[i] + s1; ** s1 += x[i+1] + s0; ** endfor ** ** If the record is not an even multiple of 8-bytes in size it is padded ** with zeroes to make it so before the checksum is updated. ** ** The checksum stored in a COMMIT, WRITE or DELETE is based on all bytes ** up to the start of the 8-byte checksum itself, including the COMMIT, ** WRITE or DELETE fields that appear before the checksum in the record. ** ** VARINT FORMAT ** ** See lsm_varint.c. */ #ifndef _LSM_INT_H # include "lsmInt.h" #endif /* Log record types */ #define LSM_LOG_EOF 0x00 #define LSM_LOG_PAD1 0x01 #define LSM_LOG_PAD2 0x02 #define LSM_LOG_COMMIT 0x03 #define LSM_LOG_JUMP 0x04 #define LSM_LOG_WRITE 0x06 #define LSM_LOG_WRITE_CKSUM 0x07 #define LSM_LOG_DELETE 0x08 #define LSM_LOG_DELETE_CKSUM 0x09 #define LSM_LOG_DRANGE 0x0A #define LSM_LOG_DRANGE_CKSUM 0x0B /* Require a checksum every 32KB. */ #define LSM_CKSUM_MAXDATA (32*1024) /* Do not wrap a log file smaller than this in bytes. */ #define LSM_MIN_LOGWRAP (128*1024) /* ** szSector: ** Commit records must be aligned to end on szSector boundaries. If ** the safety-mode is set to NORMAL or OFF, this value is 1. Otherwise, ** if the safety-mode is set to FULL, it is the size of the file-system ** sectors as reported by lsmFsSectorSize(). */ struct LogWriter { u32 cksum0; /* Checksum 0 at offset iOff */ u32 cksum1; /* Checksum 1 at offset iOff */ int iCksumBuf; /* Bytes of buf that have been checksummed */ i64 iOff; /* Offset at start of buffer buf */ int szSector; /* Sector size for this transaction */ LogRegion jump; /* Avoid writing to this region */ i64 iRegion1End; /* End of first region written by trans */ i64 iRegion2Start; /* Start of second regions written by trans */ LsmString buf; /* Buffer containing data not yet written */ }; /* ** Return the result of interpreting the first 4 bytes in buffer aIn as ** a 32-bit unsigned little-endian integer. */ static u32 getU32le(u8 *aIn){ return ((u32)aIn[3] << 24) + ((u32)aIn[2] << 16) + ((u32)aIn[1] << 8) + ((u32)aIn[0]); } /* ** This function is the same as logCksum(), except that pointer "a" need ** not be aligned to an 8-byte boundary or padded with zero bytes. This ** version is slower, but sometimes more convenient to use. */ static void logCksumUnaligned( char *z, /* Input buffer */ int n, /* Size of input buffer in bytes */ u32 *pCksum0, /* IN/OUT: Checksum value 1 */ u32 *pCksum1 /* IN/OUT: Checksum value 2 */ ){ u8 *a = (u8 *)z; u32 cksum0 = *pCksum0; u32 cksum1 = *pCksum1; int nIn = (n/8) * 8; int i; assert( n>0 ); for(i=0; i<nIn; i+=8){ cksum0 += getU32le(&a[i]) + cksum1; cksum1 += getU32le(&a[i+4]) + cksum0; } if( nIn!=n ){ u8 aBuf[8] = {0, 0, 0, 0, 0, 0, 0, 0}; assert( (n-nIn)<8 && n>nIn ); memcpy(aBuf, &a[nIn], n-nIn); cksum0 += getU32le(aBuf) + cksum1; cksum1 += getU32le(&aBuf[4]) + cksum0; } *pCksum0 = cksum0; *pCksum1 = cksum1; } /* ** Update pLog->cksum0 and pLog->cksum1 so that the first nBuf bytes in the ** write buffer (pLog->buf) are included in the checksum. */ static void logUpdateCksum(LogWriter *pLog, int nBuf){ assert( (pLog->iCksumBuf % 8)==0 ); assert( pLog->iCksumBuf<=nBuf ); assert( (nBuf % 8)==0 || nBuf==pLog->buf.n ); if( nBuf>pLog->iCksumBuf ){ logCksumUnaligned( &pLog->buf.z[pLog->iCksumBuf], nBuf-pLog->iCksumBuf, &pLog->cksum0, &pLog->cksum1 ); } pLog->iCksumBuf = nBuf; } static i64 firstByteOnSector(LogWriter *pLog, i64 iOff){ return (iOff / pLog->szSector) * pLog->szSector; } static i64 lastByteOnSector(LogWriter *pLog, i64 iOff){ return firstByteOnSector(pLog, iOff) + pLog->szSector - 1; } /* ** If possible, reclaim log file space. Log file space is reclaimed after ** a snapshot that points to the same data in the database file is synced ** into the db header. */ static int logReclaimSpace(lsm_db *pDb){ int rc; int iMeta; int bRotrans; /* True if there exists some ro-trans */ /* Test if there exists some other connection with a read-only transaction ** open. If there does, then log file space may not be reclaimed. */ rc = lsmDetectRoTrans(pDb, &bRotrans); if( rc!=LSM_OK || bRotrans ) return rc; iMeta = (int)pDb->pShmhdr->iMetaPage; if( iMeta==1 || iMeta==2 ){ DbLog *pLog = &pDb->treehdr.log; i64 iSyncedId; /* Read the snapshot-id of the snapshot stored on meta-page iMeta. Note ** that in theory, the value read is untrustworthy (due to a race ** condition - see comments above lsmFsReadSyncedId()). So it is only ** ever used to conclude that no log space can be reclaimed. If it seems ** to indicate that it may be possible to reclaim log space, a ** second call to lsmCheckpointSynced() (which does return trustworthy ** values) is made below to confirm. */ rc = lsmFsReadSyncedId(pDb, iMeta, &iSyncedId); if( rc==LSM_OK && pLog->iSnapshotId!=iSyncedId ){ i64 iSnapshotId = 0; i64 iOff = 0; rc = lsmCheckpointSynced(pDb, &iSnapshotId, &iOff, 0); if( rc==LSM_OK && pLog->iSnapshotId<iSnapshotId ){ int iRegion; for(iRegion=0; iRegion<3; iRegion++){ LogRegion *p = &pLog->aRegion[iRegion]; if( iOff>=p->iStart && iOff<=p->iEnd ) break; p->iStart = 0; p->iEnd = 0; } assert( iRegion<3 ); pLog->aRegion[iRegion].iStart = iOff; pLog->iSnapshotId = iSnapshotId; } } } return rc; } /* ** This function is called when a write-transaction is first opened. It ** is assumed that the caller is holding the client-mutex when it is ** called. ** ** Before returning, this function allocates the LogWriter object that ** will be used to write to the log file during the write transaction. ** LSM_OK is returned if no error occurs, otherwise an LSM error code. */ int lsmLogBegin(lsm_db *pDb){ int rc = LSM_OK; LogWriter *pNew; LogRegion *aReg; if( pDb->bUseLog==0 ) return LSM_OK; /* If the log file has not yet been opened, open it now. Also allocate ** the LogWriter structure, if it has not already been allocated. */ rc = lsmFsOpenLog(pDb, 0); if( pDb->pLogWriter==0 ){ pNew = lsmMallocZeroRc(pDb->pEnv, sizeof(LogWriter), &rc); if( pNew ){ lsmStringInit(&pNew->buf, pDb->pEnv); rc = lsmStringExtend(&pNew->buf, 2); } pDb->pLogWriter = pNew; }else{ pNew = pDb->pLogWriter; assert( (u8 *)(&pNew[1])==(u8 *)(&((&pNew->buf)[1])) ); memset(pNew, 0, ((u8 *)&pNew->buf) - (u8 *)pNew); pNew->buf.n = 0; } if( rc==LSM_OK ){ /* The following call detects whether or not a new snapshot has been ** synced into the database file. If so, it updates the contents of ** the pDb->treehdr.log structure to reclaim any space in the log ** file that is no longer required. ** ** TODO: Calling this every transaction is overkill. And since the ** call has to read and checksum a snapshot from the database file, ** it is expensive. It would be better to figure out a way so that ** this is only called occasionally - say for every 32KB written to ** the log file. */ rc = logReclaimSpace(pDb); } if( rc!=LSM_OK ){ lsmLogClose(pDb); return rc; } /* Set the effective sector-size for this transaction. Sectors are assumed ** to be one byte in size if the safety-mode is OFF or NORMAL, or as ** reported by lsmFsSectorSize if it is FULL. */ if( pDb->eSafety==LSM_SAFETY_FULL ){ pNew->szSector = lsmFsSectorSize(pDb->pFS); assert( pNew->szSector>0 ); }else{ pNew->szSector = 1; } /* There are now three scenarios: ** ** 1) Regions 0 and 1 are both zero bytes in size and region 2 begins ** at a file offset greater than LSM_MIN_LOGWRAP. In this case, wrap ** around to the start and write data into the start of the log file. ** ** 2) Region 1 is zero bytes in size and region 2 occurs earlier in the ** file than region 0. In this case, append data to region 2, but ** remember to jump over region 1 if required. ** ** 3) Region 2 is the last in the file. Append to it. */ aReg = &pDb->treehdr.log.aRegion[0]; assert( aReg[0].iEnd==0 || aReg[0].iEnd>aReg[0].iStart ); assert( aReg[1].iEnd==0 || aReg[1].iEnd>aReg[1].iStart ); pNew->cksum0 = pDb->treehdr.log.cksum0; pNew->cksum1 = pDb->treehdr.log.cksum1; if( aReg[0].iEnd==0 && aReg[1].iEnd==0 && aReg[2].iStart>=LSM_MIN_LOGWRAP ){ /* Case 1. Wrap around to the start of the file. Write an LSM_LOG_JUMP ** into the log file in this case. Pad it out to 8 bytes using a PAD2 ** record so that the checksums can be updated immediately. */ u8 aJump[] = { LSM_LOG_PAD2, 0x04, 0x00, 0x00, 0x00, 0x00, LSM_LOG_JUMP, 0x00 }; lsmStringBinAppend(&pNew->buf, aJump, sizeof(aJump)); logUpdateCksum(pNew, pNew->buf.n); rc = lsmFsWriteLog(pDb->pFS, aReg[2].iEnd, &pNew->buf); pNew->iCksumBuf = pNew->buf.n = 0; aReg[2].iEnd += 8; pNew->jump = aReg[0] = aReg[2]; aReg[2].iStart = aReg[2].iEnd = 0; }else if( aReg[1].iEnd==0 && aReg[2].iEnd<aReg[0].iEnd ){ /* Case 2. */ pNew->iOff = aReg[2].iEnd; pNew->jump = aReg[0]; }else{ /* Case 3. */ assert( aReg[2].iStart>=aReg[0].iEnd && aReg[2].iStart>=aReg[1].iEnd ); pNew->iOff = aReg[2].iEnd; } if( pNew->jump.iStart ){ i64 iRound; assert( pNew->jump.iStart>pNew->iOff ); iRound = firstByteOnSector(pNew, pNew->jump.iStart); if( iRound>pNew->iOff ) pNew->jump.iStart = iRound; pNew->jump.iEnd = lastByteOnSector(pNew, pNew->jump.iEnd); } assert( pDb->pLogWriter==pNew ); return rc; } /* ** This function is called when a write-transaction is being closed. ** Parameter bCommit is true if the transaction is being committed, ** or false otherwise. The caller must hold the client-mutex to call ** this function. ** ** A call to this function deletes the LogWriter object allocated by ** lsmLogBegin(). If the transaction is being committed, the shared state ** in *pLog is updated before returning. */ void lsmLogEnd(lsm_db *pDb, int bCommit){ DbLog *pLog; LogWriter *p; p = pDb->pLogWriter; if( p==0 ) return; pLog = &pDb->treehdr.log; if( bCommit ){ pLog->aRegion[2].iEnd = p->iOff; pLog->cksum0 = p->cksum0; pLog->cksum1 = p->cksum1; if( p->iRegion1End ){ /* This happens when the transaction had to jump over some other ** part of the log. */ assert( pLog->aRegion[1].iEnd==0 ); assert( pLog->aRegion[2].iStart<p->iRegion1End ); pLog->aRegion[1].iStart = pLog->aRegion[2].iStart; pLog->aRegion[1].iEnd = p->iRegion1End; pLog->aRegion[2].iStart = p->iRegion2Start; } } } static int jumpIfRequired( lsm_db *pDb, LogWriter *pLog, int nReq, int *pbJump ){ /* Determine if it is necessary to add an LSM_LOG_JUMP to jump over the ** jump region before writing the LSM_LOG_WRITE or DELETE record. This ** is necessary if there is insufficient room between the current offset ** and the jump region to fit the new WRITE/DELETE record and the largest ** possible JUMP record with up to 7 bytes of padding (a total of 17 ** bytes). */ if( (pLog->jump.iStart > (pLog->iOff + pLog->buf.n)) && (pLog->jump.iStart < (pLog->iOff + pLog->buf.n + (nReq + 17))) ){ int rc; /* Return code */ i64 iJump; /* Offset to jump to */ u8 aJump[10]; /* Encoded jump record */ int nJump; /* Valid bytes in aJump[] */ int nPad; /* Bytes of padding required */ /* Serialize the JUMP record */ iJump = pLog->jump.iEnd+1; aJump[0] = LSM_LOG_JUMP; nJump = 1 + lsmVarintPut64(&aJump[1], iJump); /* Adding padding to the contents of the buffer so that it will be a ** multiple of 8 bytes in size after the JUMP record is appended. This ** is not strictly required, it just makes the keeping the running ** checksum up to date in this file a little simpler. */ nPad = (pLog->buf.n + nJump) % 8; if( nPad ){ u8 aPad[7] = {0,0,0,0,0,0,0}; nPad = 8-nPad; if( nPad==1 ){ aPad[0] = LSM_LOG_PAD1; }else{ aPad[0] = LSM_LOG_PAD2; aPad[1] = (u8)(nPad-2); } rc = lsmStringBinAppend(&pLog->buf, aPad, nPad); if( rc!=LSM_OK ) return rc; } /* Append the JUMP record to the buffer. Then flush the buffer to disk ** and update the checksums. The next write to the log file (assuming ** there is no transaction rollback) will be to offset iJump (just past ** the jump region). */ rc = lsmStringBinAppend(&pLog->buf, aJump, nJump); if( rc!=LSM_OK ) return rc; assert( (pLog->buf.n % 8)==0 ); rc = lsmFsWriteLog(pDb->pFS, pLog->iOff, &pLog->buf); if( rc!=LSM_OK ) return rc; logUpdateCksum(pLog, pLog->buf.n); pLog->iRegion1End = (pLog->iOff + pLog->buf.n); pLog->iRegion2Start = iJump; pLog->iOff = iJump; pLog->iCksumBuf = pLog->buf.n = 0; if( pbJump ) *pbJump = 1; } return LSM_OK; } static int logCksumAndFlush(lsm_db *pDb){ int rc; /* Return code */ LogWriter *pLog = pDb->pLogWriter; /* Calculate the checksum value. Append it to the buffer. */ logUpdateCksum(pLog, pLog->buf.n); lsmPutU32((u8 *)&pLog->buf.z[pLog->buf.n], pLog->cksum0); pLog->buf.n += 4; lsmPutU32((u8 *)&pLog->buf.z[pLog->buf.n], pLog->cksum1); pLog->buf.n += 4; /* Write the contents of the buffer to disk. */ rc = lsmFsWriteLog(pDb->pFS, pLog->iOff, &pLog->buf); pLog->iOff += pLog->buf.n; pLog->iCksumBuf = pLog->buf.n = 0; return rc; } /* ** Write the contents of the log-buffer to disk. Then write either a CKSUM ** or COMMIT record, depending on the value of parameter eType. */ static int logFlush(lsm_db *pDb, int eType){ int rc; int nReq; LogWriter *pLog = pDb->pLogWriter; assert( eType==LSM_LOG_COMMIT ); assert( pLog ); /* Commit record is always 9 bytes in size. */ nReq = 9; if( eType==LSM_LOG_COMMIT && pLog->szSector>1 ) nReq += pLog->szSector + 17; rc = jumpIfRequired(pDb, pLog, nReq, 0); /* If this is a COMMIT, add padding to the log so that the COMMIT record ** is aligned against the end of a disk sector. In other words, add padding ** so that the first byte following the COMMIT record lies on a different ** sector. */ if( eType==LSM_LOG_COMMIT && pLog->szSector>1 ){ int nPad; /* Bytes of padding to add */ /* Determine the value of nPad. */ nPad = ((pLog->iOff + pLog->buf.n + 9) % pLog->szSector); if( nPad ) nPad = pLog->szSector - nPad; rc = lsmStringExtend(&pLog->buf, nPad); if( rc!=LSM_OK ) return rc; while( nPad ){ if( nPad==1 ){ pLog->buf.z[pLog->buf.n++] = LSM_LOG_PAD1; nPad = 0; }else{ int n = LSM_MIN(200, nPad-2); pLog->buf.z[pLog->buf.n++] = LSM_LOG_PAD2; pLog->buf.z[pLog->buf.n++] = (char)n; nPad -= 2; memset(&pLog->buf.z[pLog->buf.n], 0x2B, n); pLog->buf.n += n; nPad -= n; } } } /* Make sure there is room in the log-buffer to add the CKSUM or COMMIT ** record. Then add the first byte of it. */ rc = lsmStringExtend(&pLog->buf, 9); if( rc!=LSM_OK ) return rc; pLog->buf.z[pLog->buf.n++] = (char)eType; memset(&pLog->buf.z[pLog->buf.n], 0, 8); rc = logCksumAndFlush(pDb); /* If this is a commit and synchronous=full, sync the log to disk. */ if( rc==LSM_OK && eType==LSM_LOG_COMMIT && pDb->eSafety==LSM_SAFETY_FULL ){ rc = lsmFsSyncLog(pDb->pFS); } return rc; } /* ** Append an LSM_LOG_WRITE (if nVal>=0) or LSM_LOG_DELETE (if nVal<0) ** record to the database log. */ int lsmLogWrite( lsm_db *pDb, /* Database handle */ int eType, void *pKey, int nKey, /* Database key to write to log */ void *pVal, int nVal /* Database value (or nVal<0) to write */ ){ int rc = LSM_OK; LogWriter *pLog; /* Log object to write to */ int nReq; /* Bytes of space required in log */ int bCksum = 0; /* True to embed a checksum in this record */ assert( eType==LSM_WRITE || eType==LSM_DELETE || eType==LSM_DRANGE ); assert( LSM_LOG_WRITE==LSM_WRITE ); assert( LSM_LOG_DELETE==LSM_DELETE ); assert( LSM_LOG_DRANGE==LSM_DRANGE ); assert( (eType==LSM_LOG_DELETE)==(nVal<0) ); if( pDb->bUseLog==0 ) return LSM_OK; pLog = pDb->pLogWriter; /* Determine how many bytes of space are required, assuming that a checksum ** will be embedded in this record (even though it may not be). */ nReq = 1 + lsmVarintLen32(nKey) + 8 + nKey; if( eType!=LSM_LOG_DELETE ) nReq += lsmVarintLen32(nVal) + nVal; /* Jump over the jump region if required. Set bCksum to true to tell the ** code below to include a checksum in the record if either (a) writing ** this record would mean that more than LSM_CKSUM_MAXDATA bytes of data ** have been written to the log since the last checksum, or (b) the jump ** is taken. */ rc = jumpIfRequired(pDb, pLog, nReq, &bCksum); if( (pLog->buf.n+nReq) > LSM_CKSUM_MAXDATA ) bCksum = 1; if( rc==LSM_OK ){ rc = lsmStringExtend(&pLog->buf, nReq); } if( rc==LSM_OK ){ u8 *a = (u8 *)&pLog->buf.z[pLog->buf.n]; /* Write the record header - the type byte followed by either 1 (for ** DELETE) or 2 (for WRITE) varints. */ assert( LSM_LOG_WRITE_CKSUM == (LSM_LOG_WRITE | 0x0001) ); assert( LSM_LOG_DELETE_CKSUM == (LSM_LOG_DELETE | 0x0001) ); assert( LSM_LOG_DRANGE_CKSUM == (LSM_LOG_DRANGE | 0x0001) ); *(a++) = (u8)eType | (u8)bCksum; a += lsmVarintPut32(a, nKey); if( eType!=LSM_LOG_DELETE ) a += lsmVarintPut32(a, nVal); if( bCksum ){ pLog->buf.n = (a - (u8 *)pLog->buf.z); rc = logCksumAndFlush(pDb); a = (u8 *)&pLog->buf.z[pLog->buf.n]; } memcpy(a, pKey, nKey); a += nKey; if( eType!=LSM_LOG_DELETE ){ memcpy(a, pVal, nVal); a += nVal; } pLog->buf.n = a - (u8 *)pLog->buf.z; assert( pLog->buf.n<=pLog->buf.nAlloc ); } return rc; } /* ** Append an LSM_LOG_COMMIT record to the database log. */ int lsmLogCommit(lsm_db *pDb){ if( pDb->bUseLog==0 ) return LSM_OK; return logFlush(pDb, LSM_LOG_COMMIT); } /* ** Store the current offset and other checksum related information in the ** structure *pMark. Later, *pMark can be passed to lsmLogSeek() to "rewind" ** the LogWriter object to the current log file offset. This is used when ** rolling back savepoint transactions. */ void lsmLogTell( lsm_db *pDb, /* Database handle */ LogMark *pMark /* Populate this object with current offset */ ){ LogWriter *pLog; int nCksum; if( pDb->bUseLog==0 ) return; pLog = pDb->pLogWriter; nCksum = pLog->buf.n & 0xFFFFFFF8; logUpdateCksum(pLog, nCksum); assert( pLog->iCksumBuf==nCksum ); pMark->nBuf = pLog->buf.n - nCksum; memcpy(pMark->aBuf, &pLog->buf.z[nCksum], pMark->nBuf); pMark->iOff = pLog->iOff + pLog->buf.n; pMark->cksum0 = pLog->cksum0; pMark->cksum1 = pLog->cksum1; } /* ** Seek (rewind) back to the log file offset stored by an ealier call to ** lsmLogTell() in *pMark. */ void lsmLogSeek( lsm_db *pDb, /* Database handle */ LogMark *pMark /* Object containing log offset to seek to */ ){ LogWriter *pLog; if( pDb->bUseLog==0 ) return; pLog = pDb->pLogWriter; assert( pMark->iOff<=pLog->iOff+pLog->buf.n ); if( (pMark->iOff & 0xFFFFFFF8)>=pLog->iOff ){ pLog->buf.n = (int)(pMark->iOff - pLog->iOff); pLog->iCksumBuf = (pLog->buf.n & 0xFFFFFFF8); }else{ pLog->buf.n = pMark->nBuf; memcpy(pLog->buf.z, pMark->aBuf, pMark->nBuf); pLog->iCksumBuf = 0; pLog->iOff = pMark->iOff - pMark->nBuf; } pLog->cksum0 = pMark->cksum0; pLog->cksum1 = pMark->cksum1; if( pMark->iOff > pLog->iRegion1End ) pLog->iRegion1End = 0; if( pMark->iOff > pLog->iRegion2Start ) pLog->iRegion2Start = 0; } /* ** This function does the work for an lsm_info(LOG_STRUCTURE) request. */ int lsmInfoLogStructure(lsm_db *pDb, char **pzVal){ int rc = LSM_OK; char *zVal = 0; /* If there is no read or write transaction open, read the latest ** tree-header from shared-memory to report on. If necessary, update ** it based on the contents of the database header. ** ** No locks are taken here - these are passive read operations only. */ if( pDb->pCsr==0 && pDb->nTransOpen==0 ){ rc = lsmTreeLoadHeader(pDb, 0); if( rc==LSM_OK ) rc = logReclaimSpace(pDb); } if( rc==LSM_OK ){ DbLog *pLog = &pDb->treehdr.log; zVal = lsmMallocPrintf(pDb->pEnv, "%d %d %d %d %d %d", (int)pLog->aRegion[0].iStart, (int)pLog->aRegion[0].iEnd, (int)pLog->aRegion[1].iStart, (int)pLog->aRegion[1].iEnd, (int)pLog->aRegion[2].iStart, (int)pLog->aRegion[2].iEnd ); if( !zVal ) rc = LSM_NOMEM_BKPT; } *pzVal = zVal; return rc; } /************************************************************************* ** Begin code for log recovery. */ typedef struct LogReader LogReader; struct LogReader { FileSystem *pFS; /* File system to read from */ i64 iOff; /* File offset at end of buf content */ int iBuf; /* Current read offset in buf */ LsmString buf; /* Buffer containing file content */ int iCksumBuf; /* Offset in buf corresponding to cksum[01] */ u32 cksum0; /* Checksum 0 at offset iCksumBuf */ u32 cksum1; /* Checksum 1 at offset iCksumBuf */ }; static void logReaderBlob( LogReader *p, /* Log reader object */ LsmString *pBuf, /* Dynamic storage, if required */ int nBlob, /* Number of bytes to read */ u8 **ppBlob, /* OUT: Pointer to blob read */ int *pRc /* IN/OUT: Error code */ ){ static const int LOG_READ_SIZE = 512; int rc = *pRc; /* Return code */ int nReq = nBlob; /* Bytes required */ while( rc==LSM_OK && nReq>0 ){ int nAvail; /* Bytes of data available in p->buf */ if( p->buf.n==p->iBuf ){ int nCksum; /* Total bytes requiring checksum */ int nCarry = 0; /* Total bytes requiring checksum */ nCksum = p->iBuf - p->iCksumBuf; if( nCksum>0 ){ nCarry = nCksum % 8; nCksum = ((nCksum / 8) * 8); if( nCksum>0 ){ logCksumUnaligned( &p->buf.z[p->iCksumBuf], nCksum, &p->cksum0, &p->cksum1 ); } } if( nCarry>0 ) memcpy(p->buf.z, &p->buf.z[p->iBuf-nCarry], nCarry); p->buf.n = nCarry; p->iBuf = nCarry; rc = lsmFsReadLog(p->pFS, p->iOff, LOG_READ_SIZE, &p->buf); if( rc!=LSM_OK ) break; p->iCksumBuf = 0; p->iOff += LOG_READ_SIZE; } nAvail = p->buf.n - p->iBuf; if( ppBlob && nReq==nBlob && nBlob<=nAvail ){ *ppBlob = (u8 *)&p->buf.z[p->iBuf]; p->iBuf += nBlob; nReq = 0; }else{ int nCopy = LSM_MIN(nAvail, nReq); if( nBlob==nReq ){ pBuf->n = 0; } rc = lsmStringBinAppend(pBuf, (u8 *)&p->buf.z[p->iBuf], nCopy); nReq -= nCopy; p->iBuf += nCopy; if( nReq==0 && ppBlob ){ *ppBlob = (u8*)pBuf->z; } } } *pRc = rc; } static void logReaderVarint( LogReader *p, LsmString *pBuf, int *piVal, /* OUT: Value read from log */ int *pRc /* IN/OUT: Error code */ ){ if( *pRc==LSM_OK ){ u8 *aVarint; if( p->buf.n==p->iBuf ){ logReaderBlob(p, 0, 10, &aVarint, pRc); if( LSM_OK==*pRc ) p->iBuf -= (10 - lsmVarintGet32(aVarint, piVal)); }else{ logReaderBlob(p, pBuf, lsmVarintSize(p->buf.z[p->iBuf]), &aVarint, pRc); if( LSM_OK==*pRc ) lsmVarintGet32(aVarint, piVal); } } } static void logReaderByte(LogReader *p, u8 *pByte, int *pRc){ u8 *pPtr = 0; logReaderBlob(p, 0, 1, &pPtr, pRc); if( pPtr ) *pByte = *pPtr; } static void logReaderCksum(LogReader *p, LsmString *pBuf, int *pbEof, int *pRc){ if( *pRc==LSM_OK ){ u8 *pPtr = 0; u32 cksum0, cksum1; int nCksum = p->iBuf - p->iCksumBuf; /* Update in-memory (expected) checksums */ assert( nCksum>=0 ); logCksumUnaligned(&p->buf.z[p->iCksumBuf], nCksum, &p->cksum0, &p->cksum1); p->iCksumBuf = p->iBuf + 8; logReaderBlob(p, pBuf, 8, &pPtr, pRc); assert( pPtr || *pRc ); /* Read the checksums from the log file. Set *pbEof if they do not match. */ if( pPtr ){ cksum0 = lsmGetU32(pPtr); cksum1 = lsmGetU32(&pPtr[4]); *pbEof = (cksum0!=p->cksum0 || cksum1!=p->cksum1); p->iCksumBuf = p->iBuf; } } } static void logReaderInit( lsm_db *pDb, /* Database handle */ DbLog *pLog, /* Log object associated with pDb */ int bInitBuf, /* True if p->buf is uninitialized */ LogReader *p /* Initialize this LogReader object */ ){ p->pFS = pDb->pFS; p->iOff = pLog->aRegion[2].iStart; p->cksum0 = pLog->cksum0; p->cksum1 = pLog->cksum1; if( bInitBuf ){ lsmStringInit(&p->buf, pDb->pEnv); } p->buf.n = 0; p->iCksumBuf = 0; p->iBuf = 0; } /* ** This function is called after reading the header of a LOG_DELETE or ** LOG_WRITE record. Parameter nByte is the total size of the key and ** value that follow the header just read. Return true if the size and ** position of the record indicate that it should contain a checksum. */ static int logRequireCksum(LogReader *p, int nByte){ return ((p->iBuf + nByte - p->iCksumBuf) > LSM_CKSUM_MAXDATA); } /* ** Recover the contents of the log file. */ int lsmLogRecover(lsm_db *pDb){ LsmString buf1; /* Key buffer */ LsmString buf2; /* Value buffer */ LogReader reader; /* Log reader object */ int rc = LSM_OK; /* Return code */ int nCommit = 0; /* Number of transactions to recover */ int iPass; int nJump = 0; /* Number of LSM_LOG_JUMP records in pass 0 */ DbLog *pLog; int bOpen; rc = lsmFsOpenLog(pDb, &bOpen); if( rc!=LSM_OK ) return rc; rc = lsmTreeInit(pDb); if( rc!=LSM_OK ) return rc; pLog = &pDb->treehdr.log; lsmCheckpointLogoffset(pDb->pShmhdr->aSnap2, pLog); logReaderInit(pDb, pLog, 1, &reader); lsmStringInit(&buf1, pDb->pEnv); lsmStringInit(&buf2, pDb->pEnv); /* The outer for() loop runs at most twice. The first iteration is to ** count the number of committed transactions in the log. The second ** iterates through those transactions and updates the in-memory tree ** structure with their contents. */ if( bOpen ){ for(iPass=0; iPass<2 && rc==LSM_OK; iPass++){ int bEof = 0; while( rc==LSM_OK && !bEof ){ u8 eType = 0; logReaderByte(&reader, &eType, &rc); switch( eType ){ case LSM_LOG_PAD1: break; case LSM_LOG_PAD2: { int nPad; logReaderVarint(&reader, &buf1, &nPad, &rc); logReaderBlob(&reader, &buf1, nPad, 0, &rc); break; } case LSM_LOG_DRANGE: case LSM_LOG_DRANGE_CKSUM: case LSM_LOG_WRITE: case LSM_LOG_WRITE_CKSUM: { int nKey; int nVal; u8 *aVal; logReaderVarint(&reader, &buf1, &nKey, &rc); logReaderVarint(&reader, &buf2, &nVal, &rc); if( eType==LSM_LOG_WRITE_CKSUM || eType==LSM_LOG_DRANGE_CKSUM ){ logReaderCksum(&reader, &buf1, &bEof, &rc); }else{ bEof = logRequireCksum(&reader, nKey+nVal); } if( bEof ) break; logReaderBlob(&reader, &buf1, nKey, 0, &rc); logReaderBlob(&reader, &buf2, nVal, &aVal, &rc); if( iPass==1 && rc==LSM_OK ){ if( eType==LSM_LOG_WRITE || eType==LSM_LOG_WRITE_CKSUM ){ rc = lsmTreeInsert(pDb, (u8 *)buf1.z, nKey, aVal, nVal); }else{ rc = lsmTreeDelete(pDb, (u8 *)buf1.z, nKey, aVal, nVal); } } break; } case LSM_LOG_DELETE: case LSM_LOG_DELETE_CKSUM: { int nKey; u8 *aKey; logReaderVarint(&reader, &buf1, &nKey, &rc); if( eType==LSM_LOG_DELETE_CKSUM ){ logReaderCksum(&reader, &buf1, &bEof, &rc); }else{ bEof = logRequireCksum(&reader, nKey); } if( bEof ) break; logReaderBlob(&reader, &buf1, nKey, &aKey, &rc); if( iPass==1 && rc==LSM_OK ){ rc = lsmTreeInsert(pDb, aKey, nKey, NULL, -1); } break; } case LSM_LOG_COMMIT: logReaderCksum(&reader, &buf1, &bEof, &rc); if( bEof==0 ){ nCommit++; assert( nCommit>0 || iPass==1 ); if( nCommit==0 ) bEof = 1; } break; case LSM_LOG_JUMP: { int iOff = 0; logReaderVarint(&reader, &buf1, &iOff, &rc); if( rc==LSM_OK ){ if( iPass==1 ){ if( pLog->aRegion[2].iStart==0 ){ assert( pLog->aRegion[1].iStart==0 ); pLog->aRegion[1].iEnd = reader.iOff; }else{ assert( pLog->aRegion[0].iStart==0 ); pLog->aRegion[0].iStart = pLog->aRegion[2].iStart; pLog->aRegion[0].iEnd = reader.iOff-reader.buf.n+reader.iBuf; } pLog->aRegion[2].iStart = iOff; }else{ if( (nJump++)==2 ){ bEof = 1; } } reader.iOff = iOff; reader.buf.n = reader.iBuf; } break; } default: /* Including LSM_LOG_EOF */ bEof = 1; break; } } if( rc==LSM_OK && iPass==0 ){ if( nCommit==0 ){ if( pLog->aRegion[2].iStart==0 ){ iPass = 1; }else{ pLog->aRegion[2].iStart = 0; iPass = -1; lsmCheckpointZeroLogoffset(pDb); } } logReaderInit(pDb, pLog, 0, &reader); nCommit = nCommit * -1; } } } /* Initialize DbLog object */ if( rc==LSM_OK ){ pLog->aRegion[2].iEnd = reader.iOff - reader.buf.n + reader.iBuf; pLog->cksum0 = reader.cksum0; pLog->cksum1 = reader.cksum1; } if( rc==LSM_OK ){ rc = lsmFinishRecovery(pDb); }else{ lsmFinishRecovery(pDb); } if( pDb->bRoTrans ){ lsmFsCloseLog(pDb); } lsmStringClear(&buf1); lsmStringClear(&buf2); lsmStringClear(&reader.buf); return rc; } void lsmLogClose(lsm_db *db){ if( db->pLogWriter ){ lsmFree(db->pEnv, db->pLogWriter->buf.z); lsmFree(db->pEnv, db->pLogWriter); db->pLogWriter = 0; } } |
Added ext/lsm1/lsm_main.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 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 | /* ** 2011-08-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. ** ************************************************************************* ** ** The main interface to the LSM module. */ #include "lsmInt.h" #ifdef LSM_DEBUG /* ** This function returns a copy of its only argument. ** ** When the library is built with LSM_DEBUG defined, this function is called ** whenever an error code is generated (not propagated - generated). So ** if the library is mysteriously returning (say) LSM_IOERR, a breakpoint ** may be set in this function to determine why. */ int lsmErrorBkpt(int rc){ /* Set breakpoint here! */ return rc; } /* ** This function contains various assert() statements that test that the ** lsm_db structure passed as an argument is internally consistent. */ static void assert_db_state(lsm_db *pDb){ /* If there is at least one cursor or a write transaction open, the database ** handle must be holding a pointer to a client snapshot. And the reverse ** - if there are no open cursors and no write transactions then there must ** not be a client snapshot. */ assert( (pDb->pCsr!=0||pDb->nTransOpen>0)==(pDb->iReader>=0||pDb->bRoTrans) ); assert( (pDb->iReader<0 && pDb->bRoTrans==0) || pDb->pClient!=0 ); assert( pDb->nTransOpen>=0 ); } #else # define assert_db_state(x) #endif /* ** The default key-compare function. */ static int xCmp(void *p1, int n1, void *p2, int n2){ int res; res = memcmp(p1, p2, LSM_MIN(n1, n2)); if( res==0 ) res = (n1-n2); return res; } static void xLog(void *pCtx, int rc, const char *z){ (void)(rc); (void)(pCtx); fprintf(stderr, "%s\n", z); fflush(stderr); } /* ** Allocate a new db handle. */ int lsm_new(lsm_env *pEnv, lsm_db **ppDb){ lsm_db *pDb; /* If the user did not provide an environment, use the default. */ if( pEnv==0 ) pEnv = lsm_default_env(); assert( pEnv ); /* Allocate the new database handle */ *ppDb = pDb = (lsm_db *)lsmMallocZero(pEnv, sizeof(lsm_db)); if( pDb==0 ) return LSM_NOMEM_BKPT; /* Initialize the new object */ pDb->pEnv = pEnv; pDb->nTreeLimit = LSM_DFLT_AUTOFLUSH; pDb->nAutockpt = LSM_DFLT_AUTOCHECKPOINT; pDb->bAutowork = LSM_DFLT_AUTOWORK; pDb->eSafety = LSM_DFLT_SAFETY; pDb->xCmp = xCmp; pDb->nDfltPgsz = LSM_DFLT_PAGE_SIZE; pDb->nDfltBlksz = LSM_DFLT_BLOCK_SIZE; pDb->nMerge = LSM_DFLT_AUTOMERGE; pDb->nMaxFreelist = LSM_MAX_FREELIST_ENTRIES; pDb->bUseLog = LSM_DFLT_USE_LOG; pDb->iReader = -1; pDb->iRwclient = -1; pDb->bMultiProc = LSM_DFLT_MULTIPLE_PROCESSES; pDb->iMmap = LSM_DFLT_MMAP; pDb->xLog = xLog; pDb->compress.iId = LSM_COMPRESSION_NONE; return LSM_OK; } lsm_env *lsm_get_env(lsm_db *pDb){ assert( pDb->pEnv ); return pDb->pEnv; } /* ** If database handle pDb is currently holding a client snapshot, but does ** not have any open cursors or write transactions, release it. */ static void dbReleaseClientSnapshot(lsm_db *pDb){ if( pDb->nTransOpen==0 && pDb->pCsr==0 ){ lsmFinishReadTrans(pDb); } } static int getFullpathname( lsm_env *pEnv, const char *zRel, char **pzAbs ){ int nAlloc = 0; char *zAlloc = 0; int nReq = 0; int rc; do{ nAlloc = nReq; rc = pEnv->xFullpath(pEnv, zRel, zAlloc, &nReq); if( nReq>nAlloc ){ zAlloc = lsmReallocOrFreeRc(pEnv, zAlloc, nReq, &rc); } }while( nReq>nAlloc && rc==LSM_OK ); if( rc!=LSM_OK ){ lsmFree(pEnv, zAlloc); zAlloc = 0; } *pzAbs = zAlloc; return rc; } /* ** Check that the bits in the db->mLock mask are consistent with the ** value stored in db->iRwclient. An assert shall fail otherwise. */ static void assertRwclientLockValue(lsm_db *db){ #ifndef NDEBUG u64 msk; /* Mask of mLock bits for RWCLIENT locks */ u64 rwclient = 0; /* Bit corresponding to db->iRwclient */ if( db->iRwclient>=0 ){ rwclient = ((u64)1 << (LSM_LOCK_RWCLIENT(db->iRwclient)-1)); } msk = ((u64)1 << (LSM_LOCK_RWCLIENT(LSM_LOCK_NRWCLIENT)-1)) - 1; msk -= (((u64)1 << (LSM_LOCK_RWCLIENT(0)-1)) - 1); assert( (db->mLock & msk)==rwclient ); #endif } /* ** Open a new connection to database zFilename. */ int lsm_open(lsm_db *pDb, const char *zFilename){ int rc; if( pDb->pDatabase ){ rc = LSM_MISUSE; }else{ char *zFull; /* Translate the possibly relative pathname supplied by the user into ** an absolute pathname. This is required because the supplied path ** is used (either directly or with "-log" appended to it) for more ** than one purpose - to open both the database and log files, and ** perhaps to unlink the log file during disconnection. An absolute ** path is required to ensure that the correct files are operated ** on even if the application changes the cwd. */ rc = getFullpathname(pDb->pEnv, zFilename, &zFull); assert( rc==LSM_OK || zFull==0 ); /* Connect to the database. */ if( rc==LSM_OK ){ rc = lsmDbDatabaseConnect(pDb, zFull); } if( pDb->bReadonly==0 ){ /* Configure the file-system connection with the page-size and block-size ** of this database. Even if the database file is zero bytes in size ** on disk, these values have been set in shared-memory by now, and so ** are guaranteed not to change during the lifetime of this connection. */ if( rc==LSM_OK && LSM_OK==(rc = lsmCheckpointLoad(pDb, 0)) ){ lsmFsSetPageSize(pDb->pFS, lsmCheckpointPgsz(pDb->aSnapshot)); lsmFsSetBlockSize(pDb->pFS, lsmCheckpointBlksz(pDb->aSnapshot)); } } lsmFree(pDb->pEnv, zFull); assertRwclientLockValue(pDb); } assert( pDb->bReadonly==0 || pDb->bReadonly==1 ); assert( rc!=LSM_OK || (pDb->pShmhdr==0)==(pDb->bReadonly==1) ); return rc; } int lsm_close(lsm_db *pDb){ int rc = LSM_OK; if( pDb ){ assert_db_state(pDb); if( pDb->pCsr || pDb->nTransOpen ){ rc = LSM_MISUSE_BKPT; }else{ lsmMCursorFreeCache(pDb); lsmFreeSnapshot(pDb->pEnv, pDb->pClient); pDb->pClient = 0; assertRwclientLockValue(pDb); lsmDbDatabaseRelease(pDb); lsmLogClose(pDb); lsmFsClose(pDb->pFS); /* assert( pDb->mLock==0 ); */ /* Invoke any destructors registered for the compression or ** compression factory callbacks. */ if( pDb->factory.xFree ) pDb->factory.xFree(pDb->factory.pCtx); if( pDb->compress.xFree ) pDb->compress.xFree(pDb->compress.pCtx); lsmFree(pDb->pEnv, pDb->rollback.aArray); lsmFree(pDb->pEnv, pDb->aTrans); lsmFree(pDb->pEnv, pDb->apShm); lsmFree(pDb->pEnv, pDb); } } return rc; } int lsm_config(lsm_db *pDb, int eParam, ...){ int rc = LSM_OK; va_list ap; va_start(ap, eParam); switch( eParam ){ case LSM_CONFIG_AUTOFLUSH: { /* This parameter is read and written in KB. But all internal ** processing is done in bytes. */ int *piVal = va_arg(ap, int *); int iVal = *piVal; if( iVal>=0 && iVal<=(1024*1024) ){ pDb->nTreeLimit = iVal*1024; } *piVal = (pDb->nTreeLimit / 1024); break; } case LSM_CONFIG_AUTOWORK: { int *piVal = va_arg(ap, int *); if( *piVal>=0 ){ pDb->bAutowork = *piVal; } *piVal = pDb->bAutowork; break; } case LSM_CONFIG_AUTOCHECKPOINT: { /* This parameter is read and written in KB. But all internal processing ** (including the lsm_db.nAutockpt variable) is done in bytes. */ int *piVal = va_arg(ap, int *); if( *piVal>=0 ){ int iVal = *piVal; pDb->nAutockpt = (i64)iVal * 1024; } *piVal = (int)(pDb->nAutockpt / 1024); break; } case LSM_CONFIG_PAGE_SIZE: { int *piVal = va_arg(ap, int *); if( pDb->pDatabase ){ /* If lsm_open() has been called, this is a read-only parameter. ** Set the output variable to the page-size according to the ** FileSystem object. */ *piVal = lsmFsPageSize(pDb->pFS); }else{ if( *piVal>=256 && *piVal<=65536 && ((*piVal-1) & *piVal)==0 ){ pDb->nDfltPgsz = *piVal; }else{ *piVal = pDb->nDfltPgsz; } } break; } case LSM_CONFIG_BLOCK_SIZE: { /* This parameter is read and written in KB. But all internal ** processing is done in bytes. */ int *piVal = va_arg(ap, int *); if( pDb->pDatabase ){ /* If lsm_open() has been called, this is a read-only parameter. ** Set the output variable to the block-size in KB according to the ** FileSystem object. */ *piVal = lsmFsBlockSize(pDb->pFS) / 1024; }else{ int iVal = *piVal; if( iVal>=64 && iVal<=65536 && ((iVal-1) & iVal)==0 ){ pDb->nDfltBlksz = iVal * 1024; }else{ *piVal = pDb->nDfltBlksz / 1024; } } break; } case LSM_CONFIG_SAFETY: { int *piVal = va_arg(ap, int *); if( *piVal>=0 && *piVal<=2 ){ pDb->eSafety = *piVal; } *piVal = pDb->eSafety; break; } case LSM_CONFIG_MMAP: { int *piVal = va_arg(ap, int *); if( pDb->iReader<0 && *piVal>=0 ){ pDb->iMmap = *piVal; rc = lsmFsConfigure(pDb); } *piVal = pDb->iMmap; break; } case LSM_CONFIG_USE_LOG: { int *piVal = va_arg(ap, int *); if( pDb->nTransOpen==0 && (*piVal==0 || *piVal==1) ){ pDb->bUseLog = *piVal; } *piVal = pDb->bUseLog; break; } case LSM_CONFIG_AUTOMERGE: { int *piVal = va_arg(ap, int *); if( *piVal>1 ) pDb->nMerge = *piVal; *piVal = pDb->nMerge; break; } case LSM_CONFIG_MAX_FREELIST: { int *piVal = va_arg(ap, int *); if( *piVal>=2 && *piVal<=LSM_MAX_FREELIST_ENTRIES ){ pDb->nMaxFreelist = *piVal; } *piVal = pDb->nMaxFreelist; break; } case LSM_CONFIG_MULTIPLE_PROCESSES: { int *piVal = va_arg(ap, int *); if( pDb->pDatabase ){ /* If lsm_open() has been called, this is a read-only parameter. ** Set the output variable to true if this connection is currently ** in multi-process mode. */ *piVal = lsmDbMultiProc(pDb); }else{ pDb->bMultiProc = *piVal = (*piVal!=0); } break; } case LSM_CONFIG_READONLY: { int *piVal = va_arg(ap, int *); /* If lsm_open() has been called, this is a read-only parameter. */ if( pDb->pDatabase==0 && *piVal>=0 ){ pDb->bReadonly = *piVal = (*piVal!=0); } *piVal = pDb->bReadonly; break; } case LSM_CONFIG_SET_COMPRESSION: { lsm_compress *p = va_arg(ap, lsm_compress *); if( pDb->iReader>=0 && pDb->bInFactory==0 ){ /* May not change compression schemes with an open transaction */ rc = LSM_MISUSE_BKPT; }else{ if( pDb->compress.xFree ){ /* Invoke any destructor belonging to the current compression. */ pDb->compress.xFree(pDb->compress.pCtx); } if( p->xBound==0 ){ memset(&pDb->compress, 0, sizeof(lsm_compress)); pDb->compress.iId = LSM_COMPRESSION_NONE; }else{ memcpy(&pDb->compress, p, sizeof(lsm_compress)); } rc = lsmFsConfigure(pDb); } break; } case LSM_CONFIG_SET_COMPRESSION_FACTORY: { lsm_compress_factory *p = va_arg(ap, lsm_compress_factory *); if( pDb->factory.xFree ){ /* Invoke any destructor belonging to the current factory. */ pDb->factory.xFree(pDb->factory.pCtx); } memcpy(&pDb->factory, p, sizeof(lsm_compress_factory)); break; } case LSM_CONFIG_GET_COMPRESSION: { lsm_compress *p = va_arg(ap, lsm_compress *); memcpy(p, &pDb->compress, sizeof(lsm_compress)); break; } default: rc = LSM_MISUSE; break; } va_end(ap); return rc; } void lsmAppendSegmentList(LsmString *pStr, char *zPre, Segment *pSeg){ lsmStringAppendf(pStr, "%s{%d %d %d %d}", zPre, pSeg->iFirst, pSeg->iLastPg, pSeg->iRoot, pSeg->nSize ); } static int infoGetWorker(lsm_db *pDb, Snapshot **pp, int *pbUnlock){ int rc = LSM_OK; assert( *pbUnlock==0 ); if( !pDb->pWorker ){ rc = lsmBeginWork(pDb); if( rc!=LSM_OK ) return rc; *pbUnlock = 1; } if( pp ) *pp = pDb->pWorker; return rc; } static void infoFreeWorker(lsm_db *pDb, int bUnlock){ if( bUnlock ){ int rcdummy = LSM_BUSY; lsmFinishWork(pDb, 0, &rcdummy); } } int lsmStructList( lsm_db *pDb, /* Database handle */ char **pzOut /* OUT: Nul-terminated string (tcl list) */ ){ Level *pTopLevel = 0; /* Top level of snapshot to report on */ int rc = LSM_OK; Level *p; LsmString s; Snapshot *pWorker; /* Worker snapshot */ int bUnlock = 0; /* Obtain the worker snapshot */ rc = infoGetWorker(pDb, &pWorker, &bUnlock); if( rc!=LSM_OK ) return rc; /* Format the contents of the snapshot as text */ pTopLevel = lsmDbSnapshotLevel(pWorker); lsmStringInit(&s, pDb->pEnv); for(p=pTopLevel; rc==LSM_OK && p; p=p->pNext){ int i; lsmStringAppendf(&s, "%s{%d", (s.n ? " " : ""), (int)p->iAge); lsmAppendSegmentList(&s, " ", &p->lhs); for(i=0; rc==LSM_OK && i<p->nRight; i++){ lsmAppendSegmentList(&s, " ", &p->aRhs[i]); } lsmStringAppend(&s, "}", 1); } rc = s.n>=0 ? LSM_OK : LSM_NOMEM; /* Release the snapshot and return */ infoFreeWorker(pDb, bUnlock); *pzOut = s.z; return rc; } static int infoFreelistCb(void *pCtx, int iBlk, i64 iSnapshot){ LsmString *pStr = (LsmString *)pCtx; lsmStringAppendf(pStr, "%s{%d %lld}", (pStr->n?" ":""), iBlk, iSnapshot); return 0; } int lsmInfoFreelist(lsm_db *pDb, char **pzOut){ Snapshot *pWorker; /* Worker snapshot */ int bUnlock = 0; LsmString s; int rc; /* Obtain the worker snapshot */ rc = infoGetWorker(pDb, &pWorker, &bUnlock); if( rc!=LSM_OK ) return rc; lsmStringInit(&s, pDb->pEnv); rc = lsmWalkFreelist(pDb, 0, infoFreelistCb, &s); if( rc!=LSM_OK ){ lsmFree(pDb->pEnv, s.z); }else{ *pzOut = s.z; } /* Release the snapshot and return */ infoFreeWorker(pDb, bUnlock); return rc; } static int infoTreeSize(lsm_db *db, int *pnOldKB, int *pnNewKB){ ShmHeader *pShm = db->pShmhdr; TreeHeader *p = &pShm->hdr1; /* The following code suffers from two race conditions, as it accesses and ** trusts the contents of shared memory without verifying checksums: ** ** * The two values read - TreeHeader.root.nByte and oldroot.nByte - are ** 32-bit fields. It is assumed that reading from one of these ** is atomic - that it is not possible to read a partially written ** garbage value. However the two values may be mutually inconsistent. ** ** * TreeHeader.iLogOff is a 64-bit value. And lsmCheckpointLogOffset() ** reads a 64-bit value from a snapshot stored in shared memory. It ** is assumed that in each case it is possible to read a partially ** written garbage value. If this occurs, then the value returned ** for the size of the "old" tree may reflect the size of an "old" ** tree that was recently flushed to disk. ** ** Given the context in which this function is called (as a result of an ** lsm_info(LSM_INFO_TREE_SIZE) request), neither of these are considered to ** be problems. */ *pnNewKB = ((int)p->root.nByte + 1023) / 1024; if( p->iOldShmid ){ if( p->iOldLog==lsmCheckpointLogOffset(pShm->aSnap1) ){ *pnOldKB = 0; }else{ *pnOldKB = ((int)p->oldroot.nByte + 1023) / 1024; } }else{ *pnOldKB = 0; } return LSM_OK; } int lsm_info(lsm_db *pDb, int eParam, ...){ int rc = LSM_OK; va_list ap; va_start(ap, eParam); switch( eParam ){ case LSM_INFO_NWRITE: { int *piVal = va_arg(ap, int *); *piVal = lsmFsNWrite(pDb->pFS); break; } case LSM_INFO_NREAD: { int *piVal = va_arg(ap, int *); *piVal = lsmFsNRead(pDb->pFS); break; } case LSM_INFO_DB_STRUCTURE: { char **pzVal = va_arg(ap, char **); rc = lsmStructList(pDb, pzVal); break; } case LSM_INFO_ARRAY_STRUCTURE: { Pgno pgno = va_arg(ap, Pgno); char **pzVal = va_arg(ap, char **); rc = lsmInfoArrayStructure(pDb, 0, pgno, pzVal); break; } case LSM_INFO_ARRAY_PAGES: { Pgno pgno = va_arg(ap, Pgno); char **pzVal = va_arg(ap, char **); rc = lsmInfoArrayPages(pDb, pgno, pzVal); break; } case LSM_INFO_PAGE_HEX_DUMP: case LSM_INFO_PAGE_ASCII_DUMP: { Pgno pgno = va_arg(ap, Pgno); char **pzVal = va_arg(ap, char **); int bUnlock = 0; rc = infoGetWorker(pDb, 0, &bUnlock); if( rc==LSM_OK ){ int bHex = (eParam==LSM_INFO_PAGE_HEX_DUMP); rc = lsmInfoPageDump(pDb, pgno, bHex, pzVal); } infoFreeWorker(pDb, bUnlock); break; } case LSM_INFO_LOG_STRUCTURE: { char **pzVal = va_arg(ap, char **); rc = lsmInfoLogStructure(pDb, pzVal); break; } case LSM_INFO_FREELIST: { char **pzVal = va_arg(ap, char **); rc = lsmInfoFreelist(pDb, pzVal); break; } case LSM_INFO_CHECKPOINT_SIZE: { int *pnKB = va_arg(ap, int *); rc = lsmCheckpointSize(pDb, pnKB); break; } case LSM_INFO_TREE_SIZE: { int *pnOld = va_arg(ap, int *); int *pnNew = va_arg(ap, int *); rc = infoTreeSize(pDb, pnOld, pnNew); break; } case LSM_INFO_COMPRESSION_ID: { unsigned int *piOut = va_arg(ap, unsigned int *); if( pDb->pClient ){ *piOut = pDb->pClient->iCmpId; }else{ rc = lsmInfoCompressionId(pDb, piOut); } break; } default: rc = LSM_MISUSE; break; } va_end(ap); return rc; } static int doWriteOp( lsm_db *pDb, int bDeleteRange, const void *pKey, int nKey, /* Key to write or delete */ const void *pVal, int nVal /* Value to write. Or nVal==-1 for a delete */ ){ int rc = LSM_OK; /* Return code */ int bCommit = 0; /* True to commit before returning */ if( pDb->nTransOpen==0 ){ bCommit = 1; rc = lsm_begin(pDb, 1); } if( rc==LSM_OK ){ int eType = (bDeleteRange ? LSM_DRANGE : (nVal>=0?LSM_WRITE:LSM_DELETE)); rc = lsmLogWrite(pDb, eType, (void *)pKey, nKey, (void *)pVal, nVal); } lsmSortedSaveTreeCursors(pDb); if( rc==LSM_OK ){ int pgsz = lsmFsPageSize(pDb->pFS); int nQuant = LSM_AUTOWORK_QUANT * pgsz; int nBefore; int nAfter; int nDiff; if( nQuant>pDb->nTreeLimit ){ nQuant = pDb->nTreeLimit; } nBefore = lsmTreeSize(pDb); if( bDeleteRange ){ rc = lsmTreeDelete(pDb, (void *)pKey, nKey, (void *)pVal, nVal); }else{ rc = lsmTreeInsert(pDb, (void *)pKey, nKey, (void *)pVal, nVal); } nAfter = lsmTreeSize(pDb); nDiff = (nAfter/nQuant) - (nBefore/nQuant); if( rc==LSM_OK && pDb->bAutowork && nDiff!=0 ){ rc = lsmSortedAutoWork(pDb, nDiff * LSM_AUTOWORK_QUANT); } } /* If a transaction was opened at the start of this function, commit it. ** Or, if an error has occurred, roll it back. */ if( bCommit ){ if( rc==LSM_OK ){ rc = lsm_commit(pDb, 0); }else{ lsm_rollback(pDb, 0); } } return rc; } /* ** Write a new value into the database. */ int lsm_insert( lsm_db *db, /* Database connection */ const void *pKey, int nKey, /* Key to write or delete */ const void *pVal, int nVal /* Value to write. Or nVal==-1 for a delete */ ){ return doWriteOp(db, 0, pKey, nKey, pVal, nVal); } /* ** Delete a value from the database. */ int lsm_delete(lsm_db *db, const void *pKey, int nKey){ return doWriteOp(db, 0, pKey, nKey, 0, -1); } /* ** Delete a range of database keys. */ int lsm_delete_range( lsm_db *db, /* Database handle */ const void *pKey1, int nKey1, /* Lower bound of range to delete */ const void *pKey2, int nKey2 /* Upper bound of range to delete */ ){ int rc = LSM_OK; if( db->xCmp((void *)pKey1, nKey1, (void *)pKey2, nKey2)<0 ){ rc = doWriteOp(db, 1, pKey1, nKey1, pKey2, nKey2); } return rc; } /* ** Open a new cursor handle. ** ** If there are currently no other open cursor handles, and no open write ** transaction, open a read transaction here. */ int lsm_csr_open(lsm_db *pDb, lsm_cursor **ppCsr){ int rc = LSM_OK; /* Return code */ MultiCursor *pCsr = 0; /* New cursor object */ /* Open a read transaction if one is not already open. */ assert_db_state(pDb); if( pDb->pShmhdr==0 ){ assert( pDb->bReadonly ); rc = lsmBeginRoTrans(pDb); }else if( pDb->iReader<0 ){ rc = lsmBeginReadTrans(pDb); } /* Allocate the multi-cursor. */ if( rc==LSM_OK ){ rc = lsmMCursorNew(pDb, &pCsr); } /* If an error has occured, set the output to NULL and delete any partially ** allocated cursor. If this means there are no open cursors, release the ** client snapshot. */ if( rc!=LSM_OK ){ lsmMCursorClose(pCsr, 0); dbReleaseClientSnapshot(pDb); } assert_db_state(pDb); *ppCsr = (lsm_cursor *)pCsr; return rc; } /* ** Close a cursor opened using lsm_csr_open(). */ int lsm_csr_close(lsm_cursor *p){ if( p ){ lsm_db *pDb = lsmMCursorDb((MultiCursor *)p); assert_db_state(pDb); lsmMCursorClose((MultiCursor *)p, 1); dbReleaseClientSnapshot(pDb); assert_db_state(pDb); } return LSM_OK; } /* ** Attempt to seek the cursor to the database entry specified by pKey/nKey. ** If an error occurs (e.g. an OOM or IO error), return an LSM error code. ** Otherwise, return LSM_OK. */ int lsm_csr_seek(lsm_cursor *pCsr, const void *pKey, int nKey, int eSeek){ return lsmMCursorSeek((MultiCursor *)pCsr, 0, (void *)pKey, nKey, eSeek); } int lsm_csr_next(lsm_cursor *pCsr){ return lsmMCursorNext((MultiCursor *)pCsr); } int lsm_csr_prev(lsm_cursor *pCsr){ return lsmMCursorPrev((MultiCursor *)pCsr); } int lsm_csr_first(lsm_cursor *pCsr){ return lsmMCursorFirst((MultiCursor *)pCsr); } int lsm_csr_last(lsm_cursor *pCsr){ return lsmMCursorLast((MultiCursor *)pCsr); } int lsm_csr_valid(lsm_cursor *pCsr){ return lsmMCursorValid((MultiCursor *)pCsr); } int lsm_csr_key(lsm_cursor *pCsr, const void **ppKey, int *pnKey){ return lsmMCursorKey((MultiCursor *)pCsr, (void **)ppKey, pnKey); } int lsm_csr_value(lsm_cursor *pCsr, const void **ppVal, int *pnVal){ return lsmMCursorValue((MultiCursor *)pCsr, (void **)ppVal, pnVal); } void lsm_config_log( lsm_db *pDb, void (*xLog)(void *, int, const char *), void *pCtx ){ pDb->xLog = xLog; pDb->pLogCtx = pCtx; } void lsm_config_work_hook( lsm_db *pDb, void (*xWork)(lsm_db *, void *), void *pCtx ){ pDb->xWork = xWork; pDb->pWorkCtx = pCtx; } void lsmLogMessage(lsm_db *pDb, int rc, const char *zFormat, ...){ if( pDb->xLog ){ LsmString s; va_list ap, ap2; lsmStringInit(&s, pDb->pEnv); va_start(ap, zFormat); va_start(ap2, zFormat); lsmStringVAppendf(&s, zFormat, ap, ap2); va_end(ap); va_end(ap2); pDb->xLog(pDb->pLogCtx, rc, s.z); lsmStringClear(&s); } } int lsm_begin(lsm_db *pDb, int iLevel){ int rc; assert_db_state( pDb ); rc = (pDb->bReadonly ? LSM_READONLY : LSM_OK); /* A value less than zero means open one more transaction. */ if( iLevel<0 ) iLevel = pDb->nTransOpen + 1; if( iLevel>pDb->nTransOpen ){ int i; /* Extend the pDb->aTrans[] array if required. */ if( rc==LSM_OK && pDb->nTransAlloc<iLevel ){ TransMark *aNew; /* New allocation */ int nByte = sizeof(TransMark) * (iLevel+1); aNew = (TransMark *)lsmRealloc(pDb->pEnv, pDb->aTrans, nByte); if( !aNew ){ rc = LSM_NOMEM; }else{ nByte = sizeof(TransMark) * (iLevel+1 - pDb->nTransAlloc); memset(&aNew[pDb->nTransAlloc], 0, nByte); pDb->nTransAlloc = iLevel+1; pDb->aTrans = aNew; } } if( rc==LSM_OK && pDb->nTransOpen==0 ){ rc = lsmBeginWriteTrans(pDb); } if( rc==LSM_OK ){ for(i=pDb->nTransOpen; i<iLevel; i++){ lsmTreeMark(pDb, &pDb->aTrans[i].tree); lsmLogTell(pDb, &pDb->aTrans[i].log); } pDb->nTransOpen = iLevel; } } return rc; } int lsm_commit(lsm_db *pDb, int iLevel){ int rc = LSM_OK; assert_db_state( pDb ); /* A value less than zero means close the innermost nested transaction. */ if( iLevel<0 ) iLevel = LSM_MAX(0, pDb->nTransOpen - 1); if( iLevel<pDb->nTransOpen ){ if( iLevel==0 ){ int rc2; /* Commit the transaction to disk. */ if( rc==LSM_OK ) rc = lsmLogCommit(pDb); if( rc==LSM_OK && pDb->eSafety==LSM_SAFETY_FULL ){ rc = lsmFsSyncLog(pDb->pFS); } rc2 = lsmFinishWriteTrans(pDb, (rc==LSM_OK)); if( rc==LSM_OK ) rc = rc2; } pDb->nTransOpen = iLevel; } dbReleaseClientSnapshot(pDb); return rc; } int lsm_rollback(lsm_db *pDb, int iLevel){ int rc = LSM_OK; assert_db_state( pDb ); if( pDb->nTransOpen ){ /* A value less than zero means close the innermost nested transaction. */ if( iLevel<0 ) iLevel = LSM_MAX(0, pDb->nTransOpen - 1); if( iLevel<=pDb->nTransOpen ){ TransMark *pMark = &pDb->aTrans[(iLevel==0 ? 0 : iLevel-1)]; lsmTreeRollback(pDb, &pMark->tree); if( iLevel ) lsmLogSeek(pDb, &pMark->log); pDb->nTransOpen = iLevel; } if( pDb->nTransOpen==0 ){ lsmFinishWriteTrans(pDb, 0); } dbReleaseClientSnapshot(pDb); } return rc; } int lsm_get_user_version(lsm_db *pDb, unsigned int *piUsr){ int rc = LSM_OK; /* Return code */ /* Open a read transaction if one is not already open. */ assert_db_state(pDb); if( pDb->pShmhdr==0 ){ assert( pDb->bReadonly ); rc = lsmBeginRoTrans(pDb); }else if( pDb->iReader<0 ){ rc = lsmBeginReadTrans(pDb); } /* Allocate the multi-cursor. */ if( rc==LSM_OK ){ *piUsr = pDb->treehdr.iUsrVersion; } dbReleaseClientSnapshot(pDb); assert_db_state(pDb); return rc; } int lsm_set_user_version(lsm_db *pDb, unsigned int iUsr){ int rc = LSM_OK; /* Return code */ int bCommit = 0; /* True to commit before returning */ if( pDb->nTransOpen==0 ){ bCommit = 1; rc = lsm_begin(pDb, 1); } if( rc==LSM_OK ){ pDb->treehdr.iUsrVersion = iUsr; } /* If a transaction was opened at the start of this function, commit it. ** Or, if an error has occurred, roll it back. */ if( bCommit ){ if( rc==LSM_OK ){ rc = lsm_commit(pDb, 0); }else{ lsm_rollback(pDb, 0); } } return rc; } |
Added ext/lsm1/lsm_mem.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 | /* ** 2011-08-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. ** ************************************************************************* ** ** Helper routines for memory allocation. */ #include "lsmInt.h" /* ** The following routines are called internally by LSM sub-routines. In ** this case a valid environment pointer must be supplied. */ void *lsmMalloc(lsm_env *pEnv, size_t N){ assert( pEnv ); return pEnv->xMalloc(pEnv, N); } void lsmFree(lsm_env *pEnv, void *p){ assert( pEnv ); pEnv->xFree(pEnv, p); } void *lsmRealloc(lsm_env *pEnv, void *p, size_t N){ assert( pEnv ); return pEnv->xRealloc(pEnv, p, N); } /* ** Core memory allocation routines for LSM. */ void *lsm_malloc(lsm_env *pEnv, size_t N){ return lsmMalloc(pEnv ? pEnv : lsm_default_env(), N); } void lsm_free(lsm_env *pEnv, void *p){ lsmFree(pEnv ? pEnv : lsm_default_env(), p); } void *lsm_realloc(lsm_env *pEnv, void *p, size_t N){ return lsmRealloc(pEnv ? pEnv : lsm_default_env(), p, N); } void *lsmMallocZero(lsm_env *pEnv, size_t N){ void *pRet; assert( pEnv ); pRet = lsmMalloc(pEnv, N); if( pRet ) memset(pRet, 0, N); return pRet; } void *lsmMallocRc(lsm_env *pEnv, size_t N, int *pRc){ void *pRet = 0; if( *pRc==LSM_OK ){ pRet = lsmMalloc(pEnv, N); if( pRet==0 ){ *pRc = LSM_NOMEM_BKPT; } } return pRet; } void *lsmMallocZeroRc(lsm_env *pEnv, size_t N, int *pRc){ void *pRet = 0; if( *pRc==LSM_OK ){ pRet = lsmMallocZero(pEnv, N); if( pRet==0 ){ *pRc = LSM_NOMEM_BKPT; } } return pRet; } void *lsmReallocOrFree(lsm_env *pEnv, void *p, size_t N){ void *pNew; pNew = lsm_realloc(pEnv, p, N); if( !pNew ) lsm_free(pEnv, p); return pNew; } void *lsmReallocOrFreeRc(lsm_env *pEnv, void *p, size_t N, int *pRc){ void *pRet = 0; if( *pRc ){ lsmFree(pEnv, p); }else{ pRet = lsmReallocOrFree(pEnv, p, N); if( !pRet ) *pRc = LSM_NOMEM_BKPT; } return pRet; } char *lsmMallocStrdup(lsm_env *pEnv, const char *zIn){ int nByte; char *zRet; nByte = strlen(zIn); zRet = lsmMalloc(pEnv, nByte+1); if( zRet ){ memcpy(zRet, zIn, nByte+1); } return zRet; } |
Added ext/lsm1/lsm_mutex.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 | /* ** 2012-01-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. ** ************************************************************************* ** ** Mutex functions for LSM. */ #include "lsmInt.h" /* ** Allocate a new mutex. */ int lsmMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){ return pEnv->xMutexNew(pEnv, ppNew); } /* ** Return a handle for one of the static mutexes. */ int lsmMutexStatic(lsm_env *pEnv, int iMutex, lsm_mutex **ppStatic){ return pEnv->xMutexStatic(pEnv, iMutex, ppStatic); } /* ** Free a mutex allocated by lsmMutexNew(). */ void lsmMutexDel(lsm_env *pEnv, lsm_mutex *pMutex){ if( pMutex ) pEnv->xMutexDel(pMutex); } /* ** Enter a mutex. */ void lsmMutexEnter(lsm_env *pEnv, lsm_mutex *pMutex){ pEnv->xMutexEnter(pMutex); } /* ** Attempt to enter a mutex, but do not block. If successful, return zero. ** Otherwise, if the mutex is already held by some other thread and is not ** entered, return non zero. ** ** Each successful call to this function must be matched by a call to ** lsmMutexLeave(). */ int lsmMutexTry(lsm_env *pEnv, lsm_mutex *pMutex){ return pEnv->xMutexTry(pMutex); } /* ** Leave a mutex. */ void lsmMutexLeave(lsm_env *pEnv, lsm_mutex *pMutex){ pEnv->xMutexLeave(pMutex); } #ifndef NDEBUG /* ** Return non-zero if the mutex passed as the second argument is held ** by the calling thread, or zero otherwise. If the implementation is not ** able to tell if the mutex is held by the caller, it should return ** non-zero. ** ** This function is only used as part of assert() statements. */ int lsmMutexHeld(lsm_env *pEnv, lsm_mutex *pMutex){ return pEnv->xMutexHeld ? pEnv->xMutexHeld(pMutex) : 1; } /* ** Return non-zero if the mutex passed as the second argument is not ** held by the calling thread, or zero otherwise. If the implementation ** is not able to tell if the mutex is held by the caller, it should ** return non-zero. ** ** This function is only used as part of assert() statements. */ int lsmMutexNotHeld(lsm_env *pEnv, lsm_mutex *pMutex){ return pEnv->xMutexNotHeld ? pEnv->xMutexNotHeld(pMutex) : 1; } #endif |
Added ext/lsm1/lsm_shared.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 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 | /* ** 2012-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. ** ************************************************************************* ** ** Utilities used to help multiple LSM clients to coexist within the ** same process space. */ #include "lsmInt.h" /* ** Global data. All global variables used by code in this file are grouped ** into the following structure instance. ** ** pDatabase: ** Linked list of all Database objects allocated within this process. ** This list may not be traversed without holding the global mutex (see ** functions enterGlobalMutex() and leaveGlobalMutex()). */ static struct SharedData { Database *pDatabase; /* Linked list of all Database objects */ } gShared; /* ** Database structure. There is one such structure for each distinct ** database accessed by this process. They are stored in the singly linked ** list starting at global variable gShared.pDatabase. Database objects are ** reference counted. Once the number of connections to the associated ** database drops to zero, they are removed from the linked list and deleted. ** ** pFile: ** In multi-process mode, this file descriptor is used to obtain locks ** and to access shared-memory. In single process mode, its only job is ** to hold the exclusive lock on the file. ** */ struct Database { /* Protected by the global mutex (enterGlobalMutex/leaveGlobalMutex): */ char *zName; /* Canonical path to database file */ int nName; /* strlen(zName) */ int nDbRef; /* Number of associated lsm_db handles */ Database *pDbNext; /* Next Database structure in global list */ /* Protected by the local mutex (pClientMutex) */ int bReadonly; /* True if Database.pFile is read-only */ int bMultiProc; /* True if running in multi-process mode */ lsm_file *pFile; /* Used for locks/shm in multi-proc mode */ LsmFile *pLsmFile; /* List of deferred closes */ lsm_mutex *pClientMutex; /* Protects the apShmChunk[] and pConn */ int nShmChunk; /* Number of entries in apShmChunk[] array */ void **apShmChunk; /* Array of "shared" memory regions */ lsm_db *pConn; /* List of connections to this db. */ }; /* ** Functions to enter and leave the global mutex. This mutex is used ** to protect the global linked-list headed at gShared.pDatabase. */ static int enterGlobalMutex(lsm_env *pEnv){ lsm_mutex *p; int rc = lsmMutexStatic(pEnv, LSM_MUTEX_GLOBAL, &p); if( rc==LSM_OK ) lsmMutexEnter(pEnv, p); return rc; } static void leaveGlobalMutex(lsm_env *pEnv){ lsm_mutex *p; lsmMutexStatic(pEnv, LSM_MUTEX_GLOBAL, &p); lsmMutexLeave(pEnv, p); } #ifdef LSM_DEBUG static int holdingGlobalMutex(lsm_env *pEnv){ lsm_mutex *p; lsmMutexStatic(pEnv, LSM_MUTEX_GLOBAL, &p); return lsmMutexHeld(pEnv, p); } #endif #if 0 static void assertNotInFreelist(Freelist *p, int iBlk){ int i; for(i=0; i<p->nEntry; i++){ assert( p->aEntry[i].iBlk!=iBlk ); } } #else # define assertNotInFreelist(x,y) #endif /* ** Append an entry to the free-list. If (iId==-1), this is a delete. */ int freelistAppend(lsm_db *db, u32 iBlk, i64 iId){ lsm_env *pEnv = db->pEnv; Freelist *p; int i; assert( iId==-1 || iId>=0 ); p = db->bUseFreelist ? db->pFreelist : &db->pWorker->freelist; /* Extend the space allocated for the freelist, if required */ assert( p->nAlloc>=p->nEntry ); if( p->nAlloc==p->nEntry ){ int nNew; int nByte; FreelistEntry *aNew; nNew = (p->nAlloc==0 ? 4 : p->nAlloc*2); nByte = sizeof(FreelistEntry) * nNew; aNew = (FreelistEntry *)lsmRealloc(pEnv, p->aEntry, nByte); if( !aNew ) return LSM_NOMEM_BKPT; p->nAlloc = nNew; p->aEntry = aNew; } for(i=0; i<p->nEntry; i++){ assert( i==0 || p->aEntry[i].iBlk > p->aEntry[i-1].iBlk ); if( p->aEntry[i].iBlk>=iBlk ) break; } if( i<p->nEntry && p->aEntry[i].iBlk==iBlk ){ /* Clobber an existing entry */ p->aEntry[i].iId = iId; }else{ /* Insert a new entry into the list */ int nByte = sizeof(FreelistEntry)*(p->nEntry-i); memmove(&p->aEntry[i+1], &p->aEntry[i], nByte); p->aEntry[i].iBlk = iBlk; p->aEntry[i].iId = iId; p->nEntry++; } return LSM_OK; } /* ** This function frees all resources held by the Database structure passed ** as the only argument. */ static void freeDatabase(lsm_env *pEnv, Database *p){ assert( holdingGlobalMutex(pEnv) ); if( p ){ /* Free the mutexes */ lsmMutexDel(pEnv, p->pClientMutex); if( p->pFile ){ lsmEnvClose(pEnv, p->pFile); } /* Free the array of shm pointers */ lsmFree(pEnv, p->apShmChunk); /* Free the memory allocated for the Database struct itself */ lsmFree(pEnv, p); } } typedef struct DbTruncateCtx DbTruncateCtx; struct DbTruncateCtx { int nBlock; i64 iInUse; }; static int dbTruncateCb(void *pCtx, int iBlk, i64 iSnapshot){ DbTruncateCtx *p = (DbTruncateCtx *)pCtx; if( iBlk!=p->nBlock || (p->iInUse>=0 && iSnapshot>=p->iInUse) ) return 1; p->nBlock--; return 0; } static int dbTruncate(lsm_db *pDb, i64 iInUse){ int rc = LSM_OK; #if 0 int i; DbTruncateCtx ctx; assert( pDb->pWorker ); ctx.nBlock = pDb->pWorker->nBlock; ctx.iInUse = iInUse; rc = lsmWalkFreelist(pDb, 1, dbTruncateCb, (void *)&ctx); for(i=ctx.nBlock+1; rc==LSM_OK && i<=pDb->pWorker->nBlock; i++){ rc = freelistAppend(pDb, i, -1); } if( rc==LSM_OK ){ #ifdef LSM_LOG_FREELIST if( ctx.nBlock!=pDb->pWorker->nBlock ){ lsmLogMessage(pDb, 0, "dbTruncate(): truncated db to %d blocks",ctx.nBlock ); } #endif pDb->pWorker->nBlock = ctx.nBlock; } #endif return rc; } /* ** This function is called during database shutdown (when the number of ** connections drops from one to zero). It truncates the database file ** to as small a size as possible without truncating away any blocks that ** contain data. */ static int dbTruncateFile(lsm_db *pDb){ int rc; assert( pDb->pWorker==0 ); assert( lsmShmAssertLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL) ); rc = lsmCheckpointLoadWorker(pDb); if( rc==LSM_OK ){ DbTruncateCtx ctx; /* Walk the database free-block-list in reverse order. Set ctx.nBlock ** to the block number of the last block in the database that actually ** contains data. */ ctx.nBlock = pDb->pWorker->nBlock; ctx.iInUse = -1; rc = lsmWalkFreelist(pDb, 1, dbTruncateCb, (void *)&ctx); /* If the last block that contains data is not already the last block in ** the database file, truncate the database file so that it is. */ if( rc==LSM_OK ){ rc = lsmFsTruncateDb( pDb->pFS, (i64)ctx.nBlock*lsmFsBlockSize(pDb->pFS) ); } } lsmFreeSnapshot(pDb->pEnv, pDb->pWorker); pDb->pWorker = 0; return rc; } static void doDbDisconnect(lsm_db *pDb){ int rc; if( pDb->bReadonly ){ lsmShmLock(pDb, LSM_LOCK_DMS3, LSM_LOCK_UNLOCK, 0); }else{ /* Block for an exclusive lock on DMS1. This lock serializes all calls ** to doDbConnect() and doDbDisconnect() across all processes. */ rc = lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL, 1); if( rc==LSM_OK ){ lsmShmLock(pDb, LSM_LOCK_DMS2, LSM_LOCK_UNLOCK, 0); /* Try an exclusive lock on DMS2. If successful, this is the last ** connection to the database. In this case flush the contents of the ** in-memory tree to disk and write a checkpoint. */ rc = lsmShmTestLock(pDb, LSM_LOCK_DMS2, 1, LSM_LOCK_EXCL); if( rc==LSM_OK ){ rc = lsmShmTestLock(pDb, LSM_LOCK_CHECKPOINTER, 1, LSM_LOCK_EXCL); } if( rc==LSM_OK ){ int bReadonly = 0; /* True if there exist read-only conns. */ /* Flush the in-memory tree, if required. If there is data to flush, ** this will create a new client snapshot in Database.pClient. The ** checkpoint (serialization) of this snapshot may be written to disk ** by the following block. ** ** There is no need to take a WRITER lock here. That there are no ** other locks on DMS2 guarantees that there are no other read-write ** connections at this time (and the lock on DMS1 guarantees that ** no new ones may appear). */ rc = lsmTreeLoadHeader(pDb, 0); if( rc==LSM_OK && (lsmTreeHasOld(pDb) || lsmTreeSize(pDb)>0) ){ rc = lsmFlushTreeToDisk(pDb); } /* Now check if there are any read-only connections. If there are, ** then do not truncate the db file or unlink the shared-memory ** region. */ if( rc==LSM_OK ){ rc = lsmShmTestLock(pDb, LSM_LOCK_DMS3, 1, LSM_LOCK_EXCL); if( rc==LSM_BUSY ){ bReadonly = 1; rc = LSM_OK; } } /* Write a checkpoint to disk. */ if( rc==LSM_OK ){ rc = lsmCheckpointWrite(pDb, 0); } /* If the checkpoint was written successfully, delete the log file ** and, if possible, truncate the database file. */ if( rc==LSM_OK ){ int bRotrans = 0; Database *p = pDb->pDatabase; /* The log file may only be deleted if there are no clients ** read-only clients running rotrans transactions. */ rc = lsmDetectRoTrans(pDb, &bRotrans); if( rc==LSM_OK && bRotrans==0 ){ lsmFsCloseAndDeleteLog(pDb->pFS); } /* The database may only be truncated if there exist no read-only ** clients - either connected or running rotrans transactions. */ if( bReadonly==0 && bRotrans==0 ){ lsmFsUnmap(pDb->pFS); dbTruncateFile(pDb); if( p->pFile && p->bMultiProc ){ lsmEnvShmUnmap(pDb->pEnv, p->pFile, 1); } } } } } if( pDb->iRwclient>=0 ){ lsmShmLock(pDb, LSM_LOCK_RWCLIENT(pDb->iRwclient), LSM_LOCK_UNLOCK, 0); pDb->iRwclient = -1; } lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0); } pDb->pShmhdr = 0; } static int doDbConnect(lsm_db *pDb){ const int nUsMax = 100000; /* Max value for nUs */ int nUs = 1000; /* us to wait between DMS1 attempts */ int rc; /* Obtain a pointer to the shared-memory header */ assert( pDb->pShmhdr==0 ); assert( pDb->bReadonly==0 ); rc = lsmShmCacheChunks(pDb, 1); if( rc!=LSM_OK ) return rc; pDb->pShmhdr = (ShmHeader *)pDb->apShm[0]; /* Block for an exclusive lock on DMS1. This lock serializes all calls ** to doDbConnect() and doDbDisconnect() across all processes. */ while( 1 ){ rc = lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL, 1); if( rc!=LSM_BUSY ) break; lsmEnvSleep(pDb->pEnv, nUs); nUs = nUs * 2; if( nUs>nUsMax ) nUs = nUsMax; } if( rc!=LSM_OK ){ pDb->pShmhdr = 0; return rc; } /* Try an exclusive lock on DMS2/DMS3. If successful, this is the first ** and only connection to the database. In this case initialize the ** shared-memory and run log file recovery. */ assert( LSM_LOCK_DMS3==1+LSM_LOCK_DMS2 ); rc = lsmShmTestLock(pDb, LSM_LOCK_DMS2, 2, LSM_LOCK_EXCL); if( rc==LSM_OK ){ memset(pDb->pShmhdr, 0, sizeof(ShmHeader)); rc = lsmCheckpointRecover(pDb); if( rc==LSM_OK ){ rc = lsmLogRecover(pDb); } if( rc==LSM_OK ){ ShmHeader *pShm = pDb->pShmhdr; pShm->aReader[0].iLsmId = lsmCheckpointId(pShm->aSnap1, 0); pShm->aReader[0].iTreeId = pDb->treehdr.iUsedShmid; } }else if( rc==LSM_BUSY ){ rc = LSM_OK; } /* Take a shared lock on DMS2. In multi-process mode this lock "cannot" ** fail, as connections may only hold an exclusive lock on DMS2 if they ** first hold an exclusive lock on DMS1. And this connection is currently ** holding the exclusive lock on DSM1. ** ** However, if some other connection has the database open in single-process ** mode, this operation will fail. In this case, return the error to the ** caller - the attempt to connect to the db has failed. */ if( rc==LSM_OK ){ rc = lsmShmLock(pDb, LSM_LOCK_DMS2, LSM_LOCK_SHARED, 0); } /* If anything went wrong, unlock DMS2. Otherwise, try to take an exclusive ** lock on one of the LSM_LOCK_RWCLIENT() locks. Unlock DMS1 in any case. */ if( rc!=LSM_OK ){ pDb->pShmhdr = 0; }else{ int i; for(i=0; i<LSM_LOCK_NRWCLIENT; i++){ int rc2 = lsmShmLock(pDb, LSM_LOCK_RWCLIENT(i), LSM_LOCK_EXCL, 0); if( rc2==LSM_OK ) pDb->iRwclient = i; if( rc2!=LSM_BUSY ){ rc = rc2; break; } } } lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0); return rc; } static int dbOpenSharedFd(lsm_env *pEnv, Database *p, int bRoOk){ int rc; rc = lsmEnvOpen(pEnv, p->zName, 0, &p->pFile); if( rc==LSM_IOERR && bRoOk ){ rc = lsmEnvOpen(pEnv, p->zName, LSM_OPEN_READONLY, &p->pFile); p->bReadonly = 1; } return rc; } /* ** Return a reference to the shared Database handle for the database ** identified by canonical path zName. If this is the first connection to ** the named database, a new Database object is allocated. Otherwise, a ** pointer to an existing object is returned. ** ** If successful, *ppDatabase is set to point to the shared Database ** structure and LSM_OK returned. Otherwise, *ppDatabase is set to NULL ** and and LSM error code returned. ** ** Each successful call to this function should be (eventually) matched ** by a call to lsmDbDatabaseRelease(). */ int lsmDbDatabaseConnect( lsm_db *pDb, /* Database handle */ const char *zName /* Full-path to db file */ ){ lsm_env *pEnv = pDb->pEnv; int rc; /* Return code */ Database *p = 0; /* Pointer returned via *ppDatabase */ int nName = lsmStrlen(zName); assert( pDb->pDatabase==0 ); rc = enterGlobalMutex(pEnv); if( rc==LSM_OK ){ /* Search the global list for an existing object. TODO: Need something ** better than the memcmp() below to figure out if a given Database ** object represents the requested file. */ for(p=gShared.pDatabase; p; p=p->pDbNext){ if( nName==p->nName && 0==memcmp(zName, p->zName, nName) ) break; } /* If no suitable Database object was found, allocate a new one. */ if( p==0 ){ p = (Database *)lsmMallocZeroRc(pEnv, sizeof(Database)+nName+1, &rc); /* If the allocation was successful, fill in other fields and ** allocate the client mutex. */ if( rc==LSM_OK ){ p->bMultiProc = pDb->bMultiProc; p->zName = (char *)&p[1]; p->nName = nName; memcpy((void *)p->zName, zName, nName+1); rc = lsmMutexNew(pEnv, &p->pClientMutex); } /* If nothing has gone wrong so far, open the shared fd. And if that ** succeeds and this connection requested single-process mode, ** attempt to take the exclusive lock on DMS2. */ if( rc==LSM_OK ){ int bReadonly = (pDb->bReadonly && pDb->bMultiProc); rc = dbOpenSharedFd(pDb->pEnv, p, bReadonly); } if( rc==LSM_OK && p->bMultiProc==0 ){ /* Hold an exclusive lock DMS1 while grabbing DMS2. This ensures ** that any ongoing call to doDbDisconnect() (even one in another ** process) is finished before proceeding. */ assert( p->bReadonly==0 ); rc = lsmEnvLock(pDb->pEnv, p->pFile, LSM_LOCK_DMS1, LSM_LOCK_EXCL); if( rc==LSM_OK ){ rc = lsmEnvLock(pDb->pEnv, p->pFile, LSM_LOCK_DMS2, LSM_LOCK_EXCL); lsmEnvLock(pDb->pEnv, p->pFile, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK); } } if( rc==LSM_OK ){ p->pDbNext = gShared.pDatabase; gShared.pDatabase = p; }else{ freeDatabase(pEnv, p); p = 0; } } if( p ){ p->nDbRef++; } leaveGlobalMutex(pEnv); if( p ){ lsmMutexEnter(pDb->pEnv, p->pClientMutex); pDb->pNext = p->pConn; p->pConn = pDb; lsmMutexLeave(pDb->pEnv, p->pClientMutex); } } pDb->pDatabase = p; if( rc==LSM_OK ){ assert( p ); rc = lsmFsOpen(pDb, zName, p->bReadonly); } /* If the db handle is read-write, then connect to the system now. Run ** recovery as necessary. Or, if this is a read-only database handle, ** defer attempting to connect to the system until a read-transaction ** is opened. */ if( pDb->bReadonly==0 ){ if( rc==LSM_OK ){ rc = lsmFsConfigure(pDb); } if( rc==LSM_OK ){ rc = doDbConnect(pDb); } } return rc; } static void dbDeferClose(lsm_db *pDb){ if( pDb->pFS ){ LsmFile *pLsmFile; Database *p = pDb->pDatabase; pLsmFile = lsmFsDeferClose(pDb->pFS); pLsmFile->pNext = p->pLsmFile; p->pLsmFile = pLsmFile; } } LsmFile *lsmDbRecycleFd(lsm_db *db){ LsmFile *pRet; Database *p = db->pDatabase; lsmMutexEnter(db->pEnv, p->pClientMutex); if( (pRet = p->pLsmFile)!=0 ){ p->pLsmFile = pRet->pNext; } lsmMutexLeave(db->pEnv, p->pClientMutex); return pRet; } /* ** Release a reference to a Database object obtained from ** lsmDbDatabaseConnect(). There should be exactly one call to this function ** for each successful call to Find(). */ void lsmDbDatabaseRelease(lsm_db *pDb){ Database *p = pDb->pDatabase; if( p ){ lsm_db **ppDb; if( pDb->pShmhdr ){ doDbDisconnect(pDb); } lsmFsUnmap(pDb->pFS); lsmMutexEnter(pDb->pEnv, p->pClientMutex); for(ppDb=&p->pConn; *ppDb!=pDb; ppDb=&((*ppDb)->pNext)); *ppDb = pDb->pNext; dbDeferClose(pDb); lsmMutexLeave(pDb->pEnv, p->pClientMutex); enterGlobalMutex(pDb->pEnv); p->nDbRef--; if( p->nDbRef==0 ){ LsmFile *pIter; LsmFile *pNext; Database **pp; /* Remove the Database structure from the linked list. */ for(pp=&gShared.pDatabase; *pp!=p; pp=&((*pp)->pDbNext)); *pp = p->pDbNext; /* If they were allocated from the heap, free the shared memory chunks */ if( p->bMultiProc==0 ){ int i; for(i=0; i<p->nShmChunk; i++){ lsmFree(pDb->pEnv, p->apShmChunk[i]); } } /* Close any outstanding file descriptors */ for(pIter=p->pLsmFile; pIter; pIter=pNext){ pNext = pIter->pNext; lsmEnvClose(pDb->pEnv, pIter->pFile); lsmFree(pDb->pEnv, pIter); } freeDatabase(pDb->pEnv, p); } leaveGlobalMutex(pDb->pEnv); } } Level *lsmDbSnapshotLevel(Snapshot *pSnapshot){ return pSnapshot->pLevel; } void lsmDbSnapshotSetLevel(Snapshot *pSnap, Level *pLevel){ pSnap->pLevel = pLevel; } /* TODO: Shuffle things around to get rid of this */ static int firstSnapshotInUse(lsm_db *, i64 *); /* ** Context object used by the lsmWalkFreelist() utility. */ typedef struct WalkFreelistCtx WalkFreelistCtx; struct WalkFreelistCtx { lsm_db *pDb; int bReverse; Freelist *pFreelist; int iFree; int (*xUsr)(void *, int, i64); /* User callback function */ void *pUsrctx; /* User callback context */ int bDone; /* Set to true after xUsr() returns true */ }; /* ** Callback used by lsmWalkFreelist(). */ static int walkFreelistCb(void *pCtx, int iBlk, i64 iSnapshot){ WalkFreelistCtx *p = (WalkFreelistCtx *)pCtx; const int iDir = (p->bReverse ? -1 : 1); Freelist *pFree = p->pFreelist; assert( p->bDone==0 ); assert( iBlk>=0 ); if( pFree ){ while( (p->iFree < pFree->nEntry) && p->iFree>=0 ){ FreelistEntry *pEntry = &pFree->aEntry[p->iFree]; if( (p->bReverse==0 && pEntry->iBlk>(u32)iBlk) || (p->bReverse!=0 && pEntry->iBlk<(u32)iBlk) ){ break; }else{ p->iFree += iDir; if( pEntry->iId>=0 && p->xUsr(p->pUsrctx, pEntry->iBlk, pEntry->iId) ){ p->bDone = 1; return 1; } if( pEntry->iBlk==(u32)iBlk ) return 0; } } } if( p->xUsr(p->pUsrctx, iBlk, iSnapshot) ){ p->bDone = 1; return 1; } return 0; } /* ** The database handle passed as the first argument must be the worker ** connection. This function iterates through the contents of the current ** free block list, invoking the supplied callback once for each list ** element. ** ** The difference between this function and lsmSortedWalkFreelist() is ** that lsmSortedWalkFreelist() only considers those free-list elements ** stored within the LSM. This function also merges in any in-memory ** elements. */ int lsmWalkFreelist( lsm_db *pDb, /* Database handle (must be worker) */ int bReverse, /* True to iterate from largest to smallest */ int (*x)(void *, int, i64), /* Callback function */ void *pCtx /* First argument to pass to callback */ ){ const int iDir = (bReverse ? -1 : 1); int rc; int iCtx; WalkFreelistCtx ctx[2]; ctx[0].pDb = pDb; ctx[0].bReverse = bReverse; ctx[0].pFreelist = &pDb->pWorker->freelist; if( ctx[0].pFreelist && bReverse ){ ctx[0].iFree = ctx[0].pFreelist->nEntry-1; }else{ ctx[0].iFree = 0; } ctx[0].xUsr = walkFreelistCb; ctx[0].pUsrctx = (void *)&ctx[1]; ctx[0].bDone = 0; ctx[1].pDb = pDb; ctx[1].bReverse = bReverse; ctx[1].pFreelist = pDb->pFreelist; if( ctx[1].pFreelist && bReverse ){ ctx[1].iFree = ctx[1].pFreelist->nEntry-1; }else{ ctx[1].iFree = 0; } ctx[1].xUsr = x; ctx[1].pUsrctx = pCtx; ctx[1].bDone = 0; rc = lsmSortedWalkFreelist(pDb, bReverse, walkFreelistCb, (void *)&ctx[0]); if( ctx[0].bDone==0 ){ for(iCtx=0; iCtx<2; iCtx++){ int i; WalkFreelistCtx *p = &ctx[iCtx]; for(i=p->iFree; p->pFreelist && rc==LSM_OK && i<p->pFreelist->nEntry && i>=0; i += iDir ){ FreelistEntry *pEntry = &p->pFreelist->aEntry[i]; if( pEntry->iId>=0 && p->xUsr(p->pUsrctx, pEntry->iBlk, pEntry->iId) ){ return LSM_OK; } } } } return rc; } typedef struct FindFreeblockCtx FindFreeblockCtx; struct FindFreeblockCtx { i64 iInUse; int iRet; int bNotOne; }; static int findFreeblockCb(void *pCtx, int iBlk, i64 iSnapshot){ FindFreeblockCtx *p = (FindFreeblockCtx *)pCtx; if( iSnapshot<p->iInUse && (iBlk!=1 || p->bNotOne==0) ){ p->iRet = iBlk; return 1; } return 0; } static int findFreeblock(lsm_db *pDb, i64 iInUse, int bNotOne, int *piRet){ int rc; /* Return code */ FindFreeblockCtx ctx; /* Context object */ ctx.iInUse = iInUse; ctx.iRet = 0; ctx.bNotOne = bNotOne; rc = lsmWalkFreelist(pDb, 0, findFreeblockCb, (void *)&ctx); *piRet = ctx.iRet; return rc; } /* ** Allocate a new database file block to write data to, either by extending ** the database file or by recycling a free-list entry. The worker snapshot ** must be held in order to call this function. ** ** If successful, *piBlk is set to the block number allocated and LSM_OK is ** returned. Otherwise, *piBlk is zeroed and an lsm error code returned. */ int lsmBlockAllocate(lsm_db *pDb, int iBefore, int *piBlk){ Snapshot *p = pDb->pWorker; int iRet = 0; /* Block number of allocated block */ int rc = LSM_OK; i64 iInUse = 0; /* Snapshot id still in use */ i64 iSynced = 0; /* Snapshot id synced to disk */ assert( p ); #ifdef LSM_LOG_FREELIST { static int nCall = 0; char *zFree = 0; nCall++; rc = lsmInfoFreelist(pDb, &zFree); if( rc!=LSM_OK ) return rc; lsmLogMessage(pDb, 0, "lsmBlockAllocate(): %d freelist: %s", nCall, zFree); lsmFree(pDb->pEnv, zFree); } #endif /* Set iInUse to the smallest snapshot id that is either: ** ** * Currently in use by a database client, ** * May be used by a database client in the future, or ** * Is the most recently checkpointed snapshot (i.e. the one that will ** be used following recovery if a failure occurs at this point). */ rc = lsmCheckpointSynced(pDb, &iSynced, 0, 0); if( rc==LSM_OK && iSynced==0 ) iSynced = p->iId; iInUse = iSynced; if( rc==LSM_OK && pDb->iReader>=0 ){ assert( pDb->pClient ); iInUse = LSM_MIN(iInUse, pDb->pClient->iId); } if( rc==LSM_OK ) rc = firstSnapshotInUse(pDb, &iInUse); #ifdef LSM_LOG_FREELIST { lsmLogMessage(pDb, 0, "lsmBlockAllocate(): " "snapshot-in-use: %lld (iSynced=%lld) (client-id=%lld)", iInUse, iSynced, (pDb->iReader>=0 ? pDb->pClient->iId : 0) ); } #endif /* Unless there exists a read-only transaction (which prevents us from ** recycling any blocks regardless, query the free block list for a ** suitable block to reuse. ** ** It might seem more natural to check for a read-only transaction at ** the start of this function. However, it is better do wait until after ** the call to lsmCheckpointSynced() to do so. */ if( rc==LSM_OK ){ int bRotrans; rc = lsmDetectRoTrans(pDb, &bRotrans); if( rc==LSM_OK && bRotrans==0 ){ rc = findFreeblock(pDb, iInUse, (iBefore>0), &iRet); } } if( iBefore>0 && (iRet<=0 || iRet>=iBefore) ){ iRet = 0; }else if( rc==LSM_OK ){ /* If a block was found in the free block list, use it and remove it from ** the list. Otherwise, if no suitable block was found, allocate one from ** the end of the file. */ if( iRet>0 ){ #ifdef LSM_LOG_FREELIST lsmLogMessage(pDb, 0, "reusing block %d (snapshot-in-use=%lld)", iRet, iInUse); #endif rc = freelistAppend(pDb, iRet, -1); if( rc==LSM_OK ){ rc = dbTruncate(pDb, iInUse); } }else{ iRet = ++(p->nBlock); #ifdef LSM_LOG_FREELIST lsmLogMessage(pDb, 0, "extending file to %d blocks", iRet); #endif } } assert( iBefore>0 || iRet>0 || rc!=LSM_OK ); *piBlk = iRet; return rc; } /* ** Free a database block. The worker snapshot must be held in order to call ** this function. ** ** If successful, LSM_OK is returned. Otherwise, an lsm error code (e.g. ** LSM_NOMEM). */ int lsmBlockFree(lsm_db *pDb, int iBlk){ Snapshot *p = pDb->pWorker; assert( lsmShmAssertWorker(pDb) ); #ifdef LSM_LOG_FREELIST lsmLogMessage(pDb, LSM_OK, "lsmBlockFree(): Free block %d", iBlk); #endif return freelistAppend(pDb, iBlk, p->iId); } /* ** Refree a database block. The worker snapshot must be held in order to call ** this function. ** ** Refreeing is required when a block is allocated using lsmBlockAllocate() ** but then not used. This function is used to push the block back onto ** the freelist. Refreeing a block is different from freeing is, as a refreed ** block may be reused immediately. Whereas a freed block can not be reused ** until (at least) after the next checkpoint. */ int lsmBlockRefree(lsm_db *pDb, int iBlk){ int rc = LSM_OK; /* Return code */ #ifdef LSM_LOG_FREELIST lsmLogMessage(pDb, LSM_OK, "lsmBlockRefree(): Refree block %d", iBlk); #endif rc = freelistAppend(pDb, iBlk, 0); return rc; } /* ** If required, copy a database checkpoint from shared memory into the ** database itself. ** ** The WORKER lock must not be held when this is called. This is because ** this function may indirectly call fsync(). And the WORKER lock should ** not be held that long (in case it is required by a client flushing an ** in-memory tree to disk). */ int lsmCheckpointWrite(lsm_db *pDb, u32 *pnWrite){ int rc; /* Return Code */ u32 nWrite = 0; assert( pDb->pWorker==0 ); assert( 1 || pDb->pClient==0 ); assert( lsmShmAssertLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_UNLOCK) ); rc = lsmShmLock(pDb, LSM_LOCK_CHECKPOINTER, LSM_LOCK_EXCL, 0); if( rc!=LSM_OK ) return rc; rc = lsmCheckpointLoad(pDb, 0); if( rc==LSM_OK ){ int nBlock = lsmCheckpointNBlock(pDb->aSnapshot); ShmHeader *pShm = pDb->pShmhdr; int bDone = 0; /* True if checkpoint is already stored */ /* Check if this checkpoint has already been written to the database ** file. If so, set variable bDone to true. */ if( pShm->iMetaPage ){ MetaPage *pPg; /* Meta page */ u8 *aData; /* Meta-page data buffer */ int nData; /* Size of aData[] in bytes */ i64 iCkpt; /* Id of checkpoint just loaded */ i64 iDisk = 0; /* Id of checkpoint already stored in db */ iCkpt = lsmCheckpointId(pDb->aSnapshot, 0); rc = lsmFsMetaPageGet(pDb->pFS, 0, pShm->iMetaPage, &pPg); if( rc==LSM_OK ){ aData = lsmFsMetaPageData(pPg, &nData); iDisk = lsmCheckpointId((u32 *)aData, 1); nWrite = lsmCheckpointNWrite((u32 *)aData, 1); lsmFsMetaPageRelease(pPg); } bDone = (iDisk>=iCkpt); } if( rc==LSM_OK && bDone==0 ){ int iMeta = (pShm->iMetaPage % 2) + 1; if( pDb->eSafety!=LSM_SAFETY_OFF ){ rc = lsmFsSyncDb(pDb->pFS, nBlock); } if( rc==LSM_OK ) rc = lsmCheckpointStore(pDb, iMeta); if( rc==LSM_OK && pDb->eSafety!=LSM_SAFETY_OFF){ rc = lsmFsSyncDb(pDb->pFS, 0); } if( rc==LSM_OK ){ pShm->iMetaPage = iMeta; nWrite = lsmCheckpointNWrite(pDb->aSnapshot, 0) - nWrite; } #ifdef LSM_LOG_WORK lsmLogMessage(pDb, 0, "finish checkpoint %d", (int)lsmCheckpointId(pDb->aSnapshot, 0) ); #endif } } lsmShmLock(pDb, LSM_LOCK_CHECKPOINTER, LSM_LOCK_UNLOCK, 0); if( pnWrite && rc==LSM_OK ) *pnWrite = nWrite; return rc; } int lsmBeginWork(lsm_db *pDb){ int rc; /* Attempt to take the WORKER lock */ rc = lsmShmLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_EXCL, 0); /* Deserialize the current worker snapshot */ if( rc==LSM_OK ){ rc = lsmCheckpointLoadWorker(pDb); } return rc; } void lsmFreeSnapshot(lsm_env *pEnv, Snapshot *p){ if( p ){ lsmSortedFreeLevel(pEnv, p->pLevel); lsmFree(pEnv, p->freelist.aEntry); lsmFree(pEnv, p->redirect.a); lsmFree(pEnv, p); } } /* ** Attempt to populate one of the read-lock slots to contain lock values ** iLsm/iShm. Or, if such a slot exists already, this function is a no-op. ** ** It is not an error if no slot can be populated because the write-lock ** cannot be obtained. If any other error occurs, return an LSM error code. ** Otherwise, LSM_OK. ** ** This function is called at various points to try to ensure that there ** always exists at least one read-lock slot that can be used by a read-only ** client. And so that, in the usual case, there is an "exact match" available ** whenever a read transaction is opened by any client. At present this ** function is called when: ** ** * A write transaction that called lsmTreeDiscardOld() is committed, and ** * Whenever the working snapshot is updated (i.e. lsmFinishWork()). */ static int dbSetReadLock(lsm_db *db, i64 iLsm, u32 iShm){ int rc = LSM_OK; ShmHeader *pShm = db->pShmhdr; int i; /* Check if there is already a slot containing the required values. */ for(i=0; i<LSM_LOCK_NREADER; i++){ ShmReader *p = &pShm->aReader[i]; if( p->iLsmId==iLsm && p->iTreeId==iShm ) return LSM_OK; } /* Iterate through all read-lock slots, attempting to take a write-lock ** on each of them. If a write-lock succeeds, populate the locked slot ** with the required values and break out of the loop. */ for(i=0; rc==LSM_OK && i<LSM_LOCK_NREADER; i++){ rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_EXCL, 0); if( rc==LSM_BUSY ){ rc = LSM_OK; }else{ ShmReader *p = &pShm->aReader[i]; p->iLsmId = iLsm; p->iTreeId = iShm; lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_UNLOCK, 0); break; } } return rc; } /* ** Release the read-lock currently held by connection db. */ int dbReleaseReadlock(lsm_db *db){ int rc = LSM_OK; if( db->iReader>=0 ){ rc = lsmShmLock(db, LSM_LOCK_READER(db->iReader), LSM_LOCK_UNLOCK, 0); db->iReader = -1; } db->bRoTrans = 0; return rc; } /* ** Argument bFlush is true if the contents of the in-memory tree has just ** been flushed to disk. The significance of this is that once the snapshot ** created to hold the updated state of the database is synced to disk, log ** file space can be recycled. */ void lsmFinishWork(lsm_db *pDb, int bFlush, int *pRc){ int rc = *pRc; assert( rc!=0 || pDb->pWorker ); if( pDb->pWorker ){ /* If no error has occurred, serialize the worker snapshot and write ** it to shared memory. */ if( rc==LSM_OK ){ rc = lsmSaveWorker(pDb, bFlush); } /* Assuming no error has occurred, update a read lock slot with the ** new snapshot id (see comments above function dbSetReadLock()). */ if( rc==LSM_OK ){ if( pDb->iReader<0 ){ rc = lsmTreeLoadHeader(pDb, 0); } if( rc==LSM_OK ){ rc = dbSetReadLock(pDb, pDb->pWorker->iId, pDb->treehdr.iUsedShmid); } } /* Free the snapshot object. */ lsmFreeSnapshot(pDb->pEnv, pDb->pWorker); pDb->pWorker = 0; } lsmShmLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_UNLOCK, 0); *pRc = rc; } /* ** Called when recovery is finished. */ int lsmFinishRecovery(lsm_db *pDb){ lsmTreeEndTransaction(pDb, 1); return LSM_OK; } /* ** Check if the currently configured compression functions ** (LSM_CONFIG_SET_COMPRESSION) are compatible with a database that has its ** compression id set to iReq. Compression routines are compatible if iReq ** is zero (indicating the database is empty), or if it is equal to the ** compression id of the configured compression routines. ** ** If the check shows that the current compression are incompatible and there ** is a compression factory registered, give it a chance to install new ** compression routines. ** ** If, after any registered factory is invoked, the compression functions ** are still incompatible, return LSM_MISMATCH. Otherwise, LSM_OK. */ int lsmCheckCompressionId(lsm_db *pDb, u32 iReq){ if( iReq!=LSM_COMPRESSION_EMPTY && pDb->compress.iId!=iReq ){ if( pDb->factory.xFactory ){ pDb->bInFactory = 1; pDb->factory.xFactory(pDb->factory.pCtx, pDb, iReq); pDb->bInFactory = 0; } if( pDb->compress.iId!=iReq ){ /* Incompatible */ return LSM_MISMATCH; } } /* Compatible */ return LSM_OK; } /* ** Begin a read transaction. This function is a no-op if the connection ** passed as the only argument already has an open read transaction. */ int lsmBeginReadTrans(lsm_db *pDb){ const int MAX_READLOCK_ATTEMPTS = 10; const int nMaxAttempt = (pDb->bRoTrans ? 1 : MAX_READLOCK_ATTEMPTS); int rc = LSM_OK; /* Return code */ int iAttempt = 0; assert( pDb->pWorker==0 ); while( rc==LSM_OK && pDb->iReader<0 && (iAttempt++)<nMaxAttempt ){ int iTreehdr = 0; int iSnap = 0; assert( pDb->pCsr==0 && pDb->nTransOpen==0 ); /* Load the in-memory tree header. */ rc = lsmTreeLoadHeader(pDb, &iTreehdr); /* Load the database snapshot */ if( rc==LSM_OK ){ if( lsmCheckpointClientCacheOk(pDb)==0 ){ lsmFreeSnapshot(pDb->pEnv, pDb->pClient); pDb->pClient = 0; lsmMCursorFreeCache(pDb); lsmFsPurgeCache(pDb->pFS); rc = lsmCheckpointLoad(pDb, &iSnap); }else{ iSnap = 1; } } /* Take a read-lock on the tree and snapshot just loaded. Then check ** that the shared-memory still contains the same values. If so, proceed. ** Otherwise, relinquish the read-lock and retry the whole procedure ** (starting with loading the in-memory tree header). */ if( rc==LSM_OK ){ u32 iShmMax = pDb->treehdr.iUsedShmid; u32 iShmMin = pDb->treehdr.iNextShmid+1-LSM_MAX_SHMCHUNKS; rc = lsmReadlock( pDb, lsmCheckpointId(pDb->aSnapshot, 0), iShmMin, iShmMax ); if( rc==LSM_OK ){ if( lsmTreeLoadHeaderOk(pDb, iTreehdr) && lsmCheckpointLoadOk(pDb, iSnap) ){ /* Read lock has been successfully obtained. Deserialize the ** checkpoint just loaded. TODO: This will be removed after ** lsm_sorted.c is changed to work directly from the serialized ** version of the snapshot. */ if( pDb->pClient==0 ){ rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot,&pDb->pClient); } assert( (rc==LSM_OK)==(pDb->pClient!=0) ); assert( pDb->iReader>=0 ); /* Check that the client has the right compression hooks loaded. ** If not, set rc to LSM_MISMATCH. */ if( rc==LSM_OK ){ rc = lsmCheckCompressionId(pDb, pDb->pClient->iCmpId); } }else{ rc = dbReleaseReadlock(pDb); } } if( rc==LSM_BUSY ){ rc = LSM_OK; } } #if 0 if( rc==LSM_OK && pDb->pClient ){ fprintf(stderr, "reading %p: snapshot:%d used-shmid:%d trans-id:%d iOldShmid=%d\n", (void *)pDb, (int)pDb->pClient->iId, (int)pDb->treehdr.iUsedShmid, (int)pDb->treehdr.root.iTransId, (int)pDb->treehdr.iOldShmid ); } #endif } if( rc==LSM_OK ){ rc = lsmShmCacheChunks(pDb, pDb->treehdr.nChunk); } if( rc!=LSM_OK ){ dbReleaseReadlock(pDb); } if( pDb->pClient==0 && rc==LSM_OK ) rc = LSM_BUSY; return rc; } /* ** This function is used by a read-write connection to determine if there ** are currently one or more read-only transactions open on the database ** (in this context a read-only transaction is one opened by a read-only ** connection on a non-live database). ** ** If no error occurs, LSM_OK is returned and *pbExists is set to true if ** some other connection has a read-only transaction open, or false ** otherwise. If an error occurs an LSM error code is returned and the final ** value of *pbExist is undefined. */ int lsmDetectRoTrans(lsm_db *db, int *pbExist){ int rc; /* Only a read-write connection may use this function. */ assert( db->bReadonly==0 ); rc = lsmShmTestLock(db, LSM_LOCK_ROTRANS, 1, LSM_LOCK_EXCL); if( rc==LSM_BUSY ){ *pbExist = 1; rc = LSM_OK; }else{ *pbExist = 0; } return rc; } /* ** db is a read-only database handle in the disconnected state. This function ** attempts to open a read-transaction on the database. This may involve ** connecting to the database system (opening shared memory etc.). */ int lsmBeginRoTrans(lsm_db *db){ int rc = LSM_OK; assert( db->bReadonly && db->pShmhdr==0 ); assert( db->iReader<0 ); if( db->bRoTrans==0 ){ /* Attempt a shared-lock on DMS1. */ rc = lsmShmLock(db, LSM_LOCK_DMS1, LSM_LOCK_SHARED, 0); if( rc!=LSM_OK ) return rc; rc = lsmShmTestLock( db, LSM_LOCK_RWCLIENT(0), LSM_LOCK_NREADER, LSM_LOCK_SHARED ); if( rc==LSM_OK ){ /* System is not live. Take a SHARED lock on the ROTRANS byte and ** release DMS1. Locking ROTRANS tells all read-write clients that they ** may not recycle any disk space from within the database or log files, ** as a read-only client may be using it. */ rc = lsmShmLock(db, LSM_LOCK_ROTRANS, LSM_LOCK_SHARED, 0); lsmShmLock(db, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0); if( rc==LSM_OK ){ db->bRoTrans = 1; rc = lsmShmCacheChunks(db, 1); if( rc==LSM_OK ){ db->pShmhdr = (ShmHeader *)db->apShm[0]; memset(db->pShmhdr, 0, sizeof(ShmHeader)); rc = lsmCheckpointRecover(db); if( rc==LSM_OK ){ rc = lsmLogRecover(db); } } } }else if( rc==LSM_BUSY ){ /* System is live! */ rc = lsmShmLock(db, LSM_LOCK_DMS3, LSM_LOCK_SHARED, 0); lsmShmLock(db, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0); if( rc==LSM_OK ){ rc = lsmShmCacheChunks(db, 1); if( rc==LSM_OK ){ db->pShmhdr = (ShmHeader *)db->apShm[0]; } } } if( rc==LSM_OK ){ rc = lsmBeginReadTrans(db); } } return rc; } /* ** Close the currently open read transaction. */ void lsmFinishReadTrans(lsm_db *pDb){ /* Worker connections should not be closing read transactions. And ** read transactions should only be closed after all cursors and write ** transactions have been closed. Finally pClient should be non-NULL ** only iff pDb->iReader>=0. */ assert( pDb->pWorker==0 ); assert( pDb->pCsr==0 && pDb->nTransOpen==0 ); if( pDb->bRoTrans ){ int i; for(i=0; i<pDb->nShm; i++){ lsmFree(pDb->pEnv, pDb->apShm[i]); } lsmFree(pDb->pEnv, pDb->apShm); pDb->apShm = 0; pDb->nShm = 0; pDb->pShmhdr = 0; lsmShmLock(pDb, LSM_LOCK_ROTRANS, LSM_LOCK_UNLOCK, 0); } dbReleaseReadlock(pDb); } /* ** Open a write transaction. */ int lsmBeginWriteTrans(lsm_db *pDb){ int rc = LSM_OK; /* Return code */ ShmHeader *pShm = pDb->pShmhdr; /* Shared memory header */ assert( pDb->nTransOpen==0 ); assert( pDb->bDiscardOld==0 ); assert( pDb->bReadonly==0 ); /* If there is no read-transaction open, open one now. */ if( pDb->iReader<0 ){ rc = lsmBeginReadTrans(pDb); } /* Attempt to take the WRITER lock */ if( rc==LSM_OK ){ rc = lsmShmLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_EXCL, 0); } /* If the previous writer failed mid-transaction, run emergency rollback. */ if( rc==LSM_OK && pShm->bWriter ){ rc = lsmTreeRepair(pDb); if( rc==LSM_OK ) pShm->bWriter = 0; } /* Check that this connection is currently reading from the most recent ** version of the database. If not, return LSM_BUSY. */ if( rc==LSM_OK && memcmp(&pShm->hdr1, &pDb->treehdr, sizeof(TreeHeader)) ){ rc = LSM_BUSY; } if( rc==LSM_OK ){ rc = lsmLogBegin(pDb); } /* If everything was successful, set the "transaction-in-progress" flag ** and return LSM_OK. Otherwise, if some error occurred, relinquish the ** WRITER lock and return an error code. */ if( rc==LSM_OK ){ TreeHeader *p = &pDb->treehdr; pShm->bWriter = 1; p->root.iTransId++; if( lsmTreeHasOld(pDb) && p->iOldLog==pDb->pClient->iLogOff ){ lsmTreeDiscardOld(pDb); pDb->bDiscardOld = 1; } }else{ lsmShmLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_UNLOCK, 0); if( pDb->pCsr==0 ) lsmFinishReadTrans(pDb); } return rc; } /* ** End the current write transaction. The connection is left with an open ** read transaction. It is an error to call this if there is no open write ** transaction. ** ** If the transaction was committed, then a commit record has already been ** written into the log file when this function is called. Or, if the ** transaction was rolled back, both the log file and in-memory tree ** structure have already been restored. In either case, this function ** merely releases locks and other resources held by the write-transaction. ** ** LSM_OK is returned if successful, or an LSM error code otherwise. */ int lsmFinishWriteTrans(lsm_db *pDb, int bCommit){ int rc = LSM_OK; int bFlush = 0; lsmLogEnd(pDb, bCommit); if( rc==LSM_OK && bCommit && lsmTreeSize(pDb)>pDb->nTreeLimit ){ bFlush = 1; lsmTreeMakeOld(pDb); } lsmTreeEndTransaction(pDb, bCommit); if( rc==LSM_OK ){ if( bFlush && pDb->bAutowork ){ rc = lsmSortedAutoWork(pDb, 1); }else if( bCommit && pDb->bDiscardOld ){ rc = dbSetReadLock(pDb, pDb->pClient->iId, pDb->treehdr.iUsedShmid); } } pDb->bDiscardOld = 0; lsmShmLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_UNLOCK, 0); if( bFlush && pDb->bAutowork==0 && pDb->xWork ){ pDb->xWork(pDb, pDb->pWorkCtx); } return rc; } /* ** Return non-zero if the caller is holding the client mutex. */ #ifdef LSM_DEBUG int lsmHoldingClientMutex(lsm_db *pDb){ return lsmMutexHeld(pDb->pEnv, pDb->pDatabase->pClientMutex); } #endif static int slotIsUsable(ShmReader *p, i64 iLsm, u32 iShmMin, u32 iShmMax){ return( p->iLsmId && p->iLsmId<=iLsm && shm_sequence_ge(iShmMax, p->iTreeId) && shm_sequence_ge(p->iTreeId, iShmMin) ); } /* ** Obtain a read-lock on database version identified by the combination ** of snapshot iLsm and tree iTree. Return LSM_OK if successful, or ** an LSM error code otherwise. */ int lsmReadlock(lsm_db *db, i64 iLsm, u32 iShmMin, u32 iShmMax){ int rc = LSM_OK; ShmHeader *pShm = db->pShmhdr; int i; assert( db->iReader<0 ); assert( shm_sequence_ge(iShmMax, iShmMin) ); /* This is a no-op if the read-only transaction flag is set. */ if( db->bRoTrans ){ db->iReader = 0; return LSM_OK; } /* Search for an exact match. */ for(i=0; db->iReader<0 && rc==LSM_OK && i<LSM_LOCK_NREADER; i++){ ShmReader *p = &pShm->aReader[i]; if( p->iLsmId==iLsm && p->iTreeId==iShmMax ){ rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_SHARED, 0); if( rc==LSM_OK && p->iLsmId==iLsm && p->iTreeId==iShmMax ){ db->iReader = i; }else if( rc==LSM_BUSY ){ rc = LSM_OK; } } } /* Try to obtain a write-lock on each slot, in order. If successful, set ** the slot values to iLsm/iTree. */ for(i=0; db->iReader<0 && rc==LSM_OK && i<LSM_LOCK_NREADER; i++){ rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_EXCL, 0); if( rc==LSM_BUSY ){ rc = LSM_OK; }else{ ShmReader *p = &pShm->aReader[i]; p->iLsmId = iLsm; p->iTreeId = iShmMax; rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_SHARED, 0); assert( rc!=LSM_BUSY ); if( rc==LSM_OK ) db->iReader = i; } } /* Search for any usable slot */ for(i=0; db->iReader<0 && rc==LSM_OK && i<LSM_LOCK_NREADER; i++){ ShmReader *p = &pShm->aReader[i]; if( slotIsUsable(p, iLsm, iShmMin, iShmMax) ){ rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_SHARED, 0); if( rc==LSM_OK && slotIsUsable(p, iLsm, iShmMin, iShmMax) ){ db->iReader = i; }else if( rc==LSM_BUSY ){ rc = LSM_OK; } } } if( rc==LSM_OK && db->iReader<0 ){ rc = LSM_BUSY; } return rc; } /* ** This is used to check if there exists a read-lock locking a particular ** version of either the in-memory tree or database file. ** ** If iLsmId is non-zero, then it is a snapshot id. If there exists a ** read-lock using this snapshot or newer, set *pbInUse to true. Or, ** if there is no such read-lock, set it to false. ** ** Or, if iLsmId is zero, then iShmid is a shared-memory sequence id. ** Search for a read-lock using this sequence id or newer. etc. */ static int isInUse(lsm_db *db, i64 iLsmId, u32 iShmid, int *pbInUse){ ShmHeader *pShm = db->pShmhdr; int i; int rc = LSM_OK; for(i=0; rc==LSM_OK && i<LSM_LOCK_NREADER; i++){ ShmReader *p = &pShm->aReader[i]; if( p->iLsmId ){ if( (iLsmId!=0 && p->iLsmId!=0 && iLsmId>=p->iLsmId) || (iLsmId==0 && shm_sequence_ge(p->iTreeId, iShmid)) ){ rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_EXCL, 0); if( rc==LSM_OK ){ p->iLsmId = 0; lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_UNLOCK, 0); } } } } if( rc==LSM_BUSY ){ *pbInUse = 1; return LSM_OK; } *pbInUse = 0; return rc; } /* ** This function is called by worker connections to determine the smallest ** snapshot id that is currently in use by a database client. The worker ** connection uses this result to determine whether or not it is safe to ** recycle a database block. */ static int firstSnapshotInUse( lsm_db *db, /* Database handle */ i64 *piInUse /* IN/OUT: Smallest snapshot id in use */ ){ ShmHeader *pShm = db->pShmhdr; i64 iInUse = *piInUse; int i; assert( iInUse>0 ); for(i=0; i<LSM_LOCK_NREADER; i++){ ShmReader *p = &pShm->aReader[i]; if( p->iLsmId ){ i64 iThis = p->iLsmId; if( iThis!=0 && iInUse>iThis ){ int rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_EXCL, 0); if( rc==LSM_OK ){ p->iLsmId = 0; lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_UNLOCK, 0); }else if( rc==LSM_BUSY ){ iInUse = iThis; }else{ /* Some error other than LSM_BUSY. Return the error code to ** the caller in this case. */ return rc; } } } } *piInUse = iInUse; return LSM_OK; } int lsmTreeInUse(lsm_db *db, u32 iShmid, int *pbInUse){ if( db->treehdr.iUsedShmid==iShmid ){ *pbInUse = 1; return LSM_OK; } return isInUse(db, 0, iShmid, pbInUse); } int lsmLsmInUse(lsm_db *db, i64 iLsmId, int *pbInUse){ if( db->pClient && db->pClient->iId<=iLsmId ){ *pbInUse = 1; return LSM_OK; } return isInUse(db, iLsmId, 0, pbInUse); } /* ** This function may only be called after a successful call to ** lsmDbDatabaseConnect(). It returns true if the connection is in ** multi-process mode, or false otherwise. */ int lsmDbMultiProc(lsm_db *pDb){ return pDb->pDatabase && pDb->pDatabase->bMultiProc; } /************************************************************************* ************************************************************************** ************************************************************************** ************************************************************************** ************************************************************************** *************************************************************************/ /* ** Ensure that database connection db has cached pointers to at least the ** first nChunk chunks of shared memory. */ int lsmShmCacheChunks(lsm_db *db, int nChunk){ int rc = LSM_OK; if( nChunk>db->nShm ){ static const int NINCR = 16; Database *p = db->pDatabase; lsm_env *pEnv = db->pEnv; int nAlloc; int i; /* Ensure that the db->apShm[] array is large enough. If an attempt to ** allocate memory fails, return LSM_NOMEM immediately. The apShm[] array ** is always extended in multiples of 16 entries - so the actual allocated ** size can be inferred from nShm. */ nAlloc = ((db->nShm + NINCR - 1) / NINCR) * NINCR; while( nChunk>=nAlloc ){ void **apShm; nAlloc += NINCR; apShm = lsmRealloc(pEnv, db->apShm, sizeof(void*)*nAlloc); if( !apShm ) return LSM_NOMEM_BKPT; db->apShm = apShm; } if( db->bRoTrans ){ for(i=db->nShm; rc==LSM_OK && i<nChunk; i++){ db->apShm[i] = lsmMallocZeroRc(pEnv, LSM_SHM_CHUNK_SIZE, &rc); db->nShm++; } }else{ /* Enter the client mutex */ lsmMutexEnter(pEnv, p->pClientMutex); /* Extend the Database objects apShmChunk[] array if necessary. Using the ** same pattern as for the lsm_db.apShm[] array above. */ nAlloc = ((p->nShmChunk + NINCR - 1) / NINCR) * NINCR; while( nChunk>=nAlloc ){ void **apShm; nAlloc += NINCR; apShm = lsmRealloc(pEnv, p->apShmChunk, sizeof(void*)*nAlloc); if( !apShm ){ rc = LSM_NOMEM_BKPT; break; } p->apShmChunk = apShm; } for(i=db->nShm; rc==LSM_OK && i<nChunk; i++){ if( i>=p->nShmChunk ){ void *pChunk = 0; if( p->bMultiProc==0 ){ /* Single process mode */ pChunk = lsmMallocZeroRc(pEnv, LSM_SHM_CHUNK_SIZE, &rc); }else{ /* Multi-process mode */ rc = lsmEnvShmMap(pEnv, p->pFile, i, LSM_SHM_CHUNK_SIZE, &pChunk); } if( rc==LSM_OK ){ p->apShmChunk[i] = pChunk; p->nShmChunk++; } } if( rc==LSM_OK ){ db->apShm[i] = p->apShmChunk[i]; db->nShm++; } } /* Release the client mutex */ lsmMutexLeave(pEnv, p->pClientMutex); } } return rc; } static int lockSharedFile(lsm_env *pEnv, Database *p, int iLock, int eOp){ int rc = LSM_OK; if( p->bMultiProc ){ rc = lsmEnvLock(pEnv, p->pFile, iLock, eOp); } return rc; } /* ** Test if it would be possible for connection db to obtain a lock of type ** eType on the nLock locks starting at iLock. If so, return LSM_OK. If it ** would not be possible to obtain the lock due to a lock held by another ** connection, return LSM_BUSY. If an IO or other error occurs (i.e. in the ** lsm_env.xTestLock function), return some other LSM error code. ** ** Note that this function never actually locks the database - it merely ** queries the system to see if there exists a lock that would prevent ** it from doing so. */ int lsmShmTestLock( lsm_db *db, int iLock, int nLock, int eOp ){ int rc = LSM_OK; lsm_db *pIter; Database *p = db->pDatabase; int i; u64 mask = 0; for(i=iLock; i<(iLock+nLock); i++){ mask |= ((u64)1 << (iLock-1)); if( eOp==LSM_LOCK_EXCL ) mask |= ((u64)1 << (iLock+32-1)); } lsmMutexEnter(db->pEnv, p->pClientMutex); for(pIter=p->pConn; pIter; pIter=pIter->pNext){ if( pIter!=db && (pIter->mLock & mask) ){ assert( pIter!=db ); break; } } if( pIter ){ rc = LSM_BUSY; }else if( p->bMultiProc ){ rc = lsmEnvTestLock(db->pEnv, p->pFile, iLock, nLock, eOp); } lsmMutexLeave(db->pEnv, p->pClientMutex); return rc; } /* ** Attempt to obtain the lock identified by the iLock and bExcl parameters. ** If successful, return LSM_OK. If the lock cannot be obtained because ** there exists some other conflicting lock, return LSM_BUSY. If some other ** error occurs, return an LSM error code. ** ** Parameter iLock must be one of LSM_LOCK_WRITER, WORKER or CHECKPOINTER, ** or else a value returned by the LSM_LOCK_READER macro. */ int lsmShmLock( lsm_db *db, int iLock, int eOp, /* One of LSM_LOCK_UNLOCK, SHARED or EXCL */ int bBlock /* True for a blocking lock */ ){ lsm_db *pIter; const u64 me = ((u64)1 << (iLock-1)); const u64 ms = ((u64)1 << (iLock+32-1)); int rc = LSM_OK; Database *p = db->pDatabase; assert( eOp!=LSM_LOCK_EXCL || p->bReadonly==0 ); assert( iLock>=1 && iLock<=LSM_LOCK_RWCLIENT(LSM_LOCK_NRWCLIENT-1) ); assert( LSM_LOCK_RWCLIENT(LSM_LOCK_NRWCLIENT-1)<=32 ); assert( eOp==LSM_LOCK_UNLOCK || eOp==LSM_LOCK_SHARED || eOp==LSM_LOCK_EXCL ); /* Check for a no-op. Proceed only if this is not one of those. */ if( (eOp==LSM_LOCK_UNLOCK && (db->mLock & (me|ms))!=0) || (eOp==LSM_LOCK_SHARED && (db->mLock & (me|ms))!=ms) || (eOp==LSM_LOCK_EXCL && (db->mLock & me)==0) ){ int nExcl = 0; /* Number of connections holding EXCLUSIVE */ int nShared = 0; /* Number of connections holding SHARED */ lsmMutexEnter(db->pEnv, p->pClientMutex); /* Figure out the locks currently held by this process on iLock, not ** including any held by connection db. */ for(pIter=p->pConn; pIter; pIter=pIter->pNext){ assert( (pIter->mLock & me)==0 || (pIter->mLock & ms)!=0 ); if( pIter!=db ){ if( pIter->mLock & me ){ nExcl++; }else if( pIter->mLock & ms ){ nShared++; } } } assert( nExcl==0 || nExcl==1 ); assert( nExcl==0 || nShared==0 ); assert( nExcl==0 || (db->mLock & (me|ms))==0 ); switch( eOp ){ case LSM_LOCK_UNLOCK: if( nShared==0 ){ lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_UNLOCK); } db->mLock &= ~(me|ms); break; case LSM_LOCK_SHARED: if( nExcl ){ rc = LSM_BUSY; }else{ if( nShared==0 ){ rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_SHARED); } if( rc==LSM_OK ){ db->mLock |= ms; db->mLock &= ~me; } } break; default: assert( eOp==LSM_LOCK_EXCL ); if( nExcl || nShared ){ rc = LSM_BUSY; }else{ rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_EXCL); if( rc==LSM_OK ){ db->mLock |= (me|ms); } } break; } lsmMutexLeave(db->pEnv, p->pClientMutex); } return rc; } #ifdef LSM_DEBUG int shmLockType(lsm_db *db, int iLock){ const u64 me = ((u64)1 << (iLock-1)); const u64 ms = ((u64)1 << (iLock+32-1)); if( db->mLock & me ) return LSM_LOCK_EXCL; if( db->mLock & ms ) return LSM_LOCK_SHARED; return LSM_LOCK_UNLOCK; } /* ** The arguments passed to this function are similar to those passed to ** the lsmShmLock() function. However, instead of obtaining a new lock ** this function returns true if the specified connection already holds ** (or does not hold) such a lock, depending on the value of eOp. As ** follows: ** ** (eOp==LSM_LOCK_UNLOCK) -> true if db has no lock on iLock ** (eOp==LSM_LOCK_SHARED) -> true if db has at least a SHARED lock on iLock. ** (eOp==LSM_LOCK_EXCL) -> true if db has an EXCLUSIVE lock on iLock. */ int lsmShmAssertLock(lsm_db *db, int iLock, int eOp){ int ret = 0; int eHave; assert( iLock>=1 && iLock<=LSM_LOCK_READER(LSM_LOCK_NREADER-1) ); assert( iLock<=16 ); assert( eOp==LSM_LOCK_UNLOCK || eOp==LSM_LOCK_SHARED || eOp==LSM_LOCK_EXCL ); eHave = shmLockType(db, iLock); switch( eOp ){ case LSM_LOCK_UNLOCK: ret = (eHave==LSM_LOCK_UNLOCK); break; case LSM_LOCK_SHARED: ret = (eHave!=LSM_LOCK_UNLOCK); break; case LSM_LOCK_EXCL: ret = (eHave==LSM_LOCK_EXCL); break; default: assert( !"bad eOp value passed to lsmShmAssertLock()" ); break; } return ret; } int lsmShmAssertWorker(lsm_db *db){ return lsmShmAssertLock(db, LSM_LOCK_WORKER, LSM_LOCK_EXCL) && db->pWorker; } /* ** This function does not contribute to library functionality, and is not ** included in release builds. It is intended to be called from within ** an interactive debugger. ** ** When called, this function prints a single line of human readable output ** to stdout describing the locks currently held by the connection. For ** example: ** ** (gdb) call print_db_locks(pDb) ** (shared on dms2) (exclusive on writer) */ void print_db_locks(lsm_db *db){ int iLock; for(iLock=0; iLock<16; iLock++){ int bOne = 0; const char *azLock[] = {0, "shared", "exclusive"}; const char *azName[] = { 0, "dms1", "dms2", "writer", "worker", "checkpointer", "reader0", "reader1", "reader2", "reader3", "reader4", "reader5" }; int eHave = shmLockType(db, iLock); if( azLock[eHave] ){ printf("%s(%s on %s)", (bOne?" ":""), azLock[eHave], azName[iLock]); bOne = 1; } } printf("\n"); } void print_all_db_locks(lsm_db *db){ lsm_db *p; for(p=db->pDatabase->pConn; p; p=p->pNext){ printf("%s connection %p ", ((p==db)?"*":""), p); print_db_locks(p); } } #endif void lsmShmBarrier(lsm_db *db){ lsmEnvShmBarrier(db->pEnv); } int lsm_checkpoint(lsm_db *pDb, int *pnKB){ int rc; /* Return code */ u32 nWrite = 0; /* Number of pages checkpointed */ /* Attempt the checkpoint. If successful, nWrite is set to the number of ** pages written between this and the previous checkpoint. */ rc = lsmCheckpointWrite(pDb, &nWrite); /* If required, calculate the output variable (KB of data checkpointed). ** Set it to zero if an error occured. */ if( pnKB ){ int nKB = 0; if( rc==LSM_OK && nWrite ){ nKB = (((i64)nWrite * lsmFsPageSize(pDb->pFS)) + 1023) / 1024; } *pnKB = nKB; } return rc; } |
Added ext/lsm1/lsm_sorted.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 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 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 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 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 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 4176 4177 4178 4179 4180 4181 4182 4183 4184 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 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 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 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 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 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 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 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 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 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 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 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 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 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 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 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 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 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 | /* ** 2011-08-14 ** ** 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. ** ************************************************************************* ** ** PAGE FORMAT: ** ** The maximum page size is 65536 bytes. ** ** Since all records are equal to or larger than 2 bytes in size, and ** some space within the page is consumed by the page footer, there must ** be less than 2^15 records on each page. ** ** Each page ends with a footer that describes the pages contents. This ** footer serves as similar purpose to the page header in an SQLite database. ** A footer is used instead of a header because it makes it easier to ** populate a new page based on a sorted list of key/value pairs. ** ** The footer consists of the following values (starting at the end of ** the page and continuing backwards towards the start). All values are ** stored as unsigned big-endian integers. ** ** * Number of records on page (2 bytes). ** * Flags field (2 bytes). ** * Left-hand pointer value (8 bytes). ** * The starting offset of each record (2 bytes per record). ** ** Records may span pages. Unless it happens to be an exact fit, the part ** of the final record that starts on page X that does not fit on page X ** is stored at the start of page (X+1). This means there may be pages where ** (N==0). And on most pages the first record that starts on the page will ** not start at byte offset 0. For example: ** ** aaaaa bbbbb ccc <footer> cc eeeee fffff g <footer> gggg.... ** ** RECORD FORMAT: ** ** The first byte of the record is a flags byte. It is a combination ** of the following flags (defined in lsmInt.h): ** ** LSM_START_DELETE ** LSM_END_DELETE ** LSM_POINT_DELETE ** LSM_INSERT ** LSM_SEPARATOR ** LSM_SYSTEMKEY ** ** Immediately following the type byte is a pointer to the smallest key ** in the next file that is larger than the key in the current record. The ** pointer is encoded as a varint. When added to the 32-bit page number ** stored in the footer, it is the page number of the page that contains the ** smallest key in the next sorted file that is larger than this key. ** ** Next is the number of bytes in the key, encoded as a varint. ** ** If the LSM_INSERT flag is set, the number of bytes in the value, as ** a varint, is next. ** ** Finally, the blob of data containing the key, and for LSM_INSERT ** records, the value as well. */ #ifndef _LSM_INT_H # include "lsmInt.h" #endif #define LSM_LOG_STRUCTURE 0 #define LSM_LOG_DATA 0 /* ** Macros to help decode record types. */ #define rtTopic(eType) ((eType) & LSM_SYSTEMKEY) #define rtIsDelete(eType) (((eType) & 0x0F)==LSM_POINT_DELETE) #define rtIsSeparator(eType) (((eType) & LSM_SEPARATOR)!=0) #define rtIsWrite(eType) (((eType) & LSM_INSERT)!=0) #define rtIsSystem(eType) (((eType) & LSM_SYSTEMKEY)!=0) /* ** The following macros are used to access a page footer. */ #define SEGMENT_NRECORD_OFFSET(pgsz) ((pgsz) - 2) #define SEGMENT_FLAGS_OFFSET(pgsz) ((pgsz) - 2 - 2) #define SEGMENT_POINTER_OFFSET(pgsz) ((pgsz) - 2 - 2 - 8) #define SEGMENT_CELLPTR_OFFSET(pgsz, iCell) ((pgsz) - 2 - 2 - 8 - 2 - (iCell)*2) #define SEGMENT_EOF(pgsz, nEntry) SEGMENT_CELLPTR_OFFSET(pgsz, nEntry) #define SEGMENT_BTREE_FLAG 0x0001 #define PGFTR_SKIP_NEXT_FLAG 0x0002 #define PGFTR_SKIP_THIS_FLAG 0x0004 #ifndef LSM_SEGMENTPTR_FREE_THRESHOLD # define LSM_SEGMENTPTR_FREE_THRESHOLD 1024 #endif typedef struct SegmentPtr SegmentPtr; typedef struct Blob Blob; struct Blob { lsm_env *pEnv; void *pData; int nData; int nAlloc; }; /* ** A SegmentPtr object may be used for one of two purposes: ** ** * To iterate and/or seek within a single Segment (the combination of a ** main run and an optional sorted run). ** ** * To iterate through the separators array of a segment. */ struct SegmentPtr { Level *pLevel; /* Level object segment is part of */ Segment *pSeg; /* Segment to access */ /* Current page. See segmentPtrLoadPage(). */ Page *pPg; /* Current page */ u16 flags; /* Copy of page flags field */ int nCell; /* Number of cells on pPg */ Pgno iPtr; /* Base cascade pointer */ /* Current cell. See segmentPtrLoadCell() */ int iCell; /* Current record within page pPg */ int eType; /* Type of current record */ Pgno iPgPtr; /* Cascade pointer offset */ void *pKey; int nKey; /* Key associated with current record */ void *pVal; int nVal; /* Current record value (eType==WRITE only) */ /* Blobs used to allocate buffers for pKey and pVal as required */ Blob blob1; Blob blob2; }; /* ** Used to iterate through the keys stored in a b-tree hierarchy from start ** to finish. Only First() and Next() operations are required. ** ** btreeCursorNew() ** btreeCursorFirst() ** btreeCursorNext() ** btreeCursorFree() ** btreeCursorPosition() ** btreeCursorRestore() */ typedef struct BtreePg BtreePg; typedef struct BtreeCursor BtreeCursor; struct BtreePg { Page *pPage; int iCell; }; struct BtreeCursor { Segment *pSeg; /* Iterate through this segments btree */ FileSystem *pFS; /* File system to read pages from */ int nDepth; /* Allocated size of aPg[] */ int iPg; /* Current entry in aPg[]. -1 -> EOF. */ BtreePg *aPg; /* Pages from root to current location */ /* Cache of current entry. pKey==0 for EOF. */ void *pKey; int nKey; int eType; Pgno iPtr; /* Storage for key, if not local */ Blob blob; }; /* ** A cursor used for merged searches or iterations through up to one ** Tree structure and any number of sorted files. ** ** lsmMCursorNew() ** lsmMCursorSeek() ** lsmMCursorNext() ** lsmMCursorPrev() ** lsmMCursorFirst() ** lsmMCursorLast() ** lsmMCursorKey() ** lsmMCursorValue() ** lsmMCursorValid() ** ** iFree: ** This variable is only used by cursors providing input data for a ** new top-level segment. Such cursors only ever iterate forwards, not ** backwards. */ struct MultiCursor { lsm_db *pDb; /* Connection that owns this cursor */ MultiCursor *pNext; /* Next cursor owned by connection pDb */ int flags; /* Mask of CURSOR_XXX flags */ int eType; /* Cache of current key type */ Blob key; /* Cache of current key (or NULL) */ Blob val; /* Cache of current value */ /* All the component cursors: */ TreeCursor *apTreeCsr[2]; /* Up to two tree cursors */ int iFree; /* Next element of free-list (-ve for eof) */ SegmentPtr *aPtr; /* Array of segment pointers */ int nPtr; /* Size of array aPtr[] */ BtreeCursor *pBtCsr; /* b-tree cursor (db writes only) */ /* Comparison results */ int nTree; /* Size of aTree[] array */ int *aTree; /* Array of comparison results */ /* Used by cursors flushing the in-memory tree only */ void *pSystemVal; /* Pointer to buffer to free */ /* Used by worker cursors only */ Pgno *pPrevMergePtr; }; /* ** The following constants are used to assign integers to each component ** cursor of a multi-cursor. */ #define CURSOR_DATA_TREE0 0 /* Current tree cursor (apTreeCsr[0]) */ #define CURSOR_DATA_TREE1 1 /* The "old" tree, if any (apTreeCsr[1]) */ #define CURSOR_DATA_SYSTEM 2 /* Free-list entries (new-toplevel only) */ #define CURSOR_DATA_SEGMENT 3 /* First segment pointer (aPtr[0]) */ /* ** CURSOR_IGNORE_DELETE ** If set, this cursor will not visit SORTED_DELETE keys. ** ** CURSOR_FLUSH_FREELIST ** This cursor is being used to create a new toplevel. It should also ** iterate through the contents of the in-memory free block list. ** ** CURSOR_IGNORE_SYSTEM ** If set, this cursor ignores system keys. ** ** CURSOR_NEXT_OK ** Set if it is Ok to call lsm_csr_next(). ** ** CURSOR_PREV_OK ** Set if it is Ok to call lsm_csr_prev(). ** ** CURSOR_READ_SEPARATORS ** Set if this cursor should visit the separator keys in segment ** aPtr[nPtr-1]. ** ** CURSOR_SEEK_EQ ** Cursor has undergone a successful lsm_csr_seek(LSM_SEEK_EQ) operation. ** The key and value are stored in MultiCursor.key and MultiCursor.val ** respectively. */ #define CURSOR_IGNORE_DELETE 0x00000001 #define CURSOR_FLUSH_FREELIST 0x00000002 #define CURSOR_IGNORE_SYSTEM 0x00000010 #define CURSOR_NEXT_OK 0x00000020 #define CURSOR_PREV_OK 0x00000040 #define CURSOR_READ_SEPARATORS 0x00000080 #define CURSOR_SEEK_EQ 0x00000100 typedef struct MergeWorker MergeWorker; typedef struct Hierarchy Hierarchy; struct Hierarchy { Page **apHier; int nHier; }; /* ** aSave: ** When mergeWorkerNextPage() is called to advance to the next page in ** the output segment, if the bStore flag for an element of aSave[] is ** true, it is cleared and the corresponding iPgno value is set to the ** page number of the page just completed. ** ** aSave[0] is used to record the pointer value to be pushed into the ** b-tree hierarchy. aSave[1] is used to save the page number of the ** page containing the indirect key most recently written to the b-tree. ** see mergeWorkerPushHierarchy() for details. */ struct MergeWorker { lsm_db *pDb; /* Database handle */ Level *pLevel; /* Worker snapshot Level being merged */ MultiCursor *pCsr; /* Cursor to read new segment contents from */ int bFlush; /* True if this is an in-memory tree flush */ Hierarchy hier; /* B-tree hierarchy under construction */ Page *pPage; /* Current output page */ int nWork; /* Number of calls to mergeWorkerNextPage() */ Pgno *aGobble; /* Gobble point for each input segment */ Pgno iIndirect; struct SavedPgno { Pgno iPgno; int bStore; } aSave[2]; }; #ifdef LSM_DEBUG_EXPENSIVE static int assertPointersOk(lsm_db *, Segment *, Segment *, int); static int assertBtreeOk(lsm_db *, Segment *); static void assertRunInOrder(lsm_db *pDb, Segment *pSeg); #else #define assertRunInOrder(x,y) #define assertBtreeOk(x,y) #endif struct FilePage { u8 *aData; int nData; }; static u8 *fsPageData(Page *pPg, int *pnData){ *pnData = ((struct FilePage *)(pPg))->nData; return ((struct FilePage *)(pPg))->aData; } /*UNUSED static u8 *fsPageDataPtr(Page *pPg){ return ((struct FilePage *)(pPg))->aData; }*/ /* ** Write nVal as a 16-bit unsigned big-endian integer into buffer aOut. */ void lsmPutU16(u8 *aOut, u16 nVal){ aOut[0] = (u8)((nVal>>8) & 0xFF); aOut[1] = (u8)(nVal & 0xFF); } void lsmPutU32(u8 *aOut, u32 nVal){ aOut[0] = (u8)((nVal>>24) & 0xFF); aOut[1] = (u8)((nVal>>16) & 0xFF); aOut[2] = (u8)((nVal>> 8) & 0xFF); aOut[3] = (u8)((nVal ) & 0xFF); } int lsmGetU16(u8 *aOut){ return (aOut[0] << 8) + aOut[1]; } u32 lsmGetU32(u8 *aOut){ return ((u32)aOut[0] << 24) + ((u32)aOut[1] << 16) + ((u32)aOut[2] << 8) + ((u32)aOut[3]); } u64 lsmGetU64(u8 *aOut){ return ((u64)aOut[0] << 56) + ((u64)aOut[1] << 48) + ((u64)aOut[2] << 40) + ((u64)aOut[3] << 32) + ((u64)aOut[4] << 24) + ((u32)aOut[5] << 16) + ((u32)aOut[6] << 8) + ((u32)aOut[7]); } void lsmPutU64(u8 *aOut, u64 nVal){ aOut[0] = (u8)((nVal>>56) & 0xFF); aOut[1] = (u8)((nVal>>48) & 0xFF); aOut[2] = (u8)((nVal>>40) & 0xFF); aOut[3] = (u8)((nVal>>32) & 0xFF); aOut[4] = (u8)((nVal>>24) & 0xFF); aOut[5] = (u8)((nVal>>16) & 0xFF); aOut[6] = (u8)((nVal>> 8) & 0xFF); aOut[7] = (u8)((nVal ) & 0xFF); } static int sortedBlobGrow(lsm_env *pEnv, Blob *pBlob, int nData){ assert( pBlob->pEnv==pEnv || (pBlob->pEnv==0 && pBlob->pData==0) ); if( pBlob->nAlloc<nData ){ pBlob->pData = lsmReallocOrFree(pEnv, pBlob->pData, nData); if( !pBlob->pData ) return LSM_NOMEM_BKPT; pBlob->nAlloc = nData; pBlob->pEnv = pEnv; } return LSM_OK; } static int sortedBlobSet(lsm_env *pEnv, Blob *pBlob, void *pData, int nData){ if( sortedBlobGrow(pEnv, pBlob, nData) ) return LSM_NOMEM; memcpy(pBlob->pData, pData, nData); pBlob->nData = nData; return LSM_OK; } #if 0 static int sortedBlobCopy(Blob *pDest, Blob *pSrc){ return sortedBlobSet(pDest, pSrc->pData, pSrc->nData); } #endif static void sortedBlobFree(Blob *pBlob){ assert( pBlob->pEnv || pBlob->pData==0 ); if( pBlob->pData ) lsmFree(pBlob->pEnv, pBlob->pData); memset(pBlob, 0, sizeof(Blob)); } static int sortedReadData( Segment *pSeg, Page *pPg, int iOff, int nByte, void **ppData, Blob *pBlob ){ int rc = LSM_OK; int iEnd; int nData; int nCell; u8 *aData; aData = fsPageData(pPg, &nData); nCell = lsmGetU16(&aData[SEGMENT_NRECORD_OFFSET(nData)]); iEnd = SEGMENT_EOF(nData, nCell); assert( iEnd>0 && iEnd<nData ); if( iOff+nByte<=iEnd ){ *ppData = (void *)&aData[iOff]; }else{ int nRem = nByte; int i = iOff; u8 *aDest; /* Make sure the blob is big enough to store the value being loaded. */ rc = sortedBlobGrow(lsmPageEnv(pPg), pBlob, nByte); if( rc!=LSM_OK ) return rc; pBlob->nData = nByte; aDest = (u8 *)pBlob->pData; *ppData = pBlob->pData; /* Increment the pointer pages ref-count. */ lsmFsPageRef(pPg); while( rc==LSM_OK ){ Page *pNext; int flags; /* Copy data from pPg into the output buffer. */ int nCopy = LSM_MIN(nRem, iEnd-i); if( nCopy>0 ){ memcpy(&aDest[nByte-nRem], &aData[i], nCopy); nRem -= nCopy; i += nCopy; assert( nRem==0 || i==iEnd ); } assert( nRem>=0 ); if( nRem==0 ) break; i -= iEnd; /* Grab the next page in the segment */ do { rc = lsmFsDbPageNext(pSeg, pPg, 1, &pNext); if( rc==LSM_OK && pNext==0 ){ rc = LSM_CORRUPT_BKPT; } if( rc ) break; lsmFsPageRelease(pPg); pPg = pNext; aData = fsPageData(pPg, &nData); flags = lsmGetU16(&aData[SEGMENT_FLAGS_OFFSET(nData)]); }while( flags&SEGMENT_BTREE_FLAG ); iEnd = SEGMENT_EOF(nData, lsmGetU16(&aData[nData-2])); assert( iEnd>0 && iEnd<nData ); } lsmFsPageRelease(pPg); } return rc; } static int pageGetNRec(u8 *aData, int nData){ return (int)lsmGetU16(&aData[SEGMENT_NRECORD_OFFSET(nData)]); } static Pgno pageGetPtr(u8 *aData, int nData){ return (Pgno)lsmGetU64(&aData[SEGMENT_POINTER_OFFSET(nData)]); } static int pageGetFlags(u8 *aData, int nData){ return (int)lsmGetU16(&aData[SEGMENT_FLAGS_OFFSET(nData)]); } static u8 *pageGetCell(u8 *aData, int nData, int iCell){ return &aData[lsmGetU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, iCell)])]; } /* ** Return the number of cells on page pPg. */ static int pageObjGetNRec(Page *pPg){ int nData; u8 *aData = lsmFsPageData(pPg, &nData); return pageGetNRec(aData, nData); } /* ** Return the decoded (possibly relative) pointer value stored in cell ** iCell from page aData/nData. */ static Pgno pageGetRecordPtr(u8 *aData, int nData, int iCell){ Pgno iRet; /* Return value */ u8 *aCell; /* Pointer to cell iCell */ assert( iCell<pageGetNRec(aData, nData) && iCell>=0 ); aCell = pageGetCell(aData, nData, iCell); lsmVarintGet64(&aCell[1], &iRet); return iRet; } static u8 *pageGetKey( Segment *pSeg, /* Segment pPg belongs to */ Page *pPg, /* Page to read from */ int iCell, /* Index of cell on page to read */ int *piTopic, /* OUT: Topic associated with this key */ int *pnKey, /* OUT: Size of key in bytes */ Blob *pBlob /* If required, use this for dynamic memory */ ){ u8 *pKey; int nDummy; int eType; u8 *aData; int nData; aData = fsPageData(pPg, &nData); assert( !(pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG) ); assert( iCell<pageGetNRec(aData, nData) ); pKey = pageGetCell(aData, nData, iCell); eType = *pKey++; pKey += lsmVarintGet32(pKey, &nDummy); pKey += lsmVarintGet32(pKey, pnKey); if( rtIsWrite(eType) ){ pKey += lsmVarintGet32(pKey, &nDummy); } *piTopic = rtTopic(eType); sortedReadData(pSeg, pPg, pKey-aData, *pnKey, (void **)&pKey, pBlob); return pKey; } static int pageGetKeyCopy( lsm_env *pEnv, /* Environment handle */ Segment *pSeg, /* Segment pPg belongs to */ Page *pPg, /* Page to read from */ int iCell, /* Index of cell on page to read */ int *piTopic, /* OUT: Topic associated with this key */ Blob *pBlob /* If required, use this for dynamic memory */ ){ int rc = LSM_OK; int nKey; u8 *aKey; aKey = pageGetKey(pSeg, pPg, iCell, piTopic, &nKey, pBlob); assert( (void *)aKey!=pBlob->pData || nKey==pBlob->nData ); if( (void *)aKey!=pBlob->pData ){ rc = sortedBlobSet(pEnv, pBlob, aKey, nKey); } return rc; } static Pgno pageGetBtreeRef(Page *pPg, int iKey){ Pgno iRef; u8 *aData; int nData; u8 *aCell; aData = fsPageData(pPg, &nData); aCell = pageGetCell(aData, nData, iKey); assert( aCell[0]==0 ); aCell++; aCell += lsmVarintGet64(aCell, &iRef); lsmVarintGet64(aCell, &iRef); assert( iRef>0 ); return iRef; } #define GETVARINT64(a, i) (((i)=((u8*)(a))[0])<=240?1:lsmVarintGet64((a), &(i))) #define GETVARINT32(a, i) (((i)=((u8*)(a))[0])<=240?1:lsmVarintGet32((a), &(i))) static int pageGetBtreeKey( Segment *pSeg, /* Segment page pPg belongs to */ Page *pPg, int iKey, Pgno *piPtr, int *piTopic, void **ppKey, int *pnKey, Blob *pBlob ){ u8 *aData; int nData; u8 *aCell; int eType; aData = fsPageData(pPg, &nData); assert( SEGMENT_BTREE_FLAG & pageGetFlags(aData, nData) ); assert( iKey>=0 && iKey<pageGetNRec(aData, nData) ); aCell = pageGetCell(aData, nData, iKey); eType = *aCell++; aCell += GETVARINT64(aCell, *piPtr); if( eType==0 ){ int rc; Pgno iRef; /* Page number of referenced page */ Page *pRef; aCell += GETVARINT64(aCell, iRef); rc = lsmFsDbPageGet(lsmPageFS(pPg), pSeg, iRef, &pRef); if( rc!=LSM_OK ) return rc; pageGetKeyCopy(lsmPageEnv(pPg), pSeg, pRef, 0, &eType, pBlob); lsmFsPageRelease(pRef); *ppKey = pBlob->pData; *pnKey = pBlob->nData; }else{ aCell += GETVARINT32(aCell, *pnKey); *ppKey = aCell; } if( piTopic ) *piTopic = rtTopic(eType); return LSM_OK; } static int btreeCursorLoadKey(BtreeCursor *pCsr){ int rc = LSM_OK; if( pCsr->iPg<0 ){ pCsr->pKey = 0; pCsr->nKey = 0; pCsr->eType = 0; }else{ Pgno dummy; int iPg = pCsr->iPg; int iCell = pCsr->aPg[iPg].iCell; while( iCell<0 && (--iPg)>=0 ){ iCell = pCsr->aPg[iPg].iCell-1; } if( iPg<0 || iCell<0 ) return LSM_CORRUPT_BKPT; rc = pageGetBtreeKey( pCsr->pSeg, pCsr->aPg[iPg].pPage, iCell, &dummy, &pCsr->eType, &pCsr->pKey, &pCsr->nKey, &pCsr->blob ); pCsr->eType |= LSM_SEPARATOR; } return rc; } static int btreeCursorPtr(u8 *aData, int nData, int iCell){ int nCell; nCell = pageGetNRec(aData, nData); if( iCell>=nCell ){ return (int)pageGetPtr(aData, nData); } return (int)pageGetRecordPtr(aData, nData, iCell); } static int btreeCursorNext(BtreeCursor *pCsr){ int rc = LSM_OK; BtreePg *pPg = &pCsr->aPg[pCsr->iPg]; int nCell; u8 *aData; int nData; assert( pCsr->iPg>=0 ); assert( pCsr->iPg==pCsr->nDepth-1 ); aData = fsPageData(pPg->pPage, &nData); nCell = pageGetNRec(aData, nData); assert( pPg->iCell<=nCell ); pPg->iCell++; if( pPg->iCell==nCell ){ Pgno iLoad; /* Up to parent. */ lsmFsPageRelease(pPg->pPage); pPg->pPage = 0; pCsr->iPg--; while( pCsr->iPg>=0 ){ pPg = &pCsr->aPg[pCsr->iPg]; aData = fsPageData(pPg->pPage, &nData); if( pPg->iCell<pageGetNRec(aData, nData) ) break; lsmFsPageRelease(pPg->pPage); pCsr->iPg--; } /* Read the key */ rc = btreeCursorLoadKey(pCsr); /* Unless the cursor is at EOF, descend to cell -1 (yes, negative one) of ** the left-most most descendent. */ if( pCsr->iPg>=0 ){ pCsr->aPg[pCsr->iPg].iCell++; iLoad = btreeCursorPtr(aData, nData, pPg->iCell); do { Page *pLoad; pCsr->iPg++; rc = lsmFsDbPageGet(pCsr->pFS, pCsr->pSeg, iLoad, &pLoad); pCsr->aPg[pCsr->iPg].pPage = pLoad; pCsr->aPg[pCsr->iPg].iCell = 0; if( rc==LSM_OK ){ if( pCsr->iPg==(pCsr->nDepth-1) ) break; aData = fsPageData(pLoad, &nData); iLoad = btreeCursorPtr(aData, nData, 0); } }while( rc==LSM_OK && pCsr->iPg<(pCsr->nDepth-1) ); pCsr->aPg[pCsr->iPg].iCell = -1; } }else{ rc = btreeCursorLoadKey(pCsr); } if( rc==LSM_OK && pCsr->iPg>=0 ){ aData = fsPageData(pCsr->aPg[pCsr->iPg].pPage, &nData); pCsr->iPtr = btreeCursorPtr(aData, nData, pCsr->aPg[pCsr->iPg].iCell+1); } return rc; } static void btreeCursorFree(BtreeCursor *pCsr){ if( pCsr ){ int i; lsm_env *pEnv = lsmFsEnv(pCsr->pFS); for(i=0; i<=pCsr->iPg; i++){ lsmFsPageRelease(pCsr->aPg[i].pPage); } sortedBlobFree(&pCsr->blob); lsmFree(pEnv, pCsr->aPg); lsmFree(pEnv, pCsr); } } static int btreeCursorFirst(BtreeCursor *pCsr){ int rc; Page *pPg = 0; FileSystem *pFS = pCsr->pFS; int iPg = (int)pCsr->pSeg->iRoot; do { rc = lsmFsDbPageGet(pFS, pCsr->pSeg, iPg, &pPg); assert( (rc==LSM_OK)==(pPg!=0) ); if( rc==LSM_OK ){ u8 *aData; int nData; int flags; aData = fsPageData(pPg, &nData); flags = pageGetFlags(aData, nData); if( (flags & SEGMENT_BTREE_FLAG)==0 ) break; if( (pCsr->nDepth % 8)==0 ){ int nNew = pCsr->nDepth + 8; pCsr->aPg = (BtreePg *)lsmReallocOrFreeRc( lsmFsEnv(pFS), pCsr->aPg, sizeof(BtreePg) * nNew, &rc ); if( rc==LSM_OK ){ memset(&pCsr->aPg[pCsr->nDepth], 0, sizeof(BtreePg) * 8); } } if( rc==LSM_OK ){ assert( pCsr->aPg[pCsr->nDepth].iCell==0 ); pCsr->aPg[pCsr->nDepth].pPage = pPg; pCsr->nDepth++; iPg = (int)pageGetRecordPtr(aData, nData, 0); } } }while( rc==LSM_OK ); lsmFsPageRelease(pPg); pCsr->iPg = pCsr->nDepth-1; if( rc==LSM_OK && pCsr->nDepth ){ pCsr->aPg[pCsr->iPg].iCell = -1; rc = btreeCursorNext(pCsr); } return rc; } static void btreeCursorPosition(BtreeCursor *pCsr, MergeInput *p){ if( pCsr->iPg>=0 ){ p->iPg = lsmFsPageNumber(pCsr->aPg[pCsr->iPg].pPage); p->iCell = ((pCsr->aPg[pCsr->iPg].iCell + 1) << 8) + pCsr->nDepth; }else{ p->iPg = 0; p->iCell = 0; } } static void btreeCursorSplitkey(BtreeCursor *pCsr, MergeInput *p){ int iCell = pCsr->aPg[pCsr->iPg].iCell; if( iCell>=0 ){ p->iCell = iCell; p->iPg = lsmFsPageNumber(pCsr->aPg[pCsr->iPg].pPage); }else{ int i; for(i=pCsr->iPg-1; i>=0; i--){ if( pCsr->aPg[i].iCell>0 ) break; } assert( i>=0 ); p->iCell = pCsr->aPg[i].iCell-1; p->iPg = lsmFsPageNumber(pCsr->aPg[i].pPage); } } static int sortedKeyCompare( int (*xCmp)(void *, int, void *, int), int iLhsTopic, void *pLhsKey, int nLhsKey, int iRhsTopic, void *pRhsKey, int nRhsKey ){ int res = iLhsTopic - iRhsTopic; if( res==0 ){ res = xCmp(pLhsKey, nLhsKey, pRhsKey, nRhsKey); } return res; } static int btreeCursorRestore( BtreeCursor *pCsr, int (*xCmp)(void *, int, void *, int), MergeInput *p ){ int rc = LSM_OK; if( p->iPg ){ lsm_env *pEnv = lsmFsEnv(pCsr->pFS); int iCell; /* Current cell number on leaf page */ Pgno iLeaf; /* Page number of current leaf page */ int nDepth; /* Depth of b-tree structure */ Segment *pSeg = pCsr->pSeg; /* Decode the MergeInput structure */ iLeaf = p->iPg; nDepth = (p->iCell & 0x00FF); iCell = (p->iCell >> 8) - 1; /* Allocate the BtreeCursor.aPg[] array */ assert( pCsr->aPg==0 ); pCsr->aPg = (BtreePg *)lsmMallocZeroRc(pEnv, sizeof(BtreePg) * nDepth, &rc); /* Populate the last entry of the aPg[] array */ if( rc==LSM_OK ){ Page **pp = &pCsr->aPg[nDepth-1].pPage; pCsr->iPg = nDepth-1; pCsr->nDepth = nDepth; pCsr->aPg[pCsr->iPg].iCell = iCell; rc = lsmFsDbPageGet(pCsr->pFS, pSeg, iLeaf, pp); } /* Populate any other aPg[] array entries */ if( rc==LSM_OK && nDepth>1 ){ Blob blob = {0,0,0}; void *pSeek; int nSeek; int iTopicSeek; int iPg = 0; int iLoad = (int)pSeg->iRoot; Page *pPg = pCsr->aPg[nDepth-1].pPage; if( pageObjGetNRec(pPg)==0 ){ /* This can happen when pPg is the right-most leaf in the b-tree. ** In this case, set the iTopicSeek/pSeek/nSeek key to a value ** greater than any real key. */ assert( iCell==-1 ); iTopicSeek = 1000; pSeek = 0; nSeek = 0; }else{ Pgno dummy; rc = pageGetBtreeKey(pSeg, pPg, 0, &dummy, &iTopicSeek, &pSeek, &nSeek, &pCsr->blob ); } do { Page *pPg2; rc = lsmFsDbPageGet(pCsr->pFS, pSeg, iLoad, &pPg2); assert( rc==LSM_OK || pPg2==0 ); if( rc==LSM_OK ){ u8 *aData; /* Buffer containing page data */ int nData; /* Size of aData[] in bytes */ int iMin; int iMax; int iCell2; aData = fsPageData(pPg2, &nData); assert( (pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG) ); iLoad = (int)pageGetPtr(aData, nData); iCell2 = pageGetNRec(aData, nData); iMax = iCell2-1; iMin = 0; while( iMax>=iMin ){ int iTry = (iMin+iMax)/2; void *pKey; int nKey; /* Key for cell iTry */ int iTopic; /* Topic for key pKeyT/nKeyT */ Pgno iPtr; /* Pointer for cell iTry */ int res; /* (pSeek - pKeyT) */ rc = pageGetBtreeKey( pSeg, pPg2, iTry, &iPtr, &iTopic, &pKey, &nKey, &blob ); if( rc!=LSM_OK ) break; res = sortedKeyCompare( xCmp, iTopicSeek, pSeek, nSeek, iTopic, pKey, nKey ); assert( res!=0 ); if( res<0 ){ iLoad = (int)iPtr; iCell2 = iTry; iMax = iTry-1; }else{ iMin = iTry+1; } } pCsr->aPg[iPg].pPage = pPg2; pCsr->aPg[iPg].iCell = iCell2; iPg++; assert( iPg!=nDepth-1 || lsmFsRedirectPage(pCsr->pFS, pSeg->pRedirect, iLoad)==iLeaf ); } }while( rc==LSM_OK && iPg<(nDepth-1) ); sortedBlobFree(&blob); } /* Load the current key and pointer */ if( rc==LSM_OK ){ BtreePg *pBtreePg; u8 *aData; int nData; pBtreePg = &pCsr->aPg[pCsr->iPg]; aData = fsPageData(pBtreePg->pPage, &nData); pCsr->iPtr = btreeCursorPtr(aData, nData, pBtreePg->iCell+1); if( pBtreePg->iCell<0 ){ Pgno dummy; int i; for(i=pCsr->iPg-1; i>=0; i--){ if( pCsr->aPg[i].iCell>0 ) break; } assert( i>=0 ); rc = pageGetBtreeKey(pSeg, pCsr->aPg[i].pPage, pCsr->aPg[i].iCell-1, &dummy, &pCsr->eType, &pCsr->pKey, &pCsr->nKey, &pCsr->blob ); pCsr->eType |= LSM_SEPARATOR; }else{ rc = btreeCursorLoadKey(pCsr); } } } return rc; } static int btreeCursorNew( lsm_db *pDb, Segment *pSeg, BtreeCursor **ppCsr ){ int rc = LSM_OK; BtreeCursor *pCsr; assert( pSeg->iRoot ); pCsr = lsmMallocZeroRc(pDb->pEnv, sizeof(BtreeCursor), &rc); if( pCsr ){ pCsr->pFS = pDb->pFS; pCsr->pSeg = pSeg; pCsr->iPg = -1; } *ppCsr = pCsr; return rc; } static void segmentPtrSetPage(SegmentPtr *pPtr, Page *pNext){ lsmFsPageRelease(pPtr->pPg); if( pNext ){ int nData; u8 *aData = fsPageData(pNext, &nData); pPtr->nCell = pageGetNRec(aData, nData); pPtr->flags = (u16)pageGetFlags(aData, nData); pPtr->iPtr = pageGetPtr(aData, nData); } pPtr->pPg = pNext; } /* ** Load a new page into the SegmentPtr object pPtr. */ static int segmentPtrLoadPage( FileSystem *pFS, SegmentPtr *pPtr, /* Load page into this SegmentPtr object */ int iNew /* Page number of new page */ ){ Page *pPg = 0; /* The new page */ int rc; /* Return Code */ rc = lsmFsDbPageGet(pFS, pPtr->pSeg, iNew, &pPg); assert( rc==LSM_OK || pPg==0 ); segmentPtrSetPage(pPtr, pPg); return rc; } static int segmentPtrReadData( SegmentPtr *pPtr, int iOff, int nByte, void **ppData, Blob *pBlob ){ return sortedReadData(pPtr->pSeg, pPtr->pPg, iOff, nByte, ppData, pBlob); } static int segmentPtrNextPage( SegmentPtr *pPtr, /* Load page into this SegmentPtr object */ int eDir /* +1 for next(), -1 for prev() */ ){ Page *pNext; /* New page to load */ int rc; /* Return code */ assert( eDir==1 || eDir==-1 ); assert( pPtr->pPg ); assert( pPtr->pSeg || eDir>0 ); rc = lsmFsDbPageNext(pPtr->pSeg, pPtr->pPg, eDir, &pNext); assert( rc==LSM_OK || pNext==0 ); segmentPtrSetPage(pPtr, pNext); return rc; } static int segmentPtrLoadCell( SegmentPtr *pPtr, /* Load page into this SegmentPtr object */ int iNew /* Cell number of new cell */ ){ int rc = LSM_OK; if( pPtr->pPg ){ u8 *aData; /* Pointer to page data buffer */ int iOff; /* Offset in aData[] to read from */ int nPgsz; /* Size of page (aData[]) in bytes */ assert( iNew<pPtr->nCell ); pPtr->iCell = iNew; aData = fsPageData(pPtr->pPg, &nPgsz); iOff = lsmGetU16(&aData[SEGMENT_CELLPTR_OFFSET(nPgsz, pPtr->iCell)]); pPtr->eType = aData[iOff]; iOff++; iOff += GETVARINT64(&aData[iOff], pPtr->iPgPtr); iOff += GETVARINT32(&aData[iOff], pPtr->nKey); if( rtIsWrite(pPtr->eType) ){ iOff += GETVARINT32(&aData[iOff], pPtr->nVal); } assert( pPtr->nKey>=0 ); rc = segmentPtrReadData( pPtr, iOff, pPtr->nKey, &pPtr->pKey, &pPtr->blob1 ); if( rc==LSM_OK && rtIsWrite(pPtr->eType) ){ rc = segmentPtrReadData( pPtr, iOff+pPtr->nKey, pPtr->nVal, &pPtr->pVal, &pPtr->blob2 ); }else{ pPtr->nVal = 0; pPtr->pVal = 0; } } return rc; } static Segment *sortedSplitkeySegment(Level *pLevel){ Merge *pMerge = pLevel->pMerge; MergeInput *p = &pMerge->splitkey; Segment *pSeg; int i; for(i=0; i<pMerge->nInput; i++){ if( p->iPg==pMerge->aInput[i].iPg ) break; } if( pMerge->nInput==(pLevel->nRight+1) && i>=(pMerge->nInput-1) ){ pSeg = &pLevel->pNext->lhs; }else{ pSeg = &pLevel->aRhs[i]; } return pSeg; } static void sortedSplitkey(lsm_db *pDb, Level *pLevel, int *pRc){ Segment *pSeg; Page *pPg = 0; lsm_env *pEnv = pDb->pEnv; /* Environment handle */ int rc = *pRc; Merge *pMerge = pLevel->pMerge; pSeg = sortedSplitkeySegment(pLevel); if( rc==LSM_OK ){ rc = lsmFsDbPageGet(pDb->pFS, pSeg, pMerge->splitkey.iPg, &pPg); } if( rc==LSM_OK ){ int iTopic; Blob blob = {0, 0, 0, 0}; u8 *aData; int nData; aData = lsmFsPageData(pPg, &nData); if( pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG ){ void *pKey; int nKey; Pgno dummy; rc = pageGetBtreeKey(pSeg, pPg, pMerge->splitkey.iCell, &dummy, &iTopic, &pKey, &nKey, &blob ); if( rc==LSM_OK && blob.pData!=pKey ){ rc = sortedBlobSet(pEnv, &blob, pKey, nKey); } }else{ rc = pageGetKeyCopy( pEnv, pSeg, pPg, pMerge->splitkey.iCell, &iTopic, &blob ); } pLevel->iSplitTopic = iTopic; pLevel->pSplitKey = blob.pData; pLevel->nSplitKey = blob.nData; lsmFsPageRelease(pPg); } *pRc = rc; } /* ** Reset a segment cursor. Also free its buffers if they are nThreshold ** bytes or larger in size. */ static void segmentPtrReset(SegmentPtr *pPtr, int nThreshold){ lsmFsPageRelease(pPtr->pPg); pPtr->pPg = 0; pPtr->nCell = 0; pPtr->pKey = 0; pPtr->nKey = 0; pPtr->pVal = 0; pPtr->nVal = 0; pPtr->eType = 0; pPtr->iCell = 0; if( pPtr->blob1.nAlloc>=nThreshold ) sortedBlobFree(&pPtr->blob1); if( pPtr->blob2.nAlloc>=nThreshold ) sortedBlobFree(&pPtr->blob2); } static int segmentPtrIgnoreSeparators(MultiCursor *pCsr, SegmentPtr *pPtr){ return (pCsr->flags & CURSOR_READ_SEPARATORS)==0 || (pPtr!=&pCsr->aPtr[pCsr->nPtr-1]); } static int segmentPtrAdvance( MultiCursor *pCsr, SegmentPtr *pPtr, int bReverse ){ int eDir = (bReverse ? -1 : 1); Level *pLvl = pPtr->pLevel; do { int rc; int iCell; /* Number of new cell in page */ int svFlags = 0; /* SegmentPtr.eType before advance */ iCell = pPtr->iCell + eDir; assert( pPtr->pPg ); assert( iCell<=pPtr->nCell && iCell>=-1 ); if( bReverse && pPtr->pSeg!=&pPtr->pLevel->lhs ){ svFlags = pPtr->eType; assert( svFlags ); } if( iCell>=pPtr->nCell || iCell<0 ){ do { rc = segmentPtrNextPage(pPtr, eDir); }while( rc==LSM_OK && pPtr->pPg && (pPtr->nCell==0 || (pPtr->flags & SEGMENT_BTREE_FLAG) ) ); if( rc!=LSM_OK ) return rc; iCell = bReverse ? (pPtr->nCell-1) : 0; } rc = segmentPtrLoadCell(pPtr, iCell); if( rc!=LSM_OK ) return rc; if( svFlags && pPtr->pPg ){ int res = sortedKeyCompare(pCsr->pDb->xCmp, rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey, pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey ); if( res<0 ) segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD); } if( pPtr->pPg==0 && (svFlags & LSM_END_DELETE) ){ Segment *pSeg = pPtr->pSeg; rc = lsmFsDbPageGet(pCsr->pDb->pFS, pSeg, pSeg->iFirst, &pPtr->pPg); if( rc!=LSM_OK ) return rc; pPtr->eType = LSM_START_DELETE | LSM_POINT_DELETE; pPtr->eType |= (pLvl->iSplitTopic ? LSM_SYSTEMKEY : 0); pPtr->pKey = pLvl->pSplitKey; pPtr->nKey = pLvl->nSplitKey; } }while( pCsr && pPtr->pPg && segmentPtrIgnoreSeparators(pCsr, pPtr) && rtIsSeparator(pPtr->eType) ); return LSM_OK; } static void segmentPtrEndPage( FileSystem *pFS, SegmentPtr *pPtr, int bLast, int *pRc ){ if( *pRc==LSM_OK ){ Segment *pSeg = pPtr->pSeg; Page *pNew = 0; if( bLast ){ *pRc = lsmFsDbPageLast(pFS, pSeg, &pNew); }else{ *pRc = lsmFsDbPageGet(pFS, pSeg, pSeg->iFirst, &pNew); } segmentPtrSetPage(pPtr, pNew); } } /* ** Try to move the segment pointer passed as the second argument so that it ** points at either the first (bLast==0) or last (bLast==1) cell in the valid ** region of the segment defined by pPtr->iFirst and pPtr->iLast. ** ** Return LSM_OK if successful or an lsm error code if something goes ** wrong (IO error, OOM etc.). */ static int segmentPtrEnd(MultiCursor *pCsr, SegmentPtr *pPtr, int bLast){ Level *pLvl = pPtr->pLevel; int rc = LSM_OK; FileSystem *pFS = pCsr->pDb->pFS; int bIgnore; segmentPtrEndPage(pFS, pPtr, bLast, &rc); while( rc==LSM_OK && pPtr->pPg && (pPtr->nCell==0 || (pPtr->flags & SEGMENT_BTREE_FLAG)) ){ rc = segmentPtrNextPage(pPtr, (bLast ? -1 : 1)); } if( rc==LSM_OK && pPtr->pPg ){ rc = segmentPtrLoadCell(pPtr, bLast ? (pPtr->nCell-1) : 0); if( rc==LSM_OK && bLast && pPtr->pSeg!=&pLvl->lhs ){ int res = sortedKeyCompare(pCsr->pDb->xCmp, rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey, pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey ); if( res<0 ) segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD); } } bIgnore = segmentPtrIgnoreSeparators(pCsr, pPtr); if( rc==LSM_OK && pPtr->pPg && bIgnore && rtIsSeparator(pPtr->eType) ){ rc = segmentPtrAdvance(pCsr, pPtr, bLast); } #if 0 if( bLast && rc==LSM_OK && pPtr->pPg && pPtr->pSeg==&pLvl->lhs && pLvl->nRight && (pPtr->eType & LSM_START_DELETE) ){ pPtr->iCell++; pPtr->eType = LSM_END_DELETE | (pLvl->iSplitTopic); pPtr->pKey = pLvl->pSplitKey; pPtr->nKey = pLvl->nSplitKey; pPtr->pVal = 0; pPtr->nVal = 0; } #endif return rc; } static void segmentPtrKey(SegmentPtr *pPtr, void **ppKey, int *pnKey){ assert( pPtr->pPg ); *ppKey = pPtr->pKey; *pnKey = pPtr->nKey; } #if 0 /* NOT USED */ static char *keyToString(lsm_env *pEnv, void *pKey, int nKey){ int i; u8 *aKey = (u8 *)pKey; char *zRet = (char *)lsmMalloc(pEnv, nKey+1); for(i=0; i<nKey; i++){ zRet[i] = (char)(isalnum(aKey[i]) ? aKey[i] : '.'); } zRet[nKey] = '\0'; return zRet; } #endif #if 0 /* NOT USED */ /* ** Check that the page that pPtr currently has loaded is the correct page ** to search for key (pKey/nKey). If it is, return 1. Otherwise, an assert ** fails and this function does not return. */ static int assertKeyLocation( MultiCursor *pCsr, SegmentPtr *pPtr, void *pKey, int nKey ){ lsm_env *pEnv = lsmFsEnv(pCsr->pDb->pFS); Blob blob = {0, 0, 0}; int eDir; int iTopic = 0; /* TODO: Fix me */ for(eDir=-1; eDir<=1; eDir+=2){ Page *pTest = pPtr->pPg; lsmFsPageRef(pTest); while( pTest ){ Segment *pSeg = pPtr->pSeg; Page *pNext; int rc = lsmFsDbPageNext(pSeg, pTest, eDir, &pNext); lsmFsPageRelease(pTest); if( rc ) return 1; pTest = pNext; if( pTest ){ int nData; u8 *aData = fsPageData(pTest, &nData); int nCell = pageGetNRec(aData, nData); int flags = pageGetFlags(aData, nData); if( nCell && 0==(flags&SEGMENT_BTREE_FLAG) ){ int nPgKey; int iPgTopic; u8 *pPgKey; int res; int iCell; iCell = ((eDir < 0) ? (nCell-1) : 0); pPgKey = pageGetKey(pSeg, pTest, iCell, &iPgTopic, &nPgKey, &blob); res = iTopic - iPgTopic; if( res==0 ) res = pCsr->pDb->xCmp(pKey, nKey, pPgKey, nPgKey); if( (eDir==1 && res>0) || (eDir==-1 && res<0) ){ /* Taking this branch means something has gone wrong. */ char *zMsg = lsmMallocPrintf(pEnv, "Key \"%s\" is not on page %d", keyToString(pEnv, pKey, nKey), lsmFsPageNumber(pPtr->pPg) ); fprintf(stderr, "%s\n", zMsg); assert( !"assertKeyLocation() failed" ); } lsmFsPageRelease(pTest); pTest = 0; } } } } sortedBlobFree(&blob); return 1; } #endif #ifndef NDEBUG static int assertSeekResult( MultiCursor *pCsr, SegmentPtr *pPtr, int iTopic, void *pKey, int nKey, int eSeek ){ if( pPtr->pPg ){ int res; res = sortedKeyCompare(pCsr->pDb->xCmp, iTopic, pKey, nKey, rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey ); if( eSeek==LSM_SEEK_EQ ) return (res==0); if( eSeek==LSM_SEEK_LE ) return (res>=0); if( eSeek==LSM_SEEK_GE ) return (res<=0); } return 1; } #endif static int segmentPtrSearchOversized( MultiCursor *pCsr, /* Cursor context */ SegmentPtr *pPtr, /* Pointer to seek */ int iTopic, /* Topic of key to search for */ void *pKey, int nKey /* Key to seek to */ ){ int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp; int rc = LSM_OK; /* If the OVERSIZED flag is set, then there is no pointer in the ** upper level to the next page in the segment that contains at least ** one key. So compare the largest key on the current page with the ** key being sought (pKey/nKey). If (pKey/nKey) is larger, advance ** to the next page in the segment that contains at least one key. */ while( rc==LSM_OK && (pPtr->flags & PGFTR_SKIP_NEXT_FLAG) ){ u8 *pLastKey; int nLastKey; int iLastTopic; int res; /* Result of comparison */ Page *pNext; /* Load the last key on the current page. */ pLastKey = pageGetKey(pPtr->pSeg, pPtr->pPg, pPtr->nCell-1, &iLastTopic, &nLastKey, &pPtr->blob1 ); /* If the loaded key is >= than (pKey/nKey), break out of the loop. ** If (pKey/nKey) is present in this array, it must be on the current ** page. */ res = sortedKeyCompare( xCmp, iLastTopic, pLastKey, nLastKey, iTopic, pKey, nKey ); if( res>=0 ) break; /* Advance to the next page that contains at least one key. */ pNext = pPtr->pPg; lsmFsPageRef(pNext); while( 1 ){ Page *pLoad; u8 *aData; int nData; rc = lsmFsDbPageNext(pPtr->pSeg, pNext, 1, &pLoad); lsmFsPageRelease(pNext); pNext = pLoad; if( pNext==0 ) break; assert( rc==LSM_OK ); aData = lsmFsPageData(pNext, &nData); if( (pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG)==0 && pageGetNRec(aData, nData)>0 ){ break; } } if( pNext==0 ) break; segmentPtrSetPage(pPtr, pNext); /* This should probably be an LSM_CORRUPT error. */ assert( rc!=LSM_OK || (pPtr->flags & PGFTR_SKIP_THIS_FLAG) ); } return rc; } static int ptrFwdPointer( Page *pPage, int iCell, Segment *pSeg, Pgno *piPtr, int *pbFound ){ Page *pPg = pPage; int iFirst = iCell; int rc = LSM_OK; do { Page *pNext = 0; u8 *aData; int nData; aData = lsmFsPageData(pPg, &nData); if( (pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG)==0 ){ int i; int nCell = pageGetNRec(aData, nData); for(i=iFirst; i<nCell; i++){ u8 eType = *pageGetCell(aData, nData, i); if( (eType & LSM_START_DELETE)==0 ){ *pbFound = 1; *piPtr = pageGetRecordPtr(aData, nData, i) + pageGetPtr(aData, nData); lsmFsPageRelease(pPg); return LSM_OK; } } } rc = lsmFsDbPageNext(pSeg, pPg, 1, &pNext); lsmFsPageRelease(pPg); pPg = pNext; iFirst = 0; }while( pPg && rc==LSM_OK ); lsmFsPageRelease(pPg); *pbFound = 0; return rc; } static int sortedRhsFirst(MultiCursor *pCsr, Level *pLvl, SegmentPtr *pPtr){ int rc; rc = segmentPtrEnd(pCsr, pPtr, 0); while( pPtr->pPg && rc==LSM_OK ){ int res = sortedKeyCompare(pCsr->pDb->xCmp, pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey, rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey ); if( res<=0 ) break; rc = segmentPtrAdvance(pCsr, pPtr, 0); } return rc; } /* ** This function is called as part of a SEEK_GE op on a multi-cursor if the ** FC pointer read from segment *pPtr comes from an entry with the ** LSM_START_DELETE flag set. In this case the pointer value cannot be ** trusted. Instead, the pointer that should be followed is that associated ** with the next entry in *pPtr that does not have LSM_START_DELETE set. ** ** Why the pointers can't be trusted: ** ** ** ** TODO: This is a stop-gap solution: ** ** At the moment, this function is called from within segmentPtrSeek(), ** as part of the initial lsmMCursorSeek() call. However, consider a ** database where the following has occurred: ** ** 1. A range delete removes keys 1..9999 using a range delete. ** 2. Keys 1 through 9999 are reinserted. ** 3. The levels containing the ops in 1. and 2. above are merged. Call ** this level N. Level N contains FC pointers to level N+1. ** ** Then, if the user attempts to query for (key>=2 LIMIT 10), the ** lsmMCursorSeek() call will iterate through 9998 entries searching for a ** pointer down to the level N+1 that is never actually used. It would be ** much better if the multi-cursor could do this lazily - only seek to the ** level (N+1) page after the user has moved the cursor on level N passed ** the big range-delete. */ static int segmentPtrFwdPointer( MultiCursor *pCsr, /* Multi-cursor pPtr belongs to */ SegmentPtr *pPtr, /* Segment-pointer to extract FC ptr from */ Pgno *piPtr /* OUT: FC pointer value */ ){ Level *pLvl = pPtr->pLevel; Level *pNext = pLvl->pNext; Page *pPg = pPtr->pPg; int rc; int bFound; Pgno iOut = 0; if( pPtr->pSeg==&pLvl->lhs || pPtr->pSeg==&pLvl->aRhs[pLvl->nRight-1] ){ if( pNext==0 || (pNext->nRight==0 && pNext->lhs.iRoot) || (pNext->nRight!=0 && pNext->aRhs[0].iRoot) ){ /* Do nothing. The pointer will not be used anyway. */ return LSM_OK; } }else{ if( pPtr[1].pSeg->iRoot ){ return LSM_OK; } } /* Search for a pointer within the current segment. */ lsmFsPageRef(pPg); rc = ptrFwdPointer(pPg, pPtr->iCell, pPtr->pSeg, &iOut, &bFound); if( rc==LSM_OK && bFound==0 ){ /* This case happens when pPtr points to the left-hand-side of a segment ** currently undergoing an incremental merge. In this case, jump to the ** oldest segment in the right-hand-side of the same level and continue ** searching. But - do not consider any keys smaller than the levels ** split-key. */ SegmentPtr ptr; if( pPtr->pLevel->nRight==0 || pPtr->pSeg!=&pPtr->pLevel->lhs ){ return LSM_CORRUPT_BKPT; } memset(&ptr, 0, sizeof(SegmentPtr)); ptr.pLevel = pPtr->pLevel; ptr.pSeg = &ptr.pLevel->aRhs[ptr.pLevel->nRight-1]; rc = sortedRhsFirst(pCsr, ptr.pLevel, &ptr); if( rc==LSM_OK ){ rc = ptrFwdPointer(ptr.pPg, ptr.iCell, ptr.pSeg, &iOut, &bFound); ptr.pPg = 0; } segmentPtrReset(&ptr, 0); } *piPtr = iOut; return rc; } static int segmentPtrSeek( MultiCursor *pCsr, /* Cursor context */ SegmentPtr *pPtr, /* Pointer to seek */ int iTopic, /* Key topic to seek to */ void *pKey, int nKey, /* Key to seek to */ int eSeek, /* Search bias - see above */ int *piPtr, /* OUT: FC pointer */ int *pbStop ){ int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp; int res = 0; /* Result of comparison operation */ int rc = LSM_OK; int iMin; int iMax; Pgno iPtrOut = 0; /* If the current page contains an oversized entry, then there are no ** pointers to one or more of the subsequent pages in the sorted run. ** The following call ensures that the segment-ptr points to the correct ** page in this case. */ rc = segmentPtrSearchOversized(pCsr, pPtr, iTopic, pKey, nKey); iPtrOut = pPtr->iPtr; /* Assert that this page is the right page of this segment for the key ** that we are searching for. Do this by loading page (iPg-1) and testing ** that pKey/nKey is greater than all keys on that page, and then by ** loading (iPg+1) and testing that pKey/nKey is smaller than all ** the keys it houses. ** ** TODO: With range-deletes in the tree, the test described above may fail. */ #if 0 assert( assertKeyLocation(pCsr, pPtr, pKey, nKey) ); #endif assert( pPtr->nCell>0 || pPtr->pSeg->nSize==1 || lsmFsDbPageIsLast(pPtr->pSeg, pPtr->pPg) ); if( pPtr->nCell==0 ){ segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD); }else{ iMin = 0; iMax = pPtr->nCell-1; while( 1 ){ int iTry = (iMin+iMax)/2; void *pKeyT; int nKeyT; /* Key for cell iTry */ int iTopicT; assert( iTry<iMax || iMin==iMax ); rc = segmentPtrLoadCell(pPtr, iTry); if( rc!=LSM_OK ) break; segmentPtrKey(pPtr, &pKeyT, &nKeyT); iTopicT = rtTopic(pPtr->eType); res = sortedKeyCompare(xCmp, iTopicT, pKeyT, nKeyT, iTopic, pKey, nKey); if( res<=0 ){ iPtrOut = pPtr->iPtr + pPtr->iPgPtr; } if( res==0 || iMin==iMax ){ break; }else if( res>0 ){ iMax = LSM_MAX(iTry-1, iMin); }else{ iMin = iTry+1; } } if( rc==LSM_OK ){ assert( res==0 || (iMin==iMax && iMin>=0 && iMin<pPtr->nCell) ); if( res ){ rc = segmentPtrLoadCell(pPtr, iMin); } assert( rc!=LSM_OK || res>0 || iPtrOut==(pPtr->iPtr + pPtr->iPgPtr) ); if( rc==LSM_OK ){ switch( eSeek ){ case LSM_SEEK_EQ: { int eType = pPtr->eType; if( (res<0 && (eType & LSM_START_DELETE)) || (res>0 && (eType & LSM_END_DELETE)) || (res==0 && (eType & LSM_POINT_DELETE)) ){ *pbStop = 1; }else if( res==0 && (eType & LSM_INSERT) ){ lsm_env *pEnv = pCsr->pDb->pEnv; *pbStop = 1; pCsr->eType = pPtr->eType; rc = sortedBlobSet(pEnv, &pCsr->key, pPtr->pKey, pPtr->nKey); if( rc==LSM_OK ){ rc = sortedBlobSet(pEnv, &pCsr->val, pPtr->pVal, pPtr->nVal); } pCsr->flags |= CURSOR_SEEK_EQ; } segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD); break; } case LSM_SEEK_LE: if( res>0 ) rc = segmentPtrAdvance(pCsr, pPtr, 1); break; case LSM_SEEK_GE: { /* Figure out if we need to 'skip' the pointer forward or not */ if( (res<=0 && (pPtr->eType & LSM_START_DELETE)) || (res>0 && (pPtr->eType & LSM_END_DELETE)) ){ rc = segmentPtrFwdPointer(pCsr, pPtr, &iPtrOut); } if( res<0 && rc==LSM_OK ){ rc = segmentPtrAdvance(pCsr, pPtr, 0); } break; } } } } /* If the cursor seek has found a separator key, and this cursor is ** supposed to ignore separators keys, advance to the next entry. */ if( rc==LSM_OK && pPtr->pPg && segmentPtrIgnoreSeparators(pCsr, pPtr) && rtIsSeparator(pPtr->eType) ){ assert( eSeek!=LSM_SEEK_EQ ); rc = segmentPtrAdvance(pCsr, pPtr, eSeek==LSM_SEEK_LE); } } assert( rc!=LSM_OK || assertSeekResult(pCsr,pPtr,iTopic,pKey,nKey,eSeek) ); *piPtr = (int)iPtrOut; return rc; } static int seekInBtree( MultiCursor *pCsr, /* Multi-cursor object */ Segment *pSeg, /* Seek within this segment */ int iTopic, void *pKey, int nKey, /* Key to seek to */ Pgno *aPg, /* OUT: Page numbers */ Page **ppPg /* OUT: Leaf (sorted-run) page reference */ ){ int i = 0; int rc; int iPg; Page *pPg = 0; Blob blob = {0, 0, 0}; iPg = (int)pSeg->iRoot; do { Pgno *piFirst = 0; if( aPg ){ aPg[i++] = iPg; piFirst = &aPg[i]; } rc = lsmFsDbPageGet(pCsr->pDb->pFS, pSeg, iPg, &pPg); assert( rc==LSM_OK || pPg==0 ); if( rc==LSM_OK ){ u8 *aData; /* Buffer containing page data */ int nData; /* Size of aData[] in bytes */ int iMin; int iMax; int nRec; int flags; aData = fsPageData(pPg, &nData); flags = pageGetFlags(aData, nData); if( (flags & SEGMENT_BTREE_FLAG)==0 ) break; iPg = (int)pageGetPtr(aData, nData); nRec = pageGetNRec(aData, nData); iMin = 0; iMax = nRec-1; while( iMax>=iMin ){ int iTry = (iMin+iMax)/2; void *pKeyT; int nKeyT; /* Key for cell iTry */ int iTopicT; /* Topic for key pKeyT/nKeyT */ Pgno iPtr; /* Pointer associated with cell iTry */ int res; /* (pKey - pKeyT) */ rc = pageGetBtreeKey( pSeg, pPg, iTry, &iPtr, &iTopicT, &pKeyT, &nKeyT, &blob ); if( rc!=LSM_OK ) break; if( piFirst && pKeyT==blob.pData ){ *piFirst = pageGetBtreeRef(pPg, iTry); piFirst = 0; i++; } res = sortedKeyCompare( pCsr->pDb->xCmp, iTopic, pKey, nKey, iTopicT, pKeyT, nKeyT ); if( res<0 ){ iPg = (int)iPtr; iMax = iTry-1; }else{ iMin = iTry+1; } } lsmFsPageRelease(pPg); pPg = 0; } }while( rc==LSM_OK ); sortedBlobFree(&blob); assert( (rc==LSM_OK)==(pPg!=0) ); if( ppPg ){ *ppPg = pPg; }else{ lsmFsPageRelease(pPg); } return rc; } static int seekInSegment( MultiCursor *pCsr, SegmentPtr *pPtr, int iTopic, void *pKey, int nKey, int iPg, /* Page to search */ int eSeek, /* Search bias - see above */ int *piPtr, /* OUT: FC pointer */ int *pbStop /* OUT: Stop search flag */ ){ int iPtr = iPg; int rc = LSM_OK; if( pPtr->pSeg->iRoot ){ Page *pPg; assert( pPtr->pSeg->iRoot!=0 ); rc = seekInBtree(pCsr, pPtr->pSeg, iTopic, pKey, nKey, 0, &pPg); if( rc==LSM_OK ) segmentPtrSetPage(pPtr, pPg); }else{ if( iPtr==0 ){ iPtr = (int)pPtr->pSeg->iFirst; } if( rc==LSM_OK ){ rc = segmentPtrLoadPage(pCsr->pDb->pFS, pPtr, iPtr); } } if( rc==LSM_OK ){ rc = segmentPtrSeek(pCsr, pPtr, iTopic, pKey, nKey, eSeek, piPtr, pbStop); } return rc; } /* ** Seek each segment pointer in the array of (pLvl->nRight+1) at aPtr[]. ** ** pbStop: ** This parameter is only significant if parameter eSeek is set to ** LSM_SEEK_EQ. In this case, it is set to true before returning if ** the seek operation is finished. This can happen in two ways: ** ** a) A key matching (pKey/nKey) is found, or ** b) A point-delete or range-delete deleting the key is found. ** ** In case (a), the multi-cursor CURSOR_SEEK_EQ flag is set and the pCsr->key ** and pCsr->val blobs populated before returning. */ static int seekInLevel( MultiCursor *pCsr, /* Sorted cursor object to seek */ SegmentPtr *aPtr, /* Pointer to array of (nRhs+1) SPs */ int eSeek, /* Search bias - see above */ int iTopic, /* Key topic to search for */ void *pKey, int nKey, /* Key to search for */ Pgno *piPgno, /* IN/OUT: fraction cascade pointer (or 0) */ int *pbStop /* OUT: See above */ ){ Level *pLvl = aPtr[0].pLevel; /* Level to seek within */ int rc = LSM_OK; /* Return code */ int iOut = 0; /* Pointer to return to caller */ int res = -1; /* Result of xCmp(pKey, split) */ int nRhs = pLvl->nRight; /* Number of right-hand-side segments */ int bStop = 0; /* If this is a composite level (one currently undergoing an incremental ** merge), figure out if the search key is larger or smaller than the ** levels split-key. */ if( nRhs ){ res = sortedKeyCompare(pCsr->pDb->xCmp, iTopic, pKey, nKey, pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey ); } /* If (res<0), then key pKey/nKey is smaller than the split-key (or this ** is not a composite level and there is no split-key). Search the ** left-hand-side of the level in this case. */ if( res<0 ){ int iPtr = 0; if( nRhs==0 ) iPtr = (int)*piPgno; rc = seekInSegment( pCsr, &aPtr[0], iTopic, pKey, nKey, iPtr, eSeek, &iOut, &bStop ); if( rc==LSM_OK && nRhs>0 && eSeek==LSM_SEEK_GE && aPtr[0].pPg==0 ){ res = 0; } } if( res>=0 ){ int bHit = 0; /* True if at least one rhs is not EOF */ int iPtr = (int)*piPgno; int i; for(i=1; rc==LSM_OK && i<=nRhs && bStop==0; i++){ SegmentPtr *pPtr = &aPtr[i]; iOut = 0; rc = seekInSegment( pCsr, pPtr, iTopic, pKey, nKey, iPtr, eSeek, &iOut, &bStop ); iPtr = iOut; /* If the segment-pointer has settled on a key that is smaller than ** the splitkey, invalidate the segment-pointer. */ if( pPtr->pPg ){ res = sortedKeyCompare(pCsr->pDb->xCmp, rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey, pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey ); if( res<0 ){ if( pPtr->eType & LSM_START_DELETE ){ pPtr->eType &= ~LSM_INSERT; pPtr->pKey = pLvl->pSplitKey; pPtr->nKey = pLvl->nSplitKey; pPtr->pVal = 0; pPtr->nVal = 0; }else{ segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD); } } } if( aPtr[i].pKey ) bHit = 1; } if( rc==LSM_OK && eSeek==LSM_SEEK_LE && bHit==0 ){ rc = segmentPtrEnd(pCsr, &aPtr[0], 1); } } assert( eSeek==LSM_SEEK_EQ || bStop==0 ); *piPgno = iOut; *pbStop = bStop; return rc; } static void multiCursorGetKey( MultiCursor *pCsr, int iKey, int *peType, /* OUT: Key type (SORTED_WRITE etc.) */ void **ppKey, /* OUT: Pointer to buffer containing key */ int *pnKey /* OUT: Size of *ppKey in bytes */ ){ int nKey = 0; void *pKey = 0; int eType = 0; switch( iKey ){ case CURSOR_DATA_TREE0: case CURSOR_DATA_TREE1: { TreeCursor *pTreeCsr = pCsr->apTreeCsr[iKey-CURSOR_DATA_TREE0]; if( lsmTreeCursorValid(pTreeCsr) ){ lsmTreeCursorKey(pTreeCsr, &eType, &pKey, &nKey); } break; } case CURSOR_DATA_SYSTEM: { Snapshot *pWorker = pCsr->pDb->pWorker; if( pWorker && (pCsr->flags & CURSOR_FLUSH_FREELIST) ){ int nEntry = pWorker->freelist.nEntry; if( pCsr->iFree < (nEntry*2) ){ FreelistEntry *aEntry = pWorker->freelist.aEntry; int i = nEntry - 1 - (pCsr->iFree / 2); u32 iKey2 = 0; if( (pCsr->iFree % 2) ){ eType = LSM_END_DELETE|LSM_SYSTEMKEY; iKey2 = aEntry[i].iBlk-1; }else if( aEntry[i].iId>=0 ){ eType = LSM_INSERT|LSM_SYSTEMKEY; iKey2 = aEntry[i].iBlk; /* If the in-memory entry immediately before this one was a ** DELETE, and the block number is one greater than the current ** block number, mark this entry as an "end-delete-range". */ if( i<(nEntry-1) && aEntry[i+1].iBlk==iKey2+1 && aEntry[i+1].iId<0 ){ eType |= LSM_END_DELETE; } }else{ eType = LSM_START_DELETE|LSM_SYSTEMKEY; iKey2 = aEntry[i].iBlk + 1; } /* If the in-memory entry immediately after this one is a ** DELETE, and the block number is one less than the current ** key, mark this entry as an "start-delete-range". */ if( i>0 && aEntry[i-1].iBlk==iKey2-1 && aEntry[i-1].iId<0 ){ eType |= LSM_START_DELETE; } pKey = pCsr->pSystemVal; nKey = 4; lsmPutU32(pKey, ~iKey2); } } break; } default: { int iPtr = iKey - CURSOR_DATA_SEGMENT; assert( iPtr>=0 ); if( iPtr==pCsr->nPtr ){ if( pCsr->pBtCsr ){ pKey = pCsr->pBtCsr->pKey; nKey = pCsr->pBtCsr->nKey; eType = pCsr->pBtCsr->eType; } }else if( iPtr<pCsr->nPtr ){ SegmentPtr *pPtr = &pCsr->aPtr[iPtr]; if( pPtr->pPg ){ pKey = pPtr->pKey; nKey = pPtr->nKey; eType = pPtr->eType; } } break; } } if( peType ) *peType = eType; if( pnKey ) *pnKey = nKey; if( ppKey ) *ppKey = pKey; } static int sortedDbKeyCompare( MultiCursor *pCsr, int iLhsFlags, void *pLhsKey, int nLhsKey, int iRhsFlags, void *pRhsKey, int nRhsKey ){ int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp; int res; /* Compare the keys, including the system flag. */ res = sortedKeyCompare(xCmp, rtTopic(iLhsFlags), pLhsKey, nLhsKey, rtTopic(iRhsFlags), pRhsKey, nRhsKey ); /* If a key has the LSM_START_DELETE flag set, but not the LSM_INSERT or ** LSM_POINT_DELETE flags, it is considered a delta larger. This prevents ** the beginning of an open-ended set from masking a database entry or ** delete at a lower level. */ if( res==0 && (pCsr->flags & CURSOR_IGNORE_DELETE) ){ const int m = LSM_POINT_DELETE|LSM_INSERT|LSM_END_DELETE |LSM_START_DELETE; int iDel1 = 0; int iDel2 = 0; if( LSM_START_DELETE==(iLhsFlags & m) ) iDel1 = +1; if( LSM_END_DELETE ==(iLhsFlags & m) ) iDel1 = -1; if( LSM_START_DELETE==(iRhsFlags & m) ) iDel2 = +1; if( LSM_END_DELETE ==(iRhsFlags & m) ) iDel2 = -1; res = (iDel1 - iDel2); } return res; } static void multiCursorDoCompare(MultiCursor *pCsr, int iOut, int bReverse){ int i1; int i2; int iRes; void *pKey1; int nKey1; int eType1; void *pKey2; int nKey2; int eType2; const int mul = (bReverse ? -1 : 1); assert( pCsr->aTree && iOut<pCsr->nTree ); if( iOut>=(pCsr->nTree/2) ){ i1 = (iOut - pCsr->nTree/2) * 2; i2 = i1 + 1; }else{ i1 = pCsr->aTree[iOut*2]; i2 = pCsr->aTree[iOut*2+1]; } multiCursorGetKey(pCsr, i1, &eType1, &pKey1, &nKey1); multiCursorGetKey(pCsr, i2, &eType2, &pKey2, &nKey2); if( pKey1==0 ){ iRes = i2; }else if( pKey2==0 ){ iRes = i1; }else{ int res; /* Compare the keys */ res = sortedDbKeyCompare(pCsr, eType1, pKey1, nKey1, eType2, pKey2, nKey2 ); res = res * mul; if( res==0 ){ /* The two keys are identical. Normally, this means that the key from ** the newer run clobbers the old. However, if the newer key is a ** separator key, or a range-delete-boundary only, do not allow it ** to clobber an older entry. */ int nc1 = (eType1 & (LSM_INSERT|LSM_POINT_DELETE))==0; int nc2 = (eType2 & (LSM_INSERT|LSM_POINT_DELETE))==0; iRes = (nc1 > nc2) ? i2 : i1; }else if( res<0 ){ iRes = i1; }else{ iRes = i2; } } pCsr->aTree[iOut] = iRes; } /* ** This function advances segment pointer iPtr belonging to multi-cursor ** pCsr forward (bReverse==0) or backward (bReverse!=0). ** ** If the segment pointer points to a segment that is part of a composite ** level, then the following special case is handled. ** ** * If iPtr is the lhs of a composite level, and the cursor is being ** advanced forwards, and segment iPtr is at EOF, move all pointers ** that correspond to rhs segments of the same level to the first ** key in their respective data. */ static int segmentCursorAdvance( MultiCursor *pCsr, int iPtr, int bReverse ){ int rc; SegmentPtr *pPtr = &pCsr->aPtr[iPtr]; Level *pLvl = pPtr->pLevel; int bComposite; /* True if pPtr is part of composite level */ /* Advance the segment-pointer object. */ rc = segmentPtrAdvance(pCsr, pPtr, bReverse); if( rc!=LSM_OK ) return rc; bComposite = (pLvl->nRight>0 && pCsr->nPtr>pLvl->nRight); if( bComposite && pPtr->pPg==0 ){ int bFix = 0; if( (bReverse==0)==(pPtr->pSeg==&pLvl->lhs) ){ int i; if( bReverse ){ SegmentPtr *pLhs = &pCsr->aPtr[iPtr - 1 - (pPtr->pSeg - pLvl->aRhs)]; for(i=0; i<pLvl->nRight; i++){ if( pLhs[i+1].pPg ) break; } if( i==pLvl->nRight ){ bFix = 1; rc = segmentPtrEnd(pCsr, pLhs, 1); } }else{ bFix = 1; for(i=0; rc==LSM_OK && i<pLvl->nRight; i++){ rc = sortedRhsFirst(pCsr, pLvl, &pCsr->aPtr[iPtr+1+i]); } } } if( bFix ){ int i; for(i=pCsr->nTree-1; i>0; i--){ multiCursorDoCompare(pCsr, i, bReverse); } } } #if 0 if( bComposite && pPtr->pSeg==&pLvl->lhs /* lhs of composite level */ && bReverse==0 /* csr advanced forwards */ && pPtr->pPg==0 /* segment at EOF */ ){ int i; for(i=0; rc==LSM_OK && i<pLvl->nRight; i++){ rc = sortedRhsFirst(pCsr, pLvl, &pCsr->aPtr[iPtr+1+i]); } for(i=pCsr->nTree-1; i>0; i--){ multiCursorDoCompare(pCsr, i, 0); } } #endif return rc; } static void mcursorFreeComponents(MultiCursor *pCsr){ int i; lsm_env *pEnv = pCsr->pDb->pEnv; /* Close the tree cursor, if any. */ lsmTreeCursorDestroy(pCsr->apTreeCsr[0]); lsmTreeCursorDestroy(pCsr->apTreeCsr[1]); /* Reset the segment pointers */ for(i=0; i<pCsr->nPtr; i++){ segmentPtrReset(&pCsr->aPtr[i], 0); } /* And the b-tree cursor, if any */ btreeCursorFree(pCsr->pBtCsr); /* Free allocations */ lsmFree(pEnv, pCsr->aPtr); lsmFree(pEnv, pCsr->aTree); lsmFree(pEnv, pCsr->pSystemVal); /* Zero fields */ pCsr->nPtr = 0; pCsr->aPtr = 0; pCsr->nTree = 0; pCsr->aTree = 0; pCsr->pSystemVal = 0; pCsr->apTreeCsr[0] = 0; pCsr->apTreeCsr[1] = 0; pCsr->pBtCsr = 0; } void lsmMCursorFreeCache(lsm_db *pDb){ MultiCursor *p; MultiCursor *pNext; for(p=pDb->pCsrCache; p; p=pNext){ pNext = p->pNext; lsmMCursorClose(p, 0); } pDb->pCsrCache = 0; } /* ** Close the cursor passed as the first argument. ** ** If the bCache parameter is true, then shift the cursor to the pCsrCache ** list for possible reuse instead of actually deleting it. */ void lsmMCursorClose(MultiCursor *pCsr, int bCache){ if( pCsr ){ lsm_db *pDb = pCsr->pDb; MultiCursor **pp; /* Iterator variable */ /* The cursor may or may not be currently part of the linked list ** starting at lsm_db.pCsr. If it is, extract it. */ for(pp=&pDb->pCsr; *pp; pp=&((*pp)->pNext)){ if( *pp==pCsr ){ *pp = pCsr->pNext; break; } } if( bCache ){ int i; /* Used to iterate through segment-pointers */ /* Release any page references held by this cursor. */ assert( !pCsr->pBtCsr ); for(i=0; i<pCsr->nPtr; i++){ SegmentPtr *pPtr = &pCsr->aPtr[i]; lsmFsPageRelease(pPtr->pPg); pPtr->pPg = 0; } /* Reset the tree cursors */ lsmTreeCursorReset(pCsr->apTreeCsr[0]); lsmTreeCursorReset(pCsr->apTreeCsr[1]); /* Add the cursor to the pCsrCache list */ pCsr->pNext = pDb->pCsrCache; pDb->pCsrCache = pCsr; }else{ /* Free the allocation used to cache the current key, if any. */ sortedBlobFree(&pCsr->key); sortedBlobFree(&pCsr->val); /* Free the component cursors */ mcursorFreeComponents(pCsr); /* Free the cursor structure itself */ lsmFree(pDb->pEnv, pCsr); } } } #define TREE_NONE 0 #define TREE_OLD 1 #define TREE_BOTH 2 /* ** Parameter eTree is one of TREE_OLD or TREE_BOTH. */ static int multiCursorAddTree(MultiCursor *pCsr, Snapshot *pSnap, int eTree){ int rc = LSM_OK; lsm_db *db = pCsr->pDb; /* Add a tree cursor on the 'old' tree, if it exists. */ if( eTree!=TREE_NONE && lsmTreeHasOld(db) && db->treehdr.iOldLog!=pSnap->iLogOff ){ rc = lsmTreeCursorNew(db, 1, &pCsr->apTreeCsr[1]); } /* Add a tree cursor on the 'current' tree, if required. */ if( rc==LSM_OK && eTree==TREE_BOTH ){ rc = lsmTreeCursorNew(db, 0, &pCsr->apTreeCsr[0]); } return rc; } static int multiCursorAddRhs(MultiCursor *pCsr, Level *pLvl){ int i; int nRhs = pLvl->nRight; assert( pLvl->nRight>0 ); assert( pCsr->aPtr==0 ); pCsr->aPtr = lsmMallocZero(pCsr->pDb->pEnv, sizeof(SegmentPtr) * nRhs); if( !pCsr->aPtr ) return LSM_NOMEM_BKPT; pCsr->nPtr = nRhs; for(i=0; i<nRhs; i++){ pCsr->aPtr[i].pSeg = &pLvl->aRhs[i]; pCsr->aPtr[i].pLevel = pLvl; } return LSM_OK; } static void multiCursorAddOne(MultiCursor *pCsr, Level *pLvl, int *pRc){ if( *pRc==LSM_OK ){ int iPtr = pCsr->nPtr; int i; pCsr->aPtr[iPtr].pLevel = pLvl; pCsr->aPtr[iPtr].pSeg = &pLvl->lhs; iPtr++; for(i=0; i<pLvl->nRight; i++){ pCsr->aPtr[iPtr].pLevel = pLvl; pCsr->aPtr[iPtr].pSeg = &pLvl->aRhs[i]; iPtr++; } if( pLvl->nRight && pLvl->pSplitKey==0 ){ sortedSplitkey(pCsr->pDb, pLvl, pRc); } pCsr->nPtr = iPtr; } } static int multiCursorAddAll(MultiCursor *pCsr, Snapshot *pSnap){ Level *pLvl; int nPtr = 0; int rc = LSM_OK; for(pLvl=pSnap->pLevel; pLvl; pLvl=pLvl->pNext){ /* If the LEVEL_INCOMPLETE flag is set, then this function is being ** called (indirectly) from within a sortedNewToplevel() call to ** construct pLvl. In this case ignore pLvl - this cursor is going to ** be used to retrieve a freelist entry from the LSM, and the partially ** complete level may confuse it. */ if( pLvl->flags & LEVEL_INCOMPLETE ) continue; nPtr += (1 + pLvl->nRight); } assert( pCsr->aPtr==0 ); pCsr->aPtr = lsmMallocZeroRc(pCsr->pDb->pEnv, sizeof(SegmentPtr) * nPtr, &rc); for(pLvl=pSnap->pLevel; pLvl; pLvl=pLvl->pNext){ if( (pLvl->flags & LEVEL_INCOMPLETE)==0 ){ multiCursorAddOne(pCsr, pLvl, &rc); } } return rc; } static int multiCursorInit(MultiCursor *pCsr, Snapshot *pSnap){ int rc; rc = multiCursorAddAll(pCsr, pSnap); if( rc==LSM_OK ){ rc = multiCursorAddTree(pCsr, pSnap, TREE_BOTH); } pCsr->flags |= (CURSOR_IGNORE_SYSTEM | CURSOR_IGNORE_DELETE); return rc; } static MultiCursor *multiCursorNew(lsm_db *db, int *pRc){ MultiCursor *pCsr; pCsr = (MultiCursor *)lsmMallocZeroRc(db->pEnv, sizeof(MultiCursor), pRc); if( pCsr ){ pCsr->pNext = db->pCsr; db->pCsr = pCsr; pCsr->pDb = db; } return pCsr; } void lsmSortedRemap(lsm_db *pDb){ MultiCursor *pCsr; for(pCsr=pDb->pCsr; pCsr; pCsr=pCsr->pNext){ int iPtr; if( pCsr->pBtCsr ){ btreeCursorLoadKey(pCsr->pBtCsr); } for(iPtr=0; iPtr<pCsr->nPtr; iPtr++){ segmentPtrLoadCell(&pCsr->aPtr[iPtr], pCsr->aPtr[iPtr].iCell); } } } static void multiCursorReadSeparators(MultiCursor *pCsr){ if( pCsr->nPtr>0 ){ pCsr->flags |= CURSOR_READ_SEPARATORS; } } /* ** Have this cursor skip over SORTED_DELETE entries. */ static void multiCursorIgnoreDelete(MultiCursor *pCsr){ if( pCsr ) pCsr->flags |= CURSOR_IGNORE_DELETE; } /* ** If the free-block list is not empty, then have this cursor visit a key ** with (a) the system bit set, and (b) the key "FREELIST" and (c) a value ** blob containing the serialized free-block list. */ static int multiCursorVisitFreelist(MultiCursor *pCsr){ int rc = LSM_OK; pCsr->flags |= CURSOR_FLUSH_FREELIST; pCsr->pSystemVal = lsmMallocRc(pCsr->pDb->pEnv, 4 + 8, &rc); return rc; } /* ** Allocate and return a new database cursor. ** ** This method should only be called to allocate user cursors. As it may ** recycle a cursor from lsm_db.pCsrCache. */ int lsmMCursorNew( lsm_db *pDb, /* Database handle */ MultiCursor **ppCsr /* OUT: Allocated cursor */ ){ MultiCursor *pCsr = 0; int rc = LSM_OK; if( pDb->pCsrCache ){ int bOld; /* True if there is an old in-memory tree */ /* Remove a cursor from the pCsrCache list and add it to the open list. */ pCsr = pDb->pCsrCache; pDb->pCsrCache = pCsr->pNext; pCsr->pNext = pDb->pCsr; pDb->pCsr = pCsr; /* The cursor can almost be used as is, except that the old in-memory ** tree cursor may be present and not required, or required and not ** present. Fix this if required. */ bOld = (lsmTreeHasOld(pDb) && pDb->treehdr.iOldLog!=pDb->pClient->iLogOff); if( !bOld && pCsr->apTreeCsr[1] ){ lsmTreeCursorDestroy(pCsr->apTreeCsr[1]); pCsr->apTreeCsr[1] = 0; }else if( bOld && !pCsr->apTreeCsr[1] ){ rc = lsmTreeCursorNew(pDb, 1, &pCsr->apTreeCsr[1]); } pCsr->flags = (CURSOR_IGNORE_SYSTEM | CURSOR_IGNORE_DELETE); }else{ pCsr = multiCursorNew(pDb, &rc); if( rc==LSM_OK ) rc = multiCursorInit(pCsr, pDb->pClient); } if( rc!=LSM_OK ){ lsmMCursorClose(pCsr, 0); pCsr = 0; } assert( (rc==LSM_OK)==(pCsr!=0) ); *ppCsr = pCsr; return rc; } static int multiCursorGetVal( MultiCursor *pCsr, int iVal, void **ppVal, int *pnVal ){ int rc = LSM_OK; *ppVal = 0; *pnVal = 0; switch( iVal ){ case CURSOR_DATA_TREE0: case CURSOR_DATA_TREE1: { TreeCursor *pTreeCsr = pCsr->apTreeCsr[iVal-CURSOR_DATA_TREE0]; if( lsmTreeCursorValid(pTreeCsr) ){ lsmTreeCursorValue(pTreeCsr, ppVal, pnVal); }else{ *ppVal = 0; *pnVal = 0; } break; } case CURSOR_DATA_SYSTEM: { Snapshot *pWorker = pCsr->pDb->pWorker; if( pWorker && (pCsr->iFree % 2)==0 && pCsr->iFree < (pWorker->freelist.nEntry*2) ){ int iEntry = pWorker->freelist.nEntry - 1 - (pCsr->iFree / 2); u8 *aVal = &((u8 *)(pCsr->pSystemVal))[4]; lsmPutU64(aVal, pWorker->freelist.aEntry[iEntry].iId); *ppVal = aVal; *pnVal = 8; } break; } default: { int iPtr = iVal-CURSOR_DATA_SEGMENT; if( iPtr<pCsr->nPtr ){ SegmentPtr *pPtr = &pCsr->aPtr[iPtr]; if( pPtr->pPg ){ *ppVal = pPtr->pVal; *pnVal = pPtr->nVal; } } } } assert( rc==LSM_OK || (*ppVal==0 && *pnVal==0) ); return rc; } static int multiCursorAdvance(MultiCursor *pCsr, int bReverse); /* ** This function is called by worker connections to walk the part of the ** free-list stored within the LSM data structure. */ int lsmSortedWalkFreelist( lsm_db *pDb, /* Database handle */ int bReverse, /* True to iterate from largest to smallest */ int (*x)(void *, int, i64), /* Callback function */ void *pCtx /* First argument to pass to callback */ ){ MultiCursor *pCsr; /* Cursor used to read db */ int rc = LSM_OK; /* Return Code */ Snapshot *pSnap = 0; assert( pDb->pWorker ); if( pDb->bIncrMerge ){ rc = lsmCheckpointDeserialize(pDb, 0, pDb->pShmhdr->aSnap1, &pSnap); if( rc!=LSM_OK ) return rc; }else{ pSnap = pDb->pWorker; } pCsr = multiCursorNew(pDb, &rc); if( pCsr ){ rc = multiCursorAddAll(pCsr, pSnap); pCsr->flags |= CURSOR_IGNORE_DELETE; } if( rc==LSM_OK ){ if( bReverse==0 ){ rc = lsmMCursorLast(pCsr); }else{ rc = lsmMCursorSeek(pCsr, 1, "", 0, LSM_SEEK_GE); } while( rc==LSM_OK && lsmMCursorValid(pCsr) && rtIsSystem(pCsr->eType) ){ void *pKey; int nKey; void *pVal = 0; int nVal = 0; rc = lsmMCursorKey(pCsr, &pKey, &nKey); if( rc==LSM_OK ) rc = lsmMCursorValue(pCsr, &pVal, &nVal); if( rc==LSM_OK && (nKey!=4 || nVal!=8) ) rc = LSM_CORRUPT_BKPT; if( rc==LSM_OK ){ int iBlk; i64 iSnap; iBlk = (int)(~(lsmGetU32((u8 *)pKey))); iSnap = (i64)lsmGetU64((u8 *)pVal); if( x(pCtx, iBlk, iSnap) ) break; rc = multiCursorAdvance(pCsr, !bReverse); } } } lsmMCursorClose(pCsr, 0); if( pSnap!=pDb->pWorker ){ lsmFreeSnapshot(pDb->pEnv, pSnap); } return rc; } int lsmSortedLoadFreelist( lsm_db *pDb, /* Database handle (must be worker) */ void **ppVal, /* OUT: Blob containing LSM free-list */ int *pnVal /* OUT: Size of *ppVal blob in bytes */ ){ MultiCursor *pCsr; /* Cursor used to retreive free-list */ int rc = LSM_OK; /* Return Code */ assert( pDb->pWorker ); assert( *ppVal==0 && *pnVal==0 ); pCsr = multiCursorNew(pDb, &rc); if( pCsr ){ rc = multiCursorAddAll(pCsr, pDb->pWorker); pCsr->flags |= CURSOR_IGNORE_DELETE; } if( rc==LSM_OK ){ rc = lsmMCursorLast(pCsr); if( rc==LSM_OK && rtIsWrite(pCsr->eType) && rtIsSystem(pCsr->eType) && pCsr->key.nData==8 && 0==memcmp(pCsr->key.pData, "FREELIST", 8) ){ void *pVal; int nVal; /* Value read from database */ rc = lsmMCursorValue(pCsr, &pVal, &nVal); if( rc==LSM_OK ){ *ppVal = lsmMallocRc(pDb->pEnv, nVal, &rc); if( *ppVal ){ memcpy(*ppVal, pVal, nVal); *pnVal = nVal; } } } lsmMCursorClose(pCsr, 0); } return rc; } static int multiCursorAllocTree(MultiCursor *pCsr){ int rc = LSM_OK; if( pCsr->aTree==0 ){ int nByte; /* Bytes of space to allocate */ int nMin; /* Total number of cursors being merged */ nMin = CURSOR_DATA_SEGMENT + pCsr->nPtr + (pCsr->pBtCsr!=0); pCsr->nTree = 2; while( pCsr->nTree<nMin ){ pCsr->nTree = pCsr->nTree*2; } nByte = sizeof(int)*pCsr->nTree*2; pCsr->aTree = (int *)lsmMallocZeroRc(pCsr->pDb->pEnv, nByte, &rc); } return rc; } static void multiCursorCacheKey(MultiCursor *pCsr, int *pRc){ if( *pRc==LSM_OK ){ void *pKey; int nKey; multiCursorGetKey(pCsr, pCsr->aTree[1], &pCsr->eType, &pKey, &nKey); *pRc = sortedBlobSet(pCsr->pDb->pEnv, &pCsr->key, pKey, nKey); } } #ifdef LSM_DEBUG_EXPENSIVE static void assertCursorTree(MultiCursor *pCsr){ int bRev = !!(pCsr->flags & CURSOR_PREV_OK); int *aSave = pCsr->aTree; int nSave = pCsr->nTree; int rc; pCsr->aTree = 0; pCsr->nTree = 0; rc = multiCursorAllocTree(pCsr); if( rc==LSM_OK ){ int i; for(i=pCsr->nTree-1; i>0; i--){ multiCursorDoCompare(pCsr, i, bRev); } assert( nSave==pCsr->nTree && 0==memcmp(aSave, pCsr->aTree, sizeof(int)*nSave) ); lsmFree(pCsr->pDb->pEnv, pCsr->aTree); } pCsr->aTree = aSave; pCsr->nTree = nSave; } #else # define assertCursorTree(x) #endif static int mcursorLocationOk(MultiCursor *pCsr, int bDeleteOk){ int eType = pCsr->eType; int iKey; int i; int rdmask; assert( pCsr->flags & (CURSOR_NEXT_OK|CURSOR_PREV_OK) ); assertCursorTree(pCsr); rdmask = (pCsr->flags & CURSOR_NEXT_OK) ? LSM_END_DELETE : LSM_START_DELETE; /* If the cursor does not currently point to an actual database key (i.e. ** it points to a delete key, or the start or end of a range-delete), and ** the CURSOR_IGNORE_DELETE flag is set, skip past this entry. */ if( (pCsr->flags & CURSOR_IGNORE_DELETE) && bDeleteOk==0 ){ if( (eType & LSM_INSERT)==0 ) return 0; } /* If the cursor points to a system key (free-list entry), and the ** CURSOR_IGNORE_SYSTEM flag is set, skip thie entry. */ if( (pCsr->flags & CURSOR_IGNORE_SYSTEM) && rtTopic(eType)!=0 ){ return 0; } #ifndef NDEBUG /* This block fires assert() statements to check one of the assumptions ** in the comment below - that if the lhs sub-cursor of a level undergoing ** a merge is valid, then all the rhs sub-cursors must be at EOF. ** ** Also assert that all rhs sub-cursors are either at EOF or point to ** a key that is not less than the level split-key. */ for(i=0; i<pCsr->nPtr; i++){ SegmentPtr *pPtr = &pCsr->aPtr[i]; Level *pLvl = pPtr->pLevel; if( pLvl->nRight && pPtr->pPg ){ if( pPtr->pSeg==&pLvl->lhs ){ int j; for(j=0; j<pLvl->nRight; j++) assert( pPtr[j+1].pPg==0 ); }else{ int res = sortedKeyCompare(pCsr->pDb->xCmp, rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey, pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey ); assert( res>=0 ); } } } #endif /* Now check if this key has already been deleted by a range-delete. If ** so, skip past it. ** ** Assume, for the moment, that the tree contains no levels currently ** undergoing incremental merge, and that this cursor is iterating forwards ** through the database keys. The cursor currently points to a key in ** level L. This key has already been deleted if any of the sub-cursors ** that point to levels newer than L (or to the in-memory tree) point to ** a key greater than the current key with the LSM_END_DELETE flag set. ** ** Or, if the cursor is iterating backwards through data keys, if any ** such sub-cursor points to a key smaller than the current key with the ** LSM_START_DELETE flag set. ** ** Why it works with levels undergoing a merge too: ** ** When a cursor iterates forwards, the sub-cursors for the rhs of a ** level are only activated once the lhs reaches EOF. So when iterating ** forwards, the keys visited are the same as if the level was completely ** merged. ** ** If the cursor is iterating backwards, then the lhs sub-cursor is not ** initialized until the last of the rhs sub-cursors has reached EOF. ** Additionally, if the START_DELETE flag is set on the last entry (in ** reverse order - so the entry with the smallest key) of a rhs sub-cursor, ** then a pseudo-key equal to the levels split-key with the END_DELETE ** flag set is visited by the sub-cursor. */ iKey = pCsr->aTree[1]; for(i=0; i<iKey; i++){ int csrflags; multiCursorGetKey(pCsr, i, &csrflags, 0, 0); if( (rdmask & csrflags) ){ const int SD_ED = (LSM_START_DELETE|LSM_END_DELETE); if( (csrflags & SD_ED)==SD_ED || (pCsr->flags & CURSOR_IGNORE_DELETE)==0 ){ void *pKey; int nKey; multiCursorGetKey(pCsr, i, 0, &pKey, &nKey); if( 0==sortedKeyCompare(pCsr->pDb->xCmp, rtTopic(eType), pCsr->key.pData, pCsr->key.nData, rtTopic(csrflags), pKey, nKey )){ continue; } } return 0; } } /* The current cursor position is one this cursor should visit. Return 1. */ return 1; } static int multiCursorSetupTree(MultiCursor *pCsr, int bRev){ int rc; rc = multiCursorAllocTree(pCsr); if( rc==LSM_OK ){ int i; for(i=pCsr->nTree-1; i>0; i--){ multiCursorDoCompare(pCsr, i, bRev); } } assertCursorTree(pCsr); multiCursorCacheKey(pCsr, &rc); if( rc==LSM_OK && mcursorLocationOk(pCsr, 0)==0 ){ rc = multiCursorAdvance(pCsr, bRev); } return rc; } static int multiCursorEnd(MultiCursor *pCsr, int bLast){ int rc = LSM_OK; int i; pCsr->flags &= ~(CURSOR_NEXT_OK | CURSOR_PREV_OK); pCsr->flags |= (bLast ? CURSOR_PREV_OK : CURSOR_NEXT_OK); pCsr->iFree = 0; /* Position the two in-memory tree cursors */ for(i=0; rc==LSM_OK && i<2; i++){ if( pCsr->apTreeCsr[i] ){ rc = lsmTreeCursorEnd(pCsr->apTreeCsr[i], bLast); } } for(i=0; rc==LSM_OK && i<pCsr->nPtr; i++){ SegmentPtr *pPtr = &pCsr->aPtr[i]; Level *pLvl = pPtr->pLevel; int iRhs; int bHit = 0; if( bLast ){ for(iRhs=0; iRhs<pLvl->nRight && rc==LSM_OK; iRhs++){ rc = segmentPtrEnd(pCsr, &pPtr[iRhs+1], 1); if( pPtr[iRhs+1].pPg ) bHit = 1; } if( bHit==0 && rc==LSM_OK ){ rc = segmentPtrEnd(pCsr, pPtr, 1); }else{ segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD); } }else{ int bLhs = (pPtr->pSeg==&pLvl->lhs); assert( pPtr->pSeg==&pLvl->lhs || pPtr->pSeg==&pLvl->aRhs[0] ); if( bLhs ){ rc = segmentPtrEnd(pCsr, pPtr, 0); if( pPtr->pKey ) bHit = 1; } for(iRhs=0; iRhs<pLvl->nRight && rc==LSM_OK; iRhs++){ if( bHit ){ segmentPtrReset(&pPtr[iRhs+1], LSM_SEGMENTPTR_FREE_THRESHOLD); }else{ rc = sortedRhsFirst(pCsr, pLvl, &pPtr[iRhs+bLhs]); } } } i += pLvl->nRight; } /* And the b-tree cursor, if applicable */ if( rc==LSM_OK && pCsr->pBtCsr ){ assert( bLast==0 ); rc = btreeCursorFirst(pCsr->pBtCsr); } if( rc==LSM_OK ){ rc = multiCursorSetupTree(pCsr, bLast); } return rc; } int mcursorSave(MultiCursor *pCsr){ int rc = LSM_OK; if( pCsr->aTree ){ int iTree = pCsr->aTree[1]; if( iTree==CURSOR_DATA_TREE0 || iTree==CURSOR_DATA_TREE1 ){ multiCursorCacheKey(pCsr, &rc); } } mcursorFreeComponents(pCsr); return rc; } int mcursorRestore(lsm_db *pDb, MultiCursor *pCsr){ int rc; rc = multiCursorInit(pCsr, pDb->pClient); if( rc==LSM_OK && pCsr->key.pData ){ rc = lsmMCursorSeek(pCsr, rtTopic(pCsr->eType), pCsr->key.pData, pCsr->key.nData, +1 ); } return rc; } int lsmSaveCursors(lsm_db *pDb){ int rc = LSM_OK; MultiCursor *pCsr; for(pCsr=pDb->pCsr; rc==LSM_OK && pCsr; pCsr=pCsr->pNext){ rc = mcursorSave(pCsr); } return rc; } int lsmRestoreCursors(lsm_db *pDb){ int rc = LSM_OK; MultiCursor *pCsr; for(pCsr=pDb->pCsr; rc==LSM_OK && pCsr; pCsr=pCsr->pNext){ rc = mcursorRestore(pDb, pCsr); } return rc; } int lsmMCursorFirst(MultiCursor *pCsr){ return multiCursorEnd(pCsr, 0); } int lsmMCursorLast(MultiCursor *pCsr){ return multiCursorEnd(pCsr, 1); } lsm_db *lsmMCursorDb(MultiCursor *pCsr){ return pCsr->pDb; } void lsmMCursorReset(MultiCursor *pCsr){ int i; lsmTreeCursorReset(pCsr->apTreeCsr[0]); lsmTreeCursorReset(pCsr->apTreeCsr[1]); for(i=0; i<pCsr->nPtr; i++){ segmentPtrReset(&pCsr->aPtr[i], LSM_SEGMENTPTR_FREE_THRESHOLD); } pCsr->key.nData = 0; } static int treeCursorSeek( MultiCursor *pCsr, TreeCursor *pTreeCsr, void *pKey, int nKey, int eSeek, int *pbStop ){ int rc = LSM_OK; if( pTreeCsr ){ int res = 0; lsmTreeCursorSeek(pTreeCsr, pKey, nKey, &res); switch( eSeek ){ case LSM_SEEK_EQ: { int eType = lsmTreeCursorFlags(pTreeCsr); if( (res<0 && (eType & LSM_START_DELETE)) || (res>0 && (eType & LSM_END_DELETE)) || (res==0 && (eType & LSM_POINT_DELETE)) ){ *pbStop = 1; }else if( res==0 && (eType & LSM_INSERT) ){ lsm_env *pEnv = pCsr->pDb->pEnv; void *p; int n; /* Key/value from tree-cursor */ *pbStop = 1; pCsr->flags |= CURSOR_SEEK_EQ; rc = lsmTreeCursorKey(pTreeCsr, &pCsr->eType, &p, &n); if( rc==LSM_OK ) rc = sortedBlobSet(pEnv, &pCsr->key, p, n); if( rc==LSM_OK ) rc = lsmTreeCursorValue(pTreeCsr, &p, &n); if( rc==LSM_OK ) rc = sortedBlobSet(pEnv, &pCsr->val, p, n); } lsmTreeCursorReset(pTreeCsr); break; } case LSM_SEEK_GE: if( res<0 && lsmTreeCursorValid(pTreeCsr) ){ lsmTreeCursorNext(pTreeCsr); } break; default: if( res>0 ){ assert( lsmTreeCursorValid(pTreeCsr) ); lsmTreeCursorPrev(pTreeCsr); } break; } } return rc; } /* ** Seek the cursor. */ int lsmMCursorSeek( MultiCursor *pCsr, int iTopic, void *pKey, int nKey, int eSeek ){ int eESeek = eSeek; /* Effective eSeek parameter */ int bStop = 0; /* Set to true to halt search operation */ int rc = LSM_OK; /* Return code */ int iPtr = 0; /* Used to iterate through pCsr->aPtr[] */ Pgno iPgno = 0; /* FC pointer value */ assert( pCsr->apTreeCsr[0]==0 || iTopic==0 ); assert( pCsr->apTreeCsr[1]==0 || iTopic==0 ); if( eESeek==LSM_SEEK_LEFAST ) eESeek = LSM_SEEK_LE; assert( eESeek==LSM_SEEK_EQ || eESeek==LSM_SEEK_LE || eESeek==LSM_SEEK_GE ); assert( (pCsr->flags & CURSOR_FLUSH_FREELIST)==0 ); assert( pCsr->nPtr==0 || pCsr->aPtr[0].pLevel ); pCsr->flags &= ~(CURSOR_NEXT_OK | CURSOR_PREV_OK | CURSOR_SEEK_EQ); rc = treeCursorSeek(pCsr, pCsr->apTreeCsr[0], pKey, nKey, eESeek, &bStop); if( rc==LSM_OK && bStop==0 ){ rc = treeCursorSeek(pCsr, pCsr->apTreeCsr[1], pKey, nKey, eESeek, &bStop); } /* Seek all segment pointers. */ for(iPtr=0; iPtr<pCsr->nPtr && rc==LSM_OK && bStop==0; iPtr++){ SegmentPtr *pPtr = &pCsr->aPtr[iPtr]; assert( pPtr->pSeg==&pPtr->pLevel->lhs ); rc = seekInLevel(pCsr, pPtr, eESeek, iTopic, pKey, nKey, &iPgno, &bStop); iPtr += pPtr->pLevel->nRight; } if( eSeek!=LSM_SEEK_EQ ){ if( rc==LSM_OK ){ rc = multiCursorAllocTree(pCsr); } if( rc==LSM_OK ){ int i; for(i=pCsr->nTree-1; i>0; i--){ multiCursorDoCompare(pCsr, i, eESeek==LSM_SEEK_LE); } if( eSeek==LSM_SEEK_GE ) pCsr->flags |= CURSOR_NEXT_OK; if( eSeek==LSM_SEEK_LE ) pCsr->flags |= CURSOR_PREV_OK; } multiCursorCacheKey(pCsr, &rc); if( rc==LSM_OK && eSeek!=LSM_SEEK_LEFAST && 0==mcursorLocationOk(pCsr, 0) ){ switch( eESeek ){ case LSM_SEEK_EQ: lsmMCursorReset(pCsr); break; case LSM_SEEK_GE: rc = lsmMCursorNext(pCsr); break; default: rc = lsmMCursorPrev(pCsr); break; } } } return rc; } int lsmMCursorValid(MultiCursor *pCsr){ int res = 0; if( pCsr->flags & CURSOR_SEEK_EQ ){ res = 1; }else if( pCsr->aTree ){ int iKey = pCsr->aTree[1]; if( iKey==CURSOR_DATA_TREE0 || iKey==CURSOR_DATA_TREE1 ){ res = lsmTreeCursorValid(pCsr->apTreeCsr[iKey-CURSOR_DATA_TREE0]); }else{ void *pKey; multiCursorGetKey(pCsr, iKey, 0, &pKey, 0); res = pKey!=0; } } return res; } static int mcursorAdvanceOk( MultiCursor *pCsr, int bReverse, int *pRc ){ void *pNew; /* Pointer to buffer containing new key */ int nNew; /* Size of buffer pNew in bytes */ int eNewType; /* Type of new record */ if( *pRc ) return 1; /* Check the current key value. If it is not greater than (if bReverse==0) ** or less than (if bReverse!=0) the key currently cached in pCsr->key, ** then the cursor has not yet been successfully advanced. */ multiCursorGetKey(pCsr, pCsr->aTree[1], &eNewType, &pNew, &nNew); if( pNew ){ int typemask = (pCsr->flags & CURSOR_IGNORE_DELETE) ? ~(0) : LSM_SYSTEMKEY; int res = sortedDbKeyCompare(pCsr, eNewType & typemask, pNew, nNew, pCsr->eType & typemask, pCsr->key.pData, pCsr->key.nData ); if( (bReverse==0 && res<=0) || (bReverse!=0 && res>=0) ){ return 0; } multiCursorCacheKey(pCsr, pRc); assert( pCsr->eType==eNewType ); /* If this cursor is configured to skip deleted keys, and the current ** cursor points to a SORTED_DELETE entry, then the cursor has not been ** successfully advanced. ** ** Similarly, if the cursor is configured to skip system keys and the ** current cursor points to a system key, it has not yet been advanced. */ if( *pRc==LSM_OK && 0==mcursorLocationOk(pCsr, 0) ) return 0; } return 1; } static void flCsrAdvance(MultiCursor *pCsr){ assert( pCsr->flags & CURSOR_FLUSH_FREELIST ); if( pCsr->iFree % 2 ){ pCsr->iFree++; }else{ int nEntry = pCsr->pDb->pWorker->freelist.nEntry; FreelistEntry *aEntry = pCsr->pDb->pWorker->freelist.aEntry; int i = nEntry - 1 - (pCsr->iFree / 2); /* If the current entry is a delete and the "end-delete" key will not ** be attached to the next entry, increment iFree by 1 only. */ if( aEntry[i].iId<0 ){ while( 1 ){ if( i==0 || aEntry[i-1].iBlk!=aEntry[i].iBlk-1 ){ pCsr->iFree--; break; } if( aEntry[i-1].iId>=0 ) break; pCsr->iFree += 2; i--; } } pCsr->iFree += 2; } } static int multiCursorAdvance(MultiCursor *pCsr, int bReverse){ int rc = LSM_OK; /* Return Code */ if( lsmMCursorValid(pCsr) ){ do { int iKey = pCsr->aTree[1]; assertCursorTree(pCsr); /* If this multi-cursor is advancing forwards, and the sub-cursor ** being advanced is the one that separator keys may be being read ** from, record the current absolute pointer value. */ if( pCsr->pPrevMergePtr ){ if( iKey==(CURSOR_DATA_SEGMENT+pCsr->nPtr) ){ assert( pCsr->pBtCsr ); *pCsr->pPrevMergePtr = pCsr->pBtCsr->iPtr; }else if( pCsr->pBtCsr==0 && pCsr->nPtr>0 && iKey==(CURSOR_DATA_SEGMENT+pCsr->nPtr-1) ){ SegmentPtr *pPtr = &pCsr->aPtr[iKey-CURSOR_DATA_SEGMENT]; *pCsr->pPrevMergePtr = pPtr->iPtr+pPtr->iPgPtr; } } if( iKey==CURSOR_DATA_TREE0 || iKey==CURSOR_DATA_TREE1 ){ TreeCursor *pTreeCsr = pCsr->apTreeCsr[iKey-CURSOR_DATA_TREE0]; if( bReverse ){ rc = lsmTreeCursorPrev(pTreeCsr); }else{ rc = lsmTreeCursorNext(pTreeCsr); } }else if( iKey==CURSOR_DATA_SYSTEM ){ assert( pCsr->flags & CURSOR_FLUSH_FREELIST ); assert( bReverse==0 ); flCsrAdvance(pCsr); }else if( iKey==(CURSOR_DATA_SEGMENT+pCsr->nPtr) ){ assert( bReverse==0 && pCsr->pBtCsr ); rc = btreeCursorNext(pCsr->pBtCsr); }else{ rc = segmentCursorAdvance(pCsr, iKey-CURSOR_DATA_SEGMENT, bReverse); } if( rc==LSM_OK ){ int i; for(i=(iKey+pCsr->nTree)/2; i>0; i=i/2){ multiCursorDoCompare(pCsr, i, bReverse); } assertCursorTree(pCsr); } }while( mcursorAdvanceOk(pCsr, bReverse, &rc)==0 ); } return rc; } int lsmMCursorNext(MultiCursor *pCsr){ if( (pCsr->flags & CURSOR_NEXT_OK)==0 ) return LSM_MISUSE_BKPT; return multiCursorAdvance(pCsr, 0); } int lsmMCursorPrev(MultiCursor *pCsr){ if( (pCsr->flags & CURSOR_PREV_OK)==0 ) return LSM_MISUSE_BKPT; return multiCursorAdvance(pCsr, 1); } int lsmMCursorKey(MultiCursor *pCsr, void **ppKey, int *pnKey){ if( (pCsr->flags & CURSOR_SEEK_EQ) || pCsr->aTree==0 ){ *pnKey = pCsr->key.nData; *ppKey = pCsr->key.pData; }else{ int iKey = pCsr->aTree[1]; if( iKey==CURSOR_DATA_TREE0 || iKey==CURSOR_DATA_TREE1 ){ TreeCursor *pTreeCsr = pCsr->apTreeCsr[iKey-CURSOR_DATA_TREE0]; lsmTreeCursorKey(pTreeCsr, 0, ppKey, pnKey); }else{ int nKey; #ifndef NDEBUG void *pKey; int eType; multiCursorGetKey(pCsr, iKey, &eType, &pKey, &nKey); assert( eType==pCsr->eType ); assert( nKey==pCsr->key.nData ); assert( memcmp(pKey, pCsr->key.pData, nKey)==0 ); #endif nKey = pCsr->key.nData; if( nKey==0 ){ *ppKey = 0; }else{ *ppKey = pCsr->key.pData; } *pnKey = nKey; } } return LSM_OK; } /* ** Compare the current key that cursor csr points to with pKey/nKey. Set ** *piRes to the result and return LSM_OK. */ int lsm_csr_cmp(lsm_cursor *csr, const void *pKey, int nKey, int *piRes){ MultiCursor *pCsr = (MultiCursor *)csr; void *pCsrkey; int nCsrkey; int rc; rc = lsmMCursorKey(pCsr, &pCsrkey, &nCsrkey); if( rc==LSM_OK ){ int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp; *piRes = sortedKeyCompare(xCmp, 0, pCsrkey, nCsrkey, 0, (void *)pKey, nKey); } return rc; } int lsmMCursorValue(MultiCursor *pCsr, void **ppVal, int *pnVal){ void *pVal; int nVal; int rc; if( (pCsr->flags & CURSOR_SEEK_EQ) || pCsr->aTree==0 ){ rc = LSM_OK; nVal = pCsr->val.nData; pVal = pCsr->val.pData; }else{ assert( pCsr->aTree ); assert( mcursorLocationOk(pCsr, (pCsr->flags & CURSOR_IGNORE_DELETE)) ); rc = multiCursorGetVal(pCsr, pCsr->aTree[1], &pVal, &nVal); if( pVal && rc==LSM_OK ){ rc = sortedBlobSet(pCsr->pDb->pEnv, &pCsr->val, pVal, nVal); pVal = pCsr->val.pData; } if( rc!=LSM_OK ){ pVal = 0; nVal = 0; } } *ppVal = pVal; *pnVal = nVal; return rc; } int lsmMCursorType(MultiCursor *pCsr, int *peType){ assert( pCsr->aTree ); multiCursorGetKey(pCsr, pCsr->aTree[1], peType, 0, 0); return LSM_OK; } /* ** Buffer aData[], size nData, is assumed to contain a valid b-tree ** hierarchy page image. Return the offset in aData[] of the next free ** byte in the data area (where a new cell may be written if there is ** space). */ static int mergeWorkerPageOffset(u8 *aData, int nData){ int nRec; int iOff; int nKey; int eType; nRec = lsmGetU16(&aData[SEGMENT_NRECORD_OFFSET(nData)]); iOff = lsmGetU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec-1)]); eType = aData[iOff++]; assert( eType==0 || eType==(LSM_SYSTEMKEY|LSM_SEPARATOR) || eType==(LSM_SEPARATOR) ); iOff += lsmVarintGet32(&aData[iOff], &nKey); iOff += lsmVarintGet32(&aData[iOff], &nKey); return iOff + (eType ? nKey : 0); } /* ** Following a checkpoint operation, database pages that are part of the ** checkpointed state of the LSM are deemed read-only. This includes the ** right-most page of the b-tree hierarchy of any separators array under ** construction, and all pages between it and the b-tree root, inclusive. ** This is a problem, as when further pages are appended to the separators ** array, entries must be added to the indicated b-tree hierarchy pages. ** ** This function copies all such b-tree pages to new locations, so that ** they can be modified as required. ** ** The complication is that not all database pages are the same size - due ** to the way the file.c module works some (the first and last in each block) ** are 4 bytes smaller than the others. */ static int mergeWorkerMoveHierarchy( MergeWorker *pMW, /* Merge worker */ int bSep /* True for separators run */ ){ lsm_db *pDb = pMW->pDb; /* Database handle */ int rc = LSM_OK; /* Return code */ int i; Page **apHier = pMW->hier.apHier; int nHier = pMW->hier.nHier; for(i=0; rc==LSM_OK && i<nHier; i++){ Page *pNew = 0; rc = lsmFsSortedAppend(pDb->pFS, pDb->pWorker, pMW->pLevel, 1, &pNew); assert( rc==LSM_OK ); if( rc==LSM_OK ){ u8 *a1; int n1; u8 *a2; int n2; a1 = fsPageData(pNew, &n1); a2 = fsPageData(apHier[i], &n2); assert( n1==n2 || n1+4==n2 ); if( n1==n2 ){ memcpy(a1, a2, n2); }else{ int nEntry = pageGetNRec(a2, n2); int iEof1 = SEGMENT_EOF(n1, nEntry); int iEof2 = SEGMENT_EOF(n2, nEntry); memcpy(a1, a2, iEof2 - 4); memcpy(&a1[iEof1], &a2[iEof2], n2 - iEof2); } lsmFsPageRelease(apHier[i]); apHier[i] = pNew; #if 0 assert( n1==n2 || n1+4==n2 || n2+4==n1 ); if( n1>=n2 ){ /* If n1 (size of the new page) is equal to or greater than n2 (the ** size of the old page), then copy the data into the new page. If ** n1==n2, this could be done with a single memcpy(). However, ** since sometimes n1>n2, the page content and footer must be copied ** separately. */ int nEntry = pageGetNRec(a2, n2); int iEof1 = SEGMENT_EOF(n1, nEntry); int iEof2 = SEGMENT_EOF(n2, nEntry); memcpy(a1, a2, iEof2); memcpy(&a1[iEof1], &a2[iEof2], n2 - iEof2); lsmFsPageRelease(apHier[i]); apHier[i] = pNew; }else{ lsmPutU16(&a1[SEGMENT_FLAGS_OFFSET(n1)], SEGMENT_BTREE_FLAG); lsmPutU16(&a1[SEGMENT_NRECORD_OFFSET(n1)], 0); lsmPutU64(&a1[SEGMENT_POINTER_OFFSET(n1)], 0); i = i - 1; lsmFsPageRelease(pNew); } #endif } } #ifdef LSM_DEBUG if( rc==LSM_OK ){ for(i=0; i<nHier; i++) assert( lsmFsPageWritable(apHier[i]) ); } #endif return rc; } /* ** Allocate and populate the MergeWorker.apHier[] array. */ static int mergeWorkerLoadHierarchy(MergeWorker *pMW){ int rc = LSM_OK; Segment *pSeg; Hierarchy *p; pSeg = &pMW->pLevel->lhs; p = &pMW->hier; if( p->apHier==0 && pSeg->iRoot!=0 ){ FileSystem *pFS = pMW->pDb->pFS; lsm_env *pEnv = pMW->pDb->pEnv; Page **apHier = 0; int nHier = 0; int iPg = (int)pSeg->iRoot; do { Page *pPg = 0; u8 *aData; int nData; int flags; rc = lsmFsDbPageGet(pFS, pSeg, iPg, &pPg); if( rc!=LSM_OK ) break; aData = fsPageData(pPg, &nData); flags = pageGetFlags(aData, nData); if( flags&SEGMENT_BTREE_FLAG ){ Page **apNew = (Page **)lsmRealloc( pEnv, apHier, sizeof(Page *)*(nHier+1) ); if( apNew==0 ){ rc = LSM_NOMEM_BKPT; break; } apHier = apNew; memmove(&apHier[1], &apHier[0], sizeof(Page *) * nHier); nHier++; apHier[0] = pPg; iPg = (int)pageGetPtr(aData, nData); }else{ lsmFsPageRelease(pPg); break; } }while( 1 ); if( rc==LSM_OK ){ u8 *aData; int nData; aData = fsPageData(apHier[0], &nData); pMW->aSave[0].iPgno = pageGetPtr(aData, nData); p->nHier = nHier; p->apHier = apHier; rc = mergeWorkerMoveHierarchy(pMW, 0); }else{ int i; for(i=0; i<nHier; i++){ lsmFsPageRelease(apHier[i]); } lsmFree(pEnv, apHier); } } return rc; } /* ** B-tree pages use almost the same format as regular pages. The ** differences are: ** ** 1. The record format is (usually, see below) as follows: ** ** + Type byte (always SORTED_SEPARATOR or SORTED_SYSTEM_SEPARATOR), ** + Absolute pointer value (varint), ** + Number of bytes in key (varint), ** + Blob containing key data. ** ** 2. All pointer values are stored as absolute values (not offsets ** relative to the footer pointer value). ** ** 3. Each pointer that is part of a record points to a page that ** contains keys smaller than the records key (note: not "equal to or ** smaller than - smaller than"). ** ** 4. The pointer in the page footer of a b-tree page points to a page ** that contains keys equal to or larger than the largest key on the ** b-tree page. ** ** The reason for having the page footer pointer point to the right-child ** (instead of the left) is that doing things this way makes the ** mergeWorkerMoveHierarchy() operation less complicated (since the pointers ** that need to be updated are all stored as fixed-size integers within the ** page footer, not varints in page records). ** ** Records may not span b-tree pages. If this function is called to add a ** record larger than (page-size / 4) bytes, then a pointer to the indexed ** array page that contains the main record is added to the b-tree instead. ** In this case the record format is: ** ** + 0x00 byte (1 byte) ** + Absolute pointer value (varint), ** + Absolute page number of page containing key (varint). ** ** See function seekInBtree() for the code that traverses b-tree pages. */ static int mergeWorkerBtreeWrite( MergeWorker *pMW, u8 eType, Pgno iPtr, Pgno iKeyPg, void *pKey, int nKey ){ Hierarchy *p = &pMW->hier; lsm_db *pDb = pMW->pDb; /* Database handle */ int rc = LSM_OK; /* Return Code */ int iLevel; /* Level of b-tree hierachy to write to */ int nData; /* Size of aData[] in bytes */ u8 *aData; /* Page data for level iLevel */ int iOff; /* Offset on b-tree page to write record to */ int nRec; /* Initial number of records on b-tree page */ /* iKeyPg should be zero for an ordinary b-tree key, or non-zero for an ** indirect key. The flags byte for an indirect key is 0x00. */ assert( (eType==0)==(iKeyPg!=0) ); /* The MergeWorker.apHier[] array contains the right-most leaf of the b-tree ** hierarchy, the root node, and all nodes that lie on the path between. ** apHier[0] is the right-most leaf and apHier[pMW->nHier-1] is the current ** root page. ** ** This loop searches for a node with enough space to store the key on, ** starting with the leaf and iterating up towards the root. When the loop ** exits, the key may be written to apHier[iLevel]. */ for(iLevel=0; iLevel<=p->nHier; iLevel++){ int nByte; /* Number of free bytes required */ if( iLevel==p->nHier ){ /* Extend the array and allocate a new root page. */ Page **aNew; aNew = (Page **)lsmRealloc( pMW->pDb->pEnv, p->apHier, sizeof(Page *)*(p->nHier+1) ); if( !aNew ){ return LSM_NOMEM_BKPT; } p->apHier = aNew; }else{ Page *pOld; int nFree; /* If the key will fit on this page, break out of the loop here. ** The new entry will be written to page apHier[iLevel]. */ pOld = p->apHier[iLevel]; assert( lsmFsPageWritable(pOld) ); aData = fsPageData(pOld, &nData); if( eType==0 ){ nByte = 2 + 1 + lsmVarintLen32((int)iPtr) + lsmVarintLen32((int)iKeyPg); }else{ nByte = 2 + 1 + lsmVarintLen32((int)iPtr) + lsmVarintLen32(nKey) + nKey; } nRec = pageGetNRec(aData, nData); nFree = SEGMENT_EOF(nData, nRec) - mergeWorkerPageOffset(aData, nData); if( nByte<=nFree ) break; /* Otherwise, this page is full. Set the right-hand-child pointer ** to iPtr and release it. */ lsmPutU64(&aData[SEGMENT_POINTER_OFFSET(nData)], iPtr); assert( lsmFsPageNumber(pOld)==0 ); rc = lsmFsPagePersist(pOld); if( rc==LSM_OK ){ iPtr = lsmFsPageNumber(pOld); lsmFsPageRelease(pOld); } } /* Allocate a new page for apHier[iLevel]. */ p->apHier[iLevel] = 0; if( rc==LSM_OK ){ rc = lsmFsSortedAppend( pDb->pFS, pDb->pWorker, pMW->pLevel, 1, &p->apHier[iLevel] ); } if( rc!=LSM_OK ) return rc; aData = fsPageData(p->apHier[iLevel], &nData); memset(aData, 0, nData); lsmPutU16(&aData[SEGMENT_FLAGS_OFFSET(nData)], SEGMENT_BTREE_FLAG); lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], 0); if( iLevel==p->nHier ){ p->nHier++; break; } } /* Write the key into page apHier[iLevel]. */ aData = fsPageData(p->apHier[iLevel], &nData); iOff = mergeWorkerPageOffset(aData, nData); nRec = pageGetNRec(aData, nData); lsmPutU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec)], (u16)iOff); lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], (u16)(nRec+1)); if( eType==0 ){ aData[iOff++] = 0x00; iOff += lsmVarintPut32(&aData[iOff], (int)iPtr); iOff += lsmVarintPut32(&aData[iOff], (int)iKeyPg); }else{ aData[iOff++] = eType; iOff += lsmVarintPut32(&aData[iOff], (int)iPtr); iOff += lsmVarintPut32(&aData[iOff], nKey); memcpy(&aData[iOff], pKey, nKey); } return rc; } static int mergeWorkerBtreeIndirect(MergeWorker *pMW){ int rc = LSM_OK; if( pMW->iIndirect ){ Pgno iKeyPg = pMW->aSave[1].iPgno; rc = mergeWorkerBtreeWrite(pMW, 0, pMW->iIndirect, iKeyPg, 0, 0); pMW->iIndirect = 0; } return rc; } /* ** Append the database key (iTopic/pKey/nKey) to the b-tree under ** construction. This key has not yet been written to a segment page. ** The pointer that will accompany the new key in the b-tree - that ** points to the completed segment page that contains keys smaller than ** (pKey/nKey) is currently stored in pMW->aSave[0].iPgno. */ static int mergeWorkerPushHierarchy( MergeWorker *pMW, /* Merge worker object */ int iTopic, /* Topic value for this key */ void *pKey, /* Pointer to key buffer */ int nKey /* Size of pKey buffer in bytes */ ){ int rc = LSM_OK; /* Return Code */ Pgno iPtr; /* Pointer value to accompany pKey/nKey */ assert( pMW->aSave[0].bStore==0 ); assert( pMW->aSave[1].bStore==0 ); rc = mergeWorkerBtreeIndirect(pMW); /* Obtain the absolute pointer value to store along with the key in the ** page body. This pointer points to a page that contains keys that are ** smaller than pKey/nKey. */ iPtr = pMW->aSave[0].iPgno; assert( iPtr!=0 ); /* Determine if the indirect format should be used. */ if( (nKey*4 > lsmFsPageSize(pMW->pDb->pFS)) ){ pMW->iIndirect = iPtr; pMW->aSave[1].bStore = 1; }else{ rc = mergeWorkerBtreeWrite( pMW, (u8)(iTopic | LSM_SEPARATOR), iPtr, 0, pKey, nKey ); } /* Ensure that the SortedRun.iRoot field is correct. */ return rc; } static int mergeWorkerFinishHierarchy( MergeWorker *pMW /* Merge worker object */ ){ int i; /* Used to loop through apHier[] */ int rc = LSM_OK; /* Return code */ Pgno iPtr; /* New right-hand-child pointer value */ iPtr = pMW->aSave[0].iPgno; for(i=0; i<pMW->hier.nHier && rc==LSM_OK; i++){ Page *pPg = pMW->hier.apHier[i]; int nData; /* Size of aData[] in bytes */ u8 *aData; /* Page data for pPg */ aData = fsPageData(pPg, &nData); lsmPutU64(&aData[SEGMENT_POINTER_OFFSET(nData)], iPtr); rc = lsmFsPagePersist(pPg); iPtr = lsmFsPageNumber(pPg); lsmFsPageRelease(pPg); } if( pMW->hier.nHier ){ pMW->pLevel->lhs.iRoot = iPtr; lsmFree(pMW->pDb->pEnv, pMW->hier.apHier); pMW->hier.apHier = 0; pMW->hier.nHier = 0; } return rc; } static int mergeWorkerAddPadding( MergeWorker *pMW /* Merge worker object */ ){ FileSystem *pFS = pMW->pDb->pFS; return lsmFsSortedPadding(pFS, pMW->pDb->pWorker, &pMW->pLevel->lhs); } /* ** Release all page references currently held by the merge-worker passed ** as the only argument. Unless an error has occurred, all pages have ** already been released. */ static void mergeWorkerReleaseAll(MergeWorker *pMW){ int i; lsmFsPageRelease(pMW->pPage); pMW->pPage = 0; for(i=0; i<pMW->hier.nHier; i++){ lsmFsPageRelease(pMW->hier.apHier[i]); pMW->hier.apHier[i] = 0; } lsmFree(pMW->pDb->pEnv, pMW->hier.apHier); pMW->hier.apHier = 0; pMW->hier.nHier = 0; } static int keyszToSkip(FileSystem *pFS, int nKey){ int nPgsz; /* Nominal database page size */ nPgsz = lsmFsPageSize(pFS); return LSM_MIN(((nKey * 4) / nPgsz), 3); } /* ** Release the reference to the current output page of merge-worker *pMW ** (reference pMW->pPage). Set the page number values in aSave[] as ** required (see comments above struct MergeWorker for details). */ static int mergeWorkerPersistAndRelease(MergeWorker *pMW){ int rc; int i; assert( pMW->pPage || (pMW->aSave[0].bStore==0 && pMW->aSave[1].bStore==0) ); /* Persist the page */ rc = lsmFsPagePersist(pMW->pPage); /* If required, save the page number. */ for(i=0; i<2; i++){ if( pMW->aSave[i].bStore ){ pMW->aSave[i].iPgno = lsmFsPageNumber(pMW->pPage); pMW->aSave[i].bStore = 0; } } /* Release the completed output page. */ lsmFsPageRelease(pMW->pPage); pMW->pPage = 0; return rc; } /* ** Advance to the next page of an output run being populated by merge-worker ** pMW. The footer of the new page is initialized to indicate that it contains ** zero records. The flags field is cleared. The page footer pointer field ** is set to iFPtr. ** ** If successful, LSM_OK is returned. Otherwise, an error code. */ static int mergeWorkerNextPage( MergeWorker *pMW, /* Merge worker object to append page to */ Pgno iFPtr /* Pointer value for footer of new page */ ){ int rc = LSM_OK; /* Return code */ Page *pNext = 0; /* New page appended to run */ lsm_db *pDb = pMW->pDb; /* Database handle */ rc = lsmFsSortedAppend(pDb->pFS, pDb->pWorker, pMW->pLevel, 0, &pNext); assert( rc || pMW->pLevel->lhs.iFirst>0 || pMW->pDb->compress.xCompress ); if( rc==LSM_OK ){ u8 *aData; /* Data buffer belonging to page pNext */ int nData; /* Size of aData[] in bytes */ rc = mergeWorkerPersistAndRelease(pMW); pMW->pPage = pNext; pMW->pLevel->pMerge->iOutputOff = 0; aData = fsPageData(pNext, &nData); lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], 0); lsmPutU16(&aData[SEGMENT_FLAGS_OFFSET(nData)], 0); lsmPutU64(&aData[SEGMENT_POINTER_OFFSET(nData)], iFPtr); pMW->nWork++; } return rc; } /* ** Write a blob of data into an output segment being populated by a ** merge-worker object. If argument bSep is true, write into the separators ** array. Otherwise, the main array. ** ** This function is used to write the blobs of data for keys and values. */ static int mergeWorkerData( MergeWorker *pMW, /* Merge worker object */ int bSep, /* True to write to separators run */ int iFPtr, /* Footer ptr for new pages */ u8 *aWrite, /* Write data from this buffer */ int nWrite /* Size of aWrite[] in bytes */ ){ int rc = LSM_OK; /* Return code */ int nRem = nWrite; /* Number of bytes still to write */ while( rc==LSM_OK && nRem>0 ){ Merge *pMerge = pMW->pLevel->pMerge; int nCopy; /* Number of bytes to copy */ u8 *aData; /* Pointer to buffer of current output page */ int nData; /* Size of aData[] in bytes */ int nRec; /* Number of records on current output page */ int iOff; /* Offset in aData[] to write to */ assert( lsmFsPageWritable(pMW->pPage) ); aData = fsPageData(pMW->pPage, &nData); nRec = pageGetNRec(aData, nData); iOff = pMerge->iOutputOff; nCopy = LSM_MIN(nRem, SEGMENT_EOF(nData, nRec) - iOff); memcpy(&aData[iOff], &aWrite[nWrite-nRem], nCopy); nRem -= nCopy; if( nRem>0 ){ rc = mergeWorkerNextPage(pMW, iFPtr); }else{ pMerge->iOutputOff = iOff + nCopy; } } return rc; } /* ** The MergeWorker passed as the only argument is working to merge two or ** more existing segments together (not to flush an in-memory tree). It ** has not yet written the first key to the first page of the output. */ static int mergeWorkerFirstPage(MergeWorker *pMW){ int rc = LSM_OK; /* Return code */ Page *pPg = 0; /* First page of run pSeg */ int iFPtr = 0; /* Pointer value read from footer of pPg */ MultiCursor *pCsr = pMW->pCsr; assert( pMW->pPage==0 ); if( pCsr->pBtCsr ){ rc = LSM_OK; iFPtr = (int)pMW->pLevel->pNext->lhs.iFirst; }else if( pCsr->nPtr>0 ){ Segment *pSeg; pSeg = pCsr->aPtr[pCsr->nPtr-1].pSeg; rc = lsmFsDbPageGet(pMW->pDb->pFS, pSeg, pSeg->iFirst, &pPg); if( rc==LSM_OK ){ u8 *aData; /* Buffer for page pPg */ int nData; /* Size of aData[] in bytes */ aData = fsPageData(pPg, &nData); iFPtr = (int)pageGetPtr(aData, nData); lsmFsPageRelease(pPg); } } if( rc==LSM_OK ){ rc = mergeWorkerNextPage(pMW, iFPtr); if( pCsr->pPrevMergePtr ) *pCsr->pPrevMergePtr = iFPtr; pMW->aSave[0].bStore = 1; } return rc; } static int mergeWorkerWrite( MergeWorker *pMW, /* Merge worker object to write into */ int eType, /* One of SORTED_SEPARATOR, WRITE or DELETE */ void *pKey, int nKey, /* Key value */ void *pVal, int nVal, /* Value value */ int iPtr /* Absolute value of page pointer, or 0 */ ){ int rc = LSM_OK; /* Return code */ Merge *pMerge; /* Persistent part of level merge state */ int nHdr; /* Space required for this record header */ Page *pPg; /* Page to write to */ u8 *aData; /* Data buffer for page pWriter->pPage */ int nData = 0; /* Size of buffer aData[] in bytes */ int nRec = 0; /* Number of records on page pPg */ int iFPtr = 0; /* Value of pointer in footer of pPg */ int iRPtr = 0; /* Value of pointer written into record */ int iOff = 0; /* Current write offset within page pPg */ Segment *pSeg; /* Segment being written */ int flags = 0; /* If != 0, flags value for page footer */ int bFirst = 0; /* True for first key of output run */ pMerge = pMW->pLevel->pMerge; pSeg = &pMW->pLevel->lhs; if( pSeg->iFirst==0 && pMW->pPage==0 ){ rc = mergeWorkerFirstPage(pMW); bFirst = 1; } pPg = pMW->pPage; if( pPg ){ aData = fsPageData(pPg, &nData); nRec = pageGetNRec(aData, nData); iFPtr = (int)pageGetPtr(aData, nData); iRPtr = iPtr - iFPtr; } /* Figure out how much space is required by the new record. The space ** required is divided into two sections: the header and the body. The ** header consists of the intial varint fields. The body are the blobs ** of data that correspond to the key and value data. The entire header ** must be stored on the page. The body may overflow onto the next and ** subsequent pages. ** ** The header space is: ** ** 1) record type - 1 byte. ** 2) Page-pointer-offset - 1 varint ** 3) Key size - 1 varint ** 4) Value size - 1 varint (only if LSM_INSERT flag is set) */ if( rc==LSM_OK ){ nHdr = 1 + lsmVarintLen32(iRPtr) + lsmVarintLen32(nKey); if( rtIsWrite(eType) ) nHdr += lsmVarintLen32(nVal); /* If the entire header will not fit on page pPg, or if page pPg is ** marked read-only, advance to the next page of the output run. */ iOff = pMerge->iOutputOff; if( iOff<0 || pPg==0 || iOff+nHdr > SEGMENT_EOF(nData, nRec+1) ){ iFPtr = (int)*pMW->pCsr->pPrevMergePtr; iRPtr = iPtr - iFPtr; iOff = 0; nRec = 0; rc = mergeWorkerNextPage(pMW, iFPtr); pPg = pMW->pPage; } } /* If this record header will be the first on the page, and the page is ** not the very first in the entire run, add a copy of the key to the ** b-tree hierarchy. */ if( rc==LSM_OK && nRec==0 && bFirst==0 ){ assert( pMerge->nSkip>=0 ); if( pMerge->nSkip==0 ){ rc = mergeWorkerPushHierarchy(pMW, rtTopic(eType), pKey, nKey); assert( pMW->aSave[0].bStore==0 ); pMW->aSave[0].bStore = 1; pMerge->nSkip = keyszToSkip(pMW->pDb->pFS, nKey); }else{ pMerge->nSkip--; flags = PGFTR_SKIP_THIS_FLAG; } if( pMerge->nSkip ) flags |= PGFTR_SKIP_NEXT_FLAG; } /* Update the output segment */ if( rc==LSM_OK ){ aData = fsPageData(pPg, &nData); /* Update the page footer. */ lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], (u16)(nRec+1)); lsmPutU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec)], (u16)iOff); if( flags ) lsmPutU16(&aData[SEGMENT_FLAGS_OFFSET(nData)], (u16)flags); /* Write the entry header into the current page. */ aData[iOff++] = (u8)eType; /* 1 */ iOff += lsmVarintPut32(&aData[iOff], iRPtr); /* 2 */ iOff += lsmVarintPut32(&aData[iOff], nKey); /* 3 */ if( rtIsWrite(eType) ) iOff += lsmVarintPut32(&aData[iOff], nVal); /* 4 */ pMerge->iOutputOff = iOff; /* Write the key and data into the segment. */ assert( iFPtr==pageGetPtr(aData, nData) ); rc = mergeWorkerData(pMW, 0, iFPtr+iRPtr, pKey, nKey); if( rc==LSM_OK && rtIsWrite(eType) ){ if( rc==LSM_OK ){ rc = mergeWorkerData(pMW, 0, iFPtr+iRPtr, pVal, nVal); } } } return rc; } /* ** Free all resources allocated by mergeWorkerInit(). */ static void mergeWorkerShutdown(MergeWorker *pMW, int *pRc){ int i; /* Iterator variable */ int rc = *pRc; MultiCursor *pCsr = pMW->pCsr; /* Unless the merge has finished, save the cursor position in the ** Merge.aInput[] array. See function mergeWorkerInit() for the ** code to restore a cursor position based on aInput[]. */ if( rc==LSM_OK && pCsr && lsmMCursorValid(pCsr) ){ Merge *pMerge = pMW->pLevel->pMerge; int bBtree = (pCsr->pBtCsr!=0); int iPtr; /* pMerge->nInput==0 indicates that this is a FlushTree() operation. */ assert( pMerge->nInput==0 || pMW->pLevel->nRight>0 ); assert( pMerge->nInput==0 || pMerge->nInput==(pCsr->nPtr+bBtree) ); for(i=0; i<(pMerge->nInput-bBtree); i++){ SegmentPtr *pPtr = &pCsr->aPtr[i]; if( pPtr->pPg ){ pMerge->aInput[i].iPg = lsmFsPageNumber(pPtr->pPg); pMerge->aInput[i].iCell = pPtr->iCell; }else{ pMerge->aInput[i].iPg = 0; pMerge->aInput[i].iCell = 0; } } if( bBtree && pMerge->nInput ){ assert( i==pCsr->nPtr ); btreeCursorPosition(pCsr->pBtCsr, &pMerge->aInput[i]); } /* Store the location of the split-key */ iPtr = pCsr->aTree[1] - CURSOR_DATA_SEGMENT; if( iPtr<pCsr->nPtr ){ pMerge->splitkey = pMerge->aInput[iPtr]; }else{ btreeCursorSplitkey(pCsr->pBtCsr, &pMerge->splitkey); } pMerge->iOutputOff = -1; } lsmMCursorClose(pCsr, 0); /* Persist and release the output page. */ if( rc==LSM_OK ) rc = mergeWorkerPersistAndRelease(pMW); if( rc==LSM_OK ) rc = mergeWorkerBtreeIndirect(pMW); if( rc==LSM_OK ) rc = mergeWorkerFinishHierarchy(pMW); if( rc==LSM_OK ) rc = mergeWorkerAddPadding(pMW); lsmFsFlushWaiting(pMW->pDb->pFS, &rc); mergeWorkerReleaseAll(pMW); lsmFree(pMW->pDb->pEnv, pMW->aGobble); pMW->aGobble = 0; pMW->pCsr = 0; *pRc = rc; } /* ** The cursor passed as the first argument is being used as the input for ** a merge operation. When this function is called, *piFlags contains the ** database entry flags for the current entry. The entry about to be written ** to the output. ** ** Note that this function only has to work for cursors configured to ** iterate forwards (not backwards). */ static void mergeRangeDeletes(MultiCursor *pCsr, int *piVal, int *piFlags){ int f = *piFlags; int iKey = pCsr->aTree[1]; int i; assert( pCsr->flags & CURSOR_NEXT_OK ); if( pCsr->flags & CURSOR_IGNORE_DELETE ){ /* The ignore-delete flag is set when the output of the merge will form ** the oldest level in the database. In this case there is no point in ** retaining any range-delete flags. */ assert( (f & LSM_POINT_DELETE)==0 ); f &= ~(LSM_START_DELETE|LSM_END_DELETE); }else{ for(i=0; i<(CURSOR_DATA_SEGMENT + pCsr->nPtr); i++){ if( i!=iKey ){ int eType; void *pKey; int nKey; int res; multiCursorGetKey(pCsr, i, &eType, &pKey, &nKey); if( pKey ){ res = sortedKeyCompare(pCsr->pDb->xCmp, rtTopic(pCsr->eType), pCsr->key.pData, pCsr->key.nData, rtTopic(eType), pKey, nKey ); assert( res<=0 ); if( res==0 ){ if( (f & (LSM_INSERT|LSM_POINT_DELETE))==0 ){ if( eType & LSM_INSERT ){ f |= LSM_INSERT; *piVal = i; } else if( eType & LSM_POINT_DELETE ){ f |= LSM_POINT_DELETE; } } f |= (eType & (LSM_END_DELETE|LSM_START_DELETE)); } if( i>iKey && (eType & LSM_END_DELETE) && res<0 ){ if( f & (LSM_INSERT|LSM_POINT_DELETE) ){ f |= (LSM_END_DELETE|LSM_START_DELETE); }else{ f = 0; } break; } } } } assert( (f & LSM_INSERT)==0 || (f & LSM_POINT_DELETE)==0 ); if( (f & LSM_START_DELETE) && (f & LSM_END_DELETE) && (f & LSM_POINT_DELETE ) ){ f = 0; } } *piFlags = f; } static int mergeWorkerStep(MergeWorker *pMW){ lsm_db *pDb = pMW->pDb; /* Database handle */ MultiCursor *pCsr; /* Cursor to read input data from */ int rc = LSM_OK; /* Return code */ int eType; /* SORTED_SEPARATOR, WRITE or DELETE */ void *pKey; int nKey; /* Key */ Pgno iPtr; int iVal; pCsr = pMW->pCsr; /* Pull the next record out of the source cursor. */ lsmMCursorKey(pCsr, &pKey, &nKey); eType = pCsr->eType; /* Figure out if the output record may have a different pointer value ** than the previous. This is the case if the current key is identical to ** a key that appears in the lowest level run being merged. If so, set ** iPtr to the absolute pointer value. If not, leave iPtr set to zero, ** indicating that the output pointer value should be a copy of the pointer ** value written with the previous key. */ iPtr = (pCsr->pPrevMergePtr ? *pCsr->pPrevMergePtr : 0); if( pCsr->pBtCsr ){ BtreeCursor *pBtCsr = pCsr->pBtCsr; if( pBtCsr->pKey ){ int res = rtTopic(pBtCsr->eType) - rtTopic(eType); if( res==0 ) res = pDb->xCmp(pBtCsr->pKey, pBtCsr->nKey, pKey, nKey); if( 0==res ) iPtr = pBtCsr->iPtr; assert( res>=0 ); } }else if( pCsr->nPtr ){ SegmentPtr *pPtr = &pCsr->aPtr[pCsr->nPtr-1]; if( pPtr->pPg && 0==pDb->xCmp(pPtr->pKey, pPtr->nKey, pKey, nKey) ){ iPtr = pPtr->iPtr+pPtr->iPgPtr; } } iVal = pCsr->aTree[1]; mergeRangeDeletes(pCsr, &iVal, &eType); if( eType!=0 ){ if( pMW->aGobble ){ int iGobble = pCsr->aTree[1] - CURSOR_DATA_SEGMENT; if( iGobble<pCsr->nPtr && iGobble>=0 ){ SegmentPtr *pGobble = &pCsr->aPtr[iGobble]; if( (pGobble->flags & PGFTR_SKIP_THIS_FLAG)==0 ){ pMW->aGobble[iGobble] = lsmFsPageNumber(pGobble->pPg); } } } /* If this is a separator key and we know that the output pointer has not ** changed, there is no point in writing an output record. Otherwise, ** proceed. */ if( rc==LSM_OK && (rtIsSeparator(eType)==0 || iPtr!=0) ){ /* Write the record into the main run. */ void *pVal; int nVal; rc = multiCursorGetVal(pCsr, iVal, &pVal, &nVal); if( pVal && rc==LSM_OK ){ assert( nVal>=0 ); rc = sortedBlobSet(pDb->pEnv, &pCsr->val, pVal, nVal); pVal = pCsr->val.pData; } if( rc==LSM_OK ){ rc = mergeWorkerWrite(pMW, eType, pKey, nKey, pVal, nVal, (int)iPtr); } } } /* Advance the cursor to the next input record (assuming one exists). */ assert( lsmMCursorValid(pMW->pCsr) ); if( rc==LSM_OK ) rc = lsmMCursorNext(pMW->pCsr); return rc; } static int mergeWorkerDone(MergeWorker *pMW){ return pMW->pCsr==0 || !lsmMCursorValid(pMW->pCsr); } static void sortedFreeLevel(lsm_env *pEnv, Level *p){ if( p ){ lsmFree(pEnv, p->pSplitKey); lsmFree(pEnv, p->pMerge); lsmFree(pEnv, p->aRhs); lsmFree(pEnv, p); } } static void sortedInvokeWorkHook(lsm_db *pDb){ if( pDb->xWork ){ pDb->xWork(pDb, pDb->pWorkCtx); } } static int sortedNewToplevel( lsm_db *pDb, /* Connection handle */ int eTree, /* One of the TREE_XXX constants */ int *pnWrite /* OUT: Number of database pages written */ ){ int rc = LSM_OK; /* Return Code */ MultiCursor *pCsr = 0; Level *pNext = 0; /* The current top level */ Level *pNew; /* The new level itself */ Segment *pLinked = 0; /* Delete separators from this segment */ Level *pDel = 0; /* Delete this entire level */ int nWrite = 0; /* Number of database pages written */ Freelist freelist; if( eTree!=TREE_NONE ){ rc = lsmShmCacheChunks(pDb, pDb->treehdr.nChunk); } assert( pDb->bUseFreelist==0 ); pDb->pFreelist = &freelist; pDb->bUseFreelist = 1; memset(&freelist, 0, sizeof(freelist)); /* Allocate the new level structure to write to. */ pNext = lsmDbSnapshotLevel(pDb->pWorker); pNew = (Level *)lsmMallocZeroRc(pDb->pEnv, sizeof(Level), &rc); if( pNew ){ pNew->pNext = pNext; lsmDbSnapshotSetLevel(pDb->pWorker, pNew); } /* Create a cursor to gather the data required by the new segment. The new ** segment contains everything in the tree and pointers to the next segment ** in the database (if any). */ pCsr = multiCursorNew(pDb, &rc); if( pCsr ){ pCsr->pDb = pDb; rc = multiCursorVisitFreelist(pCsr); if( rc==LSM_OK ){ rc = multiCursorAddTree(pCsr, pDb->pWorker, eTree); } if( rc==LSM_OK && pNext && pNext->pMerge==0 ){ if( (pNext->flags & LEVEL_FREELIST_ONLY) ){ pDel = pNext; pCsr->aPtr = lsmMallocZeroRc(pDb->pEnv, sizeof(SegmentPtr), &rc); multiCursorAddOne(pCsr, pNext, &rc); }else if( eTree!=TREE_NONE && pNext->lhs.iRoot ){ pLinked = &pNext->lhs; rc = btreeCursorNew(pDb, pLinked, &pCsr->pBtCsr); } } /* If this will be the only segment in the database, discard any delete ** markers present in the in-memory tree. */ if( pNext==0 ){ multiCursorIgnoreDelete(pCsr); } } if( rc!=LSM_OK ){ lsmMCursorClose(pCsr, 0); }else{ Pgno iLeftPtr = 0; Merge merge; /* Merge object used to create new level */ MergeWorker mergeworker; /* MergeWorker object for the same purpose */ memset(&merge, 0, sizeof(Merge)); memset(&mergeworker, 0, sizeof(MergeWorker)); pNew->pMerge = &merge; pNew->flags |= LEVEL_INCOMPLETE; mergeworker.pDb = pDb; mergeworker.pLevel = pNew; mergeworker.pCsr = pCsr; pCsr->pPrevMergePtr = &iLeftPtr; /* Mark the separators array for the new level as a "phantom". */ mergeworker.bFlush = 1; /* Do the work to create the new merged segment on disk */ if( rc==LSM_OK ) rc = lsmMCursorFirst(pCsr); while( rc==LSM_OK && mergeWorkerDone(&mergeworker)==0 ){ rc = mergeWorkerStep(&mergeworker); } mergeWorkerShutdown(&mergeworker, &rc); assert( rc!=LSM_OK || mergeworker.nWork==0 || pNew->lhs.iFirst ); if( rc==LSM_OK && pNew->lhs.iFirst ){ rc = lsmFsSortedFinish(pDb->pFS, &pNew->lhs); } nWrite = mergeworker.nWork; pNew->flags &= ~LEVEL_INCOMPLETE; if( eTree==TREE_NONE ){ pNew->flags |= LEVEL_FREELIST_ONLY; } pNew->pMerge = 0; } if( rc!=LSM_OK || pNew->lhs.iFirst==0 ){ assert( rc!=LSM_OK || pDb->pWorker->freelist.nEntry==0 ); lsmDbSnapshotSetLevel(pDb->pWorker, pNext); sortedFreeLevel(pDb->pEnv, pNew); }else{ if( pLinked ){ pLinked->iRoot = 0; }else if( pDel ){ assert( pNew->pNext==pDel ); pNew->pNext = pDel->pNext; lsmFsSortedDelete(pDb->pFS, pDb->pWorker, 1, &pDel->lhs); sortedFreeLevel(pDb->pEnv, pDel); } #if LSM_LOG_STRUCTURE lsmSortedDumpStructure(pDb, pDb->pWorker, LSM_LOG_DATA, 0, "new-toplevel"); #endif if( freelist.nEntry ){ Freelist *p = &pDb->pWorker->freelist; lsmFree(pDb->pEnv, p->aEntry); memcpy(p, &freelist, sizeof(freelist)); freelist.aEntry = 0; }else{ pDb->pWorker->freelist.nEntry = 0; } assertBtreeOk(pDb, &pNew->lhs); sortedInvokeWorkHook(pDb); } if( pnWrite ) *pnWrite = nWrite; pDb->pWorker->nWrite += nWrite; pDb->pFreelist = 0; pDb->bUseFreelist = 0; lsmFree(pDb->pEnv, freelist.aEntry); return rc; } /* ** The nMerge levels in the LSM beginning with pLevel consist of a ** left-hand-side segment only. Replace these levels with a single new ** level consisting of a new empty segment on the left-hand-side and the ** nMerge segments from the replaced levels on the right-hand-side. ** ** Also, allocate and populate a Merge object and set Level.pMerge to ** point to it. */ static int sortedMergeSetup( lsm_db *pDb, /* Database handle */ Level *pLevel, /* First level to merge */ int nMerge, /* Merge this many levels together */ Level **ppNew /* New, merged, level */ ){ int rc = LSM_OK; /* Return Code */ Level *pNew; /* New Level object */ int bUseNext = 0; /* True to link in next separators */ Merge *pMerge; /* New Merge object */ int nByte; /* Bytes of space allocated at pMerge */ #ifdef LSM_DEBUG int iLevel; Level *pX = pLevel; for(iLevel=0; iLevel<nMerge; iLevel++){ assert( pX->nRight==0 ); pX = pX->pNext; } #endif /* Allocate the new Level object */ pNew = (Level *)lsmMallocZeroRc(pDb->pEnv, sizeof(Level), &rc); if( pNew ){ pNew->aRhs = (Segment *)lsmMallocZeroRc(pDb->pEnv, nMerge * sizeof(Segment), &rc); } /* Populate the new Level object */ if( rc==LSM_OK ){ Level *pNext = 0; /* Level following pNew */ int i; int bFreeOnly = 1; Level *pTopLevel; Level *p = pLevel; Level **pp; pNew->nRight = nMerge; pNew->iAge = pLevel->iAge+1; for(i=0; i<nMerge; i++){ assert( p->nRight==0 ); pNext = p->pNext; pNew->aRhs[i] = p->lhs; if( (p->flags & LEVEL_FREELIST_ONLY)==0 ) bFreeOnly = 0; sortedFreeLevel(pDb->pEnv, p); p = pNext; } if( bFreeOnly ) pNew->flags |= LEVEL_FREELIST_ONLY; /* Replace the old levels with the new. */ pTopLevel = lsmDbSnapshotLevel(pDb->pWorker); pNew->pNext = p; for(pp=&pTopLevel; *pp!=pLevel; pp=&((*pp)->pNext)); *pp = pNew; lsmDbSnapshotSetLevel(pDb->pWorker, pTopLevel); /* Determine whether or not the next separators will be linked in */ if( pNext && pNext->pMerge==0 && pNext->lhs.iRoot && pNext && (bFreeOnly==0 || (pNext->flags & LEVEL_FREELIST_ONLY)) ){ bUseNext = 1; } } /* Allocate the merge object */ nByte = sizeof(Merge) + sizeof(MergeInput) * (nMerge + bUseNext); pMerge = (Merge *)lsmMallocZeroRc(pDb->pEnv, nByte, &rc); if( pMerge ){ pMerge->aInput = (MergeInput *)&pMerge[1]; pMerge->nInput = nMerge + bUseNext; pNew->pMerge = pMerge; } *ppNew = pNew; return rc; } static int mergeWorkerInit( lsm_db *pDb, /* Db connection to do merge work */ Level *pLevel, /* Level to work on merging */ MergeWorker *pMW /* Object to initialize */ ){ int rc = LSM_OK; /* Return code */ Merge *pMerge = pLevel->pMerge; /* Persistent part of merge state */ MultiCursor *pCsr = 0; /* Cursor opened for pMW */ Level *pNext = pLevel->pNext; /* Next level in LSM */ assert( pDb->pWorker ); assert( pLevel->pMerge ); assert( pLevel->nRight>0 ); memset(pMW, 0, sizeof(MergeWorker)); pMW->pDb = pDb; pMW->pLevel = pLevel; pMW->aGobble = lsmMallocZeroRc(pDb->pEnv, sizeof(Pgno) * pLevel->nRight, &rc); /* Create a multi-cursor to read the data to write to the new ** segment. The new segment contains: ** ** 1. Records from LHS of each of the nMerge levels being merged. ** 2. Separators from either the last level being merged, or the ** separators attached to the LHS of the following level, or neither. ** ** If the new level is the lowest (oldest) in the db, discard any ** delete keys. Key annihilation. */ pCsr = multiCursorNew(pDb, &rc); if( pCsr ){ pCsr->flags |= CURSOR_NEXT_OK; rc = multiCursorAddRhs(pCsr, pLevel); } if( rc==LSM_OK && pMerge->nInput > pLevel->nRight ){ rc = btreeCursorNew(pDb, &pNext->lhs, &pCsr->pBtCsr); }else if( pNext ){ multiCursorReadSeparators(pCsr); }else{ multiCursorIgnoreDelete(pCsr); } assert( rc!=LSM_OK || pMerge->nInput==(pCsr->nPtr+(pCsr->pBtCsr!=0)) ); pMW->pCsr = pCsr; /* Load the b-tree hierarchy into memory. */ if( rc==LSM_OK ) rc = mergeWorkerLoadHierarchy(pMW); if( rc==LSM_OK && pMW->hier.nHier==0 ){ pMW->aSave[0].iPgno = pLevel->lhs.iFirst; } /* Position the cursor. */ if( rc==LSM_OK ){ pCsr->pPrevMergePtr = &pMerge->iCurrentPtr; if( pLevel->lhs.iFirst==0 ){ /* The output array is still empty. So position the cursor at the very ** start of the input. */ rc = multiCursorEnd(pCsr, 0); }else{ /* The output array is non-empty. Position the cursor based on the ** page/cell data saved in the Merge.aInput[] array. */ int i; for(i=0; rc==LSM_OK && i<pCsr->nPtr; i++){ MergeInput *pInput = &pMerge->aInput[i]; if( pInput->iPg ){ SegmentPtr *pPtr; assert( pCsr->aPtr[i].pPg==0 ); pPtr = &pCsr->aPtr[i]; rc = segmentPtrLoadPage(pDb->pFS, pPtr, (int)pInput->iPg); if( rc==LSM_OK && pPtr->nCell>0 ){ rc = segmentPtrLoadCell(pPtr, pInput->iCell); } } } if( rc==LSM_OK && pCsr->pBtCsr ){ int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp; assert( i==pCsr->nPtr ); rc = btreeCursorRestore(pCsr->pBtCsr, xCmp, &pMerge->aInput[i]); } if( rc==LSM_OK ){ rc = multiCursorSetupTree(pCsr, 0); } } pCsr->flags |= CURSOR_NEXT_OK; } return rc; } static int sortedBtreeGobble( lsm_db *pDb, /* Worker connection */ MultiCursor *pCsr, /* Multi-cursor being used for a merge */ int iGobble /* pCsr->aPtr[] entry to operate on */ ){ int rc = LSM_OK; if( rtTopic(pCsr->eType)==0 ){ Segment *pSeg = pCsr->aPtr[iGobble].pSeg; Pgno *aPg; int nPg; /* Seek from the root of the b-tree to the segment leaf that may contain ** a key equal to the one multi-cursor currently points to. Record the ** page number of each b-tree page and the leaf. The segment may be ** gobbled up to (but not including) the first of these page numbers. */ assert( pSeg->iRoot>0 ); aPg = lsmMallocZeroRc(pDb->pEnv, sizeof(Pgno)*32, &rc); if( rc==LSM_OK ){ rc = seekInBtree(pCsr, pSeg, rtTopic(pCsr->eType), pCsr->key.pData, pCsr->key.nData, aPg, 0 ); } if( rc==LSM_OK ){ for(nPg=0; aPg[nPg]; nPg++); lsmFsGobble(pDb, pSeg, aPg, nPg); } lsmFree(pDb->pEnv, aPg); } return rc; } /* ** Argument p points to a level of age N. Return the number of levels in ** the linked list starting at p that have age=N (always at least 1). */ static int sortedCountLevels(Level *p){ int iAge = p->iAge; int nRet = 0; do { nRet++; p = p->pNext; }while( p && p->iAge==iAge ); return nRet; } static int sortedSelectLevel(lsm_db *pDb, int nMerge, Level **ppOut){ Level *pTopLevel = lsmDbSnapshotLevel(pDb->pWorker); int rc = LSM_OK; Level *pLevel = 0; /* Output value */ Level *pBest = 0; /* Best level to work on found so far */ int nBest; /* Number of segments merged at pBest */ Level *pThis = 0; /* First in run of levels with age=iAge */ int nThis = 0; /* Number of levels starting at pThis */ assert( nMerge>=1 ); nBest = LSM_MAX(1, nMerge-1); /* Find the longest contiguous run of levels not currently undergoing a ** merge with the same age in the structure. Or the level being merged ** with the largest number of right-hand segments. Work on it. */ for(pLevel=pTopLevel; pLevel; pLevel=pLevel->pNext){ if( pLevel->nRight==0 && pThis && pLevel->iAge==pThis->iAge ){ nThis++; }else{ if( nThis>nBest ){ if( (pLevel->iAge!=pThis->iAge+1) || (pLevel->nRight==0 && sortedCountLevels(pLevel)<=pDb->nMerge) ){ pBest = pThis; nBest = nThis; } } if( pLevel->nRight ){ if( pLevel->nRight>nBest ){ nBest = pLevel->nRight; pBest = pLevel; } nThis = 0; pThis = 0; }else{ pThis = pLevel; nThis = 1; } } } if( nThis>nBest ){ assert( pThis ); pBest = pThis; nBest = nThis; } if( pBest==0 && nMerge==1 ){ int nFree = 0; int nUsr = 0; for(pLevel=pTopLevel; pLevel; pLevel=pLevel->pNext){ assert( !pLevel->nRight ); if( pLevel->flags & LEVEL_FREELIST_ONLY ){ nFree++; }else{ nUsr++; } } if( nUsr>1 ){ pBest = pTopLevel; nBest = nFree + nUsr; } } if( pBest ){ if( pBest->nRight==0 ){ rc = sortedMergeSetup(pDb, pBest, nBest, ppOut); }else{ *ppOut = pBest; } } return rc; } static int sortedDbIsFull(lsm_db *pDb){ Level *pTop = lsmDbSnapshotLevel(pDb->pWorker); if( lsmDatabaseFull(pDb) ) return 1; if( pTop && pTop->iAge==0 && (pTop->nRight || sortedCountLevels(pTop)>=pDb->nMerge) ){ return 1; } return 0; } typedef struct MoveBlockCtx MoveBlockCtx; struct MoveBlockCtx { int iSeen; /* Previous free block on list */ int iFrom; /* Total number of blocks in file */ }; static int moveBlockCb(void *pCtx, int iBlk, i64 iSnapshot){ MoveBlockCtx *p = (MoveBlockCtx *)pCtx; assert( p->iFrom==0 ); if( iBlk==(p->iSeen-1) ){ p->iSeen = iBlk; return 0; } p->iFrom = p->iSeen-1; return 1; } /* ** This function is called to further compact a database for which all ** of the content has already been merged into a single segment. If ** possible, it moves the contents of a single block from the end of the ** file to a free-block that lies closer to the start of the file (allowing ** the file to be eventually truncated). */ static int sortedMoveBlock(lsm_db *pDb, int *pnWrite){ Snapshot *p = pDb->pWorker; Level *pLvl = lsmDbSnapshotLevel(p); int iFrom; /* Block to move */ int iTo; /* Destination to move block to */ int rc; /* Return code */ MoveBlockCtx sCtx; assert( pLvl->pNext==0 && pLvl->nRight==0 ); assert( p->redirect.n<=LSM_MAX_BLOCK_REDIRECTS ); *pnWrite = 0; /* Check that the redirect array is not already full. If it is, return ** without moving any database content. */ if( p->redirect.n>=LSM_MAX_BLOCK_REDIRECTS ) return LSM_OK; /* Find the last block of content in the database file. Do this by ** traversing the free-list in reverse (descending block number) order. ** The first block not on the free list is the one that will be moved. ** Since the db consists of a single segment, there is no ambiguity as ** to which segment the block belongs to. */ sCtx.iSeen = p->nBlock+1; sCtx.iFrom = 0; rc = lsmWalkFreelist(pDb, 1, moveBlockCb, &sCtx); if( rc!=LSM_OK || sCtx.iFrom==0 ) return rc; iFrom = sCtx.iFrom; /* Find the first free block in the database, ignoring block 1. Block ** 1 is tricky as it is smaller than the other blocks. */ rc = lsmBlockAllocate(pDb, iFrom, &iTo); if( rc!=LSM_OK || iTo==0 ) return rc; assert( iTo!=1 && iTo<iFrom ); rc = lsmFsMoveBlock(pDb->pFS, &pLvl->lhs, iTo, iFrom); if( rc==LSM_OK ){ if( p->redirect.a==0 ){ int nByte = sizeof(struct RedirectEntry) * LSM_MAX_BLOCK_REDIRECTS; p->redirect.a = lsmMallocZeroRc(pDb->pEnv, nByte, &rc); } if( rc==LSM_OK ){ /* Check if the block just moved was already redirected. */ int i; for(i=0; i<p->redirect.n; i++){ if( p->redirect.a[i].iTo==iFrom ) break; } if( i==p->redirect.n ){ /* Block iFrom was not already redirected. Add a new array entry. */ memmove(&p->redirect.a[1], &p->redirect.a[0], sizeof(struct RedirectEntry) * p->redirect.n ); p->redirect.a[0].iFrom = iFrom; p->redirect.a[0].iTo = iTo; p->redirect.n++; }else{ /* Block iFrom was already redirected. Overwrite existing entry. */ p->redirect.a[i].iTo = iTo; } rc = lsmBlockFree(pDb, iFrom); *pnWrite = lsmFsBlockSize(pDb->pFS) / lsmFsPageSize(pDb->pFS); pLvl->lhs.pRedirect = &p->redirect; } } #if LSM_LOG_STRUCTURE if( rc==LSM_OK ){ char aBuf[64]; sprintf(aBuf, "move-block %d/%d", p->redirect.n-1, LSM_MAX_BLOCK_REDIRECTS); lsmSortedDumpStructure(pDb, pDb->pWorker, LSM_LOG_DATA, 0, aBuf); } #endif return rc; } /* */ static int mergeInsertFreelistSegments( lsm_db *pDb, int nFree, MergeWorker *pMW ){ int rc = LSM_OK; if( nFree>0 ){ MultiCursor *pCsr = pMW->pCsr; Level *pLvl = pMW->pLevel; SegmentPtr *aNew1; Segment *aNew2; Level *pIter; Level *pNext; int i = 0; aNew1 = (SegmentPtr *)lsmMallocZeroRc( pDb->pEnv, sizeof(SegmentPtr) * (pCsr->nPtr+nFree), &rc ); if( rc ) return rc; memcpy(&aNew1[nFree], pCsr->aPtr, sizeof(SegmentPtr)*pCsr->nPtr); pCsr->nPtr += nFree; lsmFree(pDb->pEnv, pCsr->aTree); lsmFree(pDb->pEnv, pCsr->aPtr); pCsr->aTree = 0; pCsr->aPtr = aNew1; aNew2 = (Segment *)lsmMallocZeroRc( pDb->pEnv, sizeof(Segment) * (pLvl->nRight+nFree), &rc ); if( rc ) return rc; memcpy(&aNew2[nFree], pLvl->aRhs, sizeof(Segment)*pLvl->nRight); pLvl->nRight += nFree; lsmFree(pDb->pEnv, pLvl->aRhs); pLvl->aRhs = aNew2; for(pIter=pDb->pWorker->pLevel; rc==LSM_OK && pIter!=pLvl; pIter=pNext){ Segment *pSeg = &pLvl->aRhs[i]; memcpy(pSeg, &pIter->lhs, sizeof(Segment)); pCsr->aPtr[i].pSeg = pSeg; pCsr->aPtr[i].pLevel = pLvl; rc = segmentPtrEnd(pCsr, &pCsr->aPtr[i], 0); pDb->pWorker->pLevel = pNext = pIter->pNext; sortedFreeLevel(pDb->pEnv, pIter); i++; } assert( i==nFree ); assert( rc!=LSM_OK || pDb->pWorker->pLevel==pLvl ); for(i=nFree; i<pCsr->nPtr; i++){ pCsr->aPtr[i].pSeg = &pLvl->aRhs[i]; } lsmFree(pDb->pEnv, pMW->aGobble); pMW->aGobble = 0; } return rc; } static int sortedWork( lsm_db *pDb, /* Database handle. Must be worker. */ int nWork, /* Number of pages of work to do */ int nMerge, /* Try to merge this many levels at once */ int bFlush, /* Set if call is to make room for a flush */ int *pnWrite /* OUT: Actual number of pages written */ ){ int rc = LSM_OK; /* Return Code */ int nRemaining = nWork; /* Units of work to do before returning */ Snapshot *pWorker = pDb->pWorker; assert( pWorker ); if( lsmDbSnapshotLevel(pWorker)==0 ) return LSM_OK; while( nRemaining>0 ){ Level *pLevel = 0; /* Find a level to work on. */ rc = sortedSelectLevel(pDb, nMerge, &pLevel); assert( rc==LSM_OK || pLevel==0 ); if( pLevel==0 ){ int nDone = 0; Level *pTopLevel = lsmDbSnapshotLevel(pDb->pWorker); if( bFlush==0 && nMerge==1 && pTopLevel && pTopLevel->pNext==0 ){ rc = sortedMoveBlock(pDb, &nDone); } nRemaining -= nDone; /* Could not find any work to do. Finished. */ if( nDone==0 ) break; }else{ int bSave = 0; Freelist freelist = {0, 0, 0}; MergeWorker mergeworker; /* State used to work on the level merge */ assert( pDb->bIncrMerge==0 ); assert( pDb->pFreelist==0 && pDb->bUseFreelist==0 ); pDb->bIncrMerge = 1; rc = mergeWorkerInit(pDb, pLevel, &mergeworker); assert( mergeworker.nWork==0 ); while( rc==LSM_OK && 0==mergeWorkerDone(&mergeworker) && (mergeworker.nWork<nRemaining || pDb->bUseFreelist) ){ int eType = rtTopic(mergeworker.pCsr->eType); rc = mergeWorkerStep(&mergeworker); /* If the cursor now points at the first entry past the end of the ** user data (i.e. either to EOF or to the first free-list entry ** that will be added to the run), then check if it is possible to ** merge in any free-list entries that are either in-memory or in ** free-list-only blocks. */ if( rc==LSM_OK && nMerge==1 && eType==0 && (rtTopic(mergeworker.pCsr->eType) || mergeWorkerDone(&mergeworker)) ){ int nFree = 0; /* Number of free-list-only levels to merge */ Level *pLvl; assert( pDb->pFreelist==0 && pDb->bUseFreelist==0 ); /* Now check if all levels containing data newer than this one ** are single-segment free-list only levels. If so, they will be ** merged in now. */ for(pLvl=pDb->pWorker->pLevel; pLvl!=mergeworker.pLevel && (pLvl->flags & LEVEL_FREELIST_ONLY); pLvl=pLvl->pNext ){ assert( pLvl->nRight==0 ); nFree++; } if( pLvl==mergeworker.pLevel ){ rc = mergeInsertFreelistSegments(pDb, nFree, &mergeworker); if( rc==LSM_OK ){ rc = multiCursorVisitFreelist(mergeworker.pCsr); } if( rc==LSM_OK ){ rc = multiCursorSetupTree(mergeworker.pCsr, 0); pDb->pFreelist = &freelist; pDb->bUseFreelist = 1; } } } } nRemaining -= LSM_MAX(mergeworker.nWork, 1); if( rc==LSM_OK ){ /* Check if the merge operation is completely finished. If not, ** gobble up (declare eligible for recycling) any pages from rhs ** segments for which the content has been completely merged into ** the lhs of the level. */ if( mergeWorkerDone(&mergeworker)==0 ){ int i; for(i=0; i<pLevel->nRight; i++){ SegmentPtr *pGobble = &mergeworker.pCsr->aPtr[i]; if( pGobble->pSeg->iRoot ){ rc = sortedBtreeGobble(pDb, mergeworker.pCsr, i); }else if( mergeworker.aGobble[i] ){ lsmFsGobble(pDb, pGobble->pSeg, &mergeworker.aGobble[i], 1); } } }else{ int i; int bEmpty; mergeWorkerShutdown(&mergeworker, &rc); bEmpty = (pLevel->lhs.iFirst==0); if( bEmpty==0 && rc==LSM_OK ){ rc = lsmFsSortedFinish(pDb->pFS, &pLevel->lhs); } if( pDb->bUseFreelist ){ Freelist *p = &pDb->pWorker->freelist; lsmFree(pDb->pEnv, p->aEntry); memcpy(p, &freelist, sizeof(freelist)); pDb->bUseFreelist = 0; pDb->pFreelist = 0; bSave = 1; } for(i=0; i<pLevel->nRight; i++){ lsmFsSortedDelete(pDb->pFS, pWorker, 1, &pLevel->aRhs[i]); } if( bEmpty ){ /* If the new level is completely empty, remove it from the ** database snapshot. This can only happen if all input keys were ** annihilated. Since keys are only annihilated if the new level ** is the last in the linked list (contains the most ancient of ** database content), this guarantees that pLevel->pNext==0. */ Level *pTop; /* Top level of worker snapshot */ Level **pp; /* Read/write iterator for Level.pNext list */ assert( pLevel->pNext==0 ); /* Remove the level from the worker snapshot. */ pTop = lsmDbSnapshotLevel(pWorker); for(pp=&pTop; *pp!=pLevel; pp=&((*pp)->pNext)); *pp = pLevel->pNext; lsmDbSnapshotSetLevel(pWorker, pTop); /* Free the Level structure. */ sortedFreeLevel(pDb->pEnv, pLevel); }else{ /* Free the separators of the next level, if required. */ if( pLevel->pMerge->nInput > pLevel->nRight ){ assert( pLevel->pNext->lhs.iRoot ); pLevel->pNext->lhs.iRoot = 0; } /* Zero the right-hand-side of pLevel */ lsmFree(pDb->pEnv, pLevel->aRhs); pLevel->nRight = 0; pLevel->aRhs = 0; /* Free the Merge object */ lsmFree(pDb->pEnv, pLevel->pMerge); pLevel->pMerge = 0; } if( bSave && rc==LSM_OK ){ pDb->bIncrMerge = 0; rc = lsmSaveWorker(pDb, 0); } } } /* Clean up the MergeWorker object initialized above. If no error ** has occurred, invoke the work-hook to inform the application that ** the database structure has changed. */ mergeWorkerShutdown(&mergeworker, &rc); pDb->bIncrMerge = 0; if( rc==LSM_OK ) sortedInvokeWorkHook(pDb); #if LSM_LOG_STRUCTURE lsmSortedDumpStructure(pDb, pDb->pWorker, LSM_LOG_DATA, 0, "work"); #endif assertBtreeOk(pDb, &pLevel->lhs); assertRunInOrder(pDb, &pLevel->lhs); /* If bFlush is true and the database is no longer considered "full", ** break out of the loop even if nRemaining is still greater than ** zero. The caller has an in-memory tree to flush to disk. */ if( bFlush && sortedDbIsFull(pDb)==0 ) break; } } if( pnWrite ) *pnWrite = (nWork - nRemaining); pWorker->nWrite += (nWork - nRemaining); #ifdef LSM_LOG_WORK lsmLogMessage(pDb, rc, "sortedWork(): %d pages", (nWork-nRemaining)); #endif return rc; } /* ** The database connection passed as the first argument must be a worker ** connection. This function checks if there exists an "old" in-memory tree ** ready to be flushed to disk. If so, true is returned. Otherwise false. ** ** If an error occurs, *pRc is set to an LSM error code before returning. ** It is assumed that *pRc is set to LSM_OK when this function is called. */ static int sortedTreeHasOld(lsm_db *pDb, int *pRc){ int rc = LSM_OK; int bRet = 0; assert( pDb->pWorker ); if( *pRc==LSM_OK ){ if( rc==LSM_OK && pDb->treehdr.iOldShmid && pDb->treehdr.iOldLog!=pDb->pWorker->iLogOff ){ bRet = 1; }else{ bRet = 0; } *pRc = rc; } assert( *pRc==LSM_OK || bRet==0 ); return bRet; } /* ** Create a new free-list only top-level segment. Return LSM_OK if successful ** or an LSM error code if some error occurs. */ static int sortedNewFreelistOnly(lsm_db *pDb){ return sortedNewToplevel(pDb, TREE_NONE, 0); } int lsmSaveWorker(lsm_db *pDb, int bFlush){ Snapshot *p = pDb->pWorker; if( p->freelist.nEntry>pDb->nMaxFreelist ){ int rc = sortedNewFreelistOnly(pDb); if( rc!=LSM_OK ) return rc; } return lsmCheckpointSaveWorker(pDb, bFlush); } static int doLsmSingleWork( lsm_db *pDb, int bShutdown, int nMerge, /* Minimum segments to merge together */ int nPage, /* Number of pages to write to disk */ int *pnWrite, /* OUT: Pages actually written to disk */ int *pbCkpt /* OUT: True if an auto-checkpoint is req. */ ){ Snapshot *pWorker; /* Worker snapshot */ int rc = LSM_OK; /* Return code */ int bDirty = 0; int nMax = nPage; /* Maximum pages to write to disk */ int nRem = nPage; int bCkpt = 0; assert( nPage>0 ); /* Open the worker 'transaction'. It will be closed before this function ** returns. */ assert( pDb->pWorker==0 ); rc = lsmBeginWork(pDb); if( rc!=LSM_OK ) return rc; pWorker = pDb->pWorker; /* If this connection is doing auto-checkpoints, set nMax (and nRem) so ** that this call stops writing when the auto-checkpoint is due. The ** caller will do the checkpoint, then possibly call this function again. */ if( bShutdown==0 && pDb->nAutockpt ){ u32 nSync; u32 nUnsync; int nPgsz; lsmCheckpointSynced(pDb, 0, 0, &nSync); nUnsync = lsmCheckpointNWrite(pDb->pShmhdr->aSnap1, 0); nPgsz = lsmCheckpointPgsz(pDb->pShmhdr->aSnap1); nMax = (int)LSM_MIN(nMax, (pDb->nAutockpt/nPgsz) - (int)(nUnsync-nSync)); if( nMax<nRem ){ bCkpt = 1; nRem = LSM_MAX(nMax, 0); } } /* If there exists in-memory data ready to be flushed to disk, attempt ** to flush it now. */ if( pDb->nTransOpen==0 ){ rc = lsmTreeLoadHeader(pDb, 0); } if( sortedTreeHasOld(pDb, &rc) ){ /* sortedDbIsFull() returns non-zero if either (a) there are too many ** levels in total in the db, or (b) there are too many levels with the ** the same age in the db. Either way, call sortedWork() to merge ** existing segments together until this condition is cleared. */ if( sortedDbIsFull(pDb) ){ int nPg = 0; rc = sortedWork(pDb, nRem, nMerge, 1, &nPg); nRem -= nPg; assert( rc!=LSM_OK || nRem<=0 || !sortedDbIsFull(pDb) ); bDirty = 1; } if( rc==LSM_OK && nRem>0 ){ int nPg = 0; rc = sortedNewToplevel(pDb, TREE_OLD, &nPg); nRem -= nPg; if( rc==LSM_OK ){ if( pDb->nTransOpen>0 ){ lsmTreeDiscardOld(pDb); } rc = lsmSaveWorker(pDb, 1); bDirty = 0; } } } /* If nPage is still greater than zero, do some merging. */ if( rc==LSM_OK && nRem>0 && bShutdown==0 ){ int nPg = 0; rc = sortedWork(pDb, nRem, nMerge, 0, &nPg); nRem -= nPg; if( nPg ) bDirty = 1; } /* If the in-memory part of the free-list is too large, write a new ** top-level containing just the in-memory free-list entries to disk. */ if( rc==LSM_OK && pDb->pWorker->freelist.nEntry > pDb->nMaxFreelist ){ int nPg = 0; while( rc==LSM_OK && lsmDatabaseFull(pDb) ){ rc = sortedWork(pDb, 16, nMerge, 1, &nPg); nRem -= nPg; } if( rc==LSM_OK ){ rc = sortedNewFreelistOnly(pDb); } nRem -= nPg; if( nPg ) bDirty = 1; } if( rc==LSM_OK ){ *pnWrite = (nMax - nRem); *pbCkpt = (bCkpt && nRem<=0); if( nMerge==1 && pDb->nAutockpt>0 && *pnWrite>0 && pWorker->pLevel && pWorker->pLevel->nRight==0 && pWorker->pLevel->pNext==0 ){ *pbCkpt = 1; } } if( rc==LSM_OK && bDirty ){ lsmFinishWork(pDb, 0, &rc); }else{ int rcdummy = LSM_BUSY; lsmFinishWork(pDb, 0, &rcdummy); *pnWrite = 0; } assert( pDb->pWorker==0 ); return rc; } static int doLsmWork(lsm_db *pDb, int nMerge, int nPage, int *pnWrite){ int rc = LSM_OK; /* Return code */ int nWrite = 0; /* Number of pages written */ assert( nMerge>=1 ); if( nPage!=0 ){ int bCkpt = 0; do { int nThis = 0; int nReq = (nPage>=0) ? (nPage-nWrite) : ((int)0x7FFFFFFF); bCkpt = 0; rc = doLsmSingleWork(pDb, 0, nMerge, nReq, &nThis, &bCkpt); nWrite += nThis; if( rc==LSM_OK && bCkpt ){ rc = lsm_checkpoint(pDb, 0); } }while( rc==LSM_OK && bCkpt && (nWrite<nPage || nPage<0) ); } if( pnWrite ){ if( rc==LSM_OK ){ *pnWrite = nWrite; }else{ *pnWrite = 0; } } return rc; } /* ** Perform work to merge database segments together. */ int lsm_work(lsm_db *pDb, int nMerge, int nKB, int *pnWrite){ int rc; /* Return code */ int nPgsz; /* Nominal page size in bytes */ int nPage; /* Equivalent of nKB in pages */ int nWrite = 0; /* Number of pages written */ /* This function may not be called if pDb has an open read or write ** transaction. Return LSM_MISUSE if an application attempts this. */ if( pDb->nTransOpen || pDb->pCsr ) return LSM_MISUSE_BKPT; if( nMerge<=0 ) nMerge = pDb->nMerge; lsmFsPurgeCache(pDb->pFS); /* Convert from KB to pages */ nPgsz = lsmFsPageSize(pDb->pFS); if( nKB>=0 ){ nPage = ((i64)nKB * 1024 + nPgsz - 1) / nPgsz; }else{ nPage = -1; } rc = doLsmWork(pDb, nMerge, nPage, &nWrite); if( pnWrite ){ /* Convert back from pages to KB */ *pnWrite = (int)(((i64)nWrite * 1024 + nPgsz - 1) / nPgsz); } return rc; } int lsm_flush(lsm_db *db){ int rc; if( db->nTransOpen>0 || db->pCsr ){ rc = LSM_MISUSE_BKPT; }else{ rc = lsmBeginWriteTrans(db); if( rc==LSM_OK ){ lsmFlushTreeToDisk(db); lsmTreeDiscardOld(db); lsmTreeMakeOld(db); lsmTreeDiscardOld(db); } if( rc==LSM_OK ){ rc = lsmFinishWriteTrans(db, 1); }else{ lsmFinishWriteTrans(db, 0); } lsmFinishReadTrans(db); } return rc; } /* ** This function is called in auto-work mode to perform merging work on ** the data structure. It performs enough merging work to prevent the ** height of the tree from growing indefinitely assuming that roughly ** nUnit database pages worth of data have been written to the database ** (i.e. the in-memory tree) since the last call. */ int lsmSortedAutoWork( lsm_db *pDb, /* Database handle */ int nUnit /* Pages of data written to in-memory tree */ ){ int rc = LSM_OK; /* Return code */ int nDepth = 0; /* Current height of tree (longest path) */ Level *pLevel; /* Used to iterate through levels */ int bRestore = 0; assert( pDb->pWorker==0 ); assert( pDb->nTransOpen>0 ); /* Determine how many units of work to do before returning. One unit of ** work is achieved by writing one page (~4KB) of merged data. */ for(pLevel=lsmDbSnapshotLevel(pDb->pClient); pLevel; pLevel=pLevel->pNext){ /* nDepth += LSM_MAX(1, pLevel->nRight); */ nDepth += 1; } if( lsmTreeHasOld(pDb) ){ nDepth += 1; bRestore = 1; rc = lsmSaveCursors(pDb); if( rc!=LSM_OK ) return rc; } if( nDepth>0 ){ int nRemaining; /* Units of work to do before returning */ nRemaining = nUnit * nDepth; #ifdef LSM_LOG_WORK lsmLogMessage(pDb, rc, "lsmSortedAutoWork(): %d*%d = %d pages", nUnit, nDepth, nRemaining); #endif assert( nRemaining>=0 ); rc = doLsmWork(pDb, pDb->nMerge, nRemaining, 0); if( rc==LSM_BUSY ) rc = LSM_OK; if( bRestore && pDb->pCsr ){ lsmMCursorFreeCache(pDb); lsmFreeSnapshot(pDb->pEnv, pDb->pClient); pDb->pClient = 0; if( rc==LSM_OK ){ rc = lsmCheckpointLoad(pDb, 0); } if( rc==LSM_OK ){ rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot, &pDb->pClient); } if( rc==LSM_OK ){ rc = lsmRestoreCursors(pDb); } } } return rc; } /* ** This function is only called during system shutdown. The contents of ** any in-memory trees present (old or current) are written out to disk. */ int lsmFlushTreeToDisk(lsm_db *pDb){ int rc; rc = lsmBeginWork(pDb); while( rc==LSM_OK && sortedDbIsFull(pDb) ){ rc = sortedWork(pDb, 256, pDb->nMerge, 1, 0); } if( rc==LSM_OK ){ rc = sortedNewToplevel(pDb, TREE_BOTH, 0); } lsmFinishWork(pDb, 1, &rc); return rc; } /* ** Return a string representation of the segment passed as the only argument. ** Space for the returned string is allocated using lsmMalloc(), and should ** be freed by the caller using lsmFree(). */ static char *segToString(lsm_env *pEnv, Segment *pSeg, int nMin){ int nSize = pSeg->nSize; Pgno iRoot = pSeg->iRoot; Pgno iFirst = pSeg->iFirst; Pgno iLast = pSeg->iLastPg; char *z; char *z1; char *z2; int nPad; z1 = lsmMallocPrintf(pEnv, "%d.%d", iFirst, iLast); if( iRoot ){ z2 = lsmMallocPrintf(pEnv, "root=%d", iRoot); }else{ z2 = lsmMallocPrintf(pEnv, "size=%d", nSize); } nPad = nMin - 2 - strlen(z1) - 1 - strlen(z2); nPad = LSM_MAX(0, nPad); if( iRoot ){ z = lsmMallocPrintf(pEnv, "/%s %*s%s\\", z1, nPad, "", z2); }else{ z = lsmMallocPrintf(pEnv, "|%s %*s%s|", z1, nPad, "", z2); } lsmFree(pEnv, z1); lsmFree(pEnv, z2); return z; } static int fileToString( lsm_db *pDb, /* For xMalloc() */ char *aBuf, int nBuf, int nMin, Segment *pSeg ){ int i = 0; if( pSeg ){ char *zSeg; zSeg = segToString(pDb->pEnv, pSeg, nMin); snprintf(&aBuf[i], nBuf-i, "%s", zSeg); i += strlen(&aBuf[i]); lsmFree(pDb->pEnv, zSeg); #ifdef LSM_LOG_FREELIST lsmInfoArrayStructure(pDb, 1, pSeg->iFirst, &zSeg); snprintf(&aBuf[i], nBuf-1, " (%s)", zSeg); i += strlen(&aBuf[i]); lsmFree(pDb->pEnv, zSeg); #endif aBuf[nBuf] = 0; }else{ aBuf[0] = '\0'; } return i; } void sortedDumpPage(lsm_db *pDb, Segment *pRun, Page *pPg, int bVals){ Blob blob = {0, 0, 0}; /* Blob used for keys */ LsmString s; int i; int nRec; int iPtr; int flags; u8 *aData; int nData; aData = fsPageData(pPg, &nData); nRec = pageGetNRec(aData, nData); iPtr = (int)pageGetPtr(aData, nData); flags = pageGetFlags(aData, nData); lsmStringInit(&s, pDb->pEnv); lsmStringAppendf(&s,"nCell=%d iPtr=%d flags=%d {", nRec, iPtr, flags); if( flags&SEGMENT_BTREE_FLAG ) iPtr = 0; for(i=0; i<nRec; i++){ Page *pRef = 0; /* Pointer to page iRef */ int iChar; u8 *aKey; int nKey = 0; /* Key */ u8 *aVal = 0; int nVal = 0; /* Value */ int iTopic; u8 *aCell; int iPgPtr; int eType; aCell = pageGetCell(aData, nData, i); eType = *aCell++; assert( (flags & SEGMENT_BTREE_FLAG) || eType!=0 ); aCell += lsmVarintGet32(aCell, &iPgPtr); if( eType==0 ){ Pgno iRef; /* Page number of referenced page */ aCell += lsmVarintGet64(aCell, &iRef); lsmFsDbPageGet(pDb->pFS, pRun, iRef, &pRef); aKey = pageGetKey(pRun, pRef, 0, &iTopic, &nKey, &blob); }else{ aCell += lsmVarintGet32(aCell, &nKey); if( rtIsWrite(eType) ) aCell += lsmVarintGet32(aCell, &nVal); sortedReadData(0, pPg, (aCell-aData), nKey+nVal, (void **)&aKey, &blob); aVal = &aKey[nKey]; iTopic = eType; } lsmStringAppendf(&s, "%s%2X:", (i==0?"":" "), iTopic); for(iChar=0; iChar<nKey; iChar++){ lsmStringAppendf(&s, "%c", isalnum(aKey[iChar]) ? aKey[iChar] : '.'); } if( nVal>0 && bVals ){ lsmStringAppendf(&s, "##"); for(iChar=0; iChar<nVal; iChar++){ lsmStringAppendf(&s, "%c", isalnum(aVal[iChar]) ? aVal[iChar] : '.'); } } lsmStringAppendf(&s, " %d", iPgPtr+iPtr); lsmFsPageRelease(pRef); } lsmStringAppend(&s, "}", 1); lsmLogMessage(pDb, LSM_OK, " Page %d: %s", lsmFsPageNumber(pPg), s.z); lsmStringClear(&s); sortedBlobFree(&blob); } static void infoCellDump( lsm_db *pDb, /* Database handle */ Segment *pSeg, /* Segment page belongs to */ int bIndirect, /* True to follow indirect refs */ Page *pPg, int iCell, int *peType, int *piPgPtr, u8 **paKey, int *pnKey, u8 **paVal, int *pnVal, Blob *pBlob ){ u8 *aData; int nData; /* Page data */ u8 *aKey; int nKey = 0; /* Key */ u8 *aVal = 0; int nVal = 0; /* Value */ int eType; int iPgPtr; Page *pRef = 0; /* Pointer to page iRef */ u8 *aCell; aData = fsPageData(pPg, &nData); aCell = pageGetCell(aData, nData, iCell); eType = *aCell++; aCell += lsmVarintGet32(aCell, &iPgPtr); if( eType==0 ){ int dummy; Pgno iRef; /* Page number of referenced page */ aCell += lsmVarintGet64(aCell, &iRef); if( bIndirect ){ lsmFsDbPageGet(pDb->pFS, pSeg, iRef, &pRef); pageGetKeyCopy(pDb->pEnv, pSeg, pRef, 0, &dummy, pBlob); aKey = (u8 *)pBlob->pData; nKey = pBlob->nData; lsmFsPageRelease(pRef); }else{ aKey = (u8 *)"<indirect>"; nKey = 11; } }else{ aCell += lsmVarintGet32(aCell, &nKey); if( rtIsWrite(eType) ) aCell += lsmVarintGet32(aCell, &nVal); sortedReadData(pSeg, pPg, (aCell-aData), nKey+nVal, (void **)&aKey, pBlob); aVal = &aKey[nKey]; } if( peType ) *peType = eType; if( piPgPtr ) *piPgPtr = iPgPtr; if( paKey ) *paKey = aKey; if( paVal ) *paVal = aVal; if( pnKey ) *pnKey = nKey; if( pnVal ) *pnVal = nVal; } static int infoAppendBlob(LsmString *pStr, int bHex, u8 *z, int n){ int iChar; for(iChar=0; iChar<n; iChar++){ if( bHex ){ lsmStringAppendf(pStr, "%02X", z[iChar]); }else{ lsmStringAppendf(pStr, "%c", isalnum(z[iChar]) ?z[iChar] : '.'); } } return LSM_OK; } #define INFO_PAGE_DUMP_DATA 0x01 #define INFO_PAGE_DUMP_VALUES 0x02 #define INFO_PAGE_DUMP_HEX 0x04 #define INFO_PAGE_DUMP_INDIRECT 0x08 static int infoPageDump( lsm_db *pDb, /* Database handle */ Pgno iPg, /* Page number of page to dump */ int flags, char **pzOut /* OUT: lsmMalloc'd string */ ){ int rc = LSM_OK; /* Return code */ Page *pPg = 0; /* Handle for page iPg */ int i, j; /* Loop counters */ const int perLine = 16; /* Bytes per line in the raw hex dump */ Segment *pSeg = 0; Snapshot *pSnap; int bValues = (flags & INFO_PAGE_DUMP_VALUES); int bHex = (flags & INFO_PAGE_DUMP_HEX); int bData = (flags & INFO_PAGE_DUMP_DATA); int bIndirect = (flags & INFO_PAGE_DUMP_INDIRECT); *pzOut = 0; if( iPg==0 ) return LSM_ERROR; assert( pDb->pClient || pDb->pWorker ); pSnap = pDb->pClient; if( pSnap==0 ) pSnap = pDb->pWorker; if( pSnap->redirect.n>0 ){ Level *pLvl; int bUse = 0; for(pLvl=pSnap->pLevel; pLvl->pNext; pLvl=pLvl->pNext); pSeg = (pLvl->nRight==0 ? &pLvl->lhs : &pLvl->aRhs[pLvl->nRight-1]); rc = lsmFsSegmentContainsPg(pDb->pFS, pSeg, iPg, &bUse); if( bUse==0 ){ pSeg = 0; } } /* iPg is a real page number (not subject to redirection). So it is safe ** to pass a NULL in place of the segment pointer as the second argument ** to lsmFsDbPageGet() here. */ if( rc==LSM_OK ){ rc = lsmFsDbPageGet(pDb->pFS, 0, iPg, &pPg); } if( rc==LSM_OK ){ Blob blob = {0, 0, 0, 0}; int nKeyWidth = 0; LsmString str; int nRec; int iPtr; int flags2; int iCell; u8 *aData; int nData; /* Page data and size thereof */ aData = fsPageData(pPg, &nData); nRec = pageGetNRec(aData, nData); iPtr = (int)pageGetPtr(aData, nData); flags2 = pageGetFlags(aData, nData); lsmStringInit(&str, pDb->pEnv); lsmStringAppendf(&str, "Page : %lld (%d bytes)\n", iPg, nData); lsmStringAppendf(&str, "nRec : %d\n", nRec); lsmStringAppendf(&str, "iPtr : %d\n", iPtr); lsmStringAppendf(&str, "flags: %04x\n", flags2); lsmStringAppendf(&str, "\n"); for(iCell=0; iCell<nRec; iCell++){ int nKey; infoCellDump( pDb, pSeg, bIndirect, pPg, iCell, 0, 0, 0, &nKey, 0, 0, &blob ); if( nKey>nKeyWidth ) nKeyWidth = nKey; } if( bHex ) nKeyWidth = nKeyWidth * 2; for(iCell=0; iCell<nRec; iCell++){ u8 *aKey; int nKey = 0; /* Key */ u8 *aVal; int nVal = 0; /* Value */ int iPgPtr; int eType; Pgno iAbsPtr; char zFlags[8]; infoCellDump(pDb, pSeg, bIndirect, pPg, iCell, &eType, &iPgPtr, &aKey, &nKey, &aVal, &nVal, &blob ); iAbsPtr = iPgPtr + ((flags2 & SEGMENT_BTREE_FLAG) ? 0 : iPtr); lsmFlagsToString(eType, zFlags); lsmStringAppendf(&str, "%s %d (%s) ", zFlags, iAbsPtr, (rtTopic(eType) ? "sys" : "usr") ); infoAppendBlob(&str, bHex, aKey, nKey); if( nVal>0 && bValues ){ lsmStringAppendf(&str, "%*s", nKeyWidth - (nKey*(1+bHex)), ""); lsmStringAppendf(&str, " "); infoAppendBlob(&str, bHex, aVal, nVal); } if( rtTopic(eType) ){ int iBlk = (int)~lsmGetU32(aKey); lsmStringAppendf(&str, " (block=%d", iBlk); if( nVal>0 ){ i64 iSnap = lsmGetU64(aVal); lsmStringAppendf(&str, " snapshot=%lld", iSnap); } lsmStringAppendf(&str, ")"); } lsmStringAppendf(&str, "\n"); } if( bData ){ lsmStringAppendf(&str, "\n-------------------" "-------------------------------------------------------------\n"); lsmStringAppendf(&str, "Page %d\n", iPg, (iPg-1)*nData, iPg*nData - 1); for(i=0; i<nData; i += perLine){ lsmStringAppendf(&str, "%04x: ", i); for(j=0; j<perLine; j++){ if( i+j>nData ){ lsmStringAppendf(&str, " "); }else{ lsmStringAppendf(&str, "%02x ", aData[i+j]); } } lsmStringAppendf(&str, " "); for(j=0; j<perLine; j++){ if( i+j>nData ){ lsmStringAppendf(&str, " "); }else{ lsmStringAppendf(&str,"%c", isprint(aData[i+j]) ? aData[i+j] : '.'); } } lsmStringAppendf(&str,"\n"); } } *pzOut = str.z; sortedBlobFree(&blob); lsmFsPageRelease(pPg); } return rc; } int lsmInfoPageDump( lsm_db *pDb, /* Database handle */ Pgno iPg, /* Page number of page to dump */ int bHex, /* True to output key/value in hex form */ char **pzOut /* OUT: lsmMalloc'd string */ ){ int flags = INFO_PAGE_DUMP_DATA | INFO_PAGE_DUMP_VALUES; if( bHex ) flags |= INFO_PAGE_DUMP_HEX; return infoPageDump(pDb, iPg, flags, pzOut); } void sortedDumpSegment(lsm_db *pDb, Segment *pRun, int bVals){ assert( pDb->xLog ); if( pRun && pRun->iFirst ){ int flags = (bVals ? INFO_PAGE_DUMP_VALUES : 0); char *zSeg; Page *pPg; zSeg = segToString(pDb->pEnv, pRun, 0); lsmLogMessage(pDb, LSM_OK, "Segment: %s", zSeg); lsmFree(pDb->pEnv, zSeg); lsmFsDbPageGet(pDb->pFS, pRun, pRun->iFirst, &pPg); while( pPg ){ Page *pNext; char *z = 0; infoPageDump(pDb, lsmFsPageNumber(pPg), flags, &z); lsmLogMessage(pDb, LSM_OK, "%s", z); lsmFree(pDb->pEnv, z); #if 0 sortedDumpPage(pDb, pRun, pPg, bVals); #endif lsmFsDbPageNext(pRun, pPg, 1, &pNext); lsmFsPageRelease(pPg); pPg = pNext; } } } /* ** Invoke the log callback zero or more times with messages that describe ** the current database structure. */ void lsmSortedDumpStructure( lsm_db *pDb, /* Database handle (used for xLog callback) */ Snapshot *pSnap, /* Snapshot to dump */ int bKeys, /* Output the keys from each segment */ int bVals, /* Output the values from each segment */ const char *zWhy /* Caption to print near top of dump */ ){ Snapshot *pDump = pSnap; Level *pTopLevel; char *zFree = 0; assert( pSnap ); pTopLevel = lsmDbSnapshotLevel(pDump); if( pDb->xLog && pTopLevel ){ static int nCall = 0; Level *pLevel; int iLevel = 0; nCall++; lsmLogMessage(pDb, LSM_OK, "Database structure %d (%s)", nCall, zWhy); #if 0 if( nCall==1031 || nCall==1032 ) bKeys=1; #endif for(pLevel=pTopLevel; pLevel; pLevel=pLevel->pNext){ char zLeft[1024]; char zRight[1024]; int i = 0; Segment *aLeft[24]; Segment *aRight[24]; int nLeft = 0; int nRight = 0; Segment *pSeg = &pLevel->lhs; aLeft[nLeft++] = pSeg; for(i=0; i<pLevel->nRight; i++){ aRight[nRight++] = &pLevel->aRhs[i]; } #ifdef LSM_LOG_FREELIST if( nRight ){ memmove(&aRight[1], aRight, sizeof(aRight[0])*nRight); aRight[0] = 0; nRight++; } #endif for(i=0; i<nLeft || i<nRight; i++){ int iPad = 0; char zLevel[32]; zLeft[0] = '\0'; zRight[0] = '\0'; if( i<nLeft ){ fileToString(pDb, zLeft, sizeof(zLeft), 24, aLeft[i]); } if( i<nRight ){ fileToString(pDb, zRight, sizeof(zRight), 24, aRight[i]); } if( i==0 ){ snprintf(zLevel, sizeof(zLevel), "L%d: (age=%d) (flags=%.4x)", iLevel, (int)pLevel->iAge, (int)pLevel->flags ); }else{ zLevel[0] = '\0'; } if( nRight==0 ){ iPad = 10; } lsmLogMessage(pDb, LSM_OK, "% 25s % *s% -35s %s", zLevel, iPad, "", zLeft, zRight ); } iLevel++; } if( bKeys ){ for(pLevel=pTopLevel; pLevel; pLevel=pLevel->pNext){ int i; sortedDumpSegment(pDb, &pLevel->lhs, bVals); for(i=0; i<pLevel->nRight; i++){ sortedDumpSegment(pDb, &pLevel->aRhs[i], bVals); } } } } lsmInfoFreelist(pDb, &zFree); lsmLogMessage(pDb, LSM_OK, "Freelist: %s", zFree); lsmFree(pDb->pEnv, zFree); assert( lsmFsIntegrityCheck(pDb) ); } void lsmSortedFreeLevel(lsm_env *pEnv, Level *pLevel){ Level *pNext; Level *p; for(p=pLevel; p; p=pNext){ pNext = p->pNext; sortedFreeLevel(pEnv, p); } } void lsmSortedSaveTreeCursors(lsm_db *pDb){ MultiCursor *pCsr; for(pCsr=pDb->pCsr; pCsr; pCsr=pCsr->pNext){ lsmTreeCursorSave(pCsr->apTreeCsr[0]); lsmTreeCursorSave(pCsr->apTreeCsr[1]); } } void lsmSortedExpandBtreePage(Page *pPg, int nOrig){ u8 *aData; int nData; int nEntry; int iHdr; aData = lsmFsPageData(pPg, &nData); nEntry = pageGetNRec(aData, nOrig); iHdr = SEGMENT_EOF(nOrig, nEntry); memmove(&aData[iHdr + (nData-nOrig)], &aData[iHdr], nOrig-iHdr); } #ifdef LSM_DEBUG_EXPENSIVE static void assertRunInOrder(lsm_db *pDb, Segment *pSeg){ Page *pPg = 0; Blob blob1 = {0, 0, 0, 0}; Blob blob2 = {0, 0, 0, 0}; lsmFsDbPageGet(pDb->pFS, pSeg, pSeg->iFirst, &pPg); while( pPg ){ u8 *aData; int nData; Page *pNext; aData = lsmFsPageData(pPg, &nData); if( 0==(pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG) ){ int i; int nRec = pageGetNRec(aData, nData); for(i=0; i<nRec; i++){ int iTopic1, iTopic2; pageGetKeyCopy(pDb->pEnv, pSeg, pPg, i, &iTopic1, &blob1); if( i==0 && blob2.nData ){ assert( sortedKeyCompare( pDb->xCmp, iTopic2, blob2.pData, blob2.nData, iTopic1, blob1.pData, blob1.nData )<0 ); } if( i<(nRec-1) ){ pageGetKeyCopy(pDb->pEnv, pSeg, pPg, i+1, &iTopic2, &blob2); assert( sortedKeyCompare( pDb->xCmp, iTopic1, blob1.pData, blob1.nData, iTopic2, blob2.pData, blob2.nData )<0 ); } } } lsmFsDbPageNext(pSeg, pPg, 1, &pNext); lsmFsPageRelease(pPg); pPg = pNext; } sortedBlobFree(&blob1); sortedBlobFree(&blob2); } #endif #ifdef LSM_DEBUG_EXPENSIVE /* ** This function is only included in the build if LSM_DEBUG_EXPENSIVE is ** defined. Its only purpose is to evaluate various assert() statements to ** verify that the database is well formed in certain respects. ** ** More specifically, it checks that the array pOne contains the required ** pointers to pTwo. Array pTwo must be a main array. pOne may be either a ** separators array or another main array. If pOne does not contain the ** correct set of pointers, an assert() statement fails. */ static int assertPointersOk( lsm_db *pDb, /* Database handle */ Segment *pOne, /* Segment containing pointers */ Segment *pTwo, /* Segment containing pointer targets */ int bRhs /* True if pTwo may have been Gobble()d */ ){ int rc = LSM_OK; /* Error code */ SegmentPtr ptr1; /* Iterates through pOne */ SegmentPtr ptr2; /* Iterates through pTwo */ Pgno iPrev; assert( pOne && pTwo ); memset(&ptr1, 0, sizeof(ptr1)); memset(&ptr2, 0, sizeof(ptr1)); ptr1.pSeg = pOne; ptr2.pSeg = pTwo; segmentPtrEndPage(pDb->pFS, &ptr1, 0, &rc); segmentPtrEndPage(pDb->pFS, &ptr2, 0, &rc); /* Check that the footer pointer of the first page of pOne points to ** the first page of pTwo. */ iPrev = pTwo->iFirst; if( ptr1.iPtr!=iPrev && !bRhs ){ assert( 0 ); } if( rc==LSM_OK && ptr1.nCell>0 ){ rc = segmentPtrLoadCell(&ptr1, 0); } while( rc==LSM_OK && ptr2.pPg ){ Pgno iThis; /* Advance to the next page of segment pTwo that contains at least ** one cell. Break out of the loop if the iterator reaches EOF. */ do{ rc = segmentPtrNextPage(&ptr2, 1); assert( rc==LSM_OK ); }while( rc==LSM_OK && ptr2.pPg && ptr2.nCell==0 ); if( rc!=LSM_OK || ptr2.pPg==0 ) break; iThis = lsmFsPageNumber(ptr2.pPg); if( (ptr2.flags & (PGFTR_SKIP_THIS_FLAG|SEGMENT_BTREE_FLAG))==0 ){ /* Load the first cell in the array pTwo page. */ rc = segmentPtrLoadCell(&ptr2, 0); /* Iterate forwards through pOne, searching for a key that matches the ** key ptr2.pKey/nKey. This key should have a pointer to the page that ** ptr2 currently points to. */ while( rc==LSM_OK ){ int res = rtTopic(ptr1.eType) - rtTopic(ptr2.eType); if( res==0 ){ res = pDb->xCmp(ptr1.pKey, ptr1.nKey, ptr2.pKey, ptr2.nKey); } if( res<0 ){ assert( bRhs || ptr1.iPtr+ptr1.iPgPtr==iPrev ); }else if( res>0 ){ assert( 0 ); }else{ assert( ptr1.iPtr+ptr1.iPgPtr==iThis ); iPrev = iThis; break; } rc = segmentPtrAdvance(0, &ptr1, 0); if( ptr1.pPg==0 ){ assert( 0 ); } } } } segmentPtrReset(&ptr1, 0); segmentPtrReset(&ptr2, 0); return LSM_OK; } /* ** This function is only included in the build if LSM_DEBUG_EXPENSIVE is ** defined. Its only purpose is to evaluate various assert() statements to ** verify that the database is well formed in certain respects. ** ** More specifically, it checks that the b-tree embedded in array pRun ** contains the correct keys. If not, an assert() fails. */ static int assertBtreeOk( lsm_db *pDb, Segment *pSeg ){ int rc = LSM_OK; /* Return code */ if( pSeg->iRoot ){ Blob blob = {0, 0, 0}; /* Buffer used to cache overflow keys */ FileSystem *pFS = pDb->pFS; /* File system to read from */ Page *pPg = 0; /* Main run page */ BtreeCursor *pCsr = 0; /* Btree cursor */ rc = btreeCursorNew(pDb, pSeg, &pCsr); if( rc==LSM_OK ){ rc = btreeCursorFirst(pCsr); } if( rc==LSM_OK ){ rc = lsmFsDbPageGet(pFS, pSeg, pSeg->iFirst, &pPg); } while( rc==LSM_OK ){ Page *pNext; u8 *aData; int nData; int flags; rc = lsmFsDbPageNext(pSeg, pPg, 1, &pNext); lsmFsPageRelease(pPg); pPg = pNext; if( pPg==0 ) break; aData = fsPageData(pPg, &nData); flags = pageGetFlags(aData, nData); if( rc==LSM_OK && 0==((SEGMENT_BTREE_FLAG|PGFTR_SKIP_THIS_FLAG) & flags) && 0!=pageGetNRec(aData, nData) ){ u8 *pKey; int nKey; int iTopic; pKey = pageGetKey(pSeg, pPg, 0, &iTopic, &nKey, &blob); assert( nKey==pCsr->nKey && 0==memcmp(pKey, pCsr->pKey, nKey) ); assert( lsmFsPageNumber(pPg)==pCsr->iPtr ); rc = btreeCursorNext(pCsr); } } assert( rc!=LSM_OK || pCsr->pKey==0 ); if( pPg ) lsmFsPageRelease(pPg); btreeCursorFree(pCsr); sortedBlobFree(&blob); } return rc; } #endif /* ifdef LSM_DEBUG_EXPENSIVE */ |
Added ext/lsm1/lsm_str.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 | /* ** 2012-04-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. ** ************************************************************************* ** ** Dynamic string functions. */ #include "lsmInt.h" /* ** Turn bulk and uninitialized memory into an LsmString object */ void lsmStringInit(LsmString *pStr, lsm_env *pEnv){ memset(pStr, 0, sizeof(pStr[0])); pStr->pEnv = pEnv; } /* ** Increase the memory allocated for holding the string. Realloc as needed. ** ** If a memory allocation error occurs, set pStr->n to -1 and free the existing ** allocation. If a prior memory allocation has occurred, this routine is a ** no-op. */ int lsmStringExtend(LsmString *pStr, int nNew){ assert( nNew>0 ); if( pStr->n<0 ) return LSM_NOMEM; if( pStr->n + nNew >= pStr->nAlloc ){ int nAlloc = pStr->n + nNew + 100; char *zNew = lsmRealloc(pStr->pEnv, pStr->z, nAlloc); if( zNew==0 ){ lsmFree(pStr->pEnv, pStr->z); nAlloc = 0; pStr->n = -1; } pStr->nAlloc = nAlloc; pStr->z = zNew; } return (pStr->z ? LSM_OK : LSM_NOMEM_BKPT); } /* ** Clear an LsmString object, releasing any allocated memory that it holds. ** This also clears the error indication (if any). */ void lsmStringClear(LsmString *pStr){ lsmFree(pStr->pEnv, pStr->z); lsmStringInit(pStr, pStr->pEnv); } /* ** Append N bytes of text to the end of an LsmString object. If ** N is negative, append the entire string. ** ** If the string is in an error state, this routine is a no-op. */ int lsmStringAppend(LsmString *pStr, const char *z, int N){ int rc; if( N<0 ) N = (int)strlen(z); rc = lsmStringExtend(pStr, N+1); if( pStr->nAlloc ){ memcpy(pStr->z+pStr->n, z, N+1); pStr->n += N; } return rc; } int lsmStringBinAppend(LsmString *pStr, const u8 *a, int n){ int rc; rc = lsmStringExtend(pStr, n); if( pStr->nAlloc ){ memcpy(pStr->z+pStr->n, a, n); pStr->n += n; } return rc; } /* ** Append printf-formatted content to an LsmString. */ void lsmStringVAppendf( LsmString *pStr, const char *zFormat, va_list ap1, va_list ap2 ){ #if (!defined(__STDC_VERSION__) || (__STDC_VERSION__<199901L)) && \ !defined(__APPLE__) extern int vsnprintf(char *str, size_t size, const char *format, va_list ap) /* Compatibility crutch for C89 compilation mode. sqlite3_vsnprintf() does not work identically and causes test failures if used here. For the time being we are assuming that the target has vsnprintf(), but that is not guaranteed to be the case for pure C89 platforms. */; #endif int nWrite; int nAvail; nAvail = pStr->nAlloc - pStr->n; nWrite = vsnprintf(pStr->z + pStr->n, nAvail, zFormat, ap1); if( nWrite>=nAvail ){ lsmStringExtend(pStr, nWrite+1); if( pStr->nAlloc==0 ) return; nWrite = vsnprintf(pStr->z + pStr->n, nWrite+1, zFormat, ap2); } pStr->n += nWrite; pStr->z[pStr->n] = 0; } void lsmStringAppendf(LsmString *pStr, const char *zFormat, ...){ va_list ap, ap2; va_start(ap, zFormat); va_start(ap2, zFormat); lsmStringVAppendf(pStr, zFormat, ap, ap2); va_end(ap); va_end(ap2); } int lsmStrlen(const char *zName){ int nRet = 0; while( zName[nRet] ) nRet++; return nRet; } /* ** Write into memory obtained from lsm_malloc(). */ char *lsmMallocPrintf(lsm_env *pEnv, const char *zFormat, ...){ LsmString s; va_list ap, ap2; lsmStringInit(&s, pEnv); va_start(ap, zFormat); va_start(ap2, zFormat); lsmStringVAppendf(&s, zFormat, ap, ap2); va_end(ap); va_end(ap2); if( s.n<0 ) return 0; return (char *)lsmReallocOrFree(pEnv, s.z, s.n+1); } |
Added ext/lsm1/lsm_tree.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 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 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 | /* ** 2011-08-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. ** ************************************************************************* ** ** This file contains the implementation of an in-memory tree structure. ** ** Technically the tree is a B-tree of order 4 (in the Knuth sense - each ** node may have up to 4 children). Keys are stored within B-tree nodes by ** reference. This may be slightly slower than a conventional red-black ** tree, but it is simpler. It is also an easier structure to modify to ** create a version that supports nested transaction rollback. ** ** This tree does not currently support a delete operation. One is not ** required. When LSM deletes a key from a database, it inserts a DELETE ** marker into the data structure. As a result, although the value associated ** with a key stored in the in-memory tree structure may be modified, no ** keys are ever removed. */ /* ** MVCC NOTES ** ** The in-memory tree structure supports SQLite-style MVCC. This means ** that while one client is writing to the tree structure, other clients ** may still be querying an older snapshot of the tree. ** ** One way to implement this is to use an append-only b-tree. In this ** case instead of modifying nodes in-place, a copy of the node is made ** and the required modifications made to the copy. The parent of the ** node is then modified (to update the pointer so that it points to ** the new copy), which causes a copy of the parent to be made, and so on. ** This means that each time the tree is written to a new root node is ** created. A snapshot is identified by the root node that it uses. ** ** The problem with the above is that each time the tree is written to, ** a copy of the node structure modified and all of its ancestor nodes ** is made. This may prove excessive with large tree structures. ** ** To reduce this overhead, the data structure used for a tree node is ** designed so that it may be edited in place exactly once without ** affecting existing users. In other words, the node structure is capable ** of storing two separate versions of the node at the same time. ** When a node is to be edited, if the node structure already contains ** two versions, a copy is made as in the append-only approach. Or, if ** it only contains a single version, it is edited in place. ** ** This reduces the overhead so that, roughly, one new node structure ** must be allocated for each write (on top of those allocations that ** would have been required by a non-MVCC tree). Logic: Assume that at ** any time, 50% of nodes in the tree already contain 2 versions. When ** a new entry is written to a node, there is a 50% chance that a copy ** of the node will be required. And a 25% chance that a copy of its ** parent is required. And so on. ** ** ROLLBACK ** ** The in-memory tree also supports transaction and sub-transaction ** rollback. In order to rollback to point in time X, the following is ** necessary: ** ** 1. All memory allocated since X must be freed, and ** 2. All "v2" data adding to nodes that existed at X should be zeroed. ** 3. The root node must be restored to its X value. ** ** The Mempool object used to allocate memory for the tree supports ** operation (1) - see the lsmPoolMark() and lsmPoolRevert() functions. ** ** To support (2), all nodes that have v2 data are part of a singly linked ** list, sorted by the age of the v2 data (nodes that have had data added ** most recently are at the end of the list). So to zero all v2 data added ** since X, the linked list is traversed from the first node added following ** X onwards. ** */ #ifndef _LSM_INT_H # include "lsmInt.h" #endif #include <string.h> #define MAX_DEPTH 32 typedef struct TreeKey TreeKey; typedef struct TreeNode TreeNode; typedef struct TreeLeaf TreeLeaf; typedef struct NodeVersion NodeVersion; struct TreeOld { u32 iShmid; /* Last shared-memory chunk in use by old */ u32 iRoot; /* Offset of root node in shm file */ u32 nHeight; /* Height of tree structure */ }; #if 0 /* ** assert() that a TreeKey.flags value is sane. Usage: ** ** assert( lsmAssertFlagsOk(pTreeKey->flags) ); */ static int lsmAssertFlagsOk(u8 keyflags){ /* At least one flag must be set. Otherwise, what is this key doing? */ assert( keyflags!=0 ); /* The POINT_DELETE and INSERT flags cannot both be set. */ assert( (keyflags & LSM_POINT_DELETE)==0 || (keyflags & LSM_INSERT)==0 ); /* If both the START_DELETE and END_DELETE flags are set, then the INSERT ** flag must also be set. In other words - the three DELETE flags cannot ** all be set */ assert( (keyflags & LSM_END_DELETE)==0 || (keyflags & LSM_START_DELETE)==0 || (keyflags & LSM_POINT_DELETE)==0 ); return 1; } #endif static int assert_delete_ranges_match(lsm_db *); static int treeCountEntries(lsm_db *db); /* ** Container for a key-value pair. Within the *-shm file, each key/value ** pair is stored in a single allocation (which may not actually be ** contiguous in memory). Layout is the TreeKey structure, followed by ** the nKey bytes of key blob, followed by the nValue bytes of value blob ** (if nValue is non-negative). */ struct TreeKey { int nKey; /* Size of pKey in bytes */ int nValue; /* Size of pValue. Or negative. */ u8 flags; /* Various LSM_XXX flags */ }; #define TKV_KEY(p) ((void *)&(p)[1]) #define TKV_VAL(p) ((void *)(((u8 *)&(p)[1]) + (p)->nKey)) /* ** A single tree node. A node structure may contain up to 3 key/value ** pairs. Internal (non-leaf) nodes have up to 4 children. ** ** TODO: Update the format of this to be more compact. Get it working ** first though... */ struct TreeNode { u32 aiKeyPtr[3]; /* Array of pointers to TreeKey objects */ /* The following fields are present for interior nodes only, not leaves. */ u32 aiChildPtr[4]; /* Array of pointers to child nodes */ /* The extra child pointer slot. */ u32 iV2; /* Transaction number of v2 */ u8 iV2Child; /* apChild[] entry replaced by pV2Ptr */ u32 iV2Ptr; /* Substitute pointer */ }; struct TreeLeaf { u32 aiKeyPtr[3]; /* Array of pointers to TreeKey objects */ }; typedef struct TreeBlob TreeBlob; struct TreeBlob { int n; u8 *a; }; /* ** Cursor for searching a tree structure. ** ** If a cursor does not point to any element (a.k.a. EOF), then the ** TreeCursor.iNode variable is set to a negative value. Otherwise, the ** cursor currently points to key aiCell[iNode] on node apTreeNode[iNode]. ** ** Entries in the apTreeNode[] and aiCell[] arrays contain the node and ** index of the TreeNode.apChild[] pointer followed to descend to the ** current element. Hence apTreeNode[0] always contains the root node of ** the tree. */ struct TreeCursor { lsm_db *pDb; /* Database handle for this cursor */ TreeRoot *pRoot; /* Root node and height of tree to access */ int iNode; /* Cursor points at apTreeNode[iNode] */ TreeNode *apTreeNode[MAX_DEPTH];/* Current position in tree */ u8 aiCell[MAX_DEPTH]; /* Current position in tree */ TreeKey *pSave; /* Saved key */ TreeBlob blob; /* Dynamic storage for a key */ }; /* ** A value guaranteed to be larger than the largest possible transaction ** id (TreeHeader.iTransId). */ #define WORKING_VERSION (1<<30) static int tblobGrow(lsm_db *pDb, TreeBlob *p, int n, int *pRc){ if( n>p->n ){ lsmFree(pDb->pEnv, p->a); p->a = lsmMallocRc(pDb->pEnv, n, pRc); p->n = n; } return (p->a==0); } static void tblobFree(lsm_db *pDb, TreeBlob *p){ lsmFree(pDb->pEnv, p->a); } /*********************************************************************** ** Start of IntArray methods. */ /* ** Append value iVal to the contents of IntArray *p. Return LSM_OK if ** successful, or LSM_NOMEM if an OOM condition is encountered. */ static int intArrayAppend(lsm_env *pEnv, IntArray *p, u32 iVal){ assert( p->nArray<=p->nAlloc ); if( p->nArray>=p->nAlloc ){ u32 *aNew; int nNew = p->nArray ? p->nArray*2 : 128; aNew = lsmRealloc(pEnv, p->aArray, nNew*sizeof(u32)); if( !aNew ) return LSM_NOMEM_BKPT; p->aArray = aNew; p->nAlloc = nNew; } p->aArray[p->nArray++] = iVal; return LSM_OK; } /* ** Zero the IntArray object. */ static void intArrayFree(lsm_env *pEnv, IntArray *p){ p->nArray = 0; } /* ** Return the number of entries currently in the int-array object. */ static int intArraySize(IntArray *p){ return p->nArray; } /* ** Return a copy of the iIdx'th entry in the int-array. */ static u32 intArrayEntry(IntArray *p, int iIdx){ return p->aArray[iIdx]; } /* ** Truncate the int-array so that all but the first nVal values are ** discarded. */ static void intArrayTruncate(IntArray *p, int nVal){ p->nArray = nVal; } /* End of IntArray methods. ***********************************************************************/ static int treeKeycmp(void *p1, int n1, void *p2, int n2){ int res; res = memcmp(p1, p2, LSM_MIN(n1, n2)); if( res==0 ) res = (n1-n2); return res; } /* ** The pointer passed as the first argument points to an interior node, ** not a leaf. This function returns the offset of the iCell'th child ** sub-tree of the node. */ static u32 getChildPtr(TreeNode *p, int iVersion, int iCell){ assert( iVersion>=0 ); assert( iCell>=0 && iCell<=array_size(p->aiChildPtr) ); if( p->iV2 && p->iV2<=(u32)iVersion && iCell==p->iV2Child ) return p->iV2Ptr; return p->aiChildPtr[iCell]; } /* ** Given an offset within the *-shm file, return the associated chunk number. */ static int treeOffsetToChunk(u32 iOff){ assert( LSM_SHM_CHUNK_SIZE==(1<<15) ); return (int)(iOff>>15); } #define treeShmptrUnsafe(pDb, iPtr) \ (&((u8*)((pDb)->apShm[(iPtr)>>15]))[(iPtr) & (LSM_SHM_CHUNK_SIZE-1)]) /* ** Return a pointer to the mapped memory location associated with *-shm ** file offset iPtr. */ static void *treeShmptr(lsm_db *pDb, u32 iPtr){ assert( (iPtr>>15)<(u32)pDb->nShm ); assert( pDb->apShm[iPtr>>15] ); return iPtr ? treeShmptrUnsafe(pDb, iPtr) : 0; } static ShmChunk * treeShmChunk(lsm_db *pDb, int iChunk){ return (ShmChunk *)(pDb->apShm[iChunk]); } static ShmChunk * treeShmChunkRc(lsm_db *pDb, int iChunk, int *pRc){ assert( *pRc==LSM_OK ); if( iChunk<pDb->nShm || LSM_OK==(*pRc = lsmShmCacheChunks(pDb, iChunk+1)) ){ return (ShmChunk *)(pDb->apShm[iChunk]); } return 0; } #ifndef NDEBUG static void assertIsWorkingChild( lsm_db *db, TreeNode *pNode, TreeNode *pParent, int iCell ){ TreeNode *p; u32 iPtr = getChildPtr(pParent, WORKING_VERSION, iCell); p = treeShmptr(db, iPtr); assert( p==pNode ); } #else # define assertIsWorkingChild(w,x,y,z) #endif /* Values for the third argument to treeShmkey(). */ #define TKV_LOADKEY 1 #define TKV_LOADVAL 2 static TreeKey *treeShmkey( lsm_db *pDb, /* Database handle */ u32 iPtr, /* Shmptr to TreeKey struct */ int eLoad, /* Either zero or a TREEKEY_LOADXXX value */ TreeBlob *pBlob, /* Used if dynamic memory is required */ int *pRc /* IN/OUT: Error code */ ){ TreeKey *pRet; assert( eLoad==TKV_LOADKEY || eLoad==TKV_LOADVAL ); pRet = (TreeKey *)treeShmptr(pDb, iPtr); if( pRet ){ int nReq; /* Bytes of space required at pRet */ int nAvail; /* Bytes of space available at pRet */ nReq = sizeof(TreeKey) + pRet->nKey; if( eLoad==TKV_LOADVAL && pRet->nValue>0 ){ nReq += pRet->nValue; } assert( LSM_SHM_CHUNK_SIZE==(1<<15) ); nAvail = LSM_SHM_CHUNK_SIZE - (iPtr & (LSM_SHM_CHUNK_SIZE-1)); if( nAvail<nReq ){ if( tblobGrow(pDb, pBlob, nReq, pRc)==0 ){ int nLoad = 0; while( *pRc==LSM_OK ){ ShmChunk *pChunk; void *p = treeShmptr(pDb, iPtr); int n = LSM_MIN(nAvail, nReq-nLoad); memcpy(&pBlob->a[nLoad], p, n); nLoad += n; if( nLoad==nReq ) break; pChunk = treeShmChunk(pDb, treeOffsetToChunk(iPtr)); assert( pChunk ); iPtr = (pChunk->iNext * LSM_SHM_CHUNK_SIZE) + LSM_SHM_CHUNK_HDR; nAvail = LSM_SHM_CHUNK_SIZE - LSM_SHM_CHUNK_HDR; } } pRet = (TreeKey *)(pBlob->a); } } return pRet; } #if defined(LSM_DEBUG) && defined(LSM_EXPENSIVE_ASSERT) void assert_leaf_looks_ok(TreeNode *pNode){ assert( pNode->apKey[1] ); } void assert_node_looks_ok(TreeNode *pNode, int nHeight){ if( pNode ){ assert( pNode->apKey[1] ); if( nHeight>1 ){ int i; assert( getChildPtr(pNode, WORKING_VERSION, 1) ); assert( getChildPtr(pNode, WORKING_VERSION, 2) ); for(i=0; i<4; i++){ assert_node_looks_ok(getChildPtr(pNode, WORKING_VERSION, i), nHeight-1); } } } } /* ** Run various assert() statements to check that the working-version of the ** tree is correct in the following respects: ** ** * todo... */ void assert_tree_looks_ok(int rc, Tree *pTree){ } #else # define assert_tree_looks_ok(x,y) #endif void lsmFlagsToString(int flags, char *zFlags){ zFlags[0] = (flags & LSM_END_DELETE) ? ']' : '.'; /* Only one of LSM_POINT_DELETE, LSM_INSERT and LSM_SEPARATOR should ever ** be set. If this is not true, write a '?' to the output. */ switch( flags & (LSM_POINT_DELETE|LSM_INSERT|LSM_SEPARATOR) ){ case 0: zFlags[1] = '.'; break; case LSM_POINT_DELETE: zFlags[1] = '-'; break; case LSM_INSERT: zFlags[1] = '+'; break; case LSM_SEPARATOR: zFlags[1] = '^'; break; default: zFlags[1] = '?'; break; } zFlags[2] = (flags & LSM_SYSTEMKEY) ? '*' : '.'; zFlags[3] = (flags & LSM_START_DELETE) ? '[' : '.'; zFlags[4] = '\0'; } #ifdef LSM_DEBUG /* ** Pointer pBlob points to a buffer containing a blob of binary data ** nBlob bytes long. Append the contents of this blob to *pStr, with ** each octet represented by a 2-digit hexadecimal number. For example, ** if the input blob is three bytes in size and contains {0x01, 0x44, 0xFF}, ** then "0144ff" is appended to *pStr. */ static void lsmAppendStrBlob(LsmString *pStr, void *pBlob, int nBlob){ int i; lsmStringExtend(pStr, nBlob*2); if( pStr->nAlloc==0 ) return; for(i=0; i<nBlob; i++){ u8 c = ((u8*)pBlob)[i]; if( c>='a' && c<='z' ){ pStr->z[pStr->n++] = c; }else if( c!=0 || nBlob==1 || i!=(nBlob-1) ){ pStr->z[pStr->n++] = "0123456789abcdef"[(c>>4)&0xf]; pStr->z[pStr->n++] = "0123456789abcdef"[c&0xf]; } } pStr->z[pStr->n] = 0; } #if 0 /* NOT USED */ /* ** Append nIndent space (0x20) characters to string *pStr. */ static void lsmAppendIndent(LsmString *pStr, int nIndent){ int i; lsmStringExtend(pStr, nIndent); for(i=0; i<nIndent; i++) lsmStringAppend(pStr, " ", 1); } #endif static void strAppendFlags(LsmString *pStr, u8 flags){ char zFlags[8]; lsmFlagsToString(flags, zFlags); zFlags[4] = ':'; lsmStringAppend(pStr, zFlags, 5); } void dump_node_contents( lsm_db *pDb, u32 iNode, /* Print out the contents of this node */ char *zPath, /* Path from root to this node */ int nPath, /* Number of bytes in zPath */ int nHeight /* Height: (0==leaf) (1==parent-of-leaf) */ ){ const char *zSpace = " "; int i; int rc = LSM_OK; LsmString s; TreeNode *pNode; TreeBlob b = {0, 0}; pNode = (TreeNode *)treeShmptr(pDb, iNode); if( nHeight==0 ){ /* Append the nIndent bytes of space to string s. */ lsmStringInit(&s, pDb->pEnv); /* Append each key to string s. */ for(i=0; i<3; i++){ u32 iPtr = pNode->aiKeyPtr[i]; if( iPtr ){ TreeKey *pKey = treeShmkey(pDb, pNode->aiKeyPtr[i],TKV_LOADKEY, &b,&rc); strAppendFlags(&s, pKey->flags); lsmAppendStrBlob(&s, TKV_KEY(pKey), pKey->nKey); lsmStringAppend(&s, " ", -1); } } printf("% 6d %.*sleaf%.*s: %s\n", iNode, nPath, zPath, 20-nPath-4, zSpace, s.z ); lsmStringClear(&s); }else{ for(i=0; i<4 && nHeight>0; i++){ u32 iPtr = getChildPtr(pNode, pDb->treehdr.root.iTransId, i); zPath[nPath] = (char)(i+'0'); zPath[nPath+1] = '/'; if( iPtr ){ dump_node_contents(pDb, iPtr, zPath, nPath+2, nHeight-1); } if( i!=3 && pNode->aiKeyPtr[i] ){ TreeKey *pKey = treeShmkey(pDb, pNode->aiKeyPtr[i], TKV_LOADKEY,&b,&rc); lsmStringInit(&s, pDb->pEnv); strAppendFlags(&s, pKey->flags); lsmAppendStrBlob(&s, TKV_KEY(pKey), pKey->nKey); printf("% 6d %.*s%.*s: %s\n", iNode, nPath+1, zPath, 20-nPath-1, zSpace, s.z); lsmStringClear(&s); } } } tblobFree(pDb, &b); } void dump_tree_contents(lsm_db *pDb, const char *zCaption){ char zPath[64]; TreeRoot *p = &pDb->treehdr.root; printf("\n%s\n", zCaption); zPath[0] = '/'; if( p->iRoot ){ dump_node_contents(pDb, p->iRoot, zPath, 1, p->nHeight-1); } fflush(stdout); } #endif /* ** Initialize a cursor object, the space for which has already been ** allocated. */ static void treeCursorInit(lsm_db *pDb, int bOld, TreeCursor *pCsr){ memset(pCsr, 0, sizeof(TreeCursor)); pCsr->pDb = pDb; if( bOld ){ pCsr->pRoot = &pDb->treehdr.oldroot; }else{ pCsr->pRoot = &pDb->treehdr.root; } pCsr->iNode = -1; } /* ** Return a pointer to the mapping of the TreeKey object that the cursor ** is pointing to. */ static TreeKey *csrGetKey(TreeCursor *pCsr, TreeBlob *pBlob, int *pRc){ TreeKey *pRet; lsm_db *pDb = pCsr->pDb; u32 iPtr = pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[pCsr->aiCell[pCsr->iNode]]; assert( iPtr ); pRet = (TreeKey*)treeShmptrUnsafe(pDb, iPtr); if( !(pRet->flags & LSM_CONTIGUOUS) ){ pRet = treeShmkey(pDb, iPtr, TKV_LOADVAL, pBlob, pRc); } return pRet; } /* ** Save the current position of tree cursor pCsr. */ int lsmTreeCursorSave(TreeCursor *pCsr){ int rc = LSM_OK; if( pCsr && pCsr->pSave==0 ){ int iNode = pCsr->iNode; if( iNode>=0 ){ pCsr->pSave = csrGetKey(pCsr, &pCsr->blob, &rc); } pCsr->iNode = -1; } return rc; } /* ** Restore the position of a saved tree cursor. */ static int treeCursorRestore(TreeCursor *pCsr, int *pRes){ int rc = LSM_OK; if( pCsr->pSave ){ TreeKey *pKey = pCsr->pSave; pCsr->pSave = 0; if( pRes ){ rc = lsmTreeCursorSeek(pCsr, TKV_KEY(pKey), pKey->nKey, pRes); } } return rc; } /* ** Allocate nByte bytes of space within the *-shm file. If successful, ** return LSM_OK and set *piPtr to the offset within the file at which ** the allocated space is located. */ static u32 treeShmalloc(lsm_db *pDb, int bAlign, int nByte, int *pRc){ u32 iRet = 0; if( *pRc==LSM_OK ){ const static int CHUNK_SIZE = LSM_SHM_CHUNK_SIZE; const static int CHUNK_HDR = LSM_SHM_CHUNK_HDR; u32 iWrite; /* Current write offset */ u32 iEof; /* End of current chunk */ int iChunk; /* Current chunk */ assert( nByte <= (CHUNK_SIZE-CHUNK_HDR) ); /* Check if there is enough space on the current chunk to fit the ** new allocation. If not, link in a new chunk and put the new ** allocation at the start of it. */ iWrite = pDb->treehdr.iWrite; if( bAlign ){ iWrite = (iWrite + 3) & ~0x0003; assert( (iWrite % 4)==0 ); } assert( iWrite ); iChunk = treeOffsetToChunk(iWrite-1); iEof = (iChunk+1) * CHUNK_SIZE; assert( iEof>=iWrite && (iEof-iWrite)<(u32)CHUNK_SIZE ); if( (iWrite+nByte)>iEof ){ ShmChunk *pHdr; /* Header of chunk just finished (iChunk) */ ShmChunk *pFirst; /* Header of chunk treehdr.iFirst */ ShmChunk *pNext; /* Header of new chunk */ int iNext = 0; /* Next chunk */ int rc = LSM_OK; pFirst = treeShmChunk(pDb, pDb->treehdr.iFirst); assert( shm_sequence_ge(pDb->treehdr.iUsedShmid, pFirst->iShmid) ); assert( (pDb->treehdr.iNextShmid+1-pDb->treehdr.nChunk)==pFirst->iShmid ); /* Check if the chunk at the start of the linked list is still in ** use. If not, reuse it. If so, allocate a new chunk by appending ** to the *-shm file. */ if( pDb->treehdr.iUsedShmid!=pFirst->iShmid ){ int bInUse; rc = lsmTreeInUse(pDb, pFirst->iShmid, &bInUse); if( rc!=LSM_OK ){ *pRc = rc; return 0; } if( bInUse==0 ){ iNext = pDb->treehdr.iFirst; pDb->treehdr.iFirst = pFirst->iNext; assert( pDb->treehdr.iFirst ); } } if( iNext==0 ) iNext = pDb->treehdr.nChunk++; /* Set the header values for the new chunk */ pNext = treeShmChunkRc(pDb, iNext, &rc); if( pNext ){ pNext->iNext = 0; pNext->iShmid = (pDb->treehdr.iNextShmid++); }else{ *pRc = rc; return 0; } /* Set the header values for the chunk just finished */ pHdr = (ShmChunk *)treeShmptr(pDb, iChunk*CHUNK_SIZE); pHdr->iNext = iNext; /* Advance to the next chunk */ iWrite = iNext * CHUNK_SIZE + CHUNK_HDR; } /* Allocate space at iWrite. */ iRet = iWrite; pDb->treehdr.iWrite = iWrite + nByte; pDb->treehdr.root.nByte += nByte; } return iRet; } /* ** Allocate and zero nByte bytes of space within the *-shm file. */ static void *treeShmallocZero(lsm_db *pDb, int nByte, u32 *piPtr, int *pRc){ u32 iPtr; void *p; iPtr = treeShmalloc(pDb, 1, nByte, pRc); p = treeShmptr(pDb, iPtr); if( p ){ assert( *pRc==LSM_OK ); memset(p, 0, nByte); *piPtr = iPtr; } return p; } static TreeNode *newTreeNode(lsm_db *pDb, u32 *piPtr, int *pRc){ return treeShmallocZero(pDb, sizeof(TreeNode), piPtr, pRc); } static TreeLeaf *newTreeLeaf(lsm_db *pDb, u32 *piPtr, int *pRc){ return treeShmallocZero(pDb, sizeof(TreeLeaf), piPtr, pRc); } static TreeKey *newTreeKey( lsm_db *pDb, u32 *piPtr, void *pKey, int nKey, /* Key data */ void *pVal, int nVal, /* Value data (or nVal<0 for delete) */ int *pRc ){ TreeKey *p; u32 iPtr; u32 iEnd; int nRem; u8 *a; int n; /* Allocate space for the TreeKey structure itself */ *piPtr = iPtr = treeShmalloc(pDb, 1, sizeof(TreeKey), pRc); p = treeShmptr(pDb, iPtr); if( *pRc ) return 0; p->nKey = nKey; p->nValue = nVal; /* Allocate and populate the space required for the key and value. */ n = nRem = nKey; a = (u8 *)pKey; while( a ){ while( nRem>0 ){ u8 *aAlloc; int nAlloc; u32 iWrite; iWrite = (pDb->treehdr.iWrite & (LSM_SHM_CHUNK_SIZE-1)); iWrite = LSM_MAX(iWrite, LSM_SHM_CHUNK_HDR); nAlloc = LSM_MIN((LSM_SHM_CHUNK_SIZE-iWrite), (u32)nRem); aAlloc = treeShmptr(pDb, treeShmalloc(pDb, 0, nAlloc, pRc)); if( aAlloc==0 ) break; memcpy(aAlloc, &a[n-nRem], nAlloc); nRem -= nAlloc; } a = pVal; n = nRem = nVal; pVal = 0; } iEnd = iPtr + sizeof(TreeKey) + nKey + LSM_MAX(0, nVal); if( (iPtr & ~(LSM_SHM_CHUNK_SIZE-1))!=(iEnd & ~(LSM_SHM_CHUNK_SIZE-1)) ){ p->flags = 0; }else{ p->flags = LSM_CONTIGUOUS; } if( *pRc ) return 0; #if 0 printf("store: %d %s\n", (int)iPtr, (char *)pKey); #endif return p; } static TreeNode *copyTreeNode( lsm_db *pDb, TreeNode *pOld, u32 *piNew, int *pRc ){ TreeNode *pNew; pNew = newTreeNode(pDb, piNew, pRc); if( pNew ){ memcpy(pNew->aiKeyPtr, pOld->aiKeyPtr, sizeof(pNew->aiKeyPtr)); memcpy(pNew->aiChildPtr, pOld->aiChildPtr, sizeof(pNew->aiChildPtr)); if( pOld->iV2 ) pNew->aiChildPtr[pOld->iV2Child] = pOld->iV2Ptr; } return pNew; } static TreeNode *copyTreeLeaf( lsm_db *pDb, TreeLeaf *pOld, u32 *piNew, int *pRc ){ TreeLeaf *pNew; pNew = newTreeLeaf(pDb, piNew, pRc); if( pNew ){ memcpy(pNew, pOld, sizeof(TreeLeaf)); } return (TreeNode *)pNew; } /* ** The tree cursor passed as the second argument currently points to an ** internal node (not a leaf). Specifically, to a sub-tree pointer. This ** function replaces the sub-tree that the cursor currently points to ** with sub-tree pNew. ** ** The sub-tree may be replaced either by writing the "v2 data" on the ** internal node, or by allocating a new TreeNode structure and then ** calling this function on the parent of the internal node. */ static int treeUpdatePtr(lsm_db *pDb, TreeCursor *pCsr, u32 iNew){ int rc = LSM_OK; if( pCsr->iNode<0 ){ /* iNew is the new root node */ pDb->treehdr.root.iRoot = iNew; }else{ /* If this node already has version 2 content, allocate a copy and ** update the copy with the new pointer value. Otherwise, store the ** new pointer as v2 data within the current node structure. */ TreeNode *p; /* The node to be modified */ int iChildPtr; /* apChild[] entry to modify */ p = pCsr->apTreeNode[pCsr->iNode]; iChildPtr = pCsr->aiCell[pCsr->iNode]; if( p->iV2 ){ /* The "allocate new TreeNode" option */ u32 iCopy; TreeNode *pCopy; pCopy = copyTreeNode(pDb, p, &iCopy, &rc); if( pCopy ){ assert( rc==LSM_OK ); pCopy->aiChildPtr[iChildPtr] = iNew; pCsr->iNode--; rc = treeUpdatePtr(pDb, pCsr, iCopy); } }else{ /* The "v2 data" option */ u32 iPtr; assert( pDb->treehdr.root.iTransId>0 ); if( pCsr->iNode ){ iPtr = getChildPtr( pCsr->apTreeNode[pCsr->iNode-1], pDb->treehdr.root.iTransId, pCsr->aiCell[pCsr->iNode-1] ); }else{ iPtr = pDb->treehdr.root.iRoot; } rc = intArrayAppend(pDb->pEnv, &pDb->rollback, iPtr); if( rc==LSM_OK ){ p->iV2 = pDb->treehdr.root.iTransId; p->iV2Child = (u8)iChildPtr; p->iV2Ptr = iNew; } } } return rc; } /* ** Cursor pCsr points at a node that is part of pTree. This function ** inserts a new key and optionally child node pointer into that node. ** ** The position into which the new key and pointer are inserted is ** determined by the iSlot parameter. The new key will be inserted to ** the left of the key currently stored in apKey[iSlot]. Or, if iSlot is ** greater than the index of the rightmost key in the node. ** ** Pointer pLeftPtr points to a child tree that contains keys that are ** smaller than pTreeKey. */ static int treeInsert( lsm_db *pDb, /* Database handle */ TreeCursor *pCsr, /* Cursor indicating path to insert at */ u32 iLeftPtr, /* Left child pointer */ u32 iTreeKey, /* Location of key to insert */ u32 iRightPtr, /* Right child pointer */ int iSlot /* Position to insert key into */ ){ int rc = LSM_OK; TreeNode *pNode = pCsr->apTreeNode[pCsr->iNode]; /* Check if the node is currently full. If so, split pNode in two and ** call this function recursively to add a key to the parent. Otherwise, ** insert the new key directly into pNode. */ assert( pNode->aiKeyPtr[1] ); if( pNode->aiKeyPtr[0] && pNode->aiKeyPtr[2] ){ u32 iLeft; TreeNode *pLeft; /* New left-hand sibling node */ u32 iRight; TreeNode *pRight; /* New right-hand sibling node */ pLeft = newTreeNode(pDb, &iLeft, &rc); pRight = newTreeNode(pDb, &iRight, &rc); if( rc ) return rc; pLeft->aiChildPtr[1] = getChildPtr(pNode, WORKING_VERSION, 0); pLeft->aiKeyPtr[1] = pNode->aiKeyPtr[0]; pLeft->aiChildPtr[2] = getChildPtr(pNode, WORKING_VERSION, 1); pRight->aiChildPtr[1] = getChildPtr(pNode, WORKING_VERSION, 2); pRight->aiKeyPtr[1] = pNode->aiKeyPtr[2]; pRight->aiChildPtr[2] = getChildPtr(pNode, WORKING_VERSION, 3); if( pCsr->iNode==0 ){ /* pNode is the root of the tree. Grow the tree by one level. */ u32 iRoot; TreeNode *pRoot; /* New root node */ pRoot = newTreeNode(pDb, &iRoot, &rc); pRoot->aiKeyPtr[1] = pNode->aiKeyPtr[1]; pRoot->aiChildPtr[1] = iLeft; pRoot->aiChildPtr[2] = iRight; pDb->treehdr.root.iRoot = iRoot; pDb->treehdr.root.nHeight++; }else{ pCsr->iNode--; rc = treeInsert(pDb, pCsr, iLeft, pNode->aiKeyPtr[1], iRight, pCsr->aiCell[pCsr->iNode] ); } assert( pLeft->iV2==0 ); assert( pRight->iV2==0 ); switch( iSlot ){ case 0: pLeft->aiKeyPtr[0] = iTreeKey; pLeft->aiChildPtr[0] = iLeftPtr; if( iRightPtr ) pLeft->aiChildPtr[1] = iRightPtr; break; case 1: pLeft->aiChildPtr[3] = (iRightPtr ? iRightPtr : pLeft->aiChildPtr[2]); pLeft->aiKeyPtr[2] = iTreeKey; pLeft->aiChildPtr[2] = iLeftPtr; break; case 2: pRight->aiKeyPtr[0] = iTreeKey; pRight->aiChildPtr[0] = iLeftPtr; if( iRightPtr ) pRight->aiChildPtr[1] = iRightPtr; break; case 3: pRight->aiChildPtr[3] = (iRightPtr ? iRightPtr : pRight->aiChildPtr[2]); pRight->aiKeyPtr[2] = iTreeKey; pRight->aiChildPtr[2] = iLeftPtr; break; } }else{ TreeNode *pNew; u32 *piKey; u32 *piChild; u32 iStore = 0; u32 iNew = 0; int i; /* Allocate a new version of node pNode. */ pNew = newTreeNode(pDb, &iNew, &rc); if( rc ) return rc; piKey = pNew->aiKeyPtr; piChild = pNew->aiChildPtr; for(i=0; i<iSlot; i++){ if( pNode->aiKeyPtr[i] ){ *(piKey++) = pNode->aiKeyPtr[i]; *(piChild++) = getChildPtr(pNode, WORKING_VERSION, i); } } *piKey++ = iTreeKey; *piChild++ = iLeftPtr; iStore = iRightPtr; for(i=iSlot; i<3; i++){ if( pNode->aiKeyPtr[i] ){ *(piKey++) = pNode->aiKeyPtr[i]; *(piChild++) = iStore ? iStore : getChildPtr(pNode, WORKING_VERSION, i); iStore = 0; } } if( iStore ){ *piChild = iStore; }else{ *piChild = getChildPtr(pNode, WORKING_VERSION, (pNode->aiKeyPtr[2] ? 3 : 2) ); } pCsr->iNode--; rc = treeUpdatePtr(pDb, pCsr, iNew); } return rc; } static int treeInsertLeaf( lsm_db *pDb, /* Database handle */ TreeCursor *pCsr, /* Cursor structure */ u32 iTreeKey, /* Key pointer to insert */ int iSlot /* Insert key to the left of this */ ){ int rc = LSM_OK; /* Return code */ TreeNode *pLeaf = pCsr->apTreeNode[pCsr->iNode]; TreeLeaf *pNew; u32 iNew; assert( iSlot>=0 && iSlot<=4 ); assert( pCsr->iNode>0 ); assert( pLeaf->aiKeyPtr[1] ); pCsr->iNode--; pNew = newTreeLeaf(pDb, &iNew, &rc); if( pNew ){ if( pLeaf->aiKeyPtr[0] && pLeaf->aiKeyPtr[2] ){ /* The leaf is full. Split it in two. */ TreeLeaf *pRight; u32 iRight; pRight = newTreeLeaf(pDb, &iRight, &rc); if( pRight ){ assert( rc==LSM_OK ); pNew->aiKeyPtr[1] = pLeaf->aiKeyPtr[0]; pRight->aiKeyPtr[1] = pLeaf->aiKeyPtr[2]; switch( iSlot ){ case 0: pNew->aiKeyPtr[0] = iTreeKey; break; case 1: pNew->aiKeyPtr[2] = iTreeKey; break; case 2: pRight->aiKeyPtr[0] = iTreeKey; break; case 3: pRight->aiKeyPtr[2] = iTreeKey; break; } rc = treeInsert(pDb, pCsr, iNew, pLeaf->aiKeyPtr[1], iRight, pCsr->aiCell[pCsr->iNode] ); } }else{ int iOut = 0; int i; for(i=0; i<4; i++){ if( i==iSlot ) pNew->aiKeyPtr[iOut++] = iTreeKey; if( i<3 && pLeaf->aiKeyPtr[i] ){ pNew->aiKeyPtr[iOut++] = pLeaf->aiKeyPtr[i]; } } rc = treeUpdatePtr(pDb, pCsr, iNew); } } return rc; } void lsmTreeMakeOld(lsm_db *pDb){ /* A write transaction must be open. Otherwise the code below that ** assumes (pDb->pClient->iLogOff) is current may malfunction. ** ** Update: currently this assert fails due to lsm_flush(), which does ** not set nTransOpen. */ assert( /* pDb->nTransOpen>0 && */ pDb->iReader>=0 ); if( pDb->treehdr.iOldShmid==0 ){ pDb->treehdr.iOldLog = (pDb->treehdr.log.aRegion[2].iEnd << 1); pDb->treehdr.iOldLog |= (~(pDb->pClient->iLogOff) & (i64)0x0001); pDb->treehdr.oldcksum0 = pDb->treehdr.log.cksum0; pDb->treehdr.oldcksum1 = pDb->treehdr.log.cksum1; pDb->treehdr.iOldShmid = pDb->treehdr.iNextShmid-1; memcpy(&pDb->treehdr.oldroot, &pDb->treehdr.root, sizeof(TreeRoot)); pDb->treehdr.root.iTransId = 1; pDb->treehdr.root.iRoot = 0; pDb->treehdr.root.nHeight = 0; pDb->treehdr.root.nByte = 0; } } void lsmTreeDiscardOld(lsm_db *pDb){ assert( lsmShmAssertLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_EXCL) || lsmShmAssertLock(pDb, LSM_LOCK_DMS2, LSM_LOCK_EXCL) ); pDb->treehdr.iUsedShmid = pDb->treehdr.iOldShmid; pDb->treehdr.iOldShmid = 0; } int lsmTreeHasOld(lsm_db *pDb){ return pDb->treehdr.iOldShmid!=0; } /* ** This function is called during recovery to initialize the ** tree header. Only the database connections private copy of the tree-header ** is initialized here - it will be copied into shared memory if log file ** recovery is successful. */ int lsmTreeInit(lsm_db *pDb){ ShmChunk *pOne; int rc = LSM_OK; memset(&pDb->treehdr, 0, sizeof(TreeHeader)); pDb->treehdr.root.iTransId = 1; pDb->treehdr.iFirst = 1; pDb->treehdr.nChunk = 2; pDb->treehdr.iWrite = LSM_SHM_CHUNK_SIZE + LSM_SHM_CHUNK_HDR; pDb->treehdr.iNextShmid = 2; pDb->treehdr.iUsedShmid = 1; pOne = treeShmChunkRc(pDb, 1, &rc); if( pOne ){ pOne->iNext = 0; pOne->iShmid = 1; } return rc; } static void treeHeaderChecksum( TreeHeader *pHdr, u32 *aCksum ){ u32 cksum1 = 0x12345678; u32 cksum2 = 0x9ABCDEF0; u32 *a = (u32 *)pHdr; int i; assert( (offsetof(TreeHeader, aCksum) + sizeof(u32)*2)==sizeof(TreeHeader) ); assert( (sizeof(TreeHeader) % (sizeof(u32)*2))==0 ); for(i=0; i<(offsetof(TreeHeader, aCksum) / sizeof(u32)); i+=2){ cksum1 += a[i]; cksum2 += (cksum1 + a[i+1]); } aCksum[0] = cksum1; aCksum[1] = cksum2; } /* ** Return true if the checksum stored in TreeHeader object *pHdr is ** consistent with the contents of its other fields. */ static int treeHeaderChecksumOk(TreeHeader *pHdr){ u32 aCksum[2]; treeHeaderChecksum(pHdr, aCksum); return (0==memcmp(aCksum, pHdr->aCksum, sizeof(aCksum))); } /* ** This type is used by functions lsmTreeRepair() and treeSortByShmid() to ** make relinking the linked list of shared-memory chunks easier. */ typedef struct ShmChunkLoc ShmChunkLoc; struct ShmChunkLoc { ShmChunk *pShm; u32 iLoc; }; /* ** This function checks that the linked list of shared memory chunks ** that starts at chunk db->treehdr.iFirst: ** ** 1) Includes all chunks in the shared-memory region, and ** 2) Links them together in order of ascending shm-id. ** ** If no error occurs and the conditions above are met, LSM_OK is returned. ** ** If either of the conditions are untrue, LSM_CORRUPT is returned. Or, if ** an error is encountered before the checks are completed, another LSM error ** code (i.e. LSM_IOERR or LSM_NOMEM) may be returned. */ static int treeCheckLinkedList(lsm_db *db){ int rc = LSM_OK; int nVisit = 0; ShmChunk *p; p = treeShmChunkRc(db, db->treehdr.iFirst, &rc); while( rc==LSM_OK && p ){ if( p->iNext ){ if( p->iNext>=db->treehdr.nChunk ){ rc = LSM_CORRUPT_BKPT; }else{ ShmChunk *pNext = treeShmChunkRc(db, p->iNext, &rc); if( rc==LSM_OK ){ if( pNext->iShmid!=p->iShmid+1 ){ rc = LSM_CORRUPT_BKPT; } p = pNext; } } }else{ p = 0; } nVisit++; } if( rc==LSM_OK && (u32)nVisit!=db->treehdr.nChunk-1 ){ rc = LSM_CORRUPT_BKPT; } return rc; } /* ** Iterate through the current in-memory tree. If there are any v2-pointers ** with transaction ids larger than db->treehdr.iTransId, zero them. */ static int treeRepairPtrs(lsm_db *db){ int rc = LSM_OK; if( db->treehdr.root.nHeight>1 ){ TreeCursor csr; /* Cursor used to iterate through tree */ u32 iTransId = db->treehdr.root.iTransId; /* Initialize the cursor structure. Also decrement the nHeight variable ** in the tree-header. This will prevent the cursor from visiting any ** leaf nodes. */ db->treehdr.root.nHeight--; treeCursorInit(db, 0, &csr); rc = lsmTreeCursorEnd(&csr, 0); while( rc==LSM_OK && lsmTreeCursorValid(&csr) ){ TreeNode *pNode = csr.apTreeNode[csr.iNode]; if( pNode->iV2>iTransId ){ pNode->iV2Child = 0; pNode->iV2Ptr = 0; pNode->iV2 = 0; } rc = lsmTreeCursorNext(&csr); } tblobFree(csr.pDb, &csr.blob); db->treehdr.root.nHeight++; } return rc; } static int treeRepairList(lsm_db *db){ int rc = LSM_OK; int i; ShmChunk *p; ShmChunk *pMin = 0; u32 iMin = 0; /* Iterate through all shm chunks. Find the smallest shm-id present in ** the shared-memory region. */ for(i=1; rc==LSM_OK && (u32)i<db->treehdr.nChunk; i++){ p = treeShmChunkRc(db, i, &rc); if( p && (pMin==0 || shm_sequence_ge(pMin->iShmid, p->iShmid)) ){ pMin = p; iMin = i; } } /* Fix the shm-id values on any chunks with a shm-id greater than or ** equal to treehdr.iNextShmid. Then do a merge-sort of all chunks to ** fix the ShmChunk.iNext pointers. */ if( rc==LSM_OK ){ int nSort; int nByte; u32 iPrevShmid; ShmChunkLoc *aSort; /* Allocate space for a merge sort. */ nSort = 1; while( (u32)nSort < (db->treehdr.nChunk-1) ) nSort = nSort * 2; nByte = sizeof(ShmChunkLoc) * nSort * 2; aSort = lsmMallocZeroRc(db->pEnv, nByte, &rc); iPrevShmid = pMin->iShmid; /* Fix all shm-ids, if required. */ if( rc==LSM_OK ){ iPrevShmid = pMin->iShmid-1; for(i=1; (u32)i<db->treehdr.nChunk; i++){ p = treeShmChunk(db, i); aSort[i-1].pShm = p; aSort[i-1].iLoc = i; if( (u32)i!=db->treehdr.iFirst ){ if( shm_sequence_ge(p->iShmid, db->treehdr.iNextShmid) ){ p->iShmid = iPrevShmid--; } } } if( iMin!=db->treehdr.iFirst ){ p = treeShmChunk(db, db->treehdr.iFirst); p->iShmid = iPrevShmid; } } if( rc==LSM_OK ){ ShmChunkLoc *aSpace = &aSort[nSort]; for(i=0; i<nSort; i++){ if( aSort[i].pShm ){ assert( shm_sequence_ge(aSort[i].pShm->iShmid, iPrevShmid) ); assert( aSpace[aSort[i].pShm->iShmid - iPrevShmid].pShm==0 ); aSpace[aSort[i].pShm->iShmid - iPrevShmid] = aSort[i]; } } if( aSpace[nSort-1].pShm ) aSpace[nSort-1].pShm->iNext = 0; for(i=0; i<nSort-1; i++){ if( aSpace[i].pShm ){ aSpace[i].pShm->iNext = aSpace[i+1].iLoc; } } rc = treeCheckLinkedList(db); lsmFree(db->pEnv, aSort); } } return rc; } /* ** This function is called as part of opening a write-transaction if the ** writer-flag is already set - indicating that the previous writer ** failed before ending its transaction. */ int lsmTreeRepair(lsm_db *db){ int rc = LSM_OK; TreeHeader hdr; ShmHeader *pHdr = db->pShmhdr; /* Ensure that the two tree-headers are consistent. Copy one over the other ** if necessary. Prefer the data from a tree-header for which the checksum ** computes. Or, if they both compute, prefer tree-header-1. */ if( memcmp(&pHdr->hdr1, &pHdr->hdr2, sizeof(TreeHeader)) ){ if( treeHeaderChecksumOk(&pHdr->hdr1) ){ memcpy(&pHdr->hdr2, &pHdr->hdr1, sizeof(TreeHeader)); }else{ memcpy(&pHdr->hdr1, &pHdr->hdr2, sizeof(TreeHeader)); } } /* Save the connections current copy of the tree-header. It will be ** restored before returning. */ memcpy(&hdr, &db->treehdr, sizeof(TreeHeader)); /* Walk the tree. Zero any v2 pointers with a transaction-id greater than ** the transaction-id currently in the tree-headers. */ rc = treeRepairPtrs(db); /* Repair the linked list of shared-memory chunks. */ if( rc==LSM_OK ){ rc = treeRepairList(db); } memcpy(&db->treehdr, &hdr, sizeof(TreeHeader)); return rc; } static void treeOverwriteKey(lsm_db *db, TreeCursor *pCsr, u32 iKey, int *pRc){ if( *pRc==LSM_OK ){ TreeRoot *p = &db->treehdr.root; TreeNode *pNew; u32 iNew; TreeNode *pNode = pCsr->apTreeNode[pCsr->iNode]; int iCell = pCsr->aiCell[pCsr->iNode]; /* Create a copy of this node */ if( (pCsr->iNode>0 && (u32)pCsr->iNode==(p->nHeight-1)) ){ pNew = copyTreeLeaf(db, (TreeLeaf *)pNode, &iNew, pRc); }else{ pNew = copyTreeNode(db, pNode, &iNew, pRc); } if( pNew ){ /* Modify the value in the new version */ pNew->aiKeyPtr[iCell] = iKey; /* Change the pointer in the parent (if any) to point at the new ** TreeNode */ pCsr->iNode--; treeUpdatePtr(db, pCsr, iNew); } } } static int treeNextIsEndDelete(lsm_db *db, TreeCursor *pCsr){ int iNode = pCsr->iNode; int iCell = pCsr->aiCell[iNode]+1; /* Cursor currently points to a leaf node. */ assert( (u32)pCsr->iNode==(db->treehdr.root.nHeight-1) ); while( iNode>=0 ){ TreeNode *pNode = pCsr->apTreeNode[iNode]; if( iCell<3 && pNode->aiKeyPtr[iCell] ){ int rc = LSM_OK; TreeKey *pKey = treeShmptr(db, pNode->aiKeyPtr[iCell]); assert( rc==LSM_OK ); return ((pKey->flags & LSM_END_DELETE) ? 1 : 0); } iNode--; iCell = pCsr->aiCell[iNode]; } return 0; } static int treePrevIsStartDelete(lsm_db *db, TreeCursor *pCsr){ int iNode = pCsr->iNode; /* Cursor currently points to a leaf node. */ assert( (u32)pCsr->iNode==(db->treehdr.root.nHeight-1) ); while( iNode>=0 ){ TreeNode *pNode = pCsr->apTreeNode[iNode]; int iCell = pCsr->aiCell[iNode]-1; if( iCell>=0 && pNode->aiKeyPtr[iCell] ){ int rc = LSM_OK; TreeKey *pKey = treeShmptr(db, pNode->aiKeyPtr[iCell]); assert( rc==LSM_OK ); return ((pKey->flags & LSM_START_DELETE) ? 1 : 0); } iNode--; } return 0; } static int treeInsertEntry( lsm_db *pDb, /* Database handle */ int flags, /* Flags associated with entry */ void *pKey, /* Pointer to key data */ int nKey, /* Size of key data in bytes */ void *pVal, /* Pointer to value data (or NULL) */ int nVal /* Bytes in value data (or -ve for delete) */ ){ int rc = LSM_OK; /* Return Code */ TreeKey *pTreeKey; /* New key-value being inserted */ u32 iTreeKey; TreeRoot *p = &pDb->treehdr.root; TreeCursor csr; /* Cursor to seek to pKey/nKey */ int res = 0; /* Result of seek operation on csr */ assert( nVal>=0 || pVal==0 ); assert_tree_looks_ok(LSM_OK, pTree); assert( flags==LSM_INSERT || flags==LSM_POINT_DELETE || flags==LSM_START_DELETE || flags==LSM_END_DELETE ); assert( (flags & LSM_CONTIGUOUS)==0 ); #if 0 dump_tree_contents(pDb, "before"); #endif if( p->iRoot ){ TreeKey *pRes; /* Key at end of seek operation */ treeCursorInit(pDb, 0, &csr); /* Seek to the leaf (or internal node) that the new key belongs on */ rc = lsmTreeCursorSeek(&csr, pKey, nKey, &res); pRes = csrGetKey(&csr, &csr.blob, &rc); if( rc!=LSM_OK ) return rc; assert( pRes ); if( flags==LSM_START_DELETE ){ /* When inserting a start-delete-range entry, if the key that ** occurs immediately before the new entry is already a START_DELETE, ** then the new entry is not required. */ if( (res<=0 && (pRes->flags & LSM_START_DELETE)) || (res>0 && treePrevIsStartDelete(pDb, &csr)) ){ goto insert_entry_out; } }else if( flags==LSM_END_DELETE ){ /* When inserting an start-delete-range entry, if the key that ** occurs immediately after the new entry is already an END_DELETE, ** then the new entry is not required. */ if( (res<0 && treeNextIsEndDelete(pDb, &csr)) || (res>=0 && (pRes->flags & LSM_END_DELETE)) ){ goto insert_entry_out; } } if( res==0 && (flags & (LSM_END_DELETE|LSM_START_DELETE)) ){ if( pRes->flags & LSM_INSERT ){ nVal = pRes->nValue; pVal = TKV_VAL(pRes); } flags = flags | pRes->flags; } if( flags & (LSM_INSERT|LSM_POINT_DELETE) ){ if( (res<0 && (pRes->flags & LSM_START_DELETE)) || (res>0 && (pRes->flags & LSM_END_DELETE)) ){ flags = flags | (LSM_END_DELETE|LSM_START_DELETE); }else if( res==0 ){ flags = flags | (pRes->flags & (LSM_END_DELETE|LSM_START_DELETE)); } } }else{ memset(&csr, 0, sizeof(TreeCursor)); } /* Allocate and populate a new key-value pair structure */ pTreeKey = newTreeKey(pDb, &iTreeKey, pKey, nKey, pVal, nVal, &rc); if( rc!=LSM_OK ) return rc; assert( pTreeKey->flags==0 || pTreeKey->flags==LSM_CONTIGUOUS ); pTreeKey->flags |= flags; if( p->iRoot==0 ){ /* The tree is completely empty. Add a new root node and install ** (pKey/nKey) as the middle entry. Even though it is a leaf at the ** moment, use newTreeNode() to allocate the node (i.e. allocate enough ** space for the fields used by interior nodes). This is because the ** treeInsert() routine may convert this node to an interior node. */ TreeNode *pRoot = newTreeNode(pDb, &p->iRoot, &rc); if( rc==LSM_OK ){ assert( p->nHeight==0 ); pRoot->aiKeyPtr[1] = iTreeKey; p->nHeight = 1; } }else{ if( res==0 ){ /* The search found a match within the tree. */ treeOverwriteKey(pDb, &csr, iTreeKey, &rc); }else{ /* The cursor now points to the leaf node into which the new entry should ** be inserted. There may or may not be a free slot within the leaf for ** the new key-value pair. ** ** iSlot is set to the index of the key within pLeaf that the new key ** should be inserted to the left of (or to a value 1 greater than the ** index of the rightmost key if the new key is larger than all keys ** currently stored in the node). */ int iSlot = csr.aiCell[csr.iNode] + (res<0); if( csr.iNode==0 ){ rc = treeInsert(pDb, &csr, 0, iTreeKey, 0, iSlot); }else{ rc = treeInsertLeaf(pDb, &csr, iTreeKey, iSlot); } } } #if 0 dump_tree_contents(pDb, "after"); #endif insert_entry_out: tblobFree(pDb, &csr.blob); assert_tree_looks_ok(rc, pTree); return rc; } /* ** Insert a new entry into the in-memory tree. ** ** If the value of the 5th parameter, nVal, is negative, then a delete-marker ** is inserted into the tree. In this case the value pointer, pVal, must be ** NULL. */ int lsmTreeInsert( lsm_db *pDb, /* Database handle */ void *pKey, /* Pointer to key data */ int nKey, /* Size of key data in bytes */ void *pVal, /* Pointer to value data (or NULL) */ int nVal /* Bytes in value data (or -ve for delete) */ ){ int flags; if( nVal<0 ){ flags = LSM_POINT_DELETE; }else{ flags = LSM_INSERT; } return treeInsertEntry(pDb, flags, pKey, nKey, pVal, nVal); } static int treeDeleteEntry(lsm_db *db, TreeCursor *pCsr, u32 iNewptr){ TreeRoot *p = &db->treehdr.root; TreeNode *pNode = pCsr->apTreeNode[pCsr->iNode]; int iSlot = pCsr->aiCell[pCsr->iNode]; int bLeaf; int rc = LSM_OK; assert( pNode->aiKeyPtr[1] ); assert( pNode->aiKeyPtr[iSlot] ); assert( iSlot==0 || iSlot==1 || iSlot==2 ); assert( ((u32)pCsr->iNode==(db->treehdr.root.nHeight-1))==(iNewptr==0) ); bLeaf = ((u32)pCsr->iNode==(p->nHeight-1) && p->nHeight>1); if( pNode->aiKeyPtr[0] || pNode->aiKeyPtr[2] ){ /* There are currently at least 2 keys on this node. So just create ** a new copy of the node with one of the keys removed. If the node ** happens to be the root node of the tree, allocate an entire ** TreeNode structure instead of just a TreeLeaf. */ TreeNode *pNew; u32 iNew; if( bLeaf ){ pNew = (TreeNode *)newTreeLeaf(db, &iNew, &rc); }else{ pNew = newTreeNode(db, &iNew, &rc); } if( pNew ){ int i; int iOut = 1; for(i=0; i<4; i++){ if( i==iSlot ){ i++; if( bLeaf==0 ) pNew->aiChildPtr[iOut] = iNewptr; if( i<3 ) pNew->aiKeyPtr[iOut] = pNode->aiKeyPtr[i]; iOut++; }else if( bLeaf || p->nHeight==1 ){ if( i<3 && pNode->aiKeyPtr[i] ){ pNew->aiKeyPtr[iOut++] = pNode->aiKeyPtr[i]; } }else{ if( getChildPtr(pNode, WORKING_VERSION, i) ){ pNew->aiChildPtr[iOut] = getChildPtr(pNode, WORKING_VERSION, i); if( i<3 ) pNew->aiKeyPtr[iOut] = pNode->aiKeyPtr[i]; iOut++; } } } assert( iOut<=4 ); assert( bLeaf || pNew->aiChildPtr[0]==0 ); pCsr->iNode--; rc = treeUpdatePtr(db, pCsr, iNew); } }else if( pCsr->iNode==0 ){ /* Removing the only key in the root node. iNewptr is the new root. */ assert( iSlot==1 ); db->treehdr.root.iRoot = iNewptr; db->treehdr.root.nHeight--; }else{ /* There is only one key on this node and the node is not the root ** node. Find a peer for this node. Then redistribute the contents of ** the peer and the parent cell between the parent and either one or ** two new nodes. */ TreeNode *pParent; /* Parent tree node */ int iPSlot; u32 iPeer; /* Pointer to peer leaf node */ int iDir; TreeNode *pPeer; /* The peer leaf node */ TreeNode *pNew1; u32 iNew1; /* First new leaf node */ assert( iSlot==1 ); pParent = pCsr->apTreeNode[pCsr->iNode-1]; iPSlot = pCsr->aiCell[pCsr->iNode-1]; if( iPSlot>0 && getChildPtr(pParent, WORKING_VERSION, iPSlot-1) ){ iDir = -1; }else{ iDir = +1; } iPeer = getChildPtr(pParent, WORKING_VERSION, iPSlot+iDir); pPeer = (TreeNode *)treeShmptr(db, iPeer); assertIsWorkingChild(db, pNode, pParent, iPSlot); /* Allocate the first new leaf node. This is always required. */ if( bLeaf ){ pNew1 = (TreeNode *)newTreeLeaf(db, &iNew1, &rc); }else{ pNew1 = (TreeNode *)newTreeNode(db, &iNew1, &rc); } if( pPeer->aiKeyPtr[0] && pPeer->aiKeyPtr[2] ){ /* Peer node is completely full. This means that two new leaf nodes ** and a new parent node are required. */ TreeNode *pNew2; u32 iNew2; /* Second new leaf node */ TreeNode *pNewP; u32 iNewP; /* New parent node */ if( bLeaf ){ pNew2 = (TreeNode *)newTreeLeaf(db, &iNew2, &rc); }else{ pNew2 = (TreeNode *)newTreeNode(db, &iNew2, &rc); } pNewP = copyTreeNode(db, pParent, &iNewP, &rc); if( iDir==-1 ){ pNew1->aiKeyPtr[1] = pPeer->aiKeyPtr[0]; if( bLeaf==0 ){ pNew1->aiChildPtr[1] = getChildPtr(pPeer, WORKING_VERSION, 0); pNew1->aiChildPtr[2] = getChildPtr(pPeer, WORKING_VERSION, 1); } pNewP->aiChildPtr[iPSlot-1] = iNew1; pNewP->aiKeyPtr[iPSlot-1] = pPeer->aiKeyPtr[1]; pNewP->aiChildPtr[iPSlot] = iNew2; pNew2->aiKeyPtr[0] = pPeer->aiKeyPtr[2]; pNew2->aiKeyPtr[1] = pParent->aiKeyPtr[iPSlot-1]; if( bLeaf==0 ){ pNew2->aiChildPtr[0] = getChildPtr(pPeer, WORKING_VERSION, 2); pNew2->aiChildPtr[1] = getChildPtr(pPeer, WORKING_VERSION, 3); pNew2->aiChildPtr[2] = iNewptr; } }else{ pNew1->aiKeyPtr[1] = pParent->aiKeyPtr[iPSlot]; if( bLeaf==0 ){ pNew1->aiChildPtr[1] = iNewptr; pNew1->aiChildPtr[2] = getChildPtr(pPeer, WORKING_VERSION, 0); } pNewP->aiChildPtr[iPSlot] = iNew1; pNewP->aiKeyPtr[iPSlot] = pPeer->aiKeyPtr[0]; pNewP->aiChildPtr[iPSlot+1] = iNew2; pNew2->aiKeyPtr[0] = pPeer->aiKeyPtr[1]; pNew2->aiKeyPtr[1] = pPeer->aiKeyPtr[2]; if( bLeaf==0 ){ pNew2->aiChildPtr[0] = getChildPtr(pPeer, WORKING_VERSION, 1); pNew2->aiChildPtr[1] = getChildPtr(pPeer, WORKING_VERSION, 2); pNew2->aiChildPtr[2] = getChildPtr(pPeer, WORKING_VERSION, 3); } } assert( pCsr->iNode>=1 ); pCsr->iNode -= 2; if( rc==LSM_OK ){ assert( pNew1->aiKeyPtr[1] && pNew2->aiKeyPtr[1] ); rc = treeUpdatePtr(db, pCsr, iNewP); } }else{ int iKOut = 0; int iPOut = 0; int i; pCsr->iNode--; if( iDir==1 ){ pNew1->aiKeyPtr[iKOut++] = pParent->aiKeyPtr[iPSlot]; if( bLeaf==0 ) pNew1->aiChildPtr[iPOut++] = iNewptr; } for(i=0; i<3; i++){ if( pPeer->aiKeyPtr[i] ){ pNew1->aiKeyPtr[iKOut++] = pPeer->aiKeyPtr[i]; } } if( bLeaf==0 ){ for(i=0; i<4; i++){ if( getChildPtr(pPeer, WORKING_VERSION, i) ){ pNew1->aiChildPtr[iPOut++] = getChildPtr(pPeer, WORKING_VERSION, i); } } } if( iDir==-1 ){ iPSlot--; pNew1->aiKeyPtr[iKOut++] = pParent->aiKeyPtr[iPSlot]; if( bLeaf==0 ) pNew1->aiChildPtr[iPOut++] = iNewptr; pCsr->aiCell[pCsr->iNode] = (u8)iPSlot; } rc = treeDeleteEntry(db, pCsr, iNew1); } } return rc; } /* ** Delete a range of keys from the tree structure (i.e. the lsm_delete_range() ** function, not lsm_delete()). ** ** This is a two step process: ** ** 1) Remove all entries currently stored in the tree that have keys ** that fall into the deleted range. ** ** TODO: There are surely good ways to optimize this step - removing ** a range of keys from a b-tree. But for now, this function removes ** them one at a time using the usual approach. ** ** 2) Unless the largest key smaller than or equal to (pKey1/nKey1) is ** already marked as START_DELETE, insert a START_DELETE key. ** Similarly, unless the smallest key greater than or equal to ** (pKey2/nKey2) is already START_END, insert a START_END key. */ int lsmTreeDelete( lsm_db *db, void *pKey1, int nKey1, /* Start of range */ void *pKey2, int nKey2 /* End of range */ ){ int rc = LSM_OK; int bDone = 0; TreeRoot *p = &db->treehdr.root; TreeBlob blob = {0, 0}; /* The range must be sensible - that (key1 < key2). */ assert( treeKeycmp(pKey1, nKey1, pKey2, nKey2)<0 ); assert( assert_delete_ranges_match(db) ); #if 0 static int nCall = 0; printf("\n"); nCall++; printf("%d delete %s .. %s\n", nCall, (char *)pKey1, (char *)pKey2); dump_tree_contents(db, "before delete"); #endif /* Step 1. This loop runs until the tree contains no keys within the ** range being deleted. Or until an error occurs. */ while( bDone==0 && rc==LSM_OK ){ int res; TreeCursor csr; /* Cursor to seek to first key in range */ void *pDel; int nDel; /* Key to (possibly) delete this iteration */ #ifndef NDEBUG int nEntry = treeCountEntries(db); #endif /* Seek the cursor to the first entry in the tree greater than pKey1. */ treeCursorInit(db, 0, &csr); lsmTreeCursorSeek(&csr, pKey1, nKey1, &res); if( res<=0 && lsmTreeCursorValid(&csr) ) lsmTreeCursorNext(&csr); /* If there is no such entry, or if it is greater than pKey2, then the ** tree now contains no keys in the range being deleted. In this case ** break out of the loop. */ bDone = 1; if( lsmTreeCursorValid(&csr) ){ lsmTreeCursorKey(&csr, 0, &pDel, &nDel); if( treeKeycmp(pDel, nDel, pKey2, nKey2)<0 ) bDone = 0; } if( bDone==0 ){ if( (u32)csr.iNode==(p->nHeight-1) ){ /* The element to delete already lies on a leaf node */ rc = treeDeleteEntry(db, &csr, 0); }else{ /* 1. Overwrite the current key with a copy of the next key in the ** tree (key N). ** ** 2. Seek to key N (cursor will stop at the internal node copy of ** N). Move to the next key (original copy of N). Delete ** this entry. */ u32 iKey; TreeKey *pKey; int iNode = csr.iNode; lsmTreeCursorNext(&csr); assert( (u32)csr.iNode==(p->nHeight-1) ); iKey = csr.apTreeNode[csr.iNode]->aiKeyPtr[csr.aiCell[csr.iNode]]; lsmTreeCursorPrev(&csr); treeOverwriteKey(db, &csr, iKey, &rc); pKey = treeShmkey(db, iKey, TKV_LOADKEY, &blob, &rc); if( pKey ){ rc = lsmTreeCursorSeek(&csr, TKV_KEY(pKey), pKey->nKey, &res); } if( rc==LSM_OK ){ assert( res==0 && csr.iNode==iNode ); rc = lsmTreeCursorNext(&csr); if( rc==LSM_OK ){ rc = treeDeleteEntry(db, &csr, 0); } } } } /* Clean up any memory allocated by the cursor. */ tblobFree(db, &csr.blob); #if 0 dump_tree_contents(db, "ddd delete"); #endif assert( bDone || treeCountEntries(db)==(nEntry-1) ); } #if 0 dump_tree_contents(db, "during delete"); #endif /* Now insert the START_DELETE and END_DELETE keys. */ if( rc==LSM_OK ){ rc = treeInsertEntry(db, LSM_START_DELETE, pKey1, nKey1, 0, -1); } #if 0 dump_tree_contents(db, "during delete 2"); #endif if( rc==LSM_OK ){ rc = treeInsertEntry(db, LSM_END_DELETE, pKey2, nKey2, 0, -1); } #if 0 dump_tree_contents(db, "after delete"); #endif tblobFree(db, &blob); assert( assert_delete_ranges_match(db) ); return rc; } /* ** Return, in bytes, the amount of memory currently used by the tree ** structure. */ int lsmTreeSize(lsm_db *pDb){ return pDb->treehdr.root.nByte; } /* ** Open a cursor on the in-memory tree pTree. */ int lsmTreeCursorNew(lsm_db *pDb, int bOld, TreeCursor **ppCsr){ TreeCursor *pCsr; *ppCsr = pCsr = lsmMalloc(pDb->pEnv, sizeof(TreeCursor)); if( pCsr ){ treeCursorInit(pDb, bOld, pCsr); return LSM_OK; } return LSM_NOMEM_BKPT; } /* ** Close an in-memory tree cursor. */ void lsmTreeCursorDestroy(TreeCursor *pCsr){ if( pCsr ){ tblobFree(pCsr->pDb, &pCsr->blob); lsmFree(pCsr->pDb->pEnv, pCsr); } } void lsmTreeCursorReset(TreeCursor *pCsr){ if( pCsr ){ pCsr->iNode = -1; pCsr->pSave = 0; } } #ifndef NDEBUG static int treeCsrCompare(TreeCursor *pCsr, void *pKey, int nKey, int *pRc){ TreeKey *p; int cmp = 0; assert( pCsr->iNode>=0 ); p = csrGetKey(pCsr, &pCsr->blob, pRc); if( p ){ cmp = treeKeycmp(TKV_KEY(p), p->nKey, pKey, nKey); } return cmp; } #endif /* ** Attempt to seek the cursor passed as the first argument to key (pKey/nKey) ** in the tree structure. If an exact match for the key is found, leave the ** cursor pointing to it and set *pRes to zero before returning. If an ** exact match cannot be found, do one of the following: ** ** * Leave the cursor pointing to the smallest element in the tree that ** is larger than the key and set *pRes to +1, or ** ** * Leave the cursor pointing to the largest element in the tree that ** is smaller than the key and set *pRes to -1, or ** ** * If the tree is empty, leave the cursor at EOF and set *pRes to -1. */ int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes){ int rc = LSM_OK; /* Return code */ lsm_db *pDb = pCsr->pDb; TreeRoot *pRoot = pCsr->pRoot; u32 iNodePtr; /* Location of current node in search */ /* Discard any saved position data */ treeCursorRestore(pCsr, 0); iNodePtr = pRoot->iRoot; if( iNodePtr==0 ){ /* Either an error occurred or the tree is completely empty. */ assert( rc!=LSM_OK || pRoot->iRoot==0 ); *pRes = -1; pCsr->iNode = -1; }else{ TreeBlob b = {0, 0}; int res = 0; /* Result of comparison function */ int iNode = -1; while( iNodePtr ){ TreeNode *pNode; /* Node at location iNodePtr */ int iTest; /* Index of second key to test (0 or 2) */ u32 iTreeKey; TreeKey *pTreeKey; /* Key to compare against */ pNode = (TreeNode *)treeShmptrUnsafe(pDb, iNodePtr); iNode++; pCsr->apTreeNode[iNode] = pNode; /* Compare (pKey/nKey) with the key in the middle slot of B-tree node ** pNode. The middle slot is never empty. If the comparison is a match, ** then the search is finished. Break out of the loop. */ pTreeKey = (TreeKey*)treeShmptrUnsafe(pDb, pNode->aiKeyPtr[1]); if( !(pTreeKey->flags & LSM_CONTIGUOUS) ){ pTreeKey = treeShmkey(pDb, pNode->aiKeyPtr[1], TKV_LOADKEY, &b, &rc); if( rc!=LSM_OK ) break; } res = treeKeycmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey); if( res==0 ){ pCsr->aiCell[iNode] = 1; break; } /* Based on the results of the previous comparison, compare (pKey/nKey) ** to either the left or right key of the B-tree node, if such a key ** exists. */ iTest = (res>0 ? 0 : 2); iTreeKey = pNode->aiKeyPtr[iTest]; if( iTreeKey ){ pTreeKey = (TreeKey*)treeShmptrUnsafe(pDb, iTreeKey); if( !(pTreeKey->flags & LSM_CONTIGUOUS) ){ pTreeKey = treeShmkey(pDb, iTreeKey, TKV_LOADKEY, &b, &rc); if( rc ) break; } res = treeKeycmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey); if( res==0 ){ pCsr->aiCell[iNode] = (u8)iTest; break; } }else{ iTest = 1; } if( (u32)iNode<(pRoot->nHeight-1) ){ iNodePtr = getChildPtr(pNode, pRoot->iTransId, iTest + (res<0)); }else{ iNodePtr = 0; } pCsr->aiCell[iNode] = (u8)(iTest + (iNodePtr && (res<0))); } *pRes = res; pCsr->iNode = iNode; tblobFree(pDb, &b); } /* assert() that *pRes has been set properly */ #ifndef NDEBUG if( rc==LSM_OK && lsmTreeCursorValid(pCsr) ){ int cmp = treeCsrCompare(pCsr, pKey, nKey, &rc); assert( rc!=LSM_OK || *pRes==cmp || (*pRes ^ cmp)>0 ); } #endif return rc; } int lsmTreeCursorNext(TreeCursor *pCsr){ #ifndef NDEBUG TreeKey *pK1; TreeBlob key1 = {0, 0}; #endif lsm_db *pDb = pCsr->pDb; TreeRoot *pRoot = pCsr->pRoot; const int iLeaf = pRoot->nHeight-1; int iCell; int rc = LSM_OK; TreeNode *pNode; /* Restore the cursor position, if required */ int iRestore = 0; treeCursorRestore(pCsr, &iRestore); if( iRestore>0 ) return LSM_OK; /* Save a pointer to the current key. This is used in an assert() at the ** end of this function - to check that the 'next' key really is larger ** than the current key. */ #ifndef NDEBUG pK1 = csrGetKey(pCsr, &key1, &rc); if( rc!=LSM_OK ) return rc; #endif assert( lsmTreeCursorValid(pCsr) ); assert( pCsr->aiCell[pCsr->iNode]<3 ); pNode = pCsr->apTreeNode[pCsr->iNode]; iCell = ++pCsr->aiCell[pCsr->iNode]; /* If the current node is not a leaf, and the current cell has sub-tree ** associated with it, descend to the left-most key on the left-most ** leaf of the sub-tree. */ if( pCsr->iNode<iLeaf && getChildPtr(pNode, pRoot->iTransId, iCell) ){ do { u32 iNodePtr; pCsr->iNode++; iNodePtr = getChildPtr(pNode, pRoot->iTransId, iCell); pNode = (TreeNode *)treeShmptr(pDb, iNodePtr); pCsr->apTreeNode[pCsr->iNode] = pNode; iCell = pCsr->aiCell[pCsr->iNode] = (pNode->aiKeyPtr[0]==0); }while( pCsr->iNode < iLeaf ); } /* Otherwise, the next key is found by following pointer up the tree ** until there is a key immediately to the right of the pointer followed ** to reach the sub-tree containing the current key. */ else if( iCell>=3 || pNode->aiKeyPtr[iCell]==0 ){ while( (--pCsr->iNode)>=0 ){ iCell = pCsr->aiCell[pCsr->iNode]; if( iCell<3 && pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[iCell] ) break; } } #ifndef NDEBUG if( pCsr->iNode>=0 ){ TreeKey *pK2 = csrGetKey(pCsr, &pCsr->blob, &rc); assert( rc||treeKeycmp(TKV_KEY(pK2),pK2->nKey,TKV_KEY(pK1),pK1->nKey)>=0 ); } tblobFree(pDb, &key1); #endif return rc; } int lsmTreeCursorPrev(TreeCursor *pCsr){ #ifndef NDEBUG TreeKey *pK1; TreeBlob key1 = {0, 0}; #endif lsm_db *pDb = pCsr->pDb; TreeRoot *pRoot = pCsr->pRoot; const int iLeaf = pRoot->nHeight-1; int iCell; int rc = LSM_OK; TreeNode *pNode; /* Restore the cursor position, if required */ int iRestore = 0; treeCursorRestore(pCsr, &iRestore); if( iRestore<0 ) return LSM_OK; /* Save a pointer to the current key. This is used in an assert() at the ** end of this function - to check that the 'next' key really is smaller ** than the current key. */ #ifndef NDEBUG pK1 = csrGetKey(pCsr, &key1, &rc); if( rc!=LSM_OK ) return rc; #endif assert( lsmTreeCursorValid(pCsr) ); pNode = pCsr->apTreeNode[pCsr->iNode]; iCell = pCsr->aiCell[pCsr->iNode]; assert( iCell>=0 && iCell<3 ); /* If the current node is not a leaf, and the current cell has sub-tree ** associated with it, descend to the right-most key on the right-most ** leaf of the sub-tree. */ if( pCsr->iNode<iLeaf && getChildPtr(pNode, pRoot->iTransId, iCell) ){ do { u32 iNodePtr; pCsr->iNode++; iNodePtr = getChildPtr(pNode, pRoot->iTransId, iCell); pNode = (TreeNode *)treeShmptr(pDb, iNodePtr); if( rc!=LSM_OK ) break; pCsr->apTreeNode[pCsr->iNode] = pNode; iCell = 1 + (pNode->aiKeyPtr[2]!=0) + (pCsr->iNode < iLeaf); pCsr->aiCell[pCsr->iNode] = (u8)iCell; }while( pCsr->iNode < iLeaf ); } /* Otherwise, the next key is found by following pointer up the tree until ** there is a key immediately to the left of the pointer followed to reach ** the sub-tree containing the current key. */ else{ do { iCell = pCsr->aiCell[pCsr->iNode]-1; if( iCell>=0 && pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[iCell] ) break; }while( (--pCsr->iNode)>=0 ); pCsr->aiCell[pCsr->iNode] = (u8)iCell; } #ifndef NDEBUG if( pCsr->iNode>=0 ){ TreeKey *pK2 = csrGetKey(pCsr, &pCsr->blob, &rc); assert( rc || treeKeycmp(TKV_KEY(pK2),pK2->nKey,TKV_KEY(pK1),pK1->nKey)<0 ); } tblobFree(pDb, &key1); #endif return rc; } /* ** Move the cursor to the first (bLast==0) or last (bLast!=0) entry in the ** in-memory tree. */ int lsmTreeCursorEnd(TreeCursor *pCsr, int bLast){ lsm_db *pDb = pCsr->pDb; TreeRoot *pRoot = pCsr->pRoot; int rc = LSM_OK; u32 iNodePtr; pCsr->iNode = -1; /* Discard any saved position data */ treeCursorRestore(pCsr, 0); iNodePtr = pRoot->iRoot; while( iNodePtr ){ int iCell; TreeNode *pNode; pNode = (TreeNode *)treeShmptr(pDb, iNodePtr); if( rc ) break; if( bLast ){ iCell = ((pNode->aiKeyPtr[2]==0) ? 2 : 3); }else{ iCell = ((pNode->aiKeyPtr[0]==0) ? 1 : 0); } pCsr->iNode++; pCsr->apTreeNode[pCsr->iNode] = pNode; if( (u32)pCsr->iNode<pRoot->nHeight-1 ){ iNodePtr = getChildPtr(pNode, pRoot->iTransId, iCell); }else{ iNodePtr = 0; } pCsr->aiCell[pCsr->iNode] = (u8)(iCell - (iNodePtr==0 && bLast)); } return rc; } int lsmTreeCursorFlags(TreeCursor *pCsr){ int flags = 0; if( pCsr && pCsr->iNode>=0 ){ int rc = LSM_OK; TreeKey *pKey = (TreeKey *)treeShmptrUnsafe(pCsr->pDb, pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[pCsr->aiCell[pCsr->iNode]] ); assert( rc==LSM_OK ); flags = (pKey->flags & ~LSM_CONTIGUOUS); } return flags; } int lsmTreeCursorKey(TreeCursor *pCsr, int *pFlags, void **ppKey, int *pnKey){ TreeKey *pTreeKey; int rc = LSM_OK; assert( lsmTreeCursorValid(pCsr) ); pTreeKey = pCsr->pSave; if( !pTreeKey ){ pTreeKey = csrGetKey(pCsr, &pCsr->blob, &rc); } if( rc==LSM_OK ){ *pnKey = pTreeKey->nKey; if( pFlags ) *pFlags = pTreeKey->flags; *ppKey = (void *)&pTreeKey[1]; } return rc; } int lsmTreeCursorValue(TreeCursor *pCsr, void **ppVal, int *pnVal){ int res = 0; int rc; rc = treeCursorRestore(pCsr, &res); if( res==0 ){ TreeKey *pTreeKey = csrGetKey(pCsr, &pCsr->blob, &rc); if( rc==LSM_OK ){ if( pTreeKey->flags & LSM_INSERT ){ *pnVal = pTreeKey->nValue; *ppVal = TKV_VAL(pTreeKey); }else{ *ppVal = 0; *pnVal = -1; } } }else{ *ppVal = 0; *pnVal = 0; } return rc; } /* ** Return true if the cursor currently points to a valid entry. */ int lsmTreeCursorValid(TreeCursor *pCsr){ return (pCsr && (pCsr->pSave || pCsr->iNode>=0)); } /* ** Store a mark in *pMark. Later on, a call to lsmTreeRollback() with a ** pointer to the same TreeMark structure may be used to roll the tree ** contents back to their current state. */ void lsmTreeMark(lsm_db *pDb, TreeMark *pMark){ pMark->iRoot = pDb->treehdr.root.iRoot; pMark->nHeight = pDb->treehdr.root.nHeight; pMark->iWrite = pDb->treehdr.iWrite; pMark->nChunk = pDb->treehdr.nChunk; pMark->iNextShmid = pDb->treehdr.iNextShmid; pMark->iRollback = intArraySize(&pDb->rollback); } /* ** Roll back to mark pMark. Structure *pMark should have been previously ** populated by a call to lsmTreeMark(). */ void lsmTreeRollback(lsm_db *pDb, TreeMark *pMark){ int iIdx; int nIdx; u32 iNext; ShmChunk *pChunk; u32 iChunk; u32 iShmid; /* Revert all required v2 pointers. */ nIdx = intArraySize(&pDb->rollback); for(iIdx = pMark->iRollback; iIdx<nIdx; iIdx++){ TreeNode *pNode; pNode = treeShmptr(pDb, intArrayEntry(&pDb->rollback, iIdx)); assert( pNode ); pNode->iV2 = 0; pNode->iV2Child = 0; pNode->iV2Ptr = 0; } intArrayTruncate(&pDb->rollback, pMark->iRollback); /* Restore the free-chunk list. */ assert( pMark->iWrite!=0 ); iChunk = treeOffsetToChunk(pMark->iWrite-1); pChunk = treeShmChunk(pDb, iChunk); iNext = pChunk->iNext; pChunk->iNext = 0; pChunk = treeShmChunk(pDb, pDb->treehdr.iFirst); iShmid = pChunk->iShmid-1; while( iNext ){ u32 iFree = iNext; /* Current chunk being rollback-freed */ ShmChunk *pFree; /* Pointer to chunk iFree */ pFree = treeShmChunk(pDb, iFree); iNext = pFree->iNext; if( iFree<pMark->nChunk ){ pFree->iNext = pDb->treehdr.iFirst; pFree->iShmid = iShmid--; pDb->treehdr.iFirst = iFree; } } /* Restore the tree-header fields */ pDb->treehdr.root.iRoot = pMark->iRoot; pDb->treehdr.root.nHeight = pMark->nHeight; pDb->treehdr.iWrite = pMark->iWrite; pDb->treehdr.nChunk = pMark->nChunk; pDb->treehdr.iNextShmid = pMark->iNextShmid; } /* ** Load the in-memory tree header from shared-memory into pDb->treehdr. ** If the header cannot be loaded, return LSM_PROTOCOL. ** ** If the header is successfully loaded and parameter piRead is not NULL, ** is is set to 1 if the header was loaded from ShmHeader.hdr1, or 2 if ** the header was loaded from ShmHeader.hdr2. */ int lsmTreeLoadHeader(lsm_db *pDb, int *piRead){ int nRem = LSM_ATTEMPTS_BEFORE_PROTOCOL; while( (nRem--)>0 ){ ShmHeader *pShm = pDb->pShmhdr; memcpy(&pDb->treehdr, &pShm->hdr1, sizeof(TreeHeader)); if( treeHeaderChecksumOk(&pDb->treehdr) ){ if( piRead ) *piRead = 1; return LSM_OK; } memcpy(&pDb->treehdr, &pShm->hdr2, sizeof(TreeHeader)); if( treeHeaderChecksumOk(&pDb->treehdr) ){ if( piRead ) *piRead = 2; return LSM_OK; } lsmShmBarrier(pDb); } return LSM_PROTOCOL_BKPT; } int lsmTreeLoadHeaderOk(lsm_db *pDb, int iRead){ TreeHeader *p = (iRead==1) ? &pDb->pShmhdr->hdr1 : &pDb->pShmhdr->hdr2; assert( iRead==1 || iRead==2 ); return (0==memcmp(pDb->treehdr.aCksum, p->aCksum, sizeof(u32)*2)); } /* ** This function is called to conclude a transaction. If argument bCommit ** is true, the transaction is committed. Otherwise it is rolled back. */ int lsmTreeEndTransaction(lsm_db *pDb, int bCommit){ ShmHeader *pShm = pDb->pShmhdr; treeHeaderChecksum(&pDb->treehdr, pDb->treehdr.aCksum); memcpy(&pShm->hdr2, &pDb->treehdr, sizeof(TreeHeader)); lsmShmBarrier(pDb); memcpy(&pShm->hdr1, &pDb->treehdr, sizeof(TreeHeader)); pShm->bWriter = 0; intArrayFree(pDb->pEnv, &pDb->rollback); return LSM_OK; } #ifndef NDEBUG static int assert_delete_ranges_match(lsm_db *db){ int prev = 0; TreeBlob blob = {0, 0}; TreeCursor csr; /* Cursor used to iterate through tree */ int rc; treeCursorInit(db, 0, &csr); for( rc = lsmTreeCursorEnd(&csr, 0); rc==LSM_OK && lsmTreeCursorValid(&csr); rc = lsmTreeCursorNext(&csr) ){ TreeKey *pKey = csrGetKey(&csr, &blob, &rc); if( rc!=LSM_OK ) break; assert( ((prev&LSM_START_DELETE)==0)==((pKey->flags&LSM_END_DELETE)==0) ); prev = pKey->flags; } tblobFree(csr.pDb, &csr.blob); tblobFree(csr.pDb, &blob); return 1; } static int treeCountEntries(lsm_db *db){ TreeCursor csr; /* Cursor used to iterate through tree */ int rc; int nEntry = 0; treeCursorInit(db, 0, &csr); for( rc = lsmTreeCursorEnd(&csr, 0); rc==LSM_OK && lsmTreeCursorValid(&csr); rc = lsmTreeCursorNext(&csr) ){ nEntry++; } tblobFree(csr.pDb, &csr.blob); return nEntry; } #endif |
Added ext/lsm1/lsm_unix.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 | /* ** 2011-12-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. ** ************************************************************************* ** ** Unix-specific run-time environment implementation for LSM. */ #ifndef _WIN32 #if defined(__GNUC__) || defined(__TINYC__) /* workaround for ftruncate() visibility on gcc. */ # ifndef _XOPEN_SOURCE # define _XOPEN_SOURCE 500 # endif #endif #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <assert.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #include <stdio.h> #include <ctype.h> #include <unistd.h> #include <errno.h> #include <sys/mman.h> #include "lsmInt.h" /* There is no fdatasync() call on Android */ #ifdef __ANDROID__ # define fdatasync(x) fsync(x) #endif /* ** An open file is an instance of the following object */ typedef struct PosixFile PosixFile; struct PosixFile { lsm_env *pEnv; /* The run-time environment */ const char *zName; /* Full path to file */ int fd; /* The open file descriptor */ int shmfd; /* Shared memory file-descriptor */ void *pMap; /* Pointer to mapping of file fd */ off_t nMap; /* Size of mapping at pMap in bytes */ int nShm; /* Number of entries in array apShm[] */ void **apShm; /* Array of 32K shared memory segments */ }; static char *posixShmFile(PosixFile *p){ char *zShm; int nName = strlen(p->zName); zShm = (char *)lsmMalloc(p->pEnv, nName+4+1); if( zShm ){ memcpy(zShm, p->zName, nName); memcpy(&zShm[nName], "-shm", 5); } return zShm; } static int lsmPosixOsOpen( lsm_env *pEnv, const char *zFile, int flags, lsm_file **ppFile ){ int rc = LSM_OK; PosixFile *p; p = lsm_malloc(pEnv, sizeof(PosixFile)); if( p==0 ){ rc = LSM_NOMEM; }else{ int bReadonly = (flags & LSM_OPEN_READONLY); int oflags = (bReadonly ? O_RDONLY : (O_RDWR|O_CREAT)); memset(p, 0, sizeof(PosixFile)); p->zName = zFile; p->pEnv = pEnv; p->fd = open(zFile, oflags, 0644); if( p->fd<0 ){ lsm_free(pEnv, p); p = 0; if( errno==ENOENT ){ rc = lsmErrorBkpt(LSM_IOERR_NOENT); }else{ rc = LSM_IOERR_BKPT; } } } *ppFile = (lsm_file *)p; return rc; } static int lsmPosixOsWrite( lsm_file *pFile, /* File to write to */ lsm_i64 iOff, /* Offset to write to */ void *pData, /* Write data from this buffer */ int nData /* Bytes of data to write */ ){ int rc = LSM_OK; PosixFile *p = (PosixFile *)pFile; off_t offset; offset = lseek(p->fd, (off_t)iOff, SEEK_SET); if( offset!=iOff ){ rc = LSM_IOERR_BKPT; }else{ ssize_t prc = write(p->fd, pData, (size_t)nData); if( prc<0 ) rc = LSM_IOERR_BKPT; } return rc; } static int lsmPosixOsTruncate( lsm_file *pFile, /* File to write to */ lsm_i64 nSize /* Size to truncate file to */ ){ PosixFile *p = (PosixFile *)pFile; int rc = LSM_OK; /* Return code */ int prc; /* Posix Return Code */ struct stat sStat; /* Result of fstat() invocation */ prc = fstat(p->fd, &sStat); if( prc==0 && sStat.st_size>nSize ){ prc = ftruncate(p->fd, (off_t)nSize); } if( prc<0 ) rc = LSM_IOERR_BKPT; return rc; } static int lsmPosixOsRead( lsm_file *pFile, /* File to read from */ lsm_i64 iOff, /* Offset to read from */ void *pData, /* Read data into this buffer */ int nData /* Bytes of data to read */ ){ int rc = LSM_OK; PosixFile *p = (PosixFile *)pFile; off_t offset; offset = lseek(p->fd, (off_t)iOff, SEEK_SET); if( offset!=iOff ){ rc = LSM_IOERR_BKPT; }else{ ssize_t prc = read(p->fd, pData, (size_t)nData); if( prc<0 ){ rc = LSM_IOERR_BKPT; }else if( prc<nData ){ memset(&((u8 *)pData)[prc], 0, nData - prc); } } return rc; } static int lsmPosixOsSync(lsm_file *pFile){ int rc = LSM_OK; #ifndef LSM_NO_SYNC PosixFile *p = (PosixFile *)pFile; int prc = 0; if( p->pMap ){ prc = msync(p->pMap, p->nMap, MS_SYNC); } if( prc==0 ) prc = fdatasync(p->fd); if( prc<0 ) rc = LSM_IOERR_BKPT; #else (void)pFile; #endif return rc; } static int lsmPosixOsSectorSize(lsm_file *pFile){ return 512; } static int lsmPosixOsRemap( lsm_file *pFile, lsm_i64 iMin, void **ppOut, lsm_i64 *pnOut ){ off_t iSz; int prc; PosixFile *p = (PosixFile *)pFile; struct stat buf; /* If the file is between 0 and 2MB in size, extend it in chunks of 256K. ** Thereafter, in chunks of 1MB at a time. */ const int aIncrSz[] = {256*1024, 1024*1024}; int nIncrSz = aIncrSz[iMin>(2*1024*1024)]; if( p->pMap ){ munmap(p->pMap, p->nMap); *ppOut = p->pMap = 0; *pnOut = p->nMap = 0; } if( iMin>=0 ){ memset(&buf, 0, sizeof(buf)); prc = fstat(p->fd, &buf); if( prc!=0 ) return LSM_IOERR_BKPT; iSz = buf.st_size; if( iSz<iMin ){ iSz = ((iMin + nIncrSz-1) / nIncrSz) * nIncrSz; prc = ftruncate(p->fd, iSz); if( prc!=0 ) return LSM_IOERR_BKPT; } p->pMap = mmap(0, iSz, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0); p->nMap = iSz; } *ppOut = p->pMap; *pnOut = p->nMap; return LSM_OK; } static int lsmPosixOsFullpath( lsm_env *pEnv, const char *zName, char *zOut, int *pnOut ){ int nBuf = *pnOut; int nReq; if( zName[0]!='/' ){ char *z; char *zTmp; int nTmp = 512; zTmp = lsmMalloc(pEnv, nTmp); while( zTmp ){ z = getcwd(zTmp, nTmp); if( z || errno!=ERANGE ) break; nTmp = nTmp*2; zTmp = lsmReallocOrFree(pEnv, zTmp, nTmp); } if( zTmp==0 ) return LSM_NOMEM_BKPT; if( z==0 ) return LSM_IOERR_BKPT; assert( z==zTmp ); nTmp = strlen(zTmp); nReq = nTmp + 1 + strlen(zName) + 1; if( nReq<=nBuf ){ memcpy(zOut, zTmp, nTmp); zOut[nTmp] = '/'; memcpy(&zOut[nTmp+1], zName, strlen(zName)+1); } lsmFree(pEnv, zTmp); }else{ nReq = strlen(zName)+1; if( nReq<=nBuf ){ memcpy(zOut, zName, strlen(zName)+1); } } *pnOut = nReq; return LSM_OK; } static int lsmPosixOsFileid( lsm_file *pFile, void *pBuf, int *pnBuf ){ int prc; int nBuf; int nReq; PosixFile *p = (PosixFile *)pFile; struct stat buf; nBuf = *pnBuf; nReq = (sizeof(buf.st_dev) + sizeof(buf.st_ino)); *pnBuf = nReq; if( nReq>nBuf ) return LSM_OK; memset(&buf, 0, sizeof(buf)); prc = fstat(p->fd, &buf); if( prc!=0 ) return LSM_IOERR_BKPT; memcpy(pBuf, &buf.st_dev, sizeof(buf.st_dev)); memcpy(&(((u8 *)pBuf)[sizeof(buf.st_dev)]), &buf.st_ino, sizeof(buf.st_ino)); return LSM_OK; } static int lsmPosixOsUnlink(lsm_env *pEnv, const char *zFile){ int prc = unlink(zFile); return prc ? LSM_IOERR_BKPT : LSM_OK; } static int lsmPosixOsLock(lsm_file *pFile, int iLock, int eType){ int rc = LSM_OK; PosixFile *p = (PosixFile *)pFile; static const short aType[3] = { F_UNLCK, F_RDLCK, F_WRLCK }; struct flock lock; assert( aType[LSM_LOCK_UNLOCK]==F_UNLCK ); assert( aType[LSM_LOCK_SHARED]==F_RDLCK ); assert( aType[LSM_LOCK_EXCL]==F_WRLCK ); assert( eType>=0 && eType<array_size(aType) ); assert( iLock>0 && iLock<=32 ); memset(&lock, 0, sizeof(lock)); lock.l_whence = SEEK_SET; lock.l_len = 1; lock.l_type = aType[eType]; lock.l_start = (4096-iLock); if( fcntl(p->fd, F_SETLK, &lock) ){ int e = errno; if( e==EACCES || e==EAGAIN ){ rc = LSM_BUSY; }else{ rc = LSM_IOERR_BKPT; } } return rc; } static int lsmPosixOsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){ int rc = LSM_OK; PosixFile *p = (PosixFile *)pFile; static const short aType[3] = { 0, F_RDLCK, F_WRLCK }; struct flock lock; assert( eType==LSM_LOCK_SHARED || eType==LSM_LOCK_EXCL ); assert( aType[LSM_LOCK_SHARED]==F_RDLCK ); assert( aType[LSM_LOCK_EXCL]==F_WRLCK ); assert( eType>=0 && eType<array_size(aType) ); assert( iLock>0 && iLock<=32 ); memset(&lock, 0, sizeof(lock)); lock.l_whence = SEEK_SET; lock.l_len = nLock; lock.l_type = aType[eType]; lock.l_start = (4096-iLock-nLock+1); if( fcntl(p->fd, F_GETLK, &lock) ){ rc = LSM_IOERR_BKPT; }else if( lock.l_type!=F_UNLCK ){ rc = LSM_BUSY; } return rc; } static int lsmPosixOsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){ PosixFile *p = (PosixFile *)pFile; *ppShm = 0; assert( sz==LSM_SHM_CHUNK_SIZE ); if( iChunk>=p->nShm ){ int i; void **apNew; int nNew = iChunk+1; off_t nReq = nNew * LSM_SHM_CHUNK_SIZE; struct stat sStat; /* If the shared-memory file has not been opened, open it now. */ if( p->shmfd<=0 ){ char *zShm = posixShmFile(p); if( !zShm ) return LSM_NOMEM_BKPT; p->shmfd = open(zShm, O_RDWR|O_CREAT, 0644); lsmFree(p->pEnv, zShm); if( p->shmfd<0 ){ return LSM_IOERR_BKPT; } } /* If the shared-memory file is not large enough to contain the ** requested chunk, cause it to grow. */ if( fstat(p->shmfd, &sStat) ){ return LSM_IOERR_BKPT; } if( sStat.st_size<nReq ){ if( ftruncate(p->shmfd, nReq) ){ return LSM_IOERR_BKPT; } } apNew = (void **)lsmRealloc(p->pEnv, p->apShm, sizeof(void *) * nNew); if( !apNew ) return LSM_NOMEM_BKPT; for(i=p->nShm; i<nNew; i++){ apNew[i] = 0; } p->apShm = apNew; p->nShm = nNew; } if( p->apShm[iChunk]==0 ){ p->apShm[iChunk] = mmap(0, LSM_SHM_CHUNK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->shmfd, iChunk*LSM_SHM_CHUNK_SIZE ); if( p->apShm[iChunk]==0 ) return LSM_IOERR_BKPT; } *ppShm = p->apShm[iChunk]; return LSM_OK; } static void lsmPosixOsShmBarrier(void){ } static int lsmPosixOsShmUnmap(lsm_file *pFile, int bDelete){ PosixFile *p = (PosixFile *)pFile; if( p->shmfd>0 ){ int i; for(i=0; i<p->nShm; i++){ if( p->apShm[i] ){ munmap(p->apShm[i], LSM_SHM_CHUNK_SIZE); p->apShm[i] = 0; } } close(p->shmfd); p->shmfd = 0; if( bDelete ){ char *zShm = posixShmFile(p); if( zShm ) unlink(zShm); lsmFree(p->pEnv, zShm); } } return LSM_OK; } static int lsmPosixOsClose(lsm_file *pFile){ PosixFile *p = (PosixFile *)pFile; lsmPosixOsShmUnmap(pFile, 0); if( p->pMap ) munmap(p->pMap, p->nMap); close(p->fd); lsm_free(p->pEnv, p->apShm); lsm_free(p->pEnv, p); return LSM_OK; } static int lsmPosixOsSleep(lsm_env *pEnv, int us){ #if 0 /* Apparently on Android usleep() returns void */ if( usleep(us) ) return LSM_IOERR; #endif usleep(us); return LSM_OK; } /**************************************************************************** ** Memory allocation routines. */ #define BLOCK_HDR_SIZE ROUND8( sizeof(size_t) ) static void *lsmPosixOsMalloc(lsm_env *pEnv, size_t N){ unsigned char * m; N += BLOCK_HDR_SIZE; m = (unsigned char *)malloc(N); *((size_t*)m) = N; return m + BLOCK_HDR_SIZE; } static void lsmPosixOsFree(lsm_env *pEnv, void *p){ if(p){ free( ((unsigned char *)p) - BLOCK_HDR_SIZE ); } } static void *lsmPosixOsRealloc(lsm_env *pEnv, void *p, size_t N){ unsigned char * m = (unsigned char *)p; if(1>N){ lsmPosixOsFree( pEnv, p ); return NULL; }else if(NULL==p){ return lsmPosixOsMalloc(pEnv, N); }else{ void * re = NULL; m -= BLOCK_HDR_SIZE; #if 0 /* arguable: don't shrink */ size_t * sz = (size_t*)m; if(*sz >= (size_t)N){ return p; } #endif re = realloc( m, N + BLOCK_HDR_SIZE ); if(re){ m = (unsigned char *)re; *((size_t*)m) = N; return m + BLOCK_HDR_SIZE; }else{ return NULL; } } } static size_t lsmPosixOsMSize(lsm_env *pEnv, void *p){ unsigned char * m = (unsigned char *)p; return *((size_t*)(m-BLOCK_HDR_SIZE)); } #undef BLOCK_HDR_SIZE #ifdef LSM_MUTEX_PTHREADS /************************************************************************* ** Mutex methods for pthreads based systems. If LSM_MUTEX_PTHREADS is ** missing then a no-op implementation of mutexes found in lsm_mutex.c ** will be used instead. */ #include <pthread.h> typedef struct PthreadMutex PthreadMutex; struct PthreadMutex { lsm_env *pEnv; pthread_mutex_t mutex; #ifdef LSM_DEBUG pthread_t owner; #endif }; #ifdef LSM_DEBUG # define LSM_PTHREAD_STATIC_MUTEX { 0, PTHREAD_MUTEX_INITIALIZER, 0 } #else # define LSM_PTHREAD_STATIC_MUTEX { 0, PTHREAD_MUTEX_INITIALIZER } #endif static int lsmPosixOsMutexStatic( lsm_env *pEnv, int iMutex, lsm_mutex **ppStatic ){ static PthreadMutex sMutex[2] = { LSM_PTHREAD_STATIC_MUTEX, LSM_PTHREAD_STATIC_MUTEX }; assert( iMutex==LSM_MUTEX_GLOBAL || iMutex==LSM_MUTEX_HEAP ); assert( LSM_MUTEX_GLOBAL==1 && LSM_MUTEX_HEAP==2 ); *ppStatic = (lsm_mutex *)&sMutex[iMutex-1]; return LSM_OK; } static int lsmPosixOsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){ PthreadMutex *pMutex; /* Pointer to new mutex */ pthread_mutexattr_t attr; /* Attributes object */ pMutex = (PthreadMutex *)lsmMallocZero(pEnv, sizeof(PthreadMutex)); if( !pMutex ) return LSM_NOMEM_BKPT; pMutex->pEnv = pEnv; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&pMutex->mutex, &attr); pthread_mutexattr_destroy(&attr); *ppNew = (lsm_mutex *)pMutex; return LSM_OK; } static void lsmPosixOsMutexDel(lsm_mutex *p){ PthreadMutex *pMutex = (PthreadMutex *)p; pthread_mutex_destroy(&pMutex->mutex); lsmFree(pMutex->pEnv, pMutex); } static void lsmPosixOsMutexEnter(lsm_mutex *p){ PthreadMutex *pMutex = (PthreadMutex *)p; pthread_mutex_lock(&pMutex->mutex); #ifdef LSM_DEBUG assert( !pthread_equal(pMutex->owner, pthread_self()) ); pMutex->owner = pthread_self(); assert( pthread_equal(pMutex->owner, pthread_self()) ); #endif } static int lsmPosixOsMutexTry(lsm_mutex *p){ int ret; PthreadMutex *pMutex = (PthreadMutex *)p; ret = pthread_mutex_trylock(&pMutex->mutex); #ifdef LSM_DEBUG if( ret==0 ){ assert( !pthread_equal(pMutex->owner, pthread_self()) ); pMutex->owner = pthread_self(); assert( pthread_equal(pMutex->owner, pthread_self()) ); } #endif return ret; } static void lsmPosixOsMutexLeave(lsm_mutex *p){ PthreadMutex *pMutex = (PthreadMutex *)p; #ifdef LSM_DEBUG assert( pthread_equal(pMutex->owner, pthread_self()) ); pMutex->owner = 0; assert( !pthread_equal(pMutex->owner, pthread_self()) ); #endif pthread_mutex_unlock(&pMutex->mutex); } #ifdef LSM_DEBUG static int lsmPosixOsMutexHeld(lsm_mutex *p){ PthreadMutex *pMutex = (PthreadMutex *)p; return pMutex ? pthread_equal(pMutex->owner, pthread_self()) : 1; } static int lsmPosixOsMutexNotHeld(lsm_mutex *p){ PthreadMutex *pMutex = (PthreadMutex *)p; return pMutex ? !pthread_equal(pMutex->owner, pthread_self()) : 1; } #endif /* ** End of pthreads mutex implementation. *************************************************************************/ #else /************************************************************************* ** Noop mutex implementation */ typedef struct NoopMutex NoopMutex; struct NoopMutex { lsm_env *pEnv; /* Environment handle (for xFree()) */ int bHeld; /* True if mutex is held */ int bStatic; /* True for a static mutex */ }; static NoopMutex aStaticNoopMutex[2] = { {0, 0, 1}, {0, 0, 1}, }; static int lsmPosixOsMutexStatic( lsm_env *pEnv, int iMutex, lsm_mutex **ppStatic ){ assert( iMutex>=1 && iMutex<=(int)array_size(aStaticNoopMutex) ); *ppStatic = (lsm_mutex *)&aStaticNoopMutex[iMutex-1]; return LSM_OK; } static int lsmPosixOsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){ NoopMutex *p; p = (NoopMutex *)lsmMallocZero(pEnv, sizeof(NoopMutex)); if( p ) p->pEnv = pEnv; *ppNew = (lsm_mutex *)p; return (p ? LSM_OK : LSM_NOMEM_BKPT); } static void lsmPosixOsMutexDel(lsm_mutex *pMutex) { NoopMutex *p = (NoopMutex *)pMutex; assert( p->bStatic==0 && p->pEnv ); lsmFree(p->pEnv, p); } static void lsmPosixOsMutexEnter(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; assert( p->bHeld==0 ); p->bHeld = 1; } static int lsmPosixOsMutexTry(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; assert( p->bHeld==0 ); p->bHeld = 1; return 0; } static void lsmPosixOsMutexLeave(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; assert( p->bHeld==1 ); p->bHeld = 0; } #ifdef LSM_DEBUG static int lsmPosixOsMutexHeld(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; return p ? p->bHeld : 1; } static int lsmPosixOsMutexNotHeld(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; return p ? !p->bHeld : 1; } #endif /***************************************************************************/ #endif /* else LSM_MUTEX_NONE */ /* Without LSM_DEBUG, the MutexHeld tests are never called */ #ifndef LSM_DEBUG # define lsmPosixOsMutexHeld 0 # define lsmPosixOsMutexNotHeld 0 #endif lsm_env *lsm_default_env(void){ static lsm_env posix_env = { sizeof(lsm_env), /* nByte */ 1, /* iVersion */ /***** file i/o ******************/ 0, /* pVfsCtx */ lsmPosixOsFullpath, /* xFullpath */ lsmPosixOsOpen, /* xOpen */ lsmPosixOsRead, /* xRead */ lsmPosixOsWrite, /* xWrite */ lsmPosixOsTruncate, /* xTruncate */ lsmPosixOsSync, /* xSync */ lsmPosixOsSectorSize, /* xSectorSize */ lsmPosixOsRemap, /* xRemap */ lsmPosixOsFileid, /* xFileid */ lsmPosixOsClose, /* xClose */ lsmPosixOsUnlink, /* xUnlink */ lsmPosixOsLock, /* xLock */ lsmPosixOsTestLock, /* xTestLock */ lsmPosixOsShmMap, /* xShmMap */ lsmPosixOsShmBarrier, /* xShmBarrier */ lsmPosixOsShmUnmap, /* xShmUnmap */ /***** memory allocation *********/ 0, /* pMemCtx */ lsmPosixOsMalloc, /* xMalloc */ lsmPosixOsRealloc, /* xRealloc */ lsmPosixOsFree, /* xFree */ lsmPosixOsMSize, /* xSize */ /***** mutexes *********************/ 0, /* pMutexCtx */ lsmPosixOsMutexStatic, /* xMutexStatic */ lsmPosixOsMutexNew, /* xMutexNew */ lsmPosixOsMutexDel, /* xMutexDel */ lsmPosixOsMutexEnter, /* xMutexEnter */ lsmPosixOsMutexTry, /* xMutexTry */ lsmPosixOsMutexLeave, /* xMutexLeave */ lsmPosixOsMutexHeld, /* xMutexHeld */ lsmPosixOsMutexNotHeld, /* xMutexNotHeld */ /***** other *********************/ lsmPosixOsSleep, /* xSleep */ }; return &posix_env; } #endif |
Added ext/lsm1/lsm_varint.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 | /* ** 2012-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. ** ************************************************************************* ** ** SQLite4-compatible varint implementation. */ #include "lsmInt.h" /************************************************************************* ** The following is a copy of the varint.c module from SQLite 4. */ /* ** Decode the varint in z[]. Write the integer value into *pResult and ** return the number of bytes in the varint. */ static int lsmSqlite4GetVarint64(const unsigned char *z, u64 *pResult){ unsigned int x; if( z[0]<=240 ){ *pResult = z[0]; return 1; } if( z[0]<=248 ){ *pResult = (z[0]-241)*256 + z[1] + 240; return 2; } if( z[0]==249 ){ *pResult = 2288 + 256*z[1] + z[2]; return 3; } if( z[0]==250 ){ *pResult = (z[1]<<16) + (z[2]<<8) + z[3]; return 4; } x = (z[1]<<24) + (z[2]<<16) + (z[3]<<8) + z[4]; if( z[0]==251 ){ *pResult = x; return 5; } if( z[0]==252 ){ *pResult = (((u64)x)<<8) + z[5]; return 6; } if( z[0]==253 ){ *pResult = (((u64)x)<<16) + (z[5]<<8) + z[6]; return 7; } if( z[0]==254 ){ *pResult = (((u64)x)<<24) + (z[5]<<16) + (z[6]<<8) + z[7]; return 8; } *pResult = (((u64)x)<<32) + (0xffffffff & ((z[5]<<24) + (z[6]<<16) + (z[7]<<8) + z[8])); return 9; } /* ** Write a 32-bit unsigned integer as 4 big-endian bytes. */ static void lsmVarintWrite32(unsigned char *z, unsigned int y){ z[0] = (unsigned char)(y>>24); z[1] = (unsigned char)(y>>16); z[2] = (unsigned char)(y>>8); z[3] = (unsigned char)(y); } /* ** Write a varint into z[]. The buffer z[] must be at least 9 characters ** long to accommodate the largest possible varint. Return the number of ** bytes of z[] used. */ static int lsmSqlite4PutVarint64(unsigned char *z, u64 x){ unsigned int w, y; if( x<=240 ){ z[0] = (unsigned char)x; return 1; } if( x<=2287 ){ y = (unsigned int)(x - 240); z[0] = (unsigned char)(y/256 + 241); z[1] = (unsigned char)(y%256); return 2; } if( x<=67823 ){ y = (unsigned int)(x - 2288); z[0] = 249; z[1] = (unsigned char)(y/256); z[2] = (unsigned char)(y%256); return 3; } y = (unsigned int)x; w = (unsigned int)(x>>32); if( w==0 ){ if( y<=16777215 ){ z[0] = 250; z[1] = (unsigned char)(y>>16); z[2] = (unsigned char)(y>>8); z[3] = (unsigned char)(y); return 4; } z[0] = 251; lsmVarintWrite32(z+1, y); return 5; } if( w<=255 ){ z[0] = 252; z[1] = (unsigned char)w; lsmVarintWrite32(z+2, y); return 6; } if( w<=32767 ){ z[0] = 253; z[1] = (unsigned char)(w>>8); z[2] = (unsigned char)w; lsmVarintWrite32(z+3, y); return 7; } if( w<=16777215 ){ z[0] = 254; z[1] = (unsigned char)(w>>16); z[2] = (unsigned char)(w>>8); z[3] = (unsigned char)w; lsmVarintWrite32(z+4, y); return 8; } z[0] = 255; lsmVarintWrite32(z+1, w); lsmVarintWrite32(z+5, y); return 9; } /* ** End of SQLite 4 code. *************************************************************************/ int lsmVarintPut64(u8 *aData, i64 iVal){ return lsmSqlite4PutVarint64(aData, (u64)iVal); } int lsmVarintGet64(const u8 *aData, i64 *piVal){ return lsmSqlite4GetVarint64(aData, (u64 *)piVal); } int lsmVarintPut32(u8 *aData, int iVal){ return lsmSqlite4PutVarint64(aData, (u64)iVal); } int lsmVarintGet32(u8 *z, int *piVal){ u64 i; int ret; if( z[0]<=240 ){ *piVal = z[0]; return 1; } if( z[0]<=248 ){ *piVal = (z[0]-241)*256 + z[1] + 240; return 2; } if( z[0]==249 ){ *piVal = 2288 + 256*z[1] + z[2]; return 3; } if( z[0]==250 ){ *piVal = (z[1]<<16) + (z[2]<<8) + z[3]; return 4; } ret = lsmSqlite4GetVarint64(z, &i); *piVal = (int)i; return ret; } int lsmVarintLen32(int n){ u8 aData[9]; return lsmVarintPut32(aData, n); } /* ** The argument is the first byte of a varint. This function returns the ** total number of bytes in the entire varint (including the first byte). */ int lsmVarintSize(u8 c){ if( c<241 ) return 1; if( c<249 ) return 2; return (int)(c - 246); } |
Added ext/lsm1/lsm_vtab.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 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 | /* ** 2015-11-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 implements a virtual table for SQLite3 around the LSM ** storage engine from SQLite4. ** ** USAGE ** ** CREATE VIRTUAL TABLE demo USING lsm1(filename,key,keytype,value1,...); ** ** The filename parameter is the name of the LSM database file, which is ** separate and distinct from the SQLite3 database file. ** ** The keytype must be one of: UINT, TEXT, BLOB. All keys must be of that ** one type. "UINT" means unsigned integer. The values may be of any ** SQLite datatype: BLOB, TEXT, INTEGER, FLOAT, or NULL. ** ** The virtual table contains read-only hidden columns: ** ** lsm1_key A BLOB which is the raw LSM key. If the "keytype" ** is BLOB or TEXT then this column is exactly the ** same as the key. For the UINT keytype, this column ** will be a variable-length integer encoding of the key. ** ** lsm1_value A BLOB which is the raw LSM value. All of the value ** columns are packed into this BLOB using the encoding ** described below. ** ** Attempts to write values into the lsm1_key and lsm1_value columns are ** silently ignored. ** ** EXAMPLE ** ** The virtual table declared this way: ** ** CREATE VIRTUAL TABLE demo2 USING lsm1('x.lsm',id,UINT,a,b,c,d); ** ** Results in a new virtual table named "demo2" that acts as if it has ** the following schema: ** ** CREATE TABLE demo2( ** id UINT PRIMARY KEY ON CONFLICT REPLACE, ** a ANY, ** b ANY, ** c ANY, ** d ANY, ** lsm1_key BLOB HIDDEN, ** lsm1_value BLOB HIDDEN ** ) WITHOUT ROWID; ** ** ** ** INTERNALS ** ** The key encoding for BLOB and TEXT is just a copy of the blob or text. ** UTF-8 is used for text. The key encoding for UINT is the variable-length ** integer format at https://sqlite.org/src4/doc/trunk/www/varint.wiki. ** ** The values are encoded as a single blob (since that is what lsm stores as ** its content). There is a "type integer" followed by "content" for each ** value, alternating back and forth. The content might be empty. ** ** TYPE1 CONTENT1 TYPE2 CONTENT2 TYPE3 CONTENT3 .... ** ** Each "type integer" is encoded as a variable-length integer in the ** format of the link above. Let the type integer be T. The actual ** datatype is an integer 0-5 equal to T%6. Values 1 through 5 correspond ** to SQLITE_INTEGER through SQLITE_NULL. The size of the content in bytes ** is T/6. Type value 0 means that the value is an integer whose actual ** values is T/6 and there is no content. The type-value-0 integer format ** only works for integers in the range of 0 through 40. ** ** There is no content for NULL or type-0 integers. For BLOB and TEXT ** values, the content is the blob data or the UTF-8 text data. For ** non-negative integers X, the content is a variable-length integer X*2. ** For negative integers Y, the content is varaible-length integer (1-Y)*2+1. ** For FLOAT values, the content is the IEEE754 floating point value in ** native byte-order. This means that FLOAT values will be corrupted when ** database file is moved between big-endian and little-endian machines. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include "lsm.h" #include <assert.h> #include <string.h> /* Forward declaration of subclasses of virtual table objects */ typedef struct lsm1_vtab lsm1_vtab; typedef struct lsm1_cursor lsm1_cursor; typedef struct lsm1_vblob lsm1_vblob; /* Primitive types */ typedef unsigned char u8; typedef unsigned int u32; typedef sqlite3_uint64 u64; /* An open connection to an LSM table */ struct lsm1_vtab { sqlite3_vtab base; /* Base class - must be first */ lsm_db *pDb; /* Open connection to the LSM table */ u8 keyType; /* SQLITE_BLOB, _TEXT, or _INTEGER */ u32 nVal; /* Number of value columns */ }; /* lsm1_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ struct lsm1_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ lsm_cursor *pLsmCur; /* The LSM cursor */ u8 isDesc; /* 0: scan forward. 1: scan reverse */ u8 atEof; /* True if the scan is complete */ u8 bUnique; /* True if no more than one row of output */ u8 *zData; /* Content of the current row */ u32 nData; /* Number of bytes in the current row */ u8 *aeType; /* Types for all column values */ u32 *aiOfst; /* Offsets to the various fields */ u32 *aiLen; /* Length of each field */ u8 *pKey2; /* Loop termination key, or NULL */ u32 nKey2; /* Length of the loop termination key */ }; /* An extensible buffer object. ** ** Content can be appended. Space to hold new content is automatically ** allocated. */ struct lsm1_vblob { u8 *a; /* Space to hold content, from sqlite3_malloc64() */ u64 n; /* Bytes of space used */ u64 nAlloc; /* Bytes of space allocated */ u8 errNoMem; /* True if a memory allocation error has been seen */ }; #if defined(__GNUC__) # define LSM1_NOINLINE __attribute__((noinline)) #elif defined(_MSC_VER) && _MSC_VER>=1310 # define LSM1_NOINLINE __declspec(noinline) #else # define LSM1_NOINLINE #endif /* Increase the available space in the vblob object so that it can hold ** at least N more bytes. Return the number of errors. */ static int lsm1VblobEnlarge(lsm1_vblob *p, u32 N){ if( p->n+N>p->nAlloc ){ if( p->errNoMem ) return 1; p->nAlloc += N + (p->nAlloc ? p->nAlloc : N); p->a = sqlite3_realloc64(p->a, p->nAlloc); if( p->a==0 ){ p->n = 0; p->nAlloc = 0; p->errNoMem = 1; return 1; } p->nAlloc = sqlite3_msize(p->a); } return 0; } /* Append N bytes to a vblob after first enlarging it */ static LSM1_NOINLINE void lsm1VblobEnlargeAndAppend( lsm1_vblob *p, const u8 *pData, u32 N ){ if( p->n+N>p->nAlloc && lsm1VblobEnlarge(p, N) ) return; memcpy(p->a+p->n, pData, N); p->n += N; } /* Append N bytes to a vblob */ static void lsm1VblobAppend(lsm1_vblob *p, const u8 *pData, u32 N){ sqlite3_int64 n = p->n; if( n+N>p->nAlloc ){ lsm1VblobEnlargeAndAppend(p, pData, N); }else{ p->n += N; memcpy(p->a+n, pData, N); } } /* append text to a vblob */ static void lsm1VblobAppendText(lsm1_vblob *p, const char *z){ lsm1VblobAppend(p, (u8*)z, (u32)strlen(z)); } /* Dequote the string */ static void lsm1Dequote(char *z){ int j; char cQuote = z[0]; size_t i, n; if( cQuote!='\'' && cQuote!='"' ) return; n = strlen(z); if( n<2 || z[n-1]!=z[0] ) return; for(i=1, j=0; i<n-1; i++){ if( z[i]==cQuote && z[i+1]==cQuote ) i++; z[j++] = z[i]; } z[j] = 0; } /* ** The lsm1Connect() method is invoked to create a new ** lsm1_vtab that describes the virtual table. */ static int lsm1Connect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ lsm1_vtab *pNew; int rc; char *zFilename; u8 keyType = 0; int i; lsm1_vblob sql; static const char *azTypes[] = { "UINT", "TEXT", "BLOB" }; static const u8 aeTypes[] = { SQLITE_INTEGER, SQLITE_TEXT, SQLITE_BLOB }; static const char *azArgName[] = {"filename", "key", "key type", "value1" }; for(i=0; i<sizeof(azArgName)/sizeof(azArgName[0]); i++){ if( argc<i+4 || argv[i+3]==0 || argv[i+3][0]==0 ){ *pzErr = sqlite3_mprintf("%s (%r) argument missing", azArgName[i], i+1); return SQLITE_ERROR; } } for(i=0; i<sizeof(azTypes)/sizeof(azTypes[0]); i++){ if( sqlite3_stricmp(azTypes[i],argv[5])==0 ){ keyType = aeTypes[i]; break; } } if( keyType==0 ){ *pzErr = sqlite3_mprintf("key type should be INT, TEXT, or BLOB"); return SQLITE_ERROR; } *ppVtab = sqlite3_malloc( sizeof(*pNew) ); pNew = (lsm1_vtab*)*ppVtab; if( pNew==0 ){ return SQLITE_NOMEM; } memset(pNew, 0, sizeof(*pNew)); pNew->keyType = keyType; rc = lsm_new(0, &pNew->pDb); if( rc ){ *pzErr = sqlite3_mprintf("lsm_new failed with error code %d", rc); rc = SQLITE_ERROR; goto connect_failed; } zFilename = sqlite3_mprintf("%s", argv[3]); lsm1Dequote(zFilename); rc = lsm_open(pNew->pDb, zFilename); sqlite3_free(zFilename); if( rc ){ *pzErr = sqlite3_mprintf("lsm_open failed with %d", rc); rc = SQLITE_ERROR; goto connect_failed; } memset(&sql, 0, sizeof(sql)); lsm1VblobAppendText(&sql, "CREATE TABLE x("); lsm1VblobAppendText(&sql, argv[4]); lsm1VblobAppendText(&sql, " "); lsm1VblobAppendText(&sql, argv[5]); lsm1VblobAppendText(&sql, " PRIMARY KEY"); for(i=6; i<argc; i++){ lsm1VblobAppendText(&sql, ", "); lsm1VblobAppendText(&sql, argv[i]); pNew->nVal++; } lsm1VblobAppendText(&sql, ", lsm1_command HIDDEN" ", lsm1_key HIDDEN" ", lsm1_value HIDDEN) WITHOUT ROWID"); lsm1VblobAppend(&sql, (u8*)"", 1); if( sql.errNoMem ){ rc = SQLITE_NOMEM; goto connect_failed; } rc = sqlite3_declare_vtab(db, (const char*)sql.a); sqlite3_free(sql.a); connect_failed: if( rc!=SQLITE_OK ){ if( pNew ){ if( pNew->pDb ) lsm_close(pNew->pDb); sqlite3_free(pNew); } *ppVtab = 0; } return rc; } /* ** This method is the destructor for lsm1_cursor objects. */ static int lsm1Disconnect(sqlite3_vtab *pVtab){ lsm1_vtab *p = (lsm1_vtab*)pVtab; lsm_close(p->pDb); sqlite3_free(p); return SQLITE_OK; } /* ** Constructor for a new lsm1_cursor object. */ static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){ lsm1_vtab *p = (lsm1_vtab*)pVtab; lsm1_cursor *pCur; int rc; pCur = sqlite3_malloc64( sizeof(*pCur) + p->nVal*(sizeof(pCur->aiOfst)+sizeof(pCur->aiLen)+1) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->aiOfst = (u32*)&pCur[1]; pCur->aiLen = &pCur->aiOfst[p->nVal]; pCur->aeType = (u8*)&pCur->aiLen[p->nVal]; *ppCursor = &pCur->base; rc = lsm_csr_open(p->pDb, &pCur->pLsmCur); if( rc==LSM_OK ){ rc = SQLITE_OK; }else{ sqlite3_free(pCur); *ppCursor = 0; rc = SQLITE_ERROR; } return rc; } /* ** Destructor for a lsm1_cursor. */ static int lsm1Close(sqlite3_vtab_cursor *cur){ lsm1_cursor *pCur = (lsm1_cursor*)cur; sqlite3_free(pCur->pKey2); lsm_csr_close(pCur->pLsmCur); sqlite3_free(pCur); return SQLITE_OK; } /* ** Advance a lsm1_cursor to its next row of output. */ static int lsm1Next(sqlite3_vtab_cursor *cur){ lsm1_cursor *pCur = (lsm1_cursor*)cur; int rc = LSM_OK; if( pCur->bUnique ){ pCur->atEof = 1; }else{ if( pCur->isDesc ){ rc = lsm_csr_prev(pCur->pLsmCur); }else{ rc = lsm_csr_next(pCur->pLsmCur); } if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)==0 ){ pCur->atEof = 1; } if( pCur->pKey2 && pCur->atEof==0 ){ const u8 *pVal; u32 nVal; assert( pCur->isDesc==0 ); rc = lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, (int*)&nVal); if( rc==LSM_OK ){ u32 len = pCur->nKey2; int c; if( len>nVal ) len = nVal; c = memcmp(pVal, pCur->pKey2, len); if( c==0 ) c = nVal - pCur->nKey2; if( c>0 ) pCur->atEof = 1; } } pCur->zData = 0; } return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int lsm1Eof(sqlite3_vtab_cursor *cur){ lsm1_cursor *pCur = (lsm1_cursor*)cur; return pCur->atEof; } /* ** Rowids are not supported by the underlying virtual table. So always ** return 0 for the rowid. */ static int lsm1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ *pRowid = 0; return SQLITE_OK; } /* ** Type prefixes on LSM keys */ #define LSM1_TYPE_NEGATIVE 0 #define LSM1_TYPE_POSITIVE 1 #define LSM1_TYPE_TEXT 2 #define LSM1_TYPE_BLOB 3 /* ** Write a 32-bit unsigned integer as 4 big-endian bytes. */ static void varintWrite32(unsigned char *z, unsigned int y){ z[0] = (unsigned char)(y>>24); z[1] = (unsigned char)(y>>16); z[2] = (unsigned char)(y>>8); z[3] = (unsigned char)(y); } /* ** Write a varint into z[]. The buffer z[] must be at least 9 characters ** long to accommodate the largest possible varint. Return the number of ** bytes of z[] used. */ static int lsm1PutVarint64(unsigned char *z, sqlite3_uint64 x){ unsigned int w, y; if( x<=240 ){ z[0] = (unsigned char)x; return 1; } if( x<=2287 ){ y = (unsigned int)(x - 240); z[0] = (unsigned char)(y/256 + 241); z[1] = (unsigned char)(y%256); return 2; } if( x<=67823 ){ y = (unsigned int)(x - 2288); z[0] = 249; z[1] = (unsigned char)(y/256); z[2] = (unsigned char)(y%256); return 3; } y = (unsigned int)x; w = (unsigned int)(x>>32); if( w==0 ){ if( y<=16777215 ){ z[0] = 250; z[1] = (unsigned char)(y>>16); z[2] = (unsigned char)(y>>8); z[3] = (unsigned char)(y); return 4; } z[0] = 251; varintWrite32(z+1, y); return 5; } if( w<=255 ){ z[0] = 252; z[1] = (unsigned char)w; varintWrite32(z+2, y); return 6; } if( w<=65535 ){ z[0] = 253; z[1] = (unsigned char)(w>>8); z[2] = (unsigned char)w; varintWrite32(z+3, y); return 7; } if( w<=16777215 ){ z[0] = 254; z[1] = (unsigned char)(w>>16); z[2] = (unsigned char)(w>>8); z[3] = (unsigned char)w; varintWrite32(z+4, y); return 8; } z[0] = 255; varintWrite32(z+1, w); varintWrite32(z+5, y); return 9; } /* Append non-negative integer x as a variable-length integer. */ static void lsm1VblobAppendVarint(lsm1_vblob *p, sqlite3_uint64 x){ sqlite3_int64 n = p->n; if( n+9>p->nAlloc && lsm1VblobEnlarge(p, 9) ) return; p->n += lsm1PutVarint64(p->a+p->n, x); } /* ** Decode the varint in the first n bytes z[]. Write the integer value ** into *pResult and return the number of bytes in the varint. ** ** If the decode fails because there are not enough bytes in z[] then ** return 0; */ static int lsm1GetVarint64( const unsigned char *z, int n, sqlite3_uint64 *pResult ){ unsigned int x; if( n<1 ) return 0; if( z[0]<=240 ){ *pResult = z[0]; return 1; } if( z[0]<=248 ){ if( n<2 ) return 0; *pResult = (z[0]-241)*256 + z[1] + 240; return 2; } if( n<z[0]-246 ) return 0; if( z[0]==249 ){ *pResult = 2288 + 256*z[1] + z[2]; return 3; } if( z[0]==250 ){ *pResult = (z[1]<<16) + (z[2]<<8) + z[3]; return 4; } x = (z[1]<<24) + (z[2]<<16) + (z[3]<<8) + z[4]; if( z[0]==251 ){ *pResult = x; return 5; } if( z[0]==252 ){ *pResult = (((sqlite3_uint64)x)<<8) + z[5]; return 6; } if( z[0]==253 ){ *pResult = (((sqlite3_uint64)x)<<16) + (z[5]<<8) + z[6]; return 7; } if( z[0]==254 ){ *pResult = (((sqlite3_uint64)x)<<24) + (z[5]<<16) + (z[6]<<8) + z[7]; return 8; } *pResult = (((sqlite3_uint64)x)<<32) + (0xffffffff & ((z[5]<<24) + (z[6]<<16) + (z[7]<<8) + z[8])); return 9; } /* Encoded a signed integer as a varint. Numbers close to zero uses fewer ** bytes than numbers far away from zero. However, the result is not in ** lexicographical order. ** ** Encoding: Non-negative integer X is encoding as an unsigned ** varint X*2. Negative integer Y is encoding as an unsigned ** varint (1-Y)*2 + 1. */ static int lsm1PutSignedVarint64(u8 *z, sqlite3_int64 v){ sqlite3_uint64 u; if( v>=0 ){ u = (sqlite3_uint64)v; return lsm1PutVarint64(z, u*2); }else{ u = (sqlite3_uint64)(-1-v); return lsm1PutVarint64(z, u*2+1); } } /* Decoded a signed varint. */ static int lsm1GetSignedVarint64( const unsigned char *z, int n, sqlite3_int64 *pResult ){ sqlite3_uint64 u = 0; n = lsm1GetVarint64(z, n, &u); if( u&1 ){ *pResult = -1 - (sqlite3_int64)(u>>1); }else{ *pResult = (sqlite3_int64)(u>>1); } return n; } /* ** Read the value part of the key-value pair and decode it into columns. */ static int lsm1DecodeValues(lsm1_cursor *pCur){ lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab); int i, n; int rc; u8 eType; sqlite3_uint64 v; if( pCur->zData ) return 1; rc = lsm_csr_value(pCur->pLsmCur, (const void**)&pCur->zData, (int*)&pCur->nData); if( rc ) return 0; for(i=n=0; i<pTab->nVal; i++){ v = 0; n += lsm1GetVarint64(pCur->zData+n, pCur->nData-n, &v); pCur->aeType[i] = eType = (u8)(v%6); if( eType==0 ){ pCur->aiOfst[i] = (u32)(v/6); pCur->aiLen[i] = 0; }else{ pCur->aiOfst[i] = n; n += (pCur->aiLen[i] = (u32)(v/6)); } if( n>pCur->nData ) break; } if( i<pTab->nVal ){ pCur->zData = 0; return 0; } return 1; } /* ** Return values of columns for the row at which the lsm1_cursor ** is currently pointing. */ static int lsm1Column( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ lsm1_cursor *pCur = (lsm1_cursor*)cur; lsm1_vtab *pTab = (lsm1_vtab*)(cur->pVtab); if( i==0 ){ /* The key column */ const void *pVal; int nVal; if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ if( pTab->keyType==SQLITE_BLOB ){ sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); }else if( pTab->keyType==SQLITE_TEXT ){ sqlite3_result_text(ctx,(const char*)pVal, nVal, SQLITE_TRANSIENT); }else{ const unsigned char *z = (const unsigned char*)pVal; sqlite3_uint64 v1; lsm1GetVarint64(z, nVal, &v1); sqlite3_result_int64(ctx, (sqlite3_int64)v1); } } }else if( i>pTab->nVal ){ if( i==pTab->nVal+2 ){ /* lsm1_key */ const void *pVal; int nVal; if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); } }else if( i==pTab->nVal+3 ){ /* lsm1_value */ const void *pVal; int nVal; if( lsm_csr_value(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); } } }else if( lsm1DecodeValues(pCur) ){ /* The i-th value column (where leftmost is 1) */ const u8 *zData; u32 nData; i--; zData = pCur->zData + pCur->aiOfst[i]; nData = pCur->aiLen[i]; switch( pCur->aeType[i] ){ case 0: { /* in-line integer */ sqlite3_result_int(ctx, pCur->aiOfst[i]); break; } case SQLITE_INTEGER: { sqlite3_int64 v; lsm1GetSignedVarint64(zData, nData, &v); sqlite3_result_int64(ctx, v); break; } case SQLITE_FLOAT: { double v; if( nData==sizeof(v) ){ memcpy(&v, zData, sizeof(v)); sqlite3_result_double(ctx, v); } break; } case SQLITE_TEXT: { sqlite3_result_text(ctx, (const char*)zData, nData, SQLITE_TRANSIENT); break; } case SQLITE_BLOB: { sqlite3_result_blob(ctx, zData, nData, SQLITE_TRANSIENT); break; } default: { /* A NULL. Do nothing */ } } } return SQLITE_OK; } /* Parameter "pValue" contains an SQL value that is to be used as ** a key in an LSM table. The type of the key is determined by ** "keyType". Extract the raw bytes used for the key in LSM1. */ static void lsm1KeyFromValue( int keyType, /* The key type */ sqlite3_value *pValue, /* The key value */ u8 *pBuf, /* Storage space for a generated key */ const u8 **ppKey, /* OUT: the bytes of the key */ int *pnKey /* OUT: size of the key */ ){ if( keyType==SQLITE_BLOB ){ *ppKey = (const u8*)sqlite3_value_blob(pValue); *pnKey = sqlite3_value_bytes(pValue); }else if( keyType==SQLITE_TEXT ){ *ppKey = (const u8*)sqlite3_value_text(pValue); *pnKey = sqlite3_value_bytes(pValue); }else{ sqlite3_int64 v = sqlite3_value_int64(pValue); if( v<0 ) v = 0; *pnKey = lsm1PutVarint64(pBuf, v); *ppKey = pBuf; } } /* Move to the first row to return. */ static int lsm1Filter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ lsm1_cursor *pCur = (lsm1_cursor *)pVtabCursor; lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab); int rc = LSM_OK; int seekType = -1; const u8 *pVal = 0; int nVal; u8 keyType = pTab->keyType; u8 aKey1[16]; pCur->atEof = 1; sqlite3_free(pCur->pKey2); pCur->pKey2 = 0; if( idxNum<99 ){ lsm1KeyFromValue(keyType, argv[0], aKey1, &pVal, &nVal); } switch( idxNum ){ case 0: { /* key==argv[0] */ assert( argc==1 ); seekType = LSM_SEEK_EQ; pCur->isDesc = 0; pCur->bUnique = 1; break; } case 1: { /* key>=argv[0] AND key<=argv[1] */ u8 aKey[12]; seekType = LSM_SEEK_GE; pCur->isDesc = 0; pCur->bUnique = 0; if( keyType==SQLITE_INTEGER ){ sqlite3_int64 v = sqlite3_value_int64(argv[1]); if( v<0 ) v = 0; pCur->nKey2 = lsm1PutVarint64(aKey, (sqlite3_uint64)v); pCur->pKey2 = sqlite3_malloc( pCur->nKey2 ); if( pCur->pKey2==0 ) return SQLITE_NOMEM; memcpy(pCur->pKey2, aKey, pCur->nKey2); }else{ pCur->nKey2 = sqlite3_value_bytes(argv[1]); pCur->pKey2 = sqlite3_malloc( pCur->nKey2 ); if( pCur->pKey2==0 ) return SQLITE_NOMEM; if( keyType==SQLITE_BLOB ){ memcpy(pCur->pKey2, sqlite3_value_blob(argv[1]), pCur->nKey2); }else{ memcpy(pCur->pKey2, sqlite3_value_text(argv[1]), pCur->nKey2); } } break; } case 2: { /* key>=argv[0] */ seekType = LSM_SEEK_GE; pCur->isDesc = 0; pCur->bUnique = 0; break; } case 3: { /* key<=argv[0] */ seekType = LSM_SEEK_LE; pCur->isDesc = 1; pCur->bUnique = 0; break; } default: { /* full table scan */ pCur->isDesc = 0; pCur->bUnique = 0; break; } } if( pVal ){ rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, seekType); }else{ rc = lsm_csr_first(pCur->pLsmCur); } if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){ pCur->atEof = 0; } return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; } /* ** Only comparisons against the key are allowed. The idxNum defines ** which comparisons are available: ** ** 0 key==?1 ** 1 key>=?1 AND key<=?2 ** 2 key>?1 or key>=?1 ** 3 key<?1 or key<=?1 ** 99 Full table scan only */ static int lsm1BestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; /* Loop over constraints */ int idxNum = 99; /* The query plan */ int nArg = 0; /* Number of arguments to xFilter */ int argIdx = -1; /* Index of the key== constraint, or -1 if none */ int iIdx2 = -1; /* The index of the second key */ int omit1 = 0; int omit2 = 0; const struct sqlite3_index_constraint *pConstraint; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint && idxNum<16; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; if( pConstraint->iColumn!=0 ) continue; switch( pConstraint->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: { if( idxNum>0 ){ argIdx = i; iIdx2 = -1; idxNum = 0; omit1 = 1; } break; } case SQLITE_INDEX_CONSTRAINT_GE: case SQLITE_INDEX_CONSTRAINT_GT: { if( idxNum==99 ){ argIdx = i; idxNum = 2; omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE; }else if( idxNum==3 ){ iIdx2 = idxNum; omit2 = omit1; argIdx = i; idxNum = 1; omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE; } break; } case SQLITE_INDEX_CONSTRAINT_LE: case SQLITE_INDEX_CONSTRAINT_LT: { if( idxNum==99 ){ argIdx = i; idxNum = 3; omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE; }else if( idxNum==2 ){ iIdx2 = i; idxNum = 1; omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE; } break; } } } if( argIdx>=0 ){ pIdxInfo->aConstraintUsage[argIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[argIdx].omit = omit1; } if( iIdx2>=0 ){ pIdxInfo->aConstraintUsage[iIdx2].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[iIdx2].omit = omit2; } if( idxNum==0 ){ pIdxInfo->estimatedCost = (double)1; pIdxInfo->estimatedRows = 1; pIdxInfo->orderByConsumed = 1; }else if( idxNum==1 ){ pIdxInfo->estimatedCost = (double)100; pIdxInfo->estimatedRows = 100; }else if( idxNum<99 ){ pIdxInfo->estimatedCost = (double)5000; pIdxInfo->estimatedRows = 5000; }else{ /* Full table scan */ pIdxInfo->estimatedCost = (double)2147483647; pIdxInfo->estimatedRows = 2147483647; } pIdxInfo->idxNum = idxNum; return SQLITE_OK; } /* ** The xUpdate method is normally used for INSERT, REPLACE, UPDATE, and ** DELETE. But this virtual table only supports INSERT and REPLACE. ** DELETE is accomplished by inserting a record with a value of NULL. ** UPDATE is achieved by using REPLACE. */ int lsm1Update( sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite_int64 *pRowid ){ lsm1_vtab *p = (lsm1_vtab*)pVTab; int nKey, nKey2; int i; int rc = LSM_OK; const u8 *pKey, *pKey2; unsigned char aKey[16]; unsigned char pSpace[16]; lsm1_vblob val; if( argc==1 ){ /* DELETE the record whose key is argv[0] */ lsm1KeyFromValue(p->keyType, argv[0], aKey, &pKey, &nKey); lsm_delete(p->pDb, pKey, nKey); return SQLITE_OK; } if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){ /* An UPDATE */ lsm1KeyFromValue(p->keyType, argv[0], aKey, &pKey, &nKey); lsm1KeyFromValue(p->keyType, argv[1], pSpace, &pKey2, &nKey2); if( nKey!=nKey2 || memcmp(pKey, pKey2, nKey)!=0 ){ /* The UPDATE changes the PRIMARY KEY value. DELETE the old key */ lsm_delete(p->pDb, pKey, nKey); } /* Fall through into the INSERT case to complete the UPDATE */ } /* "INSERT INTO tab(lsm1_command) VALUES('....')" is used to implement ** special commands. */ if( sqlite3_value_type(argv[3+p->nVal])!=SQLITE_NULL ){ return SQLITE_OK; } lsm1KeyFromValue(p->keyType, argv[2], aKey, &pKey, &nKey); memset(&val, 0, sizeof(val)); for(i=0; i<p->nVal; i++){ sqlite3_value *pArg = argv[3+i]; u8 eType = sqlite3_value_type(pArg); switch( eType ){ case SQLITE_NULL: { lsm1VblobAppendVarint(&val, SQLITE_NULL); break; } case SQLITE_INTEGER: { sqlite3_int64 v = sqlite3_value_int64(pArg); if( v>=0 && v<=240/6 ){ lsm1VblobAppendVarint(&val, v*6); }else{ int n = lsm1PutSignedVarint64(pSpace, v); lsm1VblobAppendVarint(&val, SQLITE_INTEGER + n*6); lsm1VblobAppend(&val, pSpace, n); } break; } case SQLITE_FLOAT: { double r = sqlite3_value_double(pArg); lsm1VblobAppendVarint(&val, SQLITE_FLOAT + 8*6); lsm1VblobAppend(&val, (u8*)&r, sizeof(r)); break; } case SQLITE_BLOB: { int n = sqlite3_value_bytes(pArg); lsm1VblobAppendVarint(&val, n*6 + SQLITE_BLOB); lsm1VblobAppend(&val, sqlite3_value_blob(pArg), n); break; } case SQLITE_TEXT: { int n = sqlite3_value_bytes(pArg); lsm1VblobAppendVarint(&val, n*6 + SQLITE_TEXT); lsm1VblobAppend(&val, sqlite3_value_text(pArg), n); break; } } } if( val.errNoMem ){ return SQLITE_NOMEM; } rc = lsm_insert(p->pDb, pKey, nKey, val.a, val.n); sqlite3_free(val.a); return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; } /* Begin a transaction */ static int lsm1Begin(sqlite3_vtab *pVtab){ lsm1_vtab *p = (lsm1_vtab*)pVtab; int rc = lsm_begin(p->pDb, 1); return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; } /* Phase 1 of a transaction commit. */ static int lsm1Sync(sqlite3_vtab *pVtab){ return SQLITE_OK; } /* Commit a transaction */ static int lsm1Commit(sqlite3_vtab *pVtab){ lsm1_vtab *p = (lsm1_vtab*)pVtab; int rc = lsm_commit(p->pDb, 0); return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; } /* Rollback a transaction */ static int lsm1Rollback(sqlite3_vtab *pVtab){ lsm1_vtab *p = (lsm1_vtab*)pVtab; int rc = lsm_rollback(p->pDb, 0); return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; } /* ** This following structure defines all the methods for the ** generate_lsm1 virtual table. */ static sqlite3_module lsm1Module = { 0, /* iVersion */ lsm1Connect, /* xCreate */ lsm1Connect, /* xConnect */ lsm1BestIndex, /* xBestIndex */ lsm1Disconnect, /* xDisconnect */ lsm1Disconnect, /* xDestroy */ lsm1Open, /* xOpen - open a cursor */ lsm1Close, /* xClose - close a cursor */ lsm1Filter, /* xFilter - configure scan constraints */ lsm1Next, /* xNext - advance a cursor */ lsm1Eof, /* xEof - check for end of scan */ lsm1Column, /* xColumn - read data */ lsm1Rowid, /* xRowid - read data */ lsm1Update, /* xUpdate */ lsm1Begin, /* xBegin */ lsm1Sync, /* xSync */ lsm1Commit, /* xCommit */ lsm1Rollback, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_lsm_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); rc = sqlite3_create_module(db, "lsm1", &lsm1Module, 0); return rc; } |
Added ext/lsm1/lsm_win32.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 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 | /* ** 2011-12-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. ** ************************************************************************* ** ** Win32-specific run-time environment implementation for LSM. */ #ifdef _WIN32 #include <assert.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #include <stdio.h> #include <ctype.h> #include "windows.h" #include "lsmInt.h" /* ** An open file is an instance of the following object */ typedef struct Win32File Win32File; struct Win32File { lsm_env *pEnv; /* The run-time environment */ const char *zName; /* Full path to file */ HANDLE hFile; /* Open file handle */ HANDLE hShmFile; /* File handle for *-shm file */ SYSTEM_INFO sysInfo; /* Operating system information */ HANDLE hMap; /* File handle for mapping */ LPVOID pMap; /* Pointer to mapping of file fd */ size_t nMap; /* Size of mapping at pMap in bytes */ int nShm; /* Number of entries in ahShm[]/apShm[] */ LPHANDLE ahShm; /* Array of handles for shared mappings */ LPVOID *apShm; /* Array of 32K shared memory segments */ }; static char *win32ShmFile(Win32File *pWin32File){ char *zShm; int nName = strlen(pWin32File->zName); zShm = (char *)lsmMallocZero(pWin32File->pEnv, nName+4+1); if( zShm ){ memcpy(zShm, pWin32File->zName, nName); memcpy(&zShm[nName], "-shm", 5); } return zShm; } static int win32Sleep(int us){ Sleep((us + 999) / 1000); return LSM_OK; } /* ** The number of times that an I/O operation will be retried following a ** locking error - probably caused by antivirus software. Also the initial ** delay before the first retry. The delay increases linearly with each ** retry. */ #ifndef LSM_WIN32_IOERR_RETRY # define LSM_WIN32_IOERR_RETRY 10 #endif #ifndef LSM_WIN32_IOERR_RETRY_DELAY # define LSM_WIN32_IOERR_RETRY_DELAY 25000 #endif static int win32IoerrRetry = LSM_WIN32_IOERR_RETRY; static int win32IoerrRetryDelay = LSM_WIN32_IOERR_RETRY_DELAY; /* ** The "win32IoerrCanRetry1" macro is used to determine if a particular ** I/O error code obtained via GetLastError() is eligible to be retried. ** It must accept the error code DWORD as its only argument and should ** return non-zero if the error code is transient in nature and the ** operation responsible for generating the original error might succeed ** upon being retried. The argument to this macro should be a variable. ** ** Additionally, a macro named "win32IoerrCanRetry2" may be defined. If ** it is defined, it will be consulted only when the macro ** "win32IoerrCanRetry1" returns zero. The "win32IoerrCanRetry2" macro ** is completely optional and may be used to include additional error ** codes in the set that should result in the failing I/O operation being ** retried by the caller. If defined, the "win32IoerrCanRetry2" macro ** must exhibit external semantics identical to those of the ** "win32IoerrCanRetry1" macro. */ #if !defined(win32IoerrCanRetry1) #define win32IoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \ ((a)==ERROR_SHARING_VIOLATION) || \ ((a)==ERROR_LOCK_VIOLATION) || \ ((a)==ERROR_DEV_NOT_EXIST) || \ ((a)==ERROR_NETNAME_DELETED) || \ ((a)==ERROR_SEM_TIMEOUT) || \ ((a)==ERROR_NETWORK_UNREACHABLE)) #endif /* ** If an I/O error occurs, invoke this routine to see if it should be ** retried. Return TRUE to retry. Return FALSE to give up with an ** error. */ static int win32RetryIoerr( lsm_env *pEnv, int *pnRetry ){ DWORD lastErrno; if( *pnRetry>=win32IoerrRetry ){ return 0; } lastErrno = GetLastError(); if( win32IoerrCanRetry1(lastErrno) ){ win32Sleep(win32IoerrRetryDelay*(1+*pnRetry)); ++*pnRetry; return 1; } #if defined(win32IoerrCanRetry2) else if( win32IoerrCanRetry2(lastErrno) ){ win32Sleep(win32IoerrRetryDelay*(1+*pnRetry)); ++*pnRetry; return 1; } #endif return 0; } /* ** Convert a UTF-8 string to Microsoft Unicode. ** ** Space to hold the returned string is obtained from lsmMalloc(). */ static LPWSTR win32Utf8ToUnicode(lsm_env *pEnv, const char *zText){ int nChar; LPWSTR zWideText; nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0); if( nChar==0 ){ return 0; } zWideText = lsmMallocZero(pEnv, nChar * sizeof(WCHAR)); if( zWideText==0 ){ return 0; } nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText, nChar); if( nChar==0 ){ lsmFree(pEnv, zWideText); zWideText = 0; } return zWideText; } /* ** Convert a Microsoft Unicode string to UTF-8. ** ** Space to hold the returned string is obtained from lsmMalloc(). */ static char *win32UnicodeToUtf8(lsm_env *pEnv, LPCWSTR zWideText){ int nByte; char *zText; nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0); if( nByte == 0 ){ return 0; } zText = lsmMallocZero(pEnv, nByte); if( zText==0 ){ return 0; } nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte, 0, 0); if( nByte == 0 ){ lsmFree(pEnv, zText); zText = 0; } return zText; } #if !defined(win32IsNotFound) #define win32IsNotFound(a) (((a)==ERROR_FILE_NOT_FOUND) || \ ((a)==ERROR_PATH_NOT_FOUND)) #endif static int win32Open( lsm_env *pEnv, const char *zFile, int flags, LPHANDLE phFile ){ int rc; LPWSTR zConverted; zConverted = win32Utf8ToUnicode(pEnv, zFile); if( zConverted==0 ){ rc = LSM_NOMEM_BKPT; }else{ int bReadonly = (flags & LSM_OPEN_READONLY); DWORD dwDesiredAccess; DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; HANDLE hFile; int nRetry = 0; if( bReadonly ){ dwDesiredAccess = GENERIC_READ; dwCreationDisposition = OPEN_EXISTING; }else{ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; dwCreationDisposition = OPEN_ALWAYS; } while( (hFile = CreateFileW((LPCWSTR)zConverted, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL))==INVALID_HANDLE_VALUE && win32RetryIoerr(pEnv, &nRetry) ){ /* Noop */ } lsmFree(pEnv, zConverted); if( hFile!=INVALID_HANDLE_VALUE ){ *phFile = hFile; rc = LSM_OK; }else{ if( win32IsNotFound(GetLastError()) ){ rc = lsmErrorBkpt(LSM_IOERR_NOENT); }else{ rc = LSM_IOERR_BKPT; } } } return rc; } static int lsmWin32OsOpen( lsm_env *pEnv, const char *zFile, int flags, lsm_file **ppFile ){ int rc = LSM_OK; Win32File *pWin32File; pWin32File = lsmMallocZero(pEnv, sizeof(Win32File)); if( pWin32File==0 ){ rc = LSM_NOMEM_BKPT; }else{ HANDLE hFile = NULL; rc = win32Open(pEnv, zFile, flags, &hFile); if( rc==LSM_OK ){ memset(&pWin32File->sysInfo, 0, sizeof(SYSTEM_INFO)); GetSystemInfo(&pWin32File->sysInfo); pWin32File->pEnv = pEnv; pWin32File->zName = zFile; pWin32File->hFile = hFile; }else{ lsmFree(pEnv, pWin32File); pWin32File = 0; } } *ppFile = (lsm_file *)pWin32File; return rc; } static int lsmWin32OsWrite( lsm_file *pFile, /* File to write to */ lsm_i64 iOff, /* Offset to write to */ void *pData, /* Write data from this buffer */ int nData /* Bytes of data to write */ ){ Win32File *pWin32File = (Win32File *)pFile; OVERLAPPED overlapped; /* The offset for WriteFile. */ u8 *aRem = (u8 *)pData; /* Data yet to be written */ int nRem = nData; /* Number of bytes yet to be written */ int nRetry = 0; /* Number of retrys */ memset(&overlapped, 0, sizeof(OVERLAPPED)); overlapped.Offset = (LONG)(iOff & 0XFFFFFFFF); overlapped.OffsetHigh = (LONG)((iOff>>32) & 0x7FFFFFFF); while( nRem>0 ){ DWORD nWrite = 0; /* Bytes written using WriteFile */ if( !WriteFile(pWin32File->hFile, aRem, nRem, &nWrite, &overlapped) ){ if( win32RetryIoerr(pWin32File->pEnv, &nRetry) ) continue; break; } assert( nWrite==0 || nWrite<=(DWORD)nRem ); if( nWrite==0 || nWrite>(DWORD)nRem ){ break; } iOff += nWrite; overlapped.Offset = (LONG)(iOff & 0xFFFFFFFF); overlapped.OffsetHigh = (LONG)((iOff>>32) & 0x7FFFFFFF); aRem += nWrite; nRem -= nWrite; } if( nRem!=0 ) return LSM_IOERR_BKPT; return LSM_OK; } static int win32Truncate( HANDLE hFile, lsm_i64 nSize ){ LARGE_INTEGER offset; offset.QuadPart = nSize; if( !SetFilePointerEx(hFile, offset, 0, FILE_BEGIN) ){ return LSM_IOERR_BKPT; } if (!SetEndOfFile(hFile) ){ return LSM_IOERR_BKPT; } return LSM_OK; } static int lsmWin32OsTruncate( lsm_file *pFile, /* File to write to */ lsm_i64 nSize /* Size to truncate file to */ ){ Win32File *pWin32File = (Win32File *)pFile; return win32Truncate(pWin32File->hFile, nSize); } static int lsmWin32OsRead( lsm_file *pFile, /* File to read from */ lsm_i64 iOff, /* Offset to read from */ void *pData, /* Read data into this buffer */ int nData /* Bytes of data to read */ ){ Win32File *pWin32File = (Win32File *)pFile; OVERLAPPED overlapped; /* The offset for ReadFile */ DWORD nRead = 0; /* Bytes read using ReadFile */ int nRetry = 0; /* Number of retrys */ memset(&overlapped, 0, sizeof(OVERLAPPED)); overlapped.Offset = (LONG)(iOff & 0XFFFFFFFF); overlapped.OffsetHigh = (LONG)((iOff>>32) & 0X7FFFFFFF); while( !ReadFile(pWin32File->hFile, pData, nData, &nRead, &overlapped) && GetLastError()!=ERROR_HANDLE_EOF ){ if( win32RetryIoerr(pWin32File->pEnv, &nRetry) ) continue; return LSM_IOERR_BKPT; } if( nRead<(DWORD)nData ){ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pData)[nRead], 0, nData - nRead); } return LSM_OK; } static int lsmWin32OsSync(lsm_file *pFile){ int rc = LSM_OK; #ifndef LSM_NO_SYNC Win32File *pWin32File = (Win32File *)pFile; if( pWin32File->pMap!=NULL ){ if( !FlushViewOfFile(pWin32File->pMap, 0) ){ rc = LSM_IOERR_BKPT; } } if( rc==LSM_OK && !FlushFileBuffers(pWin32File->hFile) ){ rc = LSM_IOERR_BKPT; } #else unused_parameter(pFile); #endif return rc; } static int lsmWin32OsSectorSize(lsm_file *pFile){ return 512; } static void win32Unmap(Win32File *pWin32File){ if( pWin32File->pMap!=NULL ){ UnmapViewOfFile(pWin32File->pMap); pWin32File->pMap = NULL; pWin32File->nMap = 0; } if( pWin32File->hMap!=NULL ){ CloseHandle(pWin32File->hMap); pWin32File->hMap = NULL; } } static int lsmWin32OsRemap( lsm_file *pFile, lsm_i64 iMin, void **ppOut, lsm_i64 *pnOut ){ Win32File *pWin32File = (Win32File *)pFile; /* If the file is between 0 and 2MB in size, extend it in chunks of 256K. ** Thereafter, in chunks of 1MB at a time. */ const int aIncrSz[] = {256*1024, 1024*1024}; int nIncrSz = aIncrSz[iMin>(2*1024*1024)]; *ppOut = NULL; *pnOut = 0; win32Unmap(pWin32File); if( iMin>=0 ){ LARGE_INTEGER fileSize; DWORD dwSizeHigh; DWORD dwSizeLow; HANDLE hMap; LPVOID pMap; memset(&fileSize, 0, sizeof(LARGE_INTEGER)); if( !GetFileSizeEx(pWin32File->hFile, &fileSize) ){ return LSM_IOERR_BKPT; } assert( fileSize.QuadPart>=0 ); if( fileSize.QuadPart<iMin ){ int rc; fileSize.QuadPart = ((iMin + nIncrSz-1) / nIncrSz) * nIncrSz; rc = lsmWin32OsTruncate(pFile, fileSize.QuadPart); if( rc!=LSM_OK ){ return rc; } } dwSizeLow = (DWORD)(fileSize.QuadPart & 0xFFFFFFFF); dwSizeHigh = (DWORD)((fileSize.QuadPart & 0x7FFFFFFFFFFFFFFF) >> 32); hMap = CreateFileMappingW(pWin32File->hFile, NULL, PAGE_READWRITE, dwSizeHigh, dwSizeLow, NULL); if( hMap==NULL ){ return LSM_IOERR_BKPT; } pWin32File->hMap = hMap; assert( fileSize.QuadPart<=0xFFFFFFFF ); pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, (SIZE_T)fileSize.QuadPart); if( pMap==NULL ){ return LSM_IOERR_BKPT; } pWin32File->pMap = pMap; pWin32File->nMap = (SIZE_T)fileSize.QuadPart; } *ppOut = pWin32File->pMap; *pnOut = pWin32File->nMap; return LSM_OK; } static BOOL win32IsDriveLetterAndColon( const char *zPathname ){ return ( isalpha(zPathname[0]) && zPathname[1]==':' ); } static int lsmWin32OsFullpath( lsm_env *pEnv, const char *zName, char *zOut, int *pnOut ){ DWORD nByte; void *zConverted; LPWSTR zTempWide; char *zTempUtf8; if( zName[0]=='/' && win32IsDriveLetterAndColon(zName+1) ){ zName++; } zConverted = win32Utf8ToUnicode(pEnv, zName); if( zConverted==0 ){ return LSM_NOMEM_BKPT; } nByte = GetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0); if( nByte==0 ){ lsmFree(pEnv, zConverted); return LSM_IOERR_BKPT; } nByte += 3; zTempWide = lsmMallocZero(pEnv, nByte * sizeof(zTempWide[0])); if( zTempWide==0 ){ lsmFree(pEnv, zConverted); return LSM_NOMEM_BKPT; } nByte = GetFullPathNameW((LPCWSTR)zConverted, nByte, zTempWide, 0); if( nByte==0 ){ lsmFree(pEnv, zConverted); lsmFree(pEnv, zTempWide); return LSM_IOERR_BKPT; } lsmFree(pEnv, zConverted); zTempUtf8 = win32UnicodeToUtf8(pEnv, zTempWide); lsmFree(pEnv, zTempWide); if( zTempUtf8 ){ int nOut = *pnOut; int nLen = strlen(zTempUtf8) + 1; if( nLen<=nOut ){ snprintf(zOut, nOut, "%s", zTempUtf8); } lsmFree(pEnv, zTempUtf8); *pnOut = nLen; return LSM_OK; }else{ return LSM_NOMEM_BKPT; } } static int lsmWin32OsFileid( lsm_file *pFile, void *pBuf, int *pnBuf ){ int nBuf; int nReq; u8 *pBuf2 = (u8 *)pBuf; Win32File *pWin32File = (Win32File *)pFile; BY_HANDLE_FILE_INFORMATION fileInfo; nBuf = *pnBuf; nReq = (sizeof(fileInfo.dwVolumeSerialNumber) + sizeof(fileInfo.nFileIndexHigh) + sizeof(fileInfo.nFileIndexLow)); *pnBuf = nReq; if( nReq>nBuf ) return LSM_OK; memset(&fileInfo, 0, sizeof(BY_HANDLE_FILE_INFORMATION)); if( !GetFileInformationByHandle(pWin32File->hFile, &fileInfo) ){ return LSM_IOERR_BKPT; } nReq = sizeof(fileInfo.dwVolumeSerialNumber); memcpy(pBuf2, &fileInfo.dwVolumeSerialNumber, nReq); pBuf2 += nReq; nReq = sizeof(fileInfo.nFileIndexHigh); memcpy(pBuf, &fileInfo.nFileIndexHigh, nReq); pBuf2 += nReq; nReq = sizeof(fileInfo.nFileIndexLow); memcpy(pBuf2, &fileInfo.nFileIndexLow, nReq); return LSM_OK; } static int win32Delete( lsm_env *pEnv, const char *zFile ){ int rc; LPWSTR zConverted; zConverted = win32Utf8ToUnicode(pEnv, zFile); if( zConverted==0 ){ rc = LSM_NOMEM_BKPT; }else{ int nRetry = 0; DWORD attr; do { attr = GetFileAttributesW(zConverted); if ( attr==INVALID_FILE_ATTRIBUTES ){ rc = LSM_IOERR_BKPT; break; } if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ rc = LSM_IOERR_BKPT; /* Files only. */ break; } if ( DeleteFileW(zConverted) ){ rc = LSM_OK; /* Deleted OK. */ break; } if ( !win32RetryIoerr(pEnv, &nRetry) ){ rc = LSM_IOERR_BKPT; /* No more retries. */ break; } }while( 1 ); } lsmFree(pEnv, zConverted); return rc; } static int lsmWin32OsUnlink(lsm_env *pEnv, const char *zFile){ return win32Delete(pEnv, zFile); } #if !defined(win32IsLockBusy) #define win32IsLockBusy(a) (((a)==ERROR_LOCK_VIOLATION) || \ ((a)==ERROR_IO_PENDING)) #endif static int win32LockFile( Win32File *pWin32File, int iLock, int nLock, int eType ){ OVERLAPPED ovlp; assert( LSM_LOCK_UNLOCK==0 ); assert( LSM_LOCK_SHARED==1 ); assert( LSM_LOCK_EXCL==2 ); assert( eType>=LSM_LOCK_UNLOCK && eType<=LSM_LOCK_EXCL ); assert( nLock>=0 ); assert( iLock>0 && iLock<=32 ); memset(&ovlp, 0, sizeof(OVERLAPPED)); ovlp.Offset = (4096-iLock-nLock+1); if( eType>LSM_LOCK_UNLOCK ){ DWORD flags = LOCKFILE_FAIL_IMMEDIATELY; if( eType>=LSM_LOCK_EXCL ) flags |= LOCKFILE_EXCLUSIVE_LOCK; if( !LockFileEx(pWin32File->hFile, flags, 0, (DWORD)nLock, 0, &ovlp) ){ if( win32IsLockBusy(GetLastError()) ){ return LSM_BUSY; }else{ return LSM_IOERR_BKPT; } } }else{ if( !UnlockFileEx(pWin32File->hFile, 0, (DWORD)nLock, 0, &ovlp) ){ return LSM_IOERR_BKPT; } } return LSM_OK; } static int lsmWin32OsLock(lsm_file *pFile, int iLock, int eType){ Win32File *pWin32File = (Win32File *)pFile; return win32LockFile(pWin32File, iLock, 1, eType); } static int lsmWin32OsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){ int rc; Win32File *pWin32File = (Win32File *)pFile; rc = win32LockFile(pWin32File, iLock, nLock, eType); if( rc!=LSM_OK ) return rc; win32LockFile(pWin32File, iLock, nLock, LSM_LOCK_UNLOCK); return LSM_OK; } static int lsmWin32OsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){ int rc; Win32File *pWin32File = (Win32File *)pFile; int iOffset = iChunk * sz; int iOffsetShift = iOffset % pWin32File->sysInfo.dwAllocationGranularity; int nNew = iChunk + 1; lsm_i64 nReq = nNew * sz; *ppShm = NULL; assert( sz>=0 ); assert( sz==LSM_SHM_CHUNK_SIZE ); if( iChunk>=pWin32File->nShm ){ LPHANDLE ahNew; LPVOID *apNew; LARGE_INTEGER fileSize; /* If the shared-memory file has not been opened, open it now. */ if( pWin32File->hShmFile==NULL ){ char *zShm = win32ShmFile(pWin32File); if( !zShm ) return LSM_NOMEM_BKPT; rc = win32Open(pWin32File->pEnv, zShm, 0, &pWin32File->hShmFile); lsmFree(pWin32File->pEnv, zShm); if( rc!=LSM_OK ){ return rc; } } /* If the shared-memory file is not large enough to contain the ** requested chunk, cause it to grow. */ memset(&fileSize, 0, sizeof(LARGE_INTEGER)); if( !GetFileSizeEx(pWin32File->hShmFile, &fileSize) ){ return LSM_IOERR_BKPT; } assert( fileSize.QuadPart>=0 ); if( fileSize.QuadPart<nReq ){ rc = win32Truncate(pWin32File->hShmFile, nReq); if( rc!=LSM_OK ){ return rc; } } ahNew = (LPHANDLE)lsmMallocZero(pWin32File->pEnv, sizeof(HANDLE) * nNew); if( !ahNew ) return LSM_NOMEM_BKPT; apNew = (LPVOID *)lsmMallocZero(pWin32File->pEnv, sizeof(LPVOID) * nNew); if( !apNew ){ lsmFree(pWin32File->pEnv, ahNew); return LSM_NOMEM_BKPT; } memcpy(ahNew, pWin32File->ahShm, sizeof(HANDLE) * pWin32File->nShm); memcpy(apNew, pWin32File->apShm, sizeof(LPVOID) * pWin32File->nShm); lsmFree(pWin32File->pEnv, pWin32File->ahShm); pWin32File->ahShm = ahNew; lsmFree(pWin32File->pEnv, pWin32File->apShm); pWin32File->apShm = apNew; pWin32File->nShm = nNew; } if( pWin32File->ahShm[iChunk]==NULL ){ HANDLE hMap; assert( nReq<=0xFFFFFFFF ); hMap = CreateFileMappingW(pWin32File->hShmFile, NULL, PAGE_READWRITE, 0, (DWORD)nReq, NULL); if( hMap==NULL ){ return LSM_IOERR_BKPT; } pWin32File->ahShm[iChunk] = hMap; } if( pWin32File->apShm[iChunk]==NULL ){ LPVOID pMap; pMap = MapViewOfFile(pWin32File->ahShm[iChunk], FILE_MAP_WRITE | FILE_MAP_READ, 0, iOffset - iOffsetShift, sz + iOffsetShift); if( pMap==NULL ){ return LSM_IOERR_BKPT; } pWin32File->apShm[iChunk] = pMap; } if( iOffsetShift!=0 ){ char *p = (char *)pWin32File->apShm[iChunk]; *ppShm = (void *)&p[iOffsetShift]; }else{ *ppShm = pWin32File->apShm[iChunk]; } return LSM_OK; } static void lsmWin32OsShmBarrier(void){ MemoryBarrier(); } static int lsmWin32OsShmUnmap(lsm_file *pFile, int bDelete){ Win32File *pWin32File = (Win32File *)pFile; if( pWin32File->hShmFile!=NULL ){ int i; for(i=0; i<pWin32File->nShm; i++){ if( pWin32File->apShm[i]!=NULL ){ UnmapViewOfFile(pWin32File->apShm[i]); pWin32File->apShm[i] = NULL; } if( pWin32File->ahShm[i]!=NULL ){ CloseHandle(pWin32File->ahShm[i]); pWin32File->ahShm[i] = NULL; } } CloseHandle(pWin32File->hShmFile); pWin32File->hShmFile = NULL; if( bDelete ){ char *zShm = win32ShmFile(pWin32File); if( zShm ){ win32Delete(pWin32File->pEnv, zShm); } lsmFree(pWin32File->pEnv, zShm); } } return LSM_OK; } #define MX_CLOSE_ATTEMPT 3 static int lsmWin32OsClose(lsm_file *pFile){ int rc; int nRetry = 0; Win32File *pWin32File = (Win32File *)pFile; lsmWin32OsShmUnmap(pFile, 0); win32Unmap(pWin32File); do{ if( pWin32File->hFile==NULL ){ rc = LSM_IOERR_BKPT; break; } rc = CloseHandle(pWin32File->hFile); if( rc ){ pWin32File->hFile = NULL; rc = LSM_OK; break; } if( ++nRetry>=MX_CLOSE_ATTEMPT ){ rc = LSM_IOERR_BKPT; break; } }while( 1 ); lsmFree(pWin32File->pEnv, pWin32File->ahShm); lsmFree(pWin32File->pEnv, pWin32File->apShm); lsmFree(pWin32File->pEnv, pWin32File); return rc; } static int lsmWin32OsSleep(lsm_env *pEnv, int us){ unused_parameter(pEnv); return win32Sleep(us); } /**************************************************************************** ** Memory allocation routines. */ static void *lsmWin32OsMalloc(lsm_env *pEnv, size_t N){ assert( HeapValidate(GetProcessHeap(), 0, NULL) ); return HeapAlloc(GetProcessHeap(), 0, (SIZE_T)N); } static void lsmWin32OsFree(lsm_env *pEnv, void *p){ assert( HeapValidate(GetProcessHeap(), 0, NULL) ); if( p ){ HeapFree(GetProcessHeap(), 0, p); } } static void *lsmWin32OsRealloc(lsm_env *pEnv, void *p, size_t N){ unsigned char *m = (unsigned char *)p; assert( HeapValidate(GetProcessHeap(), 0, NULL) ); if( 1>N ){ lsmWin32OsFree(pEnv, p); return NULL; }else if( NULL==p ){ return lsmWin32OsMalloc(pEnv, N); }else{ #if 0 /* arguable: don't shrink */ SIZE_T sz = HeapSize(GetProcessHeap(), 0, m); if( sz>=(SIZE_T)N ){ return p; } #endif return HeapReAlloc(GetProcessHeap(), 0, m, N); } } static size_t lsmWin32OsMSize(lsm_env *pEnv, void *p){ assert( HeapValidate(GetProcessHeap(), 0, NULL) ); return (size_t)HeapSize(GetProcessHeap(), 0, p); } #ifdef LSM_MUTEX_WIN32 /************************************************************************* ** Mutex methods for Win32 based systems. If LSM_MUTEX_WIN32 is ** missing then a no-op implementation of mutexes found below will be ** used instead. */ #include "windows.h" typedef struct Win32Mutex Win32Mutex; struct Win32Mutex { lsm_env *pEnv; CRITICAL_SECTION mutex; #ifdef LSM_DEBUG DWORD owner; #endif }; #ifndef WIN32_MUTEX_INITIALIZER # define WIN32_MUTEX_INITIALIZER { 0 } #endif #ifdef LSM_DEBUG # define LSM_WIN32_STATIC_MUTEX { 0, WIN32_MUTEX_INITIALIZER, 0 } #else # define LSM_WIN32_STATIC_MUTEX { 0, WIN32_MUTEX_INITIALIZER } #endif static int lsmWin32OsMutexStatic( lsm_env *pEnv, int iMutex, lsm_mutex **ppStatic ){ static volatile LONG initialized = 0; static Win32Mutex sMutex[2] = { LSM_WIN32_STATIC_MUTEX, LSM_WIN32_STATIC_MUTEX }; assert( iMutex==LSM_MUTEX_GLOBAL || iMutex==LSM_MUTEX_HEAP ); assert( LSM_MUTEX_GLOBAL==1 && LSM_MUTEX_HEAP==2 ); if( InterlockedCompareExchange(&initialized, 1, 0)==0 ){ int i; for(i=0; i<array_size(sMutex); i++){ InitializeCriticalSection(&sMutex[i].mutex); } } *ppStatic = (lsm_mutex *)&sMutex[iMutex-1]; return LSM_OK; } static int lsmWin32OsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){ Win32Mutex *pMutex; /* Pointer to new mutex */ pMutex = (Win32Mutex *)lsmMallocZero(pEnv, sizeof(Win32Mutex)); if( !pMutex ) return LSM_NOMEM_BKPT; pMutex->pEnv = pEnv; InitializeCriticalSection(&pMutex->mutex); *ppNew = (lsm_mutex *)pMutex; return LSM_OK; } static void lsmWin32OsMutexDel(lsm_mutex *p){ Win32Mutex *pMutex = (Win32Mutex *)p; DeleteCriticalSection(&pMutex->mutex); lsmFree(pMutex->pEnv, pMutex); } static void lsmWin32OsMutexEnter(lsm_mutex *p){ Win32Mutex *pMutex = (Win32Mutex *)p; EnterCriticalSection(&pMutex->mutex); #ifdef LSM_DEBUG assert( pMutex->owner!=GetCurrentThreadId() ); pMutex->owner = GetCurrentThreadId(); assert( pMutex->owner==GetCurrentThreadId() ); #endif } static int lsmWin32OsMutexTry(lsm_mutex *p){ BOOL bRet; Win32Mutex *pMutex = (Win32Mutex *)p; bRet = TryEnterCriticalSection(&pMutex->mutex); #ifdef LSM_DEBUG if( bRet ){ assert( pMutex->owner!=GetCurrentThreadId() ); pMutex->owner = GetCurrentThreadId(); assert( pMutex->owner==GetCurrentThreadId() ); } #endif return !bRet; } static void lsmWin32OsMutexLeave(lsm_mutex *p){ Win32Mutex *pMutex = (Win32Mutex *)p; #ifdef LSM_DEBUG assert( pMutex->owner==GetCurrentThreadId() ); pMutex->owner = 0; assert( pMutex->owner!=GetCurrentThreadId() ); #endif LeaveCriticalSection(&pMutex->mutex); } #ifdef LSM_DEBUG static int lsmWin32OsMutexHeld(lsm_mutex *p){ Win32Mutex *pMutex = (Win32Mutex *)p; return pMutex ? pMutex->owner==GetCurrentThreadId() : 1; } static int lsmWin32OsMutexNotHeld(lsm_mutex *p){ Win32Mutex *pMutex = (Win32Mutex *)p; return pMutex ? pMutex->owner!=GetCurrentThreadId() : 1; } #endif /* ** End of Win32 mutex implementation. *************************************************************************/ #else /************************************************************************* ** Noop mutex implementation */ typedef struct NoopMutex NoopMutex; struct NoopMutex { lsm_env *pEnv; /* Environment handle (for xFree()) */ int bHeld; /* True if mutex is held */ int bStatic; /* True for a static mutex */ }; static NoopMutex aStaticNoopMutex[2] = { {0, 0, 1}, {0, 0, 1}, }; static int lsmWin32OsMutexStatic( lsm_env *pEnv, int iMutex, lsm_mutex **ppStatic ){ assert( iMutex>=1 && iMutex<=(int)array_size(aStaticNoopMutex) ); *ppStatic = (lsm_mutex *)&aStaticNoopMutex[iMutex-1]; return LSM_OK; } static int lsmWin32OsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){ NoopMutex *p; p = (NoopMutex *)lsmMallocZero(pEnv, sizeof(NoopMutex)); if( p ) p->pEnv = pEnv; *ppNew = (lsm_mutex *)p; return (p ? LSM_OK : LSM_NOMEM_BKPT); } static void lsmWin32OsMutexDel(lsm_mutex *pMutex) { NoopMutex *p = (NoopMutex *)pMutex; assert( p->bStatic==0 && p->pEnv ); lsmFree(p->pEnv, p); } static void lsmWin32OsMutexEnter(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; assert( p->bHeld==0 ); p->bHeld = 1; } static int lsmWin32OsMutexTry(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; assert( p->bHeld==0 ); p->bHeld = 1; return 0; } static void lsmWin32OsMutexLeave(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; assert( p->bHeld==1 ); p->bHeld = 0; } #ifdef LSM_DEBUG static int lsmWin32OsMutexHeld(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; return p ? p->bHeld : 1; } static int lsmWin32OsMutexNotHeld(lsm_mutex *pMutex){ NoopMutex *p = (NoopMutex *)pMutex; return p ? !p->bHeld : 1; } #endif /***************************************************************************/ #endif /* else LSM_MUTEX_NONE */ /* Without LSM_DEBUG, the MutexHeld tests are never called */ #ifndef LSM_DEBUG # define lsmWin32OsMutexHeld 0 # define lsmWin32OsMutexNotHeld 0 #endif lsm_env *lsm_default_env(void){ static lsm_env win32_env = { sizeof(lsm_env), /* nByte */ 1, /* iVersion */ /***** file i/o ******************/ 0, /* pVfsCtx */ lsmWin32OsFullpath, /* xFullpath */ lsmWin32OsOpen, /* xOpen */ lsmWin32OsRead, /* xRead */ lsmWin32OsWrite, /* xWrite */ lsmWin32OsTruncate, /* xTruncate */ lsmWin32OsSync, /* xSync */ lsmWin32OsSectorSize, /* xSectorSize */ lsmWin32OsRemap, /* xRemap */ lsmWin32OsFileid, /* xFileid */ lsmWin32OsClose, /* xClose */ lsmWin32OsUnlink, /* xUnlink */ lsmWin32OsLock, /* xLock */ lsmWin32OsTestLock, /* xTestLock */ lsmWin32OsShmMap, /* xShmMap */ lsmWin32OsShmBarrier, /* xShmBarrier */ lsmWin32OsShmUnmap, /* xShmUnmap */ /***** memory allocation *********/ 0, /* pMemCtx */ lsmWin32OsMalloc, /* xMalloc */ lsmWin32OsRealloc, /* xRealloc */ lsmWin32OsFree, /* xFree */ lsmWin32OsMSize, /* xSize */ /***** mutexes *********************/ 0, /* pMutexCtx */ lsmWin32OsMutexStatic, /* xMutexStatic */ lsmWin32OsMutexNew, /* xMutexNew */ lsmWin32OsMutexDel, /* xMutexDel */ lsmWin32OsMutexEnter, /* xMutexEnter */ lsmWin32OsMutexTry, /* xMutexTry */ lsmWin32OsMutexLeave, /* xMutexLeave */ lsmWin32OsMutexHeld, /* xMutexHeld */ lsmWin32OsMutexNotHeld, /* xMutexNotHeld */ /***** other *********************/ lsmWin32OsSleep, /* xSleep */ }; return &win32_env; } #endif |
Added ext/lsm1/test/lsm1_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 | # 2014 Dec 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. # #*********************************************************************** # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. .. test] } source $testdir/tester.tcl # Check if the lsm1 extension has been compiled. if {$::tcl_platform(platform) == "windows"} { set lsm1 lsm.dll } else { set lsm1 lsm.so } if {[file exists [file join .. $lsm1]]} { proc return_if_no_lsm1 {} {} } else { proc return_if_no_lsm1 {} { finish_test return -code return } return } proc load_lsm1_vtab {db} { db enable_load_extension 1 db eval {SELECT load_extension('../lsm')} } |
Added ext/lsm1/test/lsm1_simple.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 | # 2017 July 14 # # 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 script is testing the lsm1 virtual table module. # source [file join [file dirname [info script]] lsm1_common.tcl] set testprefix lsm1_simple return_if_no_lsm1 load_lsm1_vtab db forcedelete testlsm.db do_execsql_test 100 { CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,UINT,b,c,d); PRAGMA table_info(x1); } { 0 a UINT 1 {} 1 1 b {} 0 {} 0 2 c {} 0 {} 0 3 d {} 0 {} 0 } do_execsql_test 110 { INSERT INTO x1(a,b,c,d) VALUES(15, 11, 22, 33),(8,'banjo',x'333231',NULL), (12,NULL,3.25,-559281390); SELECT a, quote(b), quote(c), quote(d) FROM x1; } {8 'banjo' X'333231' NULL 12 NULL 3.25 -559281390 15 11 22 33} do_execsql_test 111 { SELECT a, quote(lsm1_key), quote(lsm1_value) FROM x1; } {8 X'08' X'2162616E6A6F1633323105' 12 X'0C' X'05320000000000000A401FFB42ABE9DB' 15 X'0F' X'4284C6'} do_execsql_test 120 { UPDATE x1 SET d = d+1.0 WHERE a=15; SELECT a, quote(b), quote(c), quote(d) FROM x1; } {8 'banjo' X'333231' NULL 12 NULL 3.25 -559281390 15 11 22 34.0} do_execsql_test 130 { UPDATE x1 SET a=123456789 WHERE a=12; SELECT a, quote(b), quote(c), quote(d) FROM x1; } {8 'banjo' X'333231' NULL 15 11 22 34.0 123456789 NULL 3.25 -559281390} do_execsql_test 131 { SELECT quote(lsm1_key), printf('0x%x',a) FROM x1 WHERE a > 100000000; } {X'FB075BCD15' 0x75bcd15} do_execsql_test 140 { DELETE FROM x1 WHERE a=15; SELECT a, quote(b), quote(c), quote(d) FROM x1; } {8 'banjo' X'333231' NULL 123456789 NULL 3.25 -559281390} do_test 150 { lsort [glob testlsm.db*] } {testlsm.db testlsm.db-log testlsm.db-shm} db close do_test 160 { lsort [glob testlsm.db*] } {testlsm.db} forcedelete testlsm.db forcedelete test.db sqlite3 db test.db load_lsm1_vtab db do_execsql_test 200 { CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,TEXT,b,c,d); PRAGMA table_info(x1); } { 0 a TEXT 1 {} 1 1 b {} 0 {} 0 2 c {} 0 {} 0 3 d {} 0 {} 0 } do_execsql_test 210 { INSERT INTO x1(a,b,c,d) VALUES(15, 11, 22, 33),(8,'banjo',x'333231',NULL), (12,NULL,3.25,-559281390); SELECT quote(a), quote(b), quote(c), quote(d), '|' FROM x1; } {'12' NULL 3.25 -559281390 | '15' 11 22 33 | '8' 'banjo' X'333231' NULL |} do_execsql_test 211 { SELECT quote(a), quote(lsm1_key), quote(lsm1_value), '|' FROM x1; } {'12' X'3132' X'05320000000000000A401FFB42ABE9DB' | '15' X'3135' X'4284C6' | '8' X'38' X'2162616E6A6F1633323105' |} finish_test |
Added ext/misc/README.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 | ## Miscellaneous Extensions This folder contains a collection of smaller loadable extensions. See <https://www.sqlite.org/loadext.html> for instructions on how to compile and use loadable extensions. Each extension in this folder is implemented in a single file of C code. Each source file contains a description in its header comment. See the header comments for details about each extension. Additional notes are as follows: * **carray.c** — This module implements the [carray](https://www.sqlite.org/carray.html) table-valued function. It is a good example of how to go about implementing a custom [table-valued function](https://www.sqlite.org/vtab.html#tabfunc2). * **dbdump.c** — This is not actually a loadable extension, but rather a library that implements an approximate equivalent to the ".dump" command of the [command-line shell](https://www.sqlite.org/cli.html). * **memvfs.c** — This file implements a custom [VFS](https://www.sqlite.org/vfs.html) that stores an entire database file in a single block of RAM. It serves as a good example of how to implement a simple custom VFS. * **rot13.c** — This file implements the very simple rot13() substitution function. This file makes a good template for implementing new custom SQL functions for SQLite. * **series.c** — This is an implementation of the "generate_series" [virtual table](https://www.sqlite.org/vtab.html). It can make a good template for new custom virtual table implementations. * **shathree.c** — An implementation of the sha3() and sha3_query() SQL functions. The file is named "shathree.c" instead of "sha3.c" because the default entry point names in SQLite are based on the source filename with digits removed, so if we used the name "sha3.c" then the entry point would conflict with the prior "sha1.c" extension. |
Changes to ext/misc/amatch.c.
︙ | ︙ | |||
997 998 999 1000 1001 1002 1003 | amatchEncodeInt(pWord->iSeq, pWord->zCost+4); pWord->zCost[8] = 0; } /* Circumvent compiler warnings about the use of strcpy() by supplying ** our own implementation. */ | < < < < < < | 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 | amatchEncodeInt(pWord->iSeq, pWord->zCost+4); pWord->zCost[8] = 0; } /* Circumvent compiler warnings about the use of strcpy() by supplying ** our own implementation. */ static void amatchStrcpy(char *dest, const char *src){ while( (*(dest++) = *(src++))!=0 ){} } static void amatchStrcat(char *dest, const char *src){ while( *dest ) dest++; amatchStrcpy(dest, src); } /* ** Add a new amatch_word object to the queue. ** ** If a prior amatch_word object with the same zWord, and nMatch ** already exists, update its rCost (if the new rCost is less) but ** otherwise leave it unchanged. Do not add a duplicate. |
︙ | ︙ |
Added ext/misc/anycollseq.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 | /* ** 2017-04-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 implements a run-time loadable extension to SQLite that ** registers a sqlite3_collation_needed() callback to register a fake ** collating function for any unknown collating sequence. The fake ** collating function works like BINARY. ** ** This extension can be used to load schemas that contain one or more ** unknown collating sequences. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <string.h> static int anyCollFunc( void *NotUsed, int nKey1, const void *pKey1, int nKey2, const void *pKey2 ){ int rc, n; n = nKey1<nKey2 ? nKey1 : nKey2; rc = memcmp(pKey1, pKey2, n); if( rc==0 ) rc = nKey1 - nKey2; return rc; } static void anyCollNeeded( void *NotUsed, sqlite3 *db, int eTextRep, const char *zCollName ){ sqlite3_create_collation(db, zCollName, eTextRep, 0, anyCollFunc); } #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_anycollseq_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); rc = sqlite3_collation_needed(db, 0, anyCollNeeded); return rc; } |
Changes to ext/misc/carray.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 | ** This file demonstrates how to create a table-valued-function that ** returns the values in a C-language array. ** Examples: ** ** SELECT * FROM carray($ptr,5) ** ** The query above returns 5 integers contained in a C-language array | | > > | > > > > > > | 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 | ** This file demonstrates how to create a table-valued-function that ** returns the values in a C-language array. ** Examples: ** ** SELECT * FROM carray($ptr,5) ** ** The query above returns 5 integers contained in a C-language array ** at the address $ptr. $ptr is a pointer to the array of integers. ** The pointer value must be assigned to $ptr using the ** sqlite3_bind_pointer() interface with a pointer type of "carray". ** For example: ** ** static int aX[] = { 53, 9, 17, 2231, 4, 99 }; ** int i = sqlite3_bind_parameter_index(pStmt, "$ptr"); ** sqlite3_bind_value(pStmt, i, aX, "carray", 0); ** ** There is an optional third parameter to determine the datatype of ** the C-language array. Allowed values of the third parameter are ** 'int32', 'int64', 'double', 'char*'. Example: ** ** SELECT * FROM carray($ptr,10,'char*'); ** ** The default value of the third parameter is 'int32'. ** ** HOW IT WORKS ** ** The carray "function" is really a virtual table with the ** following schema: ** ** CREATE TABLE carray( |
︙ | ︙ | |||
69 70 71 72 73 74 75 | ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct carray_cursor carray_cursor; struct carray_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3_int64 iRowid; /* The rowid */ | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct carray_cursor carray_cursor; struct carray_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3_int64 iRowid; /* The rowid */ void *pPtr; /* Pointer to the array of values */ sqlite3_int64 iCnt; /* Number of integers in the array */ unsigned char eType; /* One of the CARRAY_type values */ }; /* ** The carrayConnect() method is invoked to create a new ** carray_vtab that describes the carray virtual table. |
︙ | ︙ | |||
163 164 165 166 167 168 169 | sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ carray_cursor *pCur = (carray_cursor*)cur; sqlite3_int64 x = 0; switch( i ){ | | | | | | | 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 | sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ carray_cursor *pCur = (carray_cursor*)cur; sqlite3_int64 x = 0; switch( i ){ case CARRAY_COLUMN_POINTER: return SQLITE_OK; case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break; case CARRAY_COLUMN_CTYPE: { sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC); return SQLITE_OK; } default: { switch( pCur->eType ){ case CARRAY_INT32: { int *p = (int*)pCur->pPtr; sqlite3_result_int(ctx, p[pCur->iRowid-1]); return SQLITE_OK; } case CARRAY_INT64: { sqlite3_int64 *p = (sqlite3_int64*)pCur->pPtr; sqlite3_result_int64(ctx, p[pCur->iRowid-1]); return SQLITE_OK; } case CARRAY_DOUBLE: { double *p = (double*)pCur->pPtr; sqlite3_result_double(ctx, p[pCur->iRowid-1]); return SQLITE_OK; } case CARRAY_TEXT: { const char **p = (const char**)pCur->pPtr; sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT); return SQLITE_OK; } } } } sqlite3_result_int64(ctx, x); |
︙ | ︙ | |||
228 229 230 231 232 233 234 | static int carrayFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ carray_cursor *pCur = (carray_cursor *)pVtabCursor; if( idxNum ){ | | | | | 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 | static int carrayFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ carray_cursor *pCur = (carray_cursor *)pVtabCursor; if( idxNum ){ pCur->pPtr = sqlite3_value_pointer(argv[0], "carray"); pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0; if( idxNum<3 ){ pCur->eType = CARRAY_INT32; }else{ unsigned char i; const char *zType = (const char*)sqlite3_value_text(argv[2]); for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){ if( sqlite3_stricmp(zType, azType[i])==0 ) break; } if( i>=sizeof(azType)/sizeof(azType[0]) ){ pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf( "unknown datatype: %Q", zType); return SQLITE_ERROR; }else{ pCur->eType = i; } } }else{ pCur->pPtr = 0; pCur->iCnt = 0; } pCur->iRowid = 1; return SQLITE_OK; } /* |
︙ | ︙ | |||
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_carray_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3_create_module(db, "carray", &carrayModule, 0); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 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 | 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; /* ** For testing purpose in the TCL test harness, we need a method for ** setting the pointer value. The inttoptr(X) SQL function accomplishes ** this. Tcl script will bind an integer to X and the inttoptr() SQL ** function will use sqlite3_result_pointer() to convert that integer into ** a pointer. ** ** This is for testing on TCL only. */ #ifdef SQLITE_TEST static void inttoptrFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ void *p; sqlite3_int64 i64; i64 = sqlite3_value_int64(argv[0]); if( sizeof(i64)==sizeof(p) ){ memcpy(&p, &i64, sizeof(p)); }else{ int i32 = i64 & 0xffffffff; memcpy(&p, &i32, sizeof(p)); } sqlite3_result_pointer(context, p, "carray", 0); } #endif /* SQLITE_TEST */ #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_carray_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3_create_module(db, "carray", &carrayModule, 0); #ifdef SQLITE_TEST if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "inttoptr", 1, SQLITE_UTF8, 0, inttoptrFunc, 0, 0); } #endif /* SQLITE_TEST */ #endif /* SQLITE_OMIT_VIRTUALTABLE */ return rc; } |
Added ext/misc/completion.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 | /* ** 2017-07-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 an eponymous virtual table that returns suggested ** completions for a partial SQL input. ** ** Suggested usage: ** ** SELECT DISTINCT candidate COLLATE nocase ** FROM completion($prefix,$wholeline) ** ORDER BY 1; ** ** The two query parameters are optional. $prefix is the text of the ** current word being typed and that is to be completed. $wholeline is ** the complete input line, used for context. ** ** The raw completion() table might return the same candidate multiple ** times, for example if the same column name is used to two or more ** tables. And the candidates are returned in an arbitrary order. Hence, ** the DISTINCT and ORDER BY are recommended. ** ** This virtual table operates at the speed of human typing, and so there ** is no attempt to make it fast. Even a slow implementation will be much ** faster than any human can type. ** */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <ctype.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* completion_vtab is a subclass of sqlite3_vtab which will ** serve as the underlying representation of a completion virtual table */ typedef struct completion_vtab completion_vtab; struct completion_vtab { sqlite3_vtab base; /* Base class - must be first */ sqlite3 *db; /* Database connection for this completion vtab */ }; /* completion_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct completion_cursor completion_cursor; struct completion_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3 *db; /* Database connection for this cursor */ int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */ char *zPrefix; /* The prefix for the word we want to complete */ char *zLine; /* The whole that we want to complete */ const char *zCurrentRow; /* Current output row */ sqlite3_stmt *pStmt; /* Current statement */ sqlite3_int64 iRowid; /* The rowid */ int ePhase; /* Current phase */ int j; /* inter-phase counter */ }; /* Values for ePhase: */ #define COMPLETION_FIRST_PHASE 1 #define COMPLETION_KEYWORDS 1 #define COMPLETION_PRAGMAS 2 #define COMPLETION_FUNCTIONS 3 #define COMPLETION_COLLATIONS 4 #define COMPLETION_INDEXES 5 #define COMPLETION_TRIGGERS 6 #define COMPLETION_DATABASES 7 #define COMPLETION_TABLES 8 #define COMPLETION_COLUMNS 9 #define COMPLETION_MODULES 10 #define COMPLETION_EOF 11 /* ** The completionConnect() method is invoked to create a new ** completion_vtab that describes the completion virtual table. ** ** Think of this routine as the constructor for completion_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the completion_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against completion will look like. */ static int completionConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ completion_vtab *pNew; int rc; (void)(pAux); /* Unused parameter */ (void)(argc); /* Unused parameter */ (void)(argv); /* Unused parameter */ (void)(pzErr); /* Unused parameter */ /* Column numbers */ #define COMPLETION_COLUMN_CANDIDATE 0 /* Suggested completion of the input */ #define COMPLETION_COLUMN_PREFIX 1 /* Prefix of the word to be completed */ #define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */ #define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(" " candidate TEXT," " prefix TEXT HIDDEN," " wholeline TEXT HIDDEN," " phase INT HIDDEN" /* Used for debugging only */ ")"); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); pNew->db = db; } return rc; } /* ** This method is the destructor for completion_cursor objects. */ static int completionDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* ** Constructor for a new completion_cursor object. */ static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ completion_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->db = ((completion_vtab*)p)->db; *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Reset the completion_cursor. */ static void completionCursorReset(completion_cursor *pCur){ sqlite3_free(pCur->zPrefix); pCur->zPrefix = 0; pCur->nPrefix = 0; sqlite3_free(pCur->zLine); pCur->zLine = 0; pCur->nLine = 0; sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; pCur->j = 0; } /* ** Destructor for a completion_cursor. */ static int completionClose(sqlite3_vtab_cursor *cur){ completionCursorReset((completion_cursor*)cur); sqlite3_free(cur); return SQLITE_OK; } /* ** All SQL keywords understood by SQLite */ static const char *completionKwrds[] = { "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH", "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA", "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE", "WITH", "WITHOUT", }; #define completionKwCount \ (int)(sizeof(completionKwrds)/sizeof(completionKwrds[0])) /* ** Advance a completion_cursor to its next row of output. ** ** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object ** record the current state of the scan. This routine sets ->zCurrentRow ** to the current row of output and then returns. If no more rows remain, ** then ->ePhase is set to COMPLETION_EOF which will signal the virtual ** table that has reached the end of its scan. ** ** The current implementation just lists potential identifiers and ** keywords and filters them by zPrefix. Future enhancements should ** take zLine into account to try to restrict the set of identifiers and ** keywords based on what would be legal at the current point of input. */ static int completionNext(sqlite3_vtab_cursor *cur){ completion_cursor *pCur = (completion_cursor*)cur; int eNextPhase = 0; /* Next phase to try if current phase reaches end */ int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */ pCur->iRowid++; while( pCur->ePhase!=COMPLETION_EOF ){ switch( pCur->ePhase ){ case COMPLETION_KEYWORDS: { if( pCur->j >= completionKwCount ){ pCur->zCurrentRow = 0; pCur->ePhase = COMPLETION_DATABASES; }else{ pCur->zCurrentRow = completionKwrds[pCur->j++]; } iCol = -1; break; } case COMPLETION_DATABASES: { if( pCur->pStmt==0 ){ sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pCur->pStmt, 0); } iCol = 1; eNextPhase = COMPLETION_TABLES; break; } case COMPLETION_TABLES: { if( pCur->pStmt==0 ){ sqlite3_stmt *pS2; char *zSql = 0; const char *zSep = ""; sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0); while( sqlite3_step(pS2)==SQLITE_ROW ){ const char *zDb = (const char*)sqlite3_column_text(pS2, 1); zSql = sqlite3_mprintf( "%z%s" "SELECT name FROM \"%w\".sqlite_master" " WHERE type='table'", zSql, zSep, zDb ); if( zSql==0 ) return SQLITE_NOMEM; zSep = " UNION "; } sqlite3_finalize(pS2); sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); sqlite3_free(zSql); } iCol = 0; eNextPhase = COMPLETION_COLUMNS; break; } case COMPLETION_COLUMNS: { if( pCur->pStmt==0 ){ sqlite3_stmt *pS2; char *zSql = 0; const char *zSep = ""; sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0); while( sqlite3_step(pS2)==SQLITE_ROW ){ const char *zDb = (const char*)sqlite3_column_text(pS2, 1); zSql = sqlite3_mprintf( "%z%s" "SELECT pti.name FROM \"%w\".sqlite_master AS sm" " JOIN pragma_table_info(sm.name,%Q) AS pti" " WHERE sm.type='table'", zSql, zSep, zDb, zDb ); if( zSql==0 ) return SQLITE_NOMEM; zSep = " UNION "; } sqlite3_finalize(pS2); sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); sqlite3_free(zSql); } iCol = 0; eNextPhase = COMPLETION_EOF; break; } } if( iCol<0 ){ /* This case is when the phase presets zCurrentRow */ if( pCur->zCurrentRow==0 ) continue; }else{ if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){ /* Extract the next row of content */ pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol); }else{ /* When all rows are finished, advance to the next phase */ sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; pCur->ePhase = eNextPhase; continue; } } if( pCur->nPrefix==0 ) break; if( sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 ){ break; } } return SQLITE_OK; } /* ** Return values of columns for the row at which the completion_cursor ** is currently pointing. */ static int completionColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ completion_cursor *pCur = (completion_cursor*)cur; switch( i ){ case COMPLETION_COLUMN_CANDIDATE: { sqlite3_result_text(ctx, pCur->zCurrentRow, -1, SQLITE_TRANSIENT); break; } case COMPLETION_COLUMN_PREFIX: { sqlite3_result_text(ctx, pCur->zPrefix, -1, SQLITE_TRANSIENT); break; } case COMPLETION_COLUMN_WHOLELINE: { sqlite3_result_text(ctx, pCur->zLine, -1, SQLITE_TRANSIENT); break; } case COMPLETION_COLUMN_PHASE: { sqlite3_result_int(ctx, pCur->ePhase); break; } } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int completionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ completion_cursor *pCur = (completion_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int completionEof(sqlite3_vtab_cursor *cur){ completion_cursor *pCur = (completion_cursor*)cur; return pCur->ePhase >= COMPLETION_EOF; } /* ** This method is called to "rewind" the completion_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to completionColumn() or completionRowid() or ** completionEof(). */ static int completionFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ completion_cursor *pCur = (completion_cursor *)pVtabCursor; int iArg = 0; (void)(idxStr); /* Unused parameter */ (void)(argc); /* Unused parameter */ completionCursorReset(pCur); if( idxNum & 1 ){ pCur->nPrefix = sqlite3_value_bytes(argv[iArg]); if( pCur->nPrefix>0 ){ pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg])); if( pCur->zPrefix==0 ) return SQLITE_NOMEM; } iArg++; } if( idxNum & 2 ){ pCur->nLine = sqlite3_value_bytes(argv[iArg]); if( pCur->nLine>0 ){ pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg])); if( pCur->zLine==0 ) return SQLITE_NOMEM; } iArg++; } if( pCur->zLine!=0 && pCur->zPrefix==0 ){ int i = pCur->nLine; while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){ i--; } pCur->nPrefix = pCur->nLine - i; if( pCur->nPrefix>0 ){ pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i); if( pCur->zPrefix==0 ) return SQLITE_NOMEM; } } pCur->iRowid = 0; pCur->ePhase = COMPLETION_FIRST_PHASE; return completionNext(pVtabCursor); } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the completion virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. ** ** There are two hidden parameters that act as arguments to the table-valued ** function: "prefix" and "wholeline". Bit 0 of idxNum is set if "prefix" ** is available and bit 1 is set if "wholeline" is available. */ static int completionBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; /* Loop over constraints */ int idxNum = 0; /* The query plan bitmask */ int prefixIdx = -1; /* Index of the start= constraint, or -1 if none */ int wholelineIdx = -1; /* Index of the stop= constraint, or -1 if none */ int nArg = 0; /* Number of arguments that completeFilter() expects */ const struct sqlite3_index_constraint *pConstraint; (void)(tab); /* Unused parameter */ pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; switch( pConstraint->iColumn ){ case COMPLETION_COLUMN_PREFIX: prefixIdx = i; idxNum |= 1; break; case COMPLETION_COLUMN_WHOLELINE: wholelineIdx = i; idxNum |= 2; break; } } if( prefixIdx>=0 ){ pIdxInfo->aConstraintUsage[prefixIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[prefixIdx].omit = 1; } if( wholelineIdx>=0 ){ pIdxInfo->aConstraintUsage[wholelineIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[wholelineIdx].omit = 1; } pIdxInfo->idxNum = idxNum; pIdxInfo->estimatedCost = (double)5000 - 1000*nArg; pIdxInfo->estimatedRows = 500 - 100*nArg; return SQLITE_OK; } /* ** This following structure defines all the methods for the ** completion virtual table. */ static sqlite3_module completionModule = { 0, /* iVersion */ 0, /* xCreate */ completionConnect, /* xConnect */ completionBestIndex, /* xBestIndex */ completionDisconnect, /* xDisconnect */ 0, /* xDestroy */ completionOpen, /* xOpen - open a cursor */ completionClose, /* xClose - close a cursor */ completionFilter, /* xFilter - configure scan constraints */ completionNext, /* xNext - advance a cursor */ completionEof, /* xEof - check for end of scan */ completionColumn, /* xColumn - read data */ completionRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0 /* xRollbackTo */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ int sqlite3CompletionVtabInit(sqlite3 *db){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3_create_module(db, "completion", &completionModule, 0); #endif return rc; } #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_completion_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); (void)(pzErrMsg); /* Unused parameter */ #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3CompletionVtabInit(db); #endif return rc; } |
Changes to ext/misc/csv.c.
︙ | ︙ | |||
73 74 75 76 77 78 79 | typedef struct CsvReader CsvReader; struct CsvReader { FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ | > | > | 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 | typedef struct CsvReader CsvReader; struct CsvReader { FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int bNotFirst; /* True if prior text has been seen */ int cTerm; /* Character that terminated the most recent field */ size_t iIn; /* Next unread character in the input buffer */ size_t nIn; /* Number of characters in the input buffer */ char *zIn; /* The input buffer */ char zErr[CSV_MXERR]; /* Error message */ }; /* Initialize a CsvReader object */ static void csv_reader_init(CsvReader *p){ p->in = 0; p->z = 0; p->n = 0; p->nAlloc = 0; p->nLine = 0; p->bNotFirst = 0; p->nIn = 0; p->zIn = 0; p->zErr[0] = 0; } /* Close and reset a CsvReader object */ static void csv_reader_reset(CsvReader *p){ |
︙ | ︙ | |||
160 161 162 163 164 165 166 | /* Return the next character of input. Return EOF at end of input. */ static int csv_getc(CsvReader *p){ if( p->iIn >= p->nIn ){ if( p->in!=0 ) return csv_getc_refill(p); return EOF; } | | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | /* Return the next character of input. Return EOF at end of input. */ static int csv_getc(CsvReader *p){ if( p->iIn >= p->nIn ){ if( p->in!=0 ) return csv_getc_refill(p); return EOF; } return ((unsigned char*)p->zIn)[p->iIn++]; } /* Increase the size of p->z and append character c to the end. ** Return 0 on success and non-zero if there is an OOM error */ static CSV_NOINLINE int csv_resize_and_append(CsvReader *p, char c){ char *zNew; int nNew = p->nAlloc*2 + 100; |
︙ | ︙ | |||
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 | } } if( csv_append(p, (char)c) ) return 0; ppc = pc; pc = c; } }else{ while( c>',' || (c!=EOF && c!=',' && c!='\n') ){ if( csv_append(p, (char)c) ) return 0; c = csv_getc(p); } if( c=='\n' ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } p->cTerm = (char)c; } if( p->z ) p->z[p->n] = 0; return p->z; } /* Forward references to the various virtual table methods implemented ** in this file. */ static int csvtabCreate(sqlite3*, void*, int, const char*const*, | > > > > > > > > > > > > > > > > | 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 | } } if( csv_append(p, (char)c) ) return 0; ppc = pc; pc = c; } }else{ /* If this is the first field being parsed and it begins with the ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ if( (c&0xff)==0xef && p->bNotFirst==0 ){ csv_append(p, (char)c); c = csv_getc(p); if( (c&0xff)==0xbb ){ csv_append(p, (char)c); c = csv_getc(p); if( (c&0xff)==0xbf ){ p->bNotFirst = 1; p->n = 0; return csv_read_one_field(p); } } } while( c>',' || (c!=EOF && c!=',' && c!='\n') ){ if( csv_append(p, (char)c) ) return 0; c = csv_getc(p); } if( c=='\n' ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } p->cTerm = (char)c; } if( p->z ) p->z[p->n] = 0; p->bNotFirst = 1; return p->z; } /* Forward references to the various virtual table methods implemented ** in this file. */ static int csvtabCreate(sqlite3*, void*, int, const char*const*, |
︙ | ︙ | |||
398 399 400 401 402 403 404 | /* Return 0 if the argument is false and 1 if it is true. Return -1 if ** we cannot really tell. */ static int csv_boolean(const char *z){ if( sqlite3_stricmp("yes",z)==0 || sqlite3_stricmp("on",z)==0 || sqlite3_stricmp("true",z)==0 | | | 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | /* Return 0 if the argument is false and 1 if it is true. Return -1 if ** we cannot really tell. */ static int csv_boolean(const char *z){ if( sqlite3_stricmp("yes",z)==0 || sqlite3_stricmp("on",z)==0 || sqlite3_stricmp("true",z)==0 || (z[0]=='1' && z[1]==0) ){ return 1; } if( sqlite3_stricmp("no",z)==0 || sqlite3_stricmp("off",z)==0 || sqlite3_stricmp("false",z)==0 || (z[0]=='0' && z[1]==0) |
︙ | ︙ | |||
658 659 660 661 662 663 664 | pCur->azVal[i] = zNew; pCur->aLen[i] = pCur->rdr.n+1; } memcpy(pCur->azVal[i], z, pCur->rdr.n+1); i++; } }while( pCur->rdr.cTerm==',' ); | < < < < < < | > > > > > > | 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 | pCur->azVal[i] = zNew; pCur->aLen[i] = pCur->rdr.n+1; } memcpy(pCur->azVal[i], z, pCur->rdr.n+1); i++; } }while( pCur->rdr.cTerm==',' ); if( z==0 || (pCur->rdr.cTerm==EOF && i<pTab->nCol) ){ pCur->iRowid = -1; }else{ pCur->iRowid++; while( i<pTab->nCol ){ sqlite3_free(pCur->azVal[i]); pCur->azVal[i] = 0; pCur->aLen[i] = 0; i++; } } return SQLITE_OK; } /* ** Return values of columns for the row at which the CsvCursor ** is currently pointing. |
︙ | ︙ |
Added ext/misc/dbdump.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 | /* ** 2016-03-13 ** ** 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 a C-language subroutine that converts the content ** of an SQLite database into UTF-8 text SQL statements that can be used ** to exactly recreate the original database. ROWID values are preserved. ** ** A prototype of the implemented subroutine is this: ** ** int sqlite3_db_dump( ** sqlite3 *db, ** const char *zSchema, ** const char *zTable, ** void (*xCallback)(void*, const char*), ** void *pArg ** ); ** ** The db parameter is the database connection. zSchema is the schema within ** that database which is to be dumped. Usually the zSchema is "main" but ** can also be "temp" or any ATTACH-ed database. If zTable is not NULL, then ** only the content of that one table is dumped. If zTable is NULL, then all ** tables are dumped. ** ** The generate text is passed to xCallback() in multiple calls. The second ** argument to xCallback() is a copy of the pArg parameter. The first ** argument is some of the output text that this routine generates. The ** signature to xCallback() is designed to make it compatible with fputs(). ** ** The sqlite3_db_dump() subroutine returns SQLITE_OK on success or some error ** code if it encounters a problem. ** ** If this file is compiled with -DDBDUMP_STANDALONE then a "main()" routine ** is included so that this routine becomes a command-line utility. The ** command-line utility takes two or three arguments which are the name ** of the database file, the schema, and optionally the table, forming the ** first three arguments of a single call to the library routine. */ #include "sqlite3.h" #include <stdarg.h> #include <string.h> #include <ctype.h> /* ** The state of the dump process. */ typedef struct DState DState; struct DState { sqlite3 *db; /* The database connection */ int nErr; /* Number of errors seen so far */ int rc; /* Error code */ int writableSchema; /* True if in writable_schema mode */ int (*xCallback)(const char*,void*); /* Send output here */ void *pArg; /* Argument to xCallback() */ }; /* ** A variable length string to which one can append text. */ typedef struct DText DText; struct DText { char *z; /* The text */ int n; /* Number of bytes of content in z[] */ int nAlloc; /* Number of bytes allocated to z[] */ }; /* ** Initialize and destroy a DText object */ static void initText(DText *p){ memset(p, 0, sizeof(*p)); } static void freeText(DText *p){ sqlite3_free(p->z); initText(p); } /* zIn is either a pointer to a NULL-terminated string in memory obtained ** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** added to zIn, and the result returned in memory obtained from malloc(). ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ static void appendText(DText *p, char const *zAppend, char quote){ int len; int i; int nAppend = (int)(strlen(zAppend) & 0x3fffffff); len = nAppend+p->n+1; if( quote ){ len += 2; for(i=0; i<nAppend; i++){ if( zAppend[i]==quote ) len++; } } if( p->n+len>=p->nAlloc ){ char *zNew; p->nAlloc = p->nAlloc*2 + len + 20; zNew = sqlite3_realloc(p->z, p->nAlloc); if( zNew==0 ){ freeText(p); return; } p->z = zNew; } if( quote ){ char *zCsr = p->z+p->n; *zCsr++ = quote; for(i=0; i<nAppend; i++){ *zCsr++ = zAppend[i]; if( zAppend[i]==quote ) *zCsr++ = quote; } *zCsr++ = quote; p->n = (int)(zCsr - p->z); *zCsr = '\0'; }else{ memcpy(p->z+p->n, zAppend, nAppend); p->n += nAppend; p->z[p->n] = '\0'; } } /* ** Attempt to determine if identifier zName needs to be quoted, either ** because it contains non-alphanumeric characters, or because it is an ** SQLite keyword. Be conservative in this estimate: When in doubt assume ** that quoting is required. ** ** Return '"' if quoting is required. Return 0 if no quoting is required. */ static char quoteChar(const char *zName){ /* All SQLite keywords, in alphabetical order */ static const char *azKeywords[] = { "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH", "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA", "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE", "WITH", "WITHOUT", }; int i, lwr, upr, mid, c; if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; for(i=0; zName[i]; i++){ if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; } lwr = 0; upr = sizeof(azKeywords)/sizeof(azKeywords[0]) - 1; while( lwr<=upr ){ mid = (lwr+upr)/2; c = sqlite3_stricmp(azKeywords[mid], zName); if( c==0 ) return '"'; if( c<0 ){ lwr = mid+1; }else{ upr = mid-1; } } return 0; } /* ** Release memory previously allocated by tableColumnList(). */ static void freeColumnList(char **azCol){ int i; for(i=1; azCol[i]; i++){ sqlite3_free(azCol[i]); } /* azCol[0] is a static string */ sqlite3_free(azCol); } /* ** Return a list of pointers to strings which are the names of all ** columns in table zTab. The memory to hold the names is dynamically ** allocated and must be released by the caller using a subsequent call ** to freeColumnList(). ** ** The azCol[0] entry is usually NULL. However, if zTab contains a rowid ** value that needs to be preserved, then azCol[0] is filled in with the ** name of the rowid column. ** ** The first regular column in the table is azCol[1]. The list is terminated ** by an entry with azCol[i]==0. */ static char **tableColumnList(DState *p, const char *zTab){ char **azCol = 0; sqlite3_stmt *pStmt = 0; char *zSql; int nCol = 0; int nAlloc = 0; int nPK = 0; /* Number of PRIMARY KEY columns seen */ int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */ int preserveRowid = 1; int rc; zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab); if( zSql==0 ) return 0; rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ) return 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nCol>=nAlloc-2 ){ char **azNew; nAlloc = nAlloc*2 + nCol + 10; azNew = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0])); if( azNew==0 ) goto col_oom; azCol = azNew; azCol[0] = 0; } azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); if( azCol[nCol]==0 ) goto col_oom; if( sqlite3_column_int(pStmt, 5) ){ nPK++; if( nPK==1 && sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2), "INTEGER")==0 ){ isIPK = 1; }else{ isIPK = 0; } } } sqlite3_finalize(pStmt); pStmt = 0; azCol[nCol+1] = 0; /* The decision of whether or not a rowid really needs to be preserved ** is tricky. We never need to preserve a rowid for a WITHOUT ROWID table ** or a table with an INTEGER PRIMARY KEY. We are unable to preserve ** rowids on tables where the rowid is inaccessible because there are other ** columns in the table named "rowid", "_rowid_", and "oid". */ if( isIPK ){ /* If a single PRIMARY KEY column with type INTEGER was seen, then it ** might be an alise for the ROWID. But it might also be a WITHOUT ROWID ** table or a INTEGER PRIMARY KEY DESC column, neither of which are ** ROWID aliases. To distinguish these cases, check to see if ** there is a "pk" entry in "PRAGMA index_list". There will be ** no "pk" index if the PRIMARY KEY really is an alias for the ROWID. */ zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)" " WHERE origin='pk'", zTab); if( zSql==0 ) goto col_oom; rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ freeColumnList(azCol); return 0; } rc = sqlite3_step(pStmt); sqlite3_finalize(pStmt); pStmt = 0; preserveRowid = rc==SQLITE_ROW; } if( preserveRowid ){ /* Only preserve the rowid if we can find a name to use for the ** rowid */ static char *azRowid[] = { "rowid", "_rowid_", "oid" }; int i, j; for(j=0; j<3; j++){ for(i=1; i<=nCol; i++){ if( sqlite3_stricmp(azRowid[j],azCol[i])==0 ) break; } if( i>nCol ){ /* At this point, we know that azRowid[j] is not the name of any ** ordinary column in the table. Verify that azRowid[j] is a valid ** name for the rowid before adding it to azCol[0]. WITHOUT ROWID ** tables will fail this last check */ int rc; rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0); if( rc==SQLITE_OK ) azCol[0] = azRowid[j]; break; } } } return azCol; col_oom: sqlite3_finalize(pStmt); freeColumnList(azCol); p->nErr++; p->rc = SQLITE_NOMEM; return 0; } /* ** Send mprintf-formatted content to the output callback. */ static void output_formatted(DState *p, const char *zFormat, ...){ va_list ap; char *z; va_start(ap, zFormat); z = sqlite3_vmprintf(zFormat, ap); va_end(ap); p->xCallback(z, p->pArg); sqlite3_free(z); } /* ** Find a string that is not found anywhere in z[]. Return a pointer ** to that string. ** ** Try to use zA and zB first. If both of those are already found in z[] ** then make up some string and store it in the buffer zBuf. */ static const char *unused_string( const char *z, /* Result must not appear anywhere in z */ const char *zA, const char *zB, /* Try these first */ char *zBuf /* Space to store a generated string */ ){ unsigned i = 0; if( strstr(z, zA)==0 ) return zA; if( strstr(z, zB)==0 ) return zB; do{ sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++); }while( strstr(z,zBuf)!=0 ); return zBuf; } /* ** Output the given string as a quoted string using SQL quoting conventions. ** Additionallly , escape the "\n" and "\r" characters so that they do not ** get corrupted by end-of-line translation facilities in some operating ** systems. */ static void output_quoted_escaped_string(DState *p, const char *z){ int i; char c; for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} if( c==0 ){ output_formatted(p,"'%s'",z); }else{ const char *zNL = 0; const char *zCR = 0; int nNL = 0; int nCR = 0; char zBuf1[20], zBuf2[20]; for(i=0; z[i]; i++){ if( z[i]=='\n' ) nNL++; if( z[i]=='\r' ) nCR++; } if( nNL ){ p->xCallback("replace(", p->pArg); zNL = unused_string(z, "\\n", "\\012", zBuf1); } if( nCR ){ p->xCallback("replace(", p->pArg); zCR = unused_string(z, "\\r", "\\015", zBuf2); } p->xCallback("'", p->pArg); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} if( c=='\'' ) i++; if( i ){ output_formatted(p, "%.*s", i, z); z += i; } if( c=='\'' ){ p->xCallback("'", p->pArg); continue; } if( c==0 ){ break; } z++; if( c=='\n' ){ p->xCallback(zNL, p->pArg); continue; } p->xCallback(zCR, p->pArg); } p->xCallback("'", p->pArg); if( nCR ){ output_formatted(p, ",'%s',char(13))", zCR); } if( nNL ){ output_formatted(p, ",'%s',char(10))", zNL); } } } /* ** This is an sqlite3_exec callback routine used for dumping the database. ** Each row received by this callback consists of a table name, ** the table type ("index" or "table") and SQL to create the table. ** This routine should print text sufficient to recreate the table. */ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ int rc; const char *zTable; const char *zType; const char *zSql; DState *p = (DState*)pArg; sqlite3_stmt *pStmt; (void)azCol; if( nArg!=3 ) return 1; zTable = azArg[0]; zType = azArg[1]; zSql = azArg[2]; if( strcmp(zTable, "sqlite_sequence")==0 ){ p->xCallback("DELETE FROM sqlite_sequence;\n", p->pArg); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){ p->xCallback("ANALYZE sqlite_master;\n", p->pArg); }else if( strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ if( !p->writableSchema ){ p->xCallback("PRAGMA writable_schema=ON;\n", p->pArg); p->writableSchema = 1; } output_formatted(p, "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); return 0; }else{ if( sqlite3_strglob("CREATE TABLE ['\"]*", zSql)==0 ){ p->xCallback("CREATE TABLE IF NOT EXISTS ", p->pArg); p->xCallback(zSql+13, p->pArg); }else{ p->xCallback(zSql, p->pArg); } p->xCallback(";\n", p->pArg); } if( strcmp(zType, "table")==0 ){ DText sSelect; DText sTable; char **azCol; int i; int nCol; azCol = tableColumnList(p, zTable); if( azCol==0 ) return 0; initText(&sTable); appendText(&sTable, "INSERT INTO ", 0); /* Always quote the table name, even if it appears to be pure ascii, ** in case it is a keyword. Ex: INSERT INTO "table" ... */ appendText(&sTable, zTable, quoteChar(zTable)); /* If preserving the rowid, add a column list after the table name. ** In other words: "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)" ** instead of the usual "INSERT INTO tab VALUES(...)". */ if( azCol[0] ){ appendText(&sTable, "(", 0); appendText(&sTable, azCol[0], 0); for(i=1; azCol[i]; i++){ appendText(&sTable, ",", 0); appendText(&sTable, azCol[i], quoteChar(azCol[i])); } appendText(&sTable, ")", 0); } appendText(&sTable, " VALUES(", 0); /* Build an appropriate SELECT statement */ initText(&sSelect); appendText(&sSelect, "SELECT ", 0); if( azCol[0] ){ appendText(&sSelect, azCol[0], 0); appendText(&sSelect, ",", 0); } for(i=1; azCol[i]; i++){ appendText(&sSelect, azCol[i], quoteChar(azCol[i])); if( azCol[i+1] ){ appendText(&sSelect, ",", 0); } } nCol = i; if( azCol[0]==0 ) nCol--; freeColumnList(azCol); appendText(&sSelect, " FROM ", 0); appendText(&sSelect, zTable, quoteChar(zTable)); rc = sqlite3_prepare_v2(p->db, sSelect.z, -1, &pStmt, 0); if( rc!=SQLITE_OK ){ p->nErr++; if( p->rc==SQLITE_OK ) p->rc = rc; }else{ while( SQLITE_ROW==sqlite3_step(pStmt) ){ p->xCallback(sTable.z, p->pArg); for(i=0; i<nCol; i++){ if( i ) p->xCallback(",", p->pArg); switch( sqlite3_column_type(pStmt,i) ){ case SQLITE_INTEGER: { output_formatted(p, "%lld", sqlite3_column_int64(pStmt,i)); break; } case SQLITE_FLOAT: { double r = sqlite3_column_double(pStmt,i); output_formatted(p, "%!.20g", r); break; } case SQLITE_NULL: { p->xCallback("NULL", p->pArg); break; } case SQLITE_TEXT: { output_quoted_escaped_string(p, (const char*)sqlite3_column_text(pStmt,i)); break; } case SQLITE_BLOB: { int nByte = sqlite3_column_bytes(pStmt,i); unsigned char *a = (unsigned char*)sqlite3_column_blob(pStmt,i); int j; p->xCallback("x'", p->pArg); for(j=0; j<nByte; j++){ char zWord[3]; zWord[0] = "0123456789abcdef"[(a[j]>>4)&15]; zWord[1] = "0123456789abcdef"[a[j]&15]; zWord[2] = 0; p->xCallback(zWord, p->pArg); } p->xCallback("'", p->pArg); break; } } } p->xCallback(");\n", p->pArg); } } sqlite3_finalize(pStmt); freeText(&sTable); freeText(&sSelect); } return 0; } /* ** Execute a query statement that will generate SQL output. Print ** the result columns, comma-separated, on a line and then add a ** semicolon terminator to the end of that line. ** ** If the number of columns is 1 and that column contains text "--" ** then write the semicolon on a separate line. That way, if a ** "--" comment occurs at the end of the statement, the comment ** won't consume the semicolon terminator. */ static void output_sql_from_query( DState *p, /* Query context */ const char *zSelect, /* SELECT statement to extract content */ ... ){ sqlite3_stmt *pSelect; int rc; int nResult; int i; const char *z; char *zSql; va_list ap; va_start(ap, zSelect); zSql = sqlite3_vmprintf(zSelect, ap); va_end(ap); if( zSql==0 ){ p->rc = SQLITE_NOMEM; p->nErr++; return; } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pSelect, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK || !pSelect ){ output_formatted(p, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); p->nErr++; return; } rc = sqlite3_step(pSelect); nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ z = (const char*)sqlite3_column_text(pSelect, 0); p->xCallback(z, p->pArg); for(i=1; i<nResult; i++){ p->xCallback(",", p->pArg); p->xCallback((const char*)sqlite3_column_text(pSelect,i), p->pArg); } if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ p->xCallback("\n;\n", p->pArg); }else{ p->xCallback(";\n", p->pArg); } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ output_formatted(p, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; } } /* ** Run zQuery. Use dump_callback() as the callback routine so that ** the contents of the query are output as SQL statements. ** ** If we get a SQLITE_CORRUPT error, rerun the query after appending ** "ORDER BY rowid DESC" to the end. */ static void run_schema_dump_query( DState *p, const char *zQuery, ... ){ char *zErr = 0; char *z; va_list ap; va_start(ap, zQuery); z = sqlite3_vmprintf(zQuery, ap); va_end(ap); sqlite3_exec(p->db, z, dump_callback, p, &zErr); sqlite3_free(z); if( zErr ){ output_formatted(p, "/****** %s ******/\n", zErr); sqlite3_free(zErr); p->nErr++; zErr = 0; } } /* ** Convert an SQLite database into SQL statements that will recreate that ** database. */ int sqlite3_db_dump( sqlite3 *db, /* The database connection */ const char *zSchema, /* Which schema to dump. Usually "main". */ const char *zTable, /* Which table to dump. NULL means everything. */ int (*xCallback)(const char*,void*), /* Output sent to this callback */ void *pArg /* Second argument of the callback */ ){ DState x; memset(&x, 0, sizeof(x)); x.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0); if( x.rc ) return x.rc; x.db = db; x.xCallback = xCallback; x.pArg = pArg; xCallback("PRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\n", pArg); if( zTable==0 ){ run_schema_dump_query(&x, "SELECT name, type, sql FROM \"%w\".sqlite_master " "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'", zSchema ); run_schema_dump_query(&x, "SELECT name, type, sql FROM \"%w\".sqlite_master " "WHERE name=='sqlite_sequence'", zSchema ); output_sql_from_query(&x, "SELECT sql FROM sqlite_master " "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 ); }else{ run_schema_dump_query(&x, "SELECT name, type, sql FROM \"%w\".sqlite_master " "WHERE tbl_name=%Q COLLATE nocase AND type=='table'" " AND sql NOT NULL", zSchema, zTable ); output_sql_from_query(&x, "SELECT sql FROM \"%w\".sqlite_master " "WHERE sql NOT NULL" " AND type IN ('index','trigger','view')" " AND tbl_name=%Q COLLATE nocase", zSchema, zTable ); } if( x.writableSchema ){ xCallback("PRAGMA writable_schema=OFF;\n", pArg); } xCallback(x.nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n", pArg); sqlite3_exec(db, "COMMIT", 0, 0, 0); return x.rc; } /* The generic subroutine is above. The code the follows implements ** the command-line interface. */ #ifdef DBDUMP_STANDALONE #include <stdio.h> /* ** Command-line interface */ int main(int argc, char **argv){ sqlite3 *db; const char *zDb; const char *zSchema; const char *zTable = 0; int rc; if( argc<2 || argc>4 ){ fprintf(stderr, "Usage: %s DATABASE ?SCHEMA? ?TABLE?\n", argv[0]); return 1; } zDb = argv[1]; zSchema = argc>=3 ? argv[2] : "main"; zTable = argc==4 ? argv[3] : 0; rc = sqlite3_open(zDb, &db); if( rc ){ fprintf(stderr, "Cannot open \"%s\": %s\n", zDb, sqlite3_errmsg(db)); sqlite3_close(db); return 1; } rc = sqlite3_db_dump(db, zSchema, zTable, (int(*)(const char*,void*))fputs, (void*)stdout); if( rc ){ fprintf(stderr, "Error: sqlite3_db_dump() returns %d\n", rc); } sqlite3_close(db); return rc!=SQLITE_OK; } #endif /* DBDUMP_STANDALONE */ |
Changes to ext/misc/fileio.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 35 36 37 38 39 40 41 | sqlite3_value **argv ){ const char *zName; FILE *in; long nIn; void *pBuf; zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; in = fopen(zName, "rb"); if( in==0 ) return; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); | > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | sqlite3_value **argv ){ const char *zName; FILE *in; long nIn; void *pBuf; (void)(argc); /* Unused parameter */ zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; in = fopen(zName, "rb"); if( in==0 ) return; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); |
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 71 72 73 | sqlite3_value **argv ){ FILE *out; const char *z; sqlite3_int64 rc; const char *zFile; zFile = (const char*)sqlite3_value_text(argv[0]); if( zFile==0 ) return; out = fopen(zFile, "wb"); if( out==0 ) return; z = (const char*)sqlite3_value_blob(argv[1]); if( z==0 ){ rc = 0; | > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | sqlite3_value **argv ){ FILE *out; const char *z; sqlite3_int64 rc; const char *zFile; (void)(argc); /* Unused parameter */ zFile = (const char*)sqlite3_value_text(argv[0]); if( zFile==0 ) return; out = fopen(zFile, "wb"); if( out==0 ) return; z = (const char*)sqlite3_value_blob(argv[1]); if( z==0 ){ rc = 0; |
︙ | ︙ |
Changes to ext/misc/json1.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** For the time being, all JSON is stored as pure text. (We might add ** a JSONB type in the future which stores a binary encoding of JSON in ** a BLOB, but there is no support for JSONB in the current implementation. ** This implementation parses JSON text at 250 MB/s, so it is hard to see ** how JSONB might improve on that.) */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** For the time being, all JSON is stored as pure text. (We might add ** a JSONB type in the future which stores a binary encoding of JSON in ** a BLOB, but there is no support for JSONB in the current implementation. ** This implementation parses JSON text at 250 MB/s, so it is hard to see ** how JSONB might improve on that.) */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) #if !defined(SQLITEINT_H) #include "sqlite3ext.h" #endif SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> |
︙ | ︙ | |||
45 46 47 48 49 50 51 | /* ** Versions of isspace(), isalnum() and isdigit() to which it is safe ** to pass signed char values. */ #ifdef sqlite3Isdigit /* Use the SQLite core versions if this routine is part of the ** SQLite amalgamation */ | | | > | | > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /* ** Versions of isspace(), isalnum() and isdigit() to which it is safe ** to pass signed char values. */ #ifdef sqlite3Isdigit /* Use the SQLite core versions if this routine is part of the ** SQLite amalgamation */ # define safe_isdigit(x) sqlite3Isdigit(x) # define safe_isalnum(x) sqlite3Isalnum(x) # define safe_isxdigit(x) sqlite3Isxdigit(x) #else /* Use the standard library for separate compilation */ #include <ctype.h> /* amalgamator: keep */ # define safe_isdigit(x) isdigit((unsigned char)(x)) # define safe_isalnum(x) isalnum((unsigned char)(x)) # define safe_isxdigit(x) isxdigit((unsigned char)(x)) #endif /* ** 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 parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). */ |
︙ | ︙ | |||
84 85 86 87 88 89 90 91 92 93 94 95 96 97 | #define safe_isspace(x) (jsonIsSpace[(unsigned char)x]) #ifndef SQLITE_AMALGAMATION /* Unsigned integer types. These are already defined in the sqliteInt.h, ** but the definitions need to be repeated for separate compilation. */ typedef sqlite3_uint64 u64; typedef unsigned int u32; typedef unsigned char u8; #endif /* Objects */ typedef struct JsonString JsonString; typedef struct JsonNode JsonNode; typedef struct JsonParse JsonParse; | > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | #define safe_isspace(x) (jsonIsSpace[(unsigned char)x]) #ifndef SQLITE_AMALGAMATION /* Unsigned integer types. These are already defined in the sqliteInt.h, ** but the definitions need to be repeated for separate compilation. */ typedef sqlite3_uint64 u64; typedef unsigned int u32; typedef unsigned short int u16; typedef unsigned char u8; #endif /* Objects */ typedef struct JsonString JsonString; typedef struct JsonNode JsonNode; typedef struct JsonParse JsonParse; |
︙ | ︙ | |||
132 133 134 135 136 137 138 | }; /* Bit values for the JsonNode.jnFlag field */ #define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ #define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ #define JNODE_REMOVE 0x04 /* Do not output */ | | > | | < > > > > > > > > > > > > > | 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 | }; /* Bit values for the JsonNode.jnFlag field */ #define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ #define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ #define JNODE_REMOVE 0x04 /* Do not output */ #define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */ #define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ #define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ #define JNODE_LABEL 0x40 /* Is a label of an object */ /* A single node of parsed JSON */ struct JsonNode { u8 eType; /* One of the JSON_ type values */ u8 jnFlags; /* JNODE flags */ u32 n; /* Bytes of content, or number of sub-nodes */ union { const char *zJContent; /* Content for INT, REAL, and STRING */ u32 iAppend; /* More terms for ARRAY and OBJECT */ u32 iKey; /* Key for ARRAY objects in json_tree() */ u32 iReplace; /* Replacement content for JNODE_REPLACE */ JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */ } u; }; /* A completely parsed JSON string */ struct JsonParse { u32 nNode; /* Number of slots of aNode[] used */ u32 nAlloc; /* Number of slots of aNode[] allocated */ JsonNode *aNode; /* Array of nodes containing the parse */ const char *zJson; /* Original JSON string */ u32 *aUp; /* Index of parent of each node */ u8 oom; /* Set to true if out of memory */ u8 nErr; /* Number of errors seen */ u16 iDepth; /* Nesting depth */ int nJson; /* Length of the zJson string in bytes */ }; /* ** 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 2000 is far deeper than any sane JSON ** should go. */ #define JSON_MAX_DEPTH 2000 /************************************************************************** ** Utility routines for dealing with JsonString objects **************************************************************************/ /* Set the JsonString object to an empty string */ static void jsonZero(JsonString *p){ |
︙ | ︙ | |||
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 | sqlite3_free(pParse->aNode); pParse->aNode = 0; pParse->nNode = 0; pParse->nAlloc = 0; sqlite3_free(pParse->aUp); pParse->aUp = 0; } /* ** Convert the JsonNode pNode into a pure JSON string and ** append to pOut. Subsubstructure is also included. Return ** the number of JsonNode objects that are encoded. */ static void jsonRenderNode( JsonNode *pNode, /* The node to render */ JsonString *pOut, /* Write JSON here */ sqlite3_value **aReplace /* Replacement values */ ){ switch( pNode->eType ){ default: { assert( pNode->eType==JSON_NULL ); jsonAppendRaw(pOut, "null", 4); break; } case JSON_TRUE: { | > > > > > > > > > > > > > > > | 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 | sqlite3_free(pParse->aNode); pParse->aNode = 0; pParse->nNode = 0; pParse->nAlloc = 0; sqlite3_free(pParse->aUp); pParse->aUp = 0; } /* ** Free a JsonParse object that was obtained from sqlite3_malloc(). */ static void jsonParseFree(JsonParse *pParse){ jsonParseReset(pParse); sqlite3_free(pParse); } /* ** Convert the JsonNode pNode into a pure JSON string and ** append to pOut. Subsubstructure is also included. Return ** the number of JsonNode objects that are encoded. */ static void jsonRenderNode( JsonNode *pNode, /* The node to render */ JsonString *pOut, /* Write JSON here */ sqlite3_value **aReplace /* Replacement values */ ){ if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){ if( pNode->jnFlags & JNODE_REPLACE ){ jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); return; } pNode = pNode->u.pPatch; } switch( pNode->eType ){ default: { assert( pNode->eType==JSON_NULL ); jsonAppendRaw(pOut, "null", 4); break; } case JSON_TRUE: { |
︙ | ︙ | |||
435 436 437 438 439 440 441 | break; } case JSON_ARRAY: { u32 j = 1; jsonAppendChar(pOut, '['); for(;;){ while( j<=pNode->n ){ | < | < < < < | 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 | break; } case JSON_ARRAY: { u32 j = 1; jsonAppendChar(pOut, '['); for(;;){ while( j<=pNode->n ){ if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){ jsonAppendSeparator(pOut); jsonRenderNode(&pNode[j], pOut, aReplace); } j += jsonNodeSize(&pNode[j]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; pNode = &pNode[pNode->u.iAppend]; |
︙ | ︙ | |||
462 463 464 465 466 467 468 | jsonAppendChar(pOut, '{'); for(;;){ while( j<=pNode->n ){ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){ jsonAppendSeparator(pOut); jsonRenderNode(&pNode[j], pOut, aReplace); jsonAppendChar(pOut, ':'); | < < < | < | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | jsonAppendChar(pOut, '{'); for(;;){ while( j<=pNode->n ){ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){ jsonAppendSeparator(pOut); jsonRenderNode(&pNode[j], pOut, aReplace); jsonAppendChar(pOut, ':'); jsonRenderNode(&pNode[j+1], pOut, aReplace); } j += 1 + jsonNodeSize(&pNode[j+1]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; pNode = &pNode[pNode->u.iAppend]; j = 1; } |
︙ | ︙ | |||
589 590 591 592 593 594 595 | char c = z[i]; if( c!='\\' ){ zOut[j++] = c; }else{ c = z[++i]; if( c=='u' ){ u32 v = 0, k; | | > > | | | < | 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 | char c = z[i]; if( c!='\\' ){ zOut[j++] = c; }else{ c = z[++i]; if( c=='u' ){ u32 v = 0, k; for(k=0; k<4; i++, k++){ assert( i<n-2 ); c = z[i+1]; assert( safe_isxdigit(c) ); if( c<='9' ) v = v*16 + c - '0'; else if( c<='F' ) v = v*16 + c - 'A' + 10; else v = v*16 + c - 'a' + 10; } if( v==0 ) break; if( v<=0x7f ){ zOut[j++] = (char)v; }else if( v<=0x7ff ){ zOut[j++] = (char)(0xc0 | (v>>6)); zOut[j++] = 0x80 | (v&0x3f); |
︙ | ︙ | |||
692 693 694 695 696 697 698 | JsonNode *p; if( pParse->nNode>=pParse->nAlloc ){ return jsonParseAddNodeExpand(pParse, eType, n, zContent); } p = &pParse->aNode[pParse->nNode]; p->eType = (u8)eType; p->jnFlags = 0; | < > > > > > > > > > > | | | > > | | > | | | > > | | | > > | > | | > > | > > > | | | | | | | > > > > > | | | | | | | | 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 | JsonNode *p; if( pParse->nNode>=pParse->nAlloc ){ return jsonParseAddNodeExpand(pParse, eType, n, zContent); } p = &pParse->aNode[pParse->nNode]; p->eType = (u8)eType; p->jnFlags = 0; p->n = n; p->u.zJContent = zContent; return pParse->nNode++; } /* ** Return true if z[] begins with 4 (or more) hexadecimal digits */ static int jsonIs4Hex(const char *z){ int i; for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0; return 1; } /* ** Parse a single JSON value which begins at pParse->zJson[i]. Return the ** index of the first character past the end of the value parsed. ** ** Return negative for a syntax error. Special cases: return -2 if the ** first non-whitespace character is '}' and return -3 if the first ** non-whitespace character is ']'. */ static int jsonParseValue(JsonParse *pParse, u32 i){ char c; u32 j; int iThis; int x; JsonNode *pNode; const char *z = pParse->zJson; while( safe_isspace(z[i]) ){ i++; } if( (c = z[i])=='{' ){ /* Parse object */ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; for(j=i+1;;j++){ while( safe_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); if( x<0 ){ pParse->iDepth--; if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1; return -1; } if( pParse->oom ) return -1; pNode = &pParse->aNode[pParse->nNode-1]; if( pNode->eType!=JSON_STRING ) return -1; pNode->jnFlags |= JNODE_LABEL; j = x; while( safe_isspace(z[j]) ){ j++; } if( z[j]!=':' ) return -1; j++; x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ) return -1; j = x; while( safe_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!='}' ) return -1; break; } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; return j+1; }else if( c=='[' ){ /* Parse array */ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); if( iThis<0 ) return -1; for(j=i+1;;j++){ while( safe_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ){ if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1; return -1; } j = x; while( safe_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!=']' ) return -1; break; } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; return j+1; }else if( c=='"' ){ /* Parse string */ u8 jnFlags = 0; j = i+1; for(;;){ c = z[j]; if( (c & ~0x1f)==0 ){ /* Control characters are not allowed in strings */ return -1; } 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)) ){ jnFlags = JNODE_ESCAPE; }else{ return -1; } }else if( c=='"' ){ break; } j++; } jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]); if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; return j+1; }else if( c=='n' && strncmp(z+i,"null",4)==0 && !safe_isalnum(z[i+4]) ){ jsonParseAddNode(pParse, JSON_NULL, 0, 0); return i+4; }else if( c=='t' && strncmp(z+i,"true",4)==0 && !safe_isalnum(z[i+4]) ){ jsonParseAddNode(pParse, JSON_TRUE, 0, 0); return i+4; }else if( c=='f' && strncmp(z+i,"false",5)==0 && !safe_isalnum(z[i+5]) ){ jsonParseAddNode(pParse, JSON_FALSE, 0, 0); return i+5; }else if( c=='-' || (c>='0' && c<='9') ){ /* Parse number */ u8 seenDP = 0; u8 seenE = 0; assert( '-' < '0' ); if( c<='0' ){ j = c=='-' ? i+1 : i; if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1; } j = i+1; for(;; j++){ c = z[j]; if( c>='0' && c<='9' ) continue; if( c=='.' ){ if( z[j-1]=='-' ) return -1; if( seenDP ) return -1; seenDP = 1; continue; } if( c=='e' || c=='E' ){ if( z[j-1]<'0' ) return -1; if( seenE ) return -1; seenDP = seenE = 1; c = z[j+1]; if( c=='+' || c=='-' ){ j++; c = z[j+1]; } if( c<'0' || c>'9' ) return -1; continue; } break; } if( z[j-1]<'0' ) return -1; jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, j - i, &z[i]); return j; }else if( c=='}' ){ return -2; /* End of {...} */ }else if( c==']' ){ return -3; /* End of [...] */ }else if( c==0 ){ return 0; /* End of file */ |
︙ | ︙ | |||
859 860 861 862 863 864 865 866 867 868 869 870 871 872 | int i; memset(pParse, 0, sizeof(*pParse)); if( zJson==0 ) return 1; pParse->zJson = zJson; i = jsonParseValue(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ while( safe_isspace(zJson[i]) ) i++; if( zJson[i] ) i = -1; } if( i<=0 ){ if( pCtx!=0 ){ if( pParse->oom ){ sqlite3_result_error_nomem(pCtx); | > | 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 | int i; memset(pParse, 0, sizeof(*pParse)); if( zJson==0 ) return 1; pParse->zJson = zJson; i = jsonParseValue(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); while( safe_isspace(zJson[i]) ) i++; if( zJson[i] ) i = -1; } if( i<=0 ){ if( pCtx!=0 ){ if( pParse->oom ){ sqlite3_result_error_nomem(pCtx); |
︙ | ︙ | |||
917 918 919 920 921 922 923 924 925 926 927 928 929 930 | if( aUp==0 ){ pParse->oom = 1; return SQLITE_NOMEM; } jsonParseFillInParentage(pParse, 0, 0); return SQLITE_OK; } /* ** Compare the OBJECT label at pNode against zKey,nKey. Return true on ** a match. */ static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ if( pNode->jnFlags & JNODE_RAW ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( aUp==0 ){ pParse->oom = 1; return SQLITE_NOMEM; } jsonParseFillInParentage(pParse, 0, 0); return SQLITE_OK; } /* ** Magic number used for the JSON parse cache in sqlite3_get_auxdata() */ #define JSON_CACHE_ID (-429938) /* ** Obtain a complete parse of the JSON found in the first argument ** of the argv array. Use the sqlite3_get_auxdata() cache for this ** parse if it is available. If the cache is not available or if it ** is no longer valid, parse the JSON again and return the new parse, ** and also register the new parse so that it will be available for ** future sqlite3_get_auxdata() calls. */ static JsonParse *jsonParseCached( sqlite3_context *pCtx, sqlite3_value **argv ){ const char *zJson = (const char*)sqlite3_value_text(argv[0]); int nJson = sqlite3_value_bytes(argv[0]); JsonParse *p; if( zJson==0 ) return 0; p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID); if( p && p->nJson==nJson && memcmp(p->zJson,zJson,nJson)==0 ){ p->nErr = 0; return p; /* The cached entry matches, so return it */ } p = sqlite3_malloc( sizeof(*p) + nJson + 1 ); if( p==0 ){ sqlite3_result_error_nomem(pCtx); return 0; } memset(p, 0, sizeof(*p)); p->zJson = (char*)&p[1]; memcpy((char*)p->zJson, zJson, nJson+1); if( jsonParse(p, pCtx, p->zJson) ){ sqlite3_free(p); return 0; } p->nJson = nJson; sqlite3_set_auxdata(pCtx, JSON_CACHE_ID, p, (void(*)(void*))jsonParseFree); return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID); } /* ** Compare the OBJECT label at pNode against zKey,nKey. Return true on ** a match. */ static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ if( pNode->jnFlags & JNODE_RAW ){ |
︙ | ︙ | |||
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 | ){ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", zFuncName); sqlite3_result_error(pCtx, zMsg, -1); sqlite3_free(zMsg); } /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ #ifdef SQLITE_DEBUG /* | > > > > > > > > > > > > > > > > > > > | 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 | ){ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", zFuncName); sqlite3_result_error(pCtx, zMsg, -1); sqlite3_free(zMsg); } /* ** Mark all NULL entries in the Object passed in as JNODE_REMOVE. */ static void jsonRemoveAllNulls(JsonNode *pNode){ int i, n; assert( pNode->eType==JSON_OBJECT ); n = pNode->n; for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){ switch( pNode[i].eType ){ case JSON_NULL: pNode[i].jnFlags |= JNODE_REMOVE; break; case JSON_OBJECT: jsonRemoveAllNulls(&pNode[i]); break; } } } /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ #ifdef SQLITE_DEBUG /* |
︙ | ︙ | |||
1264 1265 1266 1267 1268 1269 1270 | ** Return 0 if the input is not a well-formed JSON array. */ static void jsonArrayLengthFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ | | | > | | | | > | | < | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > | 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 | ** 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 n = 0; u32 i; JsonNode *pNode; p = jsonParseCached(ctx, argv); if( p==0 ) return; assert( p->nNode ); if( argc==2 ){ const char *zPath = (const char*)sqlite3_value_text(argv[1]); pNode = jsonLookup(p, zPath, 0, ctx); }else{ pNode = p->aNode; } if( pNode==0 ){ return; } if( pNode->eType==JSON_ARRAY ){ assert( (pNode->jnFlags & JNODE_APPEND)==0 ); for(i=1; i<=pNode->n; n++){ i += jsonNodeSize(&pNode[i]); } } sqlite3_result_int64(ctx, n); } /* ** json_extract(JSON, PATH, ...) ** ** Return the element described by PATH. Return NULL if there is no ** PATH element. If there are multiple PATHs, then return a JSON array ** with the result from each path. Throw an error if the JSON or any PATH ** is malformed. */ static void jsonExtractFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ JsonParse *p; /* The parse */ JsonNode *pNode; const char *zPath; JsonString jx; int i; if( argc<2 ) return; p = jsonParseCached(ctx, argv); if( p==0 ) return; jsonInit(&jx, ctx); jsonAppendChar(&jx, '['); for(i=1; i<argc; i++){ zPath = (const char*)sqlite3_value_text(argv[i]); pNode = jsonLookup(p, zPath, 0, ctx); if( p->nErr ) break; if( argc>2 ){ jsonAppendSeparator(&jx); if( pNode ){ jsonRenderNode(pNode, &jx, 0); }else{ jsonAppendRaw(&jx, "null", 4); } }else if( pNode ){ jsonReturn(pNode, ctx, 0); } } if( argc>2 && i==argc ){ jsonAppendChar(&jx, ']'); jsonResult(&jx); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } jsonReset(&jx); } /* This is the RFC 7396 MergePatch algorithm. */ static JsonNode *jsonMergePatch( JsonParse *pParse, /* The JSON parser that contains the TARGET */ u32 iTarget, /* Node of the TARGET in pParse */ JsonNode *pPatch /* The PATCH */ ){ u32 i, j; u32 iRoot; JsonNode *pTarget; if( pPatch->eType!=JSON_OBJECT ){ return pPatch; } assert( iTarget>=0 && iTarget<pParse->nNode ); pTarget = &pParse->aNode[iTarget]; assert( (pPatch->jnFlags & JNODE_APPEND)==0 ); if( pTarget->eType!=JSON_OBJECT ){ jsonRemoveAllNulls(pPatch); return pPatch; } iRoot = iTarget; for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){ u32 nKey; const char *zKey; assert( pPatch[i].eType==JSON_STRING ); assert( pPatch[i].jnFlags & JNODE_LABEL ); nKey = pPatch[i].n; zKey = pPatch[i].u.zJContent; assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){ assert( pTarget[j].eType==JSON_STRING ); assert( pTarget[j].jnFlags & JNODE_LABEL ); assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){ if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; if( pPatch[i+1].eType==JSON_NULL ){ pTarget[j+1].jnFlags |= JNODE_REMOVE; }else{ JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); if( pNew==0 ) return 0; pTarget = &pParse->aNode[iTarget]; if( pNew!=&pTarget[j+1] ){ pTarget[j+1].u.pPatch = pNew; pTarget[j+1].jnFlags |= JNODE_PATCH; } } break; } } if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){ int iStart, iPatch; iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0); if( pParse->oom ) return 0; jsonRemoveAllNulls(pPatch); pTarget = &pParse->aNode[iTarget]; pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; pParse->aNode[iRoot].u.iAppend = iStart - iRoot; iRoot = iStart; pParse->aNode[iPatch].jnFlags |= JNODE_PATCH; pParse->aNode[iPatch].u.pPatch = &pPatch[i+1]; } } return pTarget; } /* ** 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 x; /* The JSON that is being patched */ JsonParse y; /* The patch */ JsonNode *pResult; /* The result of the merge */ UNUSED_PARAM(argc); if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){ jsonParseReset(&x); return; } pResult = jsonMergePatch(&x, 0, y.aNode); assert( pResult!=0 || x.oom ); if( pResult ){ jsonReturnJson(pResult, ctx, 0); }else{ sqlite3_result_error_nomem(ctx); } jsonParseReset(&x); jsonParseReset(&y); } /* ** 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. */ static void jsonObjectFunc( |
︙ | ︙ | |||
1439 1440 1441 1442 1443 1444 1445 | assert( x.nNode ); for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); pNode = jsonLookup(&x, zPath, 0, ctx); if( x.nErr ) goto replace_err; if( pNode ){ pNode->jnFlags |= (u8)JNODE_REPLACE; | | | | 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 | assert( x.nNode ); for(i=1; i<(u32)argc; i+=2){ zPath = (const char*)sqlite3_value_text(argv[i]); pNode = jsonLookup(&x, zPath, 0, ctx); if( x.nErr ) goto replace_err; if( pNode ){ pNode->jnFlags |= (u8)JNODE_REPLACE; pNode->u.iReplace = i + 1; } } if( x.aNode[0].jnFlags & JNODE_REPLACE ){ sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); }else{ jsonReturnJson(x.aNode, ctx, argv); } replace_err: jsonParseReset(&x); } |
︙ | ︙ | |||
1493 1494 1495 1496 1497 1498 1499 | if( x.oom ){ sqlite3_result_error_nomem(ctx); goto jsonSetDone; }else if( x.nErr ){ goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ pNode->jnFlags |= (u8)JNODE_REPLACE; | | | | 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 | if( x.oom ){ sqlite3_result_error_nomem(ctx); goto jsonSetDone; }else if( x.nErr ){ goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ pNode->jnFlags |= (u8)JNODE_REPLACE; pNode->u.iReplace = i + 1; } } if( x.aNode[0].jnFlags & JNODE_REPLACE ){ sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); }else{ jsonReturnJson(x.aNode, ctx, argv); } jsonSetDone: jsonParseReset(&x); } |
︙ | ︙ | |||
1641 1642 1643 1644 1645 1646 1647 | } static void jsonObjectFinal(sqlite3_context *ctx){ JsonString *pStr; pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ jsonAppendChar(pStr, '}'); if( pStr->bErr ){ | | | 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 | } static void jsonObjectFinal(sqlite3_context *ctx){ JsonString *pStr; pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ jsonAppendChar(pStr, '}'); if( pStr->bErr ){ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); assert( pStr->bStatic ); }else{ sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed, pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); pStr->bStatic = 1; } }else{ |
︙ | ︙ | |||
1919 1920 1921 1922 1923 1924 1925 | jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); jsonResult(&x); break; } /* For json_each() path and root are the same so fall through ** into the root case */ } | | | | 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 | jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); jsonResult(&x); break; } /* For json_each() path and root are the same so fall through ** into the root case */ } default: { const char *zRoot = p->zRoot; if( zRoot==0 ) zRoot = "$"; sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); break; } case JEACH_JSON: { assert( i==JEACH_JSON ); sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); break; |
︙ | ︙ | |||
2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 | { "json", 1, 0, jsonRemoveFunc }, { "json_array", -1, 0, jsonArrayFunc }, { "json_array_length", 1, 0, jsonArrayLengthFunc }, { "json_array_length", 2, 0, jsonArrayLengthFunc }, { "json_extract", -1, 0, jsonExtractFunc }, { "json_insert", -1, 0, jsonSetFunc }, { "json_object", -1, 0, jsonObjectFunc }, { "json_quote", 1, 0, jsonQuoteFunc }, { "json_remove", -1, 0, jsonRemoveFunc }, { "json_replace", -1, 0, jsonReplaceFunc }, { "json_set", -1, 1, jsonSetFunc }, { "json_type", 1, 0, jsonTypeFunc }, { "json_type", 2, 0, jsonTypeFunc }, { "json_valid", 1, 0, jsonValidFunc }, | > | 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 | { "json", 1, 0, jsonRemoveFunc }, { "json_array", -1, 0, jsonArrayFunc }, { "json_array_length", 1, 0, jsonArrayLengthFunc }, { "json_array_length", 2, 0, jsonArrayLengthFunc }, { "json_extract", -1, 0, jsonExtractFunc }, { "json_insert", -1, 0, jsonSetFunc }, { "json_object", -1, 0, jsonObjectFunc }, { "json_patch", 2, 0, jsonPatchFunc }, { "json_quote", 1, 0, jsonQuoteFunc }, { "json_remove", -1, 0, jsonRemoveFunc }, { "json_replace", -1, 0, jsonReplaceFunc }, { "json_set", -1, 1, jsonSetFunc }, { "json_type", 1, 0, jsonTypeFunc }, { "json_type", 2, 0, jsonTypeFunc }, { "json_valid", 1, 0, jsonValidFunc }, |
︙ | ︙ |
Added ext/misc/mmapwarm.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 | /* ** 2017-09-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. ** ************************************************************************* ** */ #include "sqlite3.h" /* ** This function is used to touch each page of a mapping of a memory ** mapped SQLite database. Assuming that the system has sufficient free ** memory and supports sufficiently large mappings, this causes the OS ** to cache the entire database in main memory, making subsequent ** database accesses faster. ** ** If the second parameter to this function is not NULL, it is the name of ** the specific database to operate on (i.e. "main" or the name of an ** attached database). ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ** It is not considered an error if the file is not memory-mapped, or if ** the mapping does not span the entire file. If an error does occur, a ** transaction may be left open on the database file. ** ** It is illegal to call this function when the database handle has an ** open transaction. SQLITE_MISUSE is returned in this case. */ int sqlite3_mmap_warm(sqlite3 *db, const char *zDb){ int rc = SQLITE_OK; char *zSql = 0; int pgsz = 0; int nTotal = 0; if( 0==sqlite3_get_autocommit(db) ) return SQLITE_MISUSE; /* Open a read-only transaction on the file in question */ zSql = sqlite3_mprintf("BEGIN; SELECT * FROM %s%q%ssqlite_master", (zDb ? "'" : ""), (zDb ? zDb : ""), (zDb ? "'." : "") ); if( zSql==0 ) return SQLITE_NOMEM; rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); /* Find the SQLite page size of the file */ if( rc==SQLITE_OK ){ zSql = sqlite3_mprintf("PRAGMA %s%q%spage_size", (zDb ? "'" : ""), (zDb ? zDb : ""), (zDb ? "'." : "") ); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ sqlite3_stmt *pPgsz = 0; rc = sqlite3_prepare_v2(db, zSql, -1, &pPgsz, 0); sqlite3_free(zSql); if( rc==SQLITE_OK ){ if( sqlite3_step(pPgsz)==SQLITE_ROW ){ pgsz = sqlite3_column_int(pPgsz, 0); } rc = sqlite3_finalize(pPgsz); } if( rc==SQLITE_OK && pgsz==0 ){ rc = SQLITE_ERROR; } } } /* Touch each mmap'd page of the file */ if( rc==SQLITE_OK ){ int rc2; sqlite3_file *pFd = 0; rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFd); if( rc==SQLITE_OK && pFd->pMethods->iVersion>=3 ){ sqlite3_int64 iPg = 1; sqlite3_io_methods const *p = pFd->pMethods; while( 1 ){ unsigned char *pMap; rc = p->xFetch(pFd, pgsz*iPg, pgsz, (void**)&pMap); if( rc!=SQLITE_OK || pMap==0 ) break; nTotal += pMap[0]; nTotal += pMap[pgsz-1]; rc = p->xUnfetch(pFd, pgsz*iPg, (void*)pMap); if( rc!=SQLITE_OK ) break; iPg++; } sqlite3_log(SQLITE_OK, "sqlite3_mmap_warm_cache: Warmed up %d pages of %s", iPg==1?0:iPg, sqlite3_db_filename(db, zDb) ); } rc2 = sqlite3_exec(db, "END", 0, 0, 0); if( rc==SQLITE_OK ) rc = rc2; } return rc; } |
Added ext/misc/remember.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 | /* ** 2016-08-09 ** ** 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 demonstrates how to create an SQL function that is a pass-through ** for integer values (it returns a copy of its argument) but also saves the ** value that is passed through into a C-language variable. The address of ** the C-language variable is supplied as the second argument. ** ** This allows, for example, a counter to incremented and the original ** value retrieved, atomically, using a single statement: ** ** UPDATE counterTab SET cnt=remember(cnt,$PTR)+1 WHERE id=$ID ** ** Prepare the above statement once. Then to use it, bind the address ** of the output variable to $PTR using sqlite3_bind_pointer() with a ** pointer type of "carray" and bind the id of the counter to $ID and ** run the prepared statement. ** ** This implementation of the remember() function uses a "carray" ** pointer so that it can share pointers with the carray() extension. ** ** One can imagine doing similar things with floating-point values and ** strings, but this demonstration extension will stick to using just ** integers. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <assert.h> /* ** remember(V,PTR) ** ** Return the integer value V. Also save the value of V in a ** C-language variable whose address is PTR. */ static void rememberFunc( sqlite3_context *pCtx, int argc, sqlite3_value **argv ){ sqlite3_int64 v; sqlite3_int64 *ptr; assert( argc==2 ); v = sqlite3_value_int64(argv[0]); ptr = sqlite3_value_pointer(argv[1], "carray"); if( ptr ) *ptr = v; sqlite3_result_int64(pCtx, v); } #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_remember_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); rc = sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0, rememberFunc, 0, 0); return rc; } |
Changes to ext/misc/series.c.
︙ | ︙ | |||
29 30 31 32 33 34 35 | ** Integers 20 through 29. ** ** HOW IT WORKS ** ** The generate_series "function" is really a virtual table with the ** following schema: ** | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ** Integers 20 through 29. ** ** HOW IT WORKS ** ** The generate_series "function" is really a virtual table with the ** following schema: ** ** CREATE TABLE generate_series( ** value, ** start HIDDEN, ** stop HIDDEN, ** step HIDDEN ** ); ** ** Function arguments in queries against this virtual table are translated |
︙ | ︙ | |||
191 192 193 194 195 196 197 | default: x = pCur->iValue; break; } sqlite3_result_int64(ctx, x); return SQLITE_OK; } /* | | > | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | default: x = pCur->iValue; break; } sqlite3_result_int64(ctx, x); return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** first row returned is assigned rowid value 1, and each subsequent ** row a value 1 more than that of the previous. */ static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ series_cursor *pCur = (series_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } |
︙ | ︙ |
Added ext/misc/sha1.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 | /* ** 2017-01-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 SQLite extension implements a functions that compute SHA1 hashes. ** Two SQL functions are implemented: ** ** sha1(X) ** sha1_query(Y) ** ** The sha1(X) function computes the SHA1 hash of the input X, or NULL if ** X is NULL. ** ** The sha1_query(Y) function evalutes all queries in the SQL statements of Y ** and returns a hash of their results. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <stdarg.h> /****************************************************************************** ** The Hash Engine */ /* Context for the SHA1 hash */ typedef struct SHA1Context SHA1Context; struct SHA1Context { unsigned int state[5]; unsigned int count[2]; unsigned char buffer[64]; }; #if __GNUC__ && (defined(__i386__) || defined(__x86_64__)) /* * GCC by itself only generates left rotates. Use right rotates if * possible to be kinder to dinky implementations with iterative rotate * instructions. */ #define SHA_ROT(op, x, k) \ ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; }) #define rol(x,k) SHA_ROT("roll", x, k) #define ror(x,k) SHA_ROT("rorl", x, k) #else /* Generic C equivalent */ #define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) #define rol(x,k) SHA_ROT(x,k,32-(k)) #define ror(x,k) SHA_ROT(x,32-(k),k) #endif #define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ |(rol(block[i],8)&0x00FF00FF)) #define blk0be(i) block[i] #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ ^block[(i+2)&15]^block[i&15],1)) /* * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 * * Rl0() for little-endian and Rb0() for big-endian. Endianness is * determined at run-time. */ #define Rl0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); #define Rb0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R1(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R2(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); #define R3(v,w,x,y,z,i) \ z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); #define R4(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); /* * Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){ unsigned int qq[5]; /* a, b, c, d, e; */ static int one = 1; unsigned int block[16]; memcpy(block, buffer, 64); memcpy(qq,state,5*sizeof(unsigned int)); #define a qq[0] #define b qq[1] #define c qq[2] #define d qq[3] #define e qq[4] /* Copy p->state[] to working vars */ /* a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; */ /* 4 rounds of 20 operations each. Loop unrolled. */ if( 1 == *(unsigned char*)&one ){ Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); }else{ Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); } R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; #undef a #undef b #undef c #undef d #undef e } /* Initialize a SHA1 context */ static void hash_init(SHA1Context *p){ /* SHA1 initialization constants */ p->state[0] = 0x67452301; p->state[1] = 0xEFCDAB89; p->state[2] = 0x98BADCFE; p->state[3] = 0x10325476; p->state[4] = 0xC3D2E1F0; p->count[0] = p->count[1] = 0; } /* Add new content to the SHA1 hash */ static void hash_step( SHA1Context *p, /* Add content to this context */ const unsigned char *data, /* Data to be added */ unsigned int len /* Number of bytes in data */ ){ unsigned int i, j; j = p->count[0]; if( (p->count[0] += len << 3) < j ){ p->count[1] += (len>>29)+1; } j = (j >> 3) & 63; if( (j + len) > 63 ){ (void)memcpy(&p->buffer[j], data, (i = 64-j)); SHA1Transform(p->state, p->buffer); for(; i + 63 < len; i += 64){ SHA1Transform(p->state, &data[i]); } j = 0; }else{ i = 0; } (void)memcpy(&p->buffer[j], &data[i], len - i); } /* Compute a string using sqlite3_vsnprintf() and hash it */ static void hash_step_vformat( SHA1Context *p, /* Add content to this context */ const char *zFormat, ... ){ va_list ap; int n; char zBuf[50]; va_start(ap, zFormat); sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); va_end(ap); n = (int)strlen(zBuf); hash_step(p, (unsigned char*)zBuf, n); } /* Add padding and compute the message digest. Render the ** message digest as lower-case hexadecimal and put it into ** zOut[]. zOut[] must be at least 41 bytes long. */ static void hash_finish( SHA1Context *p, /* The SHA1 context to finish and render */ char *zOut /* Store hexadecimal hash here */ ){ unsigned int i; unsigned char finalcount[8]; unsigned char digest[20]; static const char zEncode[] = "0123456789abcdef"; for (i = 0; i < 8; i++){ finalcount[i] = (unsigned char)((p->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } hash_step(p, (const unsigned char *)"\200", 1); while ((p->count[0] & 504) != 448){ hash_step(p, (const unsigned char *)"\0", 1); } hash_step(p, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++){ digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } for(i=0; i<20; i++){ zOut[i*2] = zEncode[(digest[i]>>4)&0xf]; zOut[i*2+1] = zEncode[digest[i] & 0xf]; } zOut[i*2]= 0; } /* End of the hashing logic *****************************************************************************/ /* ** Implementation of the sha1(X) function. ** ** Return a lower-case hexadecimal rendering of the SHA1 hash of the ** argument X. If X is a BLOB, it is hashed as is. For all other ** types of input, X is converted into a UTF-8 string and the string ** is hash without the trailing 0x00 terminator. The hash of a NULL ** value is NULL. */ static void sha1Func( sqlite3_context *context, int argc, sqlite3_value **argv ){ SHA1Context cx; int eType = sqlite3_value_type(argv[0]); int nByte = sqlite3_value_bytes(argv[0]); char zOut[44]; assert( argc==1 ); if( eType==SQLITE_NULL ) return; hash_init(&cx); if( eType==SQLITE_BLOB ){ hash_step(&cx, sqlite3_value_blob(argv[0]), nByte); }else{ hash_step(&cx, sqlite3_value_text(argv[0]), nByte); } hash_finish(&cx, zOut); sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT); } /* ** Implementation of the sha1_query(SQL) function. ** ** This function compiles and runs the SQL statement(s) given in the ** argument. The results are hashed using SHA1 and that hash is returned. ** ** The original SQL text is included as part of the hash. ** ** The hash is not just a concatenation of the outputs. Each query ** is delimited and each row and value within the query is delimited, ** with all values being marked with their datatypes. */ static void sha1QueryFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3 *db = sqlite3_context_db_handle(context); const char *zSql = (const char*)sqlite3_value_text(argv[0]); sqlite3_stmt *pStmt = 0; int nCol; /* Number of columns in the result set */ int i; /* Loop counter */ int rc; int n; const char *z; SHA1Context cx; char zOut[44]; assert( argc==1 ); if( zSql==0 ) return; hash_init(&cx); while( zSql[0] ){ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); if( rc ){ char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s", zSql, sqlite3_errmsg(db)); sqlite3_finalize(pStmt); sqlite3_result_error(context, zMsg, -1); sqlite3_free(zMsg); return; } if( !sqlite3_stmt_readonly(pStmt) ){ char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt)); sqlite3_finalize(pStmt); sqlite3_result_error(context, zMsg, -1); sqlite3_free(zMsg); return; } nCol = sqlite3_column_count(pStmt); z = sqlite3_sql(pStmt); n = (int)strlen(z); hash_step_vformat(&cx,"S%d:",n); hash_step(&cx,(unsigned char*)z,n); /* Compute a hash over the result of the query */ while( SQLITE_ROW==sqlite3_step(pStmt) ){ hash_step(&cx,(const unsigned char*)"R",1); for(i=0; i<nCol; i++){ switch( sqlite3_column_type(pStmt,i) ){ case SQLITE_NULL: { hash_step(&cx, (const unsigned char*)"N",1); break; } case SQLITE_INTEGER: { sqlite3_uint64 u; int j; unsigned char x[9]; sqlite3_int64 v = sqlite3_column_int64(pStmt,i); memcpy(&u, &v, 8); for(j=8; j>=1; j--){ x[j] = u & 0xff; u >>= 8; } x[0] = 'I'; hash_step(&cx, x, 9); break; } case SQLITE_FLOAT: { sqlite3_uint64 u; int j; unsigned char x[9]; double r = sqlite3_column_double(pStmt,i); memcpy(&u, &r, 8); for(j=8; j>=1; j--){ x[j] = u & 0xff; u >>= 8; } x[0] = 'F'; hash_step(&cx,x,9); break; } case SQLITE_TEXT: { int n2 = sqlite3_column_bytes(pStmt, i); const unsigned char *z2 = sqlite3_column_text(pStmt, i); hash_step_vformat(&cx,"T%d:",n2); hash_step(&cx, z2, n2); break; } case SQLITE_BLOB: { int n2 = sqlite3_column_bytes(pStmt, i); const unsigned char *z2 = sqlite3_column_blob(pStmt, i); hash_step_vformat(&cx,"B%d:",n2); hash_step(&cx, z2, n2); break; } } } } sqlite3_finalize(pStmt); } hash_finish(&cx, zOut); sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT); } #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_sha_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); (void)pzErrMsg; /* Unused parameter */ rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8, 0, sha1Func, 0, 0); if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "sha1_query", 1, SQLITE_UTF8, 0, sha1QueryFunc, 0, 0); } return rc; } |
Added ext/misc/shathree.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 | /* ** 2017-03-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. ** ****************************************************************************** ** ** This SQLite extension implements a functions that compute SHA1 hashes. ** Two SQL functions are implemented: ** ** sha3(X,SIZE) ** sha3_query(Y,SIZE) ** ** The sha3(X) function computes the SHA3 hash of the input X, or NULL if ** X is NULL. ** ** The sha3_query(Y) function evalutes all queries in the SQL statements of Y ** and returns a hash of their results. ** ** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm ** is used. If SIZE is included it must be one of the integers 224, 256, ** 384, or 512, to determine SHA3 hash variant that is computed. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <stdarg.h> typedef sqlite3_uint64 u64; /****************************************************************************** ** The Hash Engine */ /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** ** For best performance, an attempt is made to guess at the byte-order ** using C-preprocessor macros. If that is unsuccessful, or if ** -DSHA3_BYTEORDER=0 is set, then byte-order is determined ** at run-time. */ #ifndef SHA3_BYTEORDER # if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__arm__) # define SHA3_BYTEORDER 1234 # elif defined(sparc) || defined(__ppc__) # define SHA3_BYTEORDER 4321 # else # define SHA3_BYTEORDER 0 # endif #endif /* ** State structure for a SHA3 hash in progress */ typedef struct SHA3Context SHA3Context; struct SHA3Context { union { u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */ unsigned char x[1600]; /* ... or 1600 bytes */ } u; unsigned nRate; /* Bytes of input accepted per Keccak iteration */ unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ }; /* ** A single step of the Keccak mixing function for a 1600-bit state */ static void KeccakF1600Step(SHA3Context *p){ int i; u64 B0, B1, B2, B3, B4; u64 C0, C1, C2, C3, C4; u64 D0, D1, D2, D3, D4; static const u64 RC[] = { 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL }; # define A00 (p->u.s[0]) # define A01 (p->u.s[1]) # define A02 (p->u.s[2]) # define A03 (p->u.s[3]) # define A04 (p->u.s[4]) # define A10 (p->u.s[5]) # define A11 (p->u.s[6]) # define A12 (p->u.s[7]) # define A13 (p->u.s[8]) # define A14 (p->u.s[9]) # define A20 (p->u.s[10]) # define A21 (p->u.s[11]) # define A22 (p->u.s[12]) # define A23 (p->u.s[13]) # define A24 (p->u.s[14]) # define A30 (p->u.s[15]) # define A31 (p->u.s[16]) # define A32 (p->u.s[17]) # define A33 (p->u.s[18]) # define A34 (p->u.s[19]) # define A40 (p->u.s[20]) # define A41 (p->u.s[21]) # define A42 (p->u.s[22]) # define A43 (p->u.s[23]) # define A44 (p->u.s[24]) # define ROL64(a,x) ((a<<x)|(a>>(64-x))) for(i=0; i<24; i+=4){ C0 = A00^A10^A20^A30^A40; C1 = A01^A11^A21^A31^A41; C2 = A02^A12^A22^A32^A42; C3 = A03^A13^A23^A33^A43; C4 = A04^A14^A24^A34^A44; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A11^D1), 44); B2 = ROL64((A22^D2), 43); B3 = ROL64((A33^D3), 21); B4 = ROL64((A44^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i]; A11 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B2 = ROL64((A20^D0), 3); B3 = ROL64((A31^D1), 45); B4 = ROL64((A42^D2), 61); B0 = ROL64((A03^D3), 28); B1 = ROL64((A14^D4), 20); A20 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A40^D0), 18); B0 = ROL64((A01^D1), 1); B1 = ROL64((A12^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A34^D4), 8); A40 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B1 = ROL64((A10^D0), 36); B2 = ROL64((A21^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A43^D3), 56); B0 = ROL64((A04^D4), 27); A10 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B3 = ROL64((A30^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A02^D2), 62); B1 = ROL64((A13^D3), 55); B2 = ROL64((A24^D4), 39); A30 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); C0 = A00^A20^A40^A10^A30; C1 = A11^A31^A01^A21^A41; C2 = A22^A42^A12^A32^A02; C3 = A33^A03^A23^A43^A13; C4 = A44^A14^A34^A04^A24; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A31^D1), 44); B2 = ROL64((A12^D2), 43); B3 = ROL64((A43^D3), 21); B4 = ROL64((A24^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+1]; A31 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B2 = ROL64((A40^D0), 3); B3 = ROL64((A21^D1), 45); B4 = ROL64((A02^D2), 61); B0 = ROL64((A33^D3), 28); B1 = ROL64((A14^D4), 20); A40 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A30^D0), 18); B0 = ROL64((A11^D1), 1); B1 = ROL64((A42^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A04^D4), 8); A30 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B1 = ROL64((A20^D0), 36); B2 = ROL64((A01^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A13^D3), 56); B0 = ROL64((A44^D4), 27); A20 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B3 = ROL64((A10^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A22^D2), 62); B1 = ROL64((A03^D3), 55); B2 = ROL64((A34^D4), 39); A10 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); C0 = A00^A40^A30^A20^A10; C1 = A31^A21^A11^A01^A41; C2 = A12^A02^A42^A32^A22; C3 = A43^A33^A23^A13^A03; C4 = A24^A14^A04^A44^A34; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A21^D1), 44); B2 = ROL64((A42^D2), 43); B3 = ROL64((A13^D3), 21); B4 = ROL64((A34^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+2]; A21 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B2 = ROL64((A30^D0), 3); B3 = ROL64((A01^D1), 45); B4 = ROL64((A22^D2), 61); B0 = ROL64((A43^D3), 28); B1 = ROL64((A14^D4), 20); A30 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A10^D0), 18); B0 = ROL64((A31^D1), 1); B1 = ROL64((A02^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A44^D4), 8); A10 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B1 = ROL64((A40^D0), 36); B2 = ROL64((A11^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A03^D3), 56); B0 = ROL64((A24^D4), 27); A40 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B3 = ROL64((A20^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A12^D2), 62); B1 = ROL64((A33^D3), 55); B2 = ROL64((A04^D4), 39); A20 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); C0 = A00^A30^A10^A40^A20; C1 = A21^A01^A31^A11^A41; C2 = A42^A22^A02^A32^A12; C3 = A13^A43^A23^A03^A33; C4 = A34^A14^A44^A24^A04; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A01^D1), 44); B2 = ROL64((A02^D2), 43); B3 = ROL64((A03^D3), 21); B4 = ROL64((A04^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+3]; A01 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B2 = ROL64((A10^D0), 3); B3 = ROL64((A11^D1), 45); B4 = ROL64((A12^D2), 61); B0 = ROL64((A13^D3), 28); B1 = ROL64((A14^D4), 20); A10 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A20^D0), 18); B0 = ROL64((A21^D1), 1); B1 = ROL64((A22^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A24^D4), 8); A20 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B1 = ROL64((A30^D0), 36); B2 = ROL64((A31^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A33^D3), 56); B0 = ROL64((A34^D4), 27); A30 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B3 = ROL64((A40^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A42^D2), 62); B1 = ROL64((A43^D3), 55); B2 = ROL64((A44^D4), 39); A40 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); } } /* ** Initialize a new hash. iSize determines the size of the hash ** in bits and should be one of 224, 256, 384, or 512. Or iSize ** can be zero to use the default hash size of 256 bits. */ static void SHA3Init(SHA3Context *p, int iSize){ memset(p, 0, sizeof(*p)); if( iSize>=128 && iSize<=512 ){ p->nRate = (1600 - ((iSize + 31)&~31)*2)/8; }else{ p->nRate = (1600 - 2*256)/8; } #if SHA3_BYTEORDER==1234 /* Known to be little-endian at compile-time. No-op */ #elif SHA3_BYTEORDER==4321 p->ixMask = 7; /* Big-endian */ #else { static unsigned int one = 1; if( 1==*(unsigned char*)&one ){ /* Little endian. No byte swapping. */ p->ixMask = 0; }else{ /* Big endian. Byte swap. */ p->ixMask = 7; } } #endif } /* ** Make consecutive calls to the SHA3Update function to add new content ** to the hash */ static void SHA3Update( SHA3Context *p, const unsigned char *aData, unsigned int nData ){ unsigned int i = 0; #if SHA3_BYTEORDER==1234 if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){ for(; i+7<nData; i+=8){ p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i]; p->nLoaded += 8; if( p->nLoaded>=p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } } #endif for(; i<nData; i++){ #if SHA3_BYTEORDER==1234 p->u.x[p->nLoaded] ^= aData[i]; #elif SHA3_BYTEORDER==4321 p->u.x[p->nLoaded^0x07] ^= aData[i]; #else p->u.x[p->nLoaded^p->ixMask] ^= aData[i]; #endif p->nLoaded++; if( p->nLoaded==p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } } /* ** After all content has been added, invoke SHA3Final() to compute ** the final hash. The function returns a pointer to the binary ** hash value. */ static unsigned char *SHA3Final(SHA3Context *p){ unsigned int i; if( p->nLoaded==p->nRate-1 ){ const unsigned char c1 = 0x86; SHA3Update(p, &c1, 1); }else{ const unsigned char c2 = 0x06; const unsigned char c3 = 0x80; SHA3Update(p, &c2, 1); p->nLoaded = p->nRate - 1; SHA3Update(p, &c3, 1); } for(i=0; i<p->nRate; i++){ p->u.x[i+p->nRate] = p->u.x[i^p->ixMask]; } return &p->u.x[p->nRate]; } /* End of the hashing logic *****************************************************************************/ /* ** Implementation of the sha3(X,SIZE) function. ** ** Return a BLOB which is the SIZE-bit SHA3 hash of X. The default ** size is 256. If X is a BLOB, it is hashed as is. ** For all other non-NULL types of input, X is converted into a UTF-8 string ** and the string is hashed without the trailing 0x00 terminator. The hash ** of a NULL value is NULL. */ static void sha3Func( sqlite3_context *context, int argc, sqlite3_value **argv ){ SHA3Context cx; int eType = sqlite3_value_type(argv[0]); int nByte = sqlite3_value_bytes(argv[0]); int iSize; if( argc==1 ){ iSize = 256; }else{ iSize = sqlite3_value_int(argv[1]); if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){ sqlite3_result_error(context, "SHA3 size should be one of: 224 256 " "384 512", -1); return; } } if( eType==SQLITE_NULL ) return; SHA3Init(&cx, iSize); if( eType==SQLITE_BLOB ){ SHA3Update(&cx, sqlite3_value_blob(argv[0]), nByte); }else{ SHA3Update(&cx, sqlite3_value_text(argv[0]), nByte); } sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); } /* Compute a string using sqlite3_vsnprintf() with a maximum length ** of 50 bytes and add it to the hash. */ static void hash_step_vformat( SHA3Context *p, /* Add content to this context */ const char *zFormat, ... ){ va_list ap; int n; char zBuf[50]; va_start(ap, zFormat); sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); va_end(ap); n = (int)strlen(zBuf); SHA3Update(p, (unsigned char*)zBuf, n); } /* ** Implementation of the sha3_query(SQL,SIZE) function. ** ** This function compiles and runs the SQL statement(s) given in the ** argument. The results are hashed using a SIZE-bit SHA3. The default ** size is 256. ** ** The format of the byte stream that is hashed is summarized as follows: ** ** S<n>:<sql> ** R ** N ** I<int> ** F<ieee-float> ** B<size>:<bytes> ** T<size>:<text> ** ** <sql> is the original SQL text for each statement run and <n> is ** the size of that text. The SQL text is UTF-8. A single R character ** occurs before the start of each row. N means a NULL value. ** I mean an 8-byte little-endian integer <int>. F is a floating point ** number with an 8-byte little-endian IEEE floating point value <ieee-float>. ** B means blobs of <size> bytes. T means text rendered as <size> ** bytes of UTF-8. The <n> and <size> values are expressed as an ASCII ** text integers. ** ** For each SQL statement in the X input, there is one S segment. Each ** S segment is followed by zero or more R segments, one for each row in the ** result set. After each R, there are one or more N, I, F, B, or T segments, ** one for each column in the result set. Segments are concatentated directly ** with no delimiters of any kind. */ static void sha3QueryFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3 *db = sqlite3_context_db_handle(context); const char *zSql = (const char*)sqlite3_value_text(argv[0]); sqlite3_stmt *pStmt = 0; int nCol; /* Number of columns in the result set */ int i; /* Loop counter */ int rc; int n; const char *z; SHA3Context cx; int iSize; if( argc==1 ){ iSize = 256; }else{ iSize = sqlite3_value_int(argv[1]); if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){ sqlite3_result_error(context, "SHA3 size should be one of: 224 256 " "384 512", -1); return; } } if( zSql==0 ) return; SHA3Init(&cx, iSize); while( zSql[0] ){ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); if( rc ){ char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s", zSql, sqlite3_errmsg(db)); sqlite3_finalize(pStmt); sqlite3_result_error(context, zMsg, -1); sqlite3_free(zMsg); return; } if( !sqlite3_stmt_readonly(pStmt) ){ char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt)); sqlite3_finalize(pStmt); sqlite3_result_error(context, zMsg, -1); sqlite3_free(zMsg); return; } nCol = sqlite3_column_count(pStmt); z = sqlite3_sql(pStmt); n = (int)strlen(z); hash_step_vformat(&cx,"S%d:",n); SHA3Update(&cx,(unsigned char*)z,n); /* Compute a hash over the result of the query */ while( SQLITE_ROW==sqlite3_step(pStmt) ){ SHA3Update(&cx,(const unsigned char*)"R",1); for(i=0; i<nCol; i++){ switch( sqlite3_column_type(pStmt,i) ){ case SQLITE_NULL: { SHA3Update(&cx, (const unsigned char*)"N",1); break; } case SQLITE_INTEGER: { sqlite3_uint64 u; int j; unsigned char x[9]; sqlite3_int64 v = sqlite3_column_int64(pStmt,i); memcpy(&u, &v, 8); for(j=8; j>=1; j--){ x[j] = u & 0xff; u >>= 8; } x[0] = 'I'; SHA3Update(&cx, x, 9); break; } case SQLITE_FLOAT: { sqlite3_uint64 u; int j; unsigned char x[9]; double r = sqlite3_column_double(pStmt,i); memcpy(&u, &r, 8); for(j=8; j>=1; j--){ x[j] = u & 0xff; u >>= 8; } x[0] = 'F'; SHA3Update(&cx,x,9); break; } case SQLITE_TEXT: { int n2 = sqlite3_column_bytes(pStmt, i); const unsigned char *z2 = sqlite3_column_text(pStmt, i); hash_step_vformat(&cx,"T%d:",n2); SHA3Update(&cx, z2, n2); break; } case SQLITE_BLOB: { int n2 = sqlite3_column_bytes(pStmt, i); const unsigned char *z2 = sqlite3_column_blob(pStmt, i); hash_step_vformat(&cx,"B%d:",n2); SHA3Update(&cx, z2, n2); break; } } } } sqlite3_finalize(pStmt); } sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); } #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_shathree_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); (void)pzErrMsg; /* Unused parameter */ rc = sqlite3_create_function(db, "sha3", 1, SQLITE_UTF8, 0, sha3Func, 0, 0); if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "sha3", 2, SQLITE_UTF8, 0, sha3Func, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "sha3_query", 1, SQLITE_UTF8, 0, sha3QueryFunc, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "sha3_query", 2, SQLITE_UTF8, 0, sha3QueryFunc, 0, 0); } return rc; } |
Added ext/misc/stmt.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 | /* ** 2017-05-31 ** ** 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 demonstrates an eponymous virtual table that returns information ** about all prepared statements for the database connection. ** ** Usage example: ** ** .load ./stmt ** .mode line ** .header on ** SELECT * FROM stmt; */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) #if !defined(SQLITEINT_H) #include "sqlite3ext.h" #endif SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* stmt_vtab is a subclass of sqlite3_vtab which will ** serve as the underlying representation of a stmt virtual table */ typedef struct stmt_vtab stmt_vtab; struct stmt_vtab { sqlite3_vtab base; /* Base class - must be first */ sqlite3 *db; /* Database connection for this stmt vtab */ }; /* stmt_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct stmt_cursor stmt_cursor; struct stmt_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3 *db; /* Database connection for this cursor */ sqlite3_stmt *pStmt; /* Statement cursor is currently pointing at */ sqlite3_int64 iRowid; /* The rowid */ }; /* ** The stmtConnect() method is invoked to create a new ** stmt_vtab that describes the stmt virtual table. ** ** Think of this routine as the constructor for stmt_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the stmt_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against stmt will look like. */ static int stmtConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ stmt_vtab *pNew; int rc; /* Column numbers */ #define STMT_COLUMN_SQL 0 /* SQL for the statement */ #define STMT_COLUMN_NCOL 1 /* Number of result columns */ #define STMT_COLUMN_RO 2 /* True if read-only */ #define STMT_COLUMN_BUSY 3 /* True if currently busy */ #define STMT_COLUMN_NSCAN 4 /* SQLITE_STMTSTATUS_FULLSCAN_STEP */ #define STMT_COLUMN_NSORT 5 /* SQLITE_STMTSTATUS_SORT */ #define STMT_COLUMN_NAIDX 6 /* SQLITE_STMTSTATUS_AUTOINDEX */ #define STMT_COLUMN_NSTEP 7 /* SQLITE_STMTSTATUS_VM_STEP */ #define STMT_COLUMN_REPREP 8 /* SQLITE_STMTSTATUS_REPREPARE */ #define STMT_COLUMN_RUN 9 /* SQLITE_STMTSTATUS_RUN */ #define STMT_COLUMN_MEM 10 /* SQLITE_STMTSTATUS_MEMUSED */ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep," "reprep,run,mem)"); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); pNew->db = db; } return rc; } /* ** This method is the destructor for stmt_cursor objects. */ static int stmtDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* ** Constructor for a new stmt_cursor object. */ static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ stmt_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->db = ((stmt_vtab*)p)->db; *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Destructor for a stmt_cursor. */ static int stmtClose(sqlite3_vtab_cursor *cur){ sqlite3_free(cur); return SQLITE_OK; } /* ** Advance a stmt_cursor to its next row of output. */ static int stmtNext(sqlite3_vtab_cursor *cur){ stmt_cursor *pCur = (stmt_cursor*)cur; pCur->iRowid++; pCur->pStmt = sqlite3_next_stmt(pCur->db, pCur->pStmt); return SQLITE_OK; } /* ** Return values of columns for the row at which the stmt_cursor ** is currently pointing. */ static int stmtColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ stmt_cursor *pCur = (stmt_cursor*)cur; switch( i ){ case STMT_COLUMN_SQL: { sqlite3_result_text(ctx, sqlite3_sql(pCur->pStmt), -1, SQLITE_TRANSIENT); break; } case STMT_COLUMN_NCOL: { sqlite3_result_int(ctx, sqlite3_column_count(pCur->pStmt)); break; } case STMT_COLUMN_RO: { sqlite3_result_int(ctx, sqlite3_stmt_readonly(pCur->pStmt)); break; } case STMT_COLUMN_BUSY: { sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt)); break; } case STMT_COLUMN_MEM: { i = SQLITE_STMTSTATUS_MEMUSED + STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; /* Fall thru */ } case STMT_COLUMN_NSCAN: case STMT_COLUMN_NSORT: case STMT_COLUMN_NAIDX: case STMT_COLUMN_NSTEP: case STMT_COLUMN_REPREP: case STMT_COLUMN_RUN: { sqlite3_result_int(ctx, sqlite3_stmt_status(pCur->pStmt, i-STMT_COLUMN_NSCAN+SQLITE_STMTSTATUS_FULLSCAN_STEP, 0)); break; } } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ stmt_cursor *pCur = (stmt_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int stmtEof(sqlite3_vtab_cursor *cur){ stmt_cursor *pCur = (stmt_cursor*)cur; return pCur->pStmt==0; } /* ** This method is called to "rewind" the stmt_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to stmtColumn() or stmtRowid() or ** stmtEof(). */ static int stmtFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ stmt_cursor *pCur = (stmt_cursor *)pVtabCursor; pCur->pStmt = 0; pCur->iRowid = 0; return stmtNext(pVtabCursor); } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the stmt virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. */ static int stmtBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ pIdxInfo->estimatedCost = (double)500; pIdxInfo->estimatedRows = 500; return SQLITE_OK; } /* ** This following structure defines all the methods for the ** stmt virtual table. */ static sqlite3_module stmtModule = { 0, /* iVersion */ 0, /* xCreate */ stmtConnect, /* xConnect */ stmtBestIndex, /* xBestIndex */ stmtDisconnect, /* xDisconnect */ 0, /* xDestroy */ stmtOpen, /* xOpen - open a cursor */ stmtClose, /* xClose - close a cursor */ stmtFilter, /* xFilter - configure scan constraints */ stmtNext, /* xNext - advance a cursor */ stmtEof, /* xEof - check for end of scan */ stmtColumn, /* xColumn - read data */ stmtRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ int sqlite3StmtVtabInit(sqlite3 *db){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3_create_module(db, "sqlite_stmt", &stmtModule, 0); #endif return rc; } #ifndef SQLITE_CORE #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_stmt_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3StmtVtabInit(db); #endif return rc; } #endif /* SQLITE_CORE */ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
Added ext/misc/unionvtab.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 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 | /* ** 2017 July 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. ** ************************************************************************* ** ** This file contains the implementation of the "unionvtab" and "swarmvtab" ** virtual tables. These modules provide read-only access to multiple tables, ** possibly in multiple database files, via a single database object. ** The source tables must have the following characteristics: ** ** * They must all be rowid tables (not VIRTUAL or WITHOUT ROWID ** tables or views). ** ** * Each table must have the same set of columns, declared in ** the same order and with the same declared types. ** ** * The tables must not feature a user-defined column named "_rowid_". ** ** * Each table must contain a distinct range of rowid values. ** ** The difference between the two virtual table modules is that for ** "unionvtab", all source tables must be located in the main database or ** in databases ATTACHed to the main database by the user. For "swarmvtab", ** the tables may be located in any database file on disk. The "swarmvtab" ** implementation takes care of opening and closing database files ** automatically. ** ** UNIONVTAB ** ** A "unionvtab" virtual table is created as follows: ** ** CREATE VIRTUAL TABLE <name> USING unionvtab(<sql-statement>); ** ** The implementation evalutes <sql statement> whenever a unionvtab virtual ** table is created or opened. It should return one row for each source ** database table. The four columns required of each row are: ** ** 1. The name of the database containing the table ("main" or "temp" or ** the name of an attached database). Or NULL to indicate that all ** databases should be searched for the table in the usual fashion. ** ** 2. The name of the database table. ** ** 3. The smallest rowid in the range of rowids that may be stored in the ** database table (an integer). ** ** 4. The largest rowid in the range of rowids that may be stored in the ** database table (an integer). ** ** SWARMVTAB ** ** A "swarmvtab" virtual table is created similarly to a unionvtab table: ** ** CREATE VIRTUAL TABLE <name> ** USING swarmvtab(<sql-statement>, <callback>); ** ** The difference is that for a swarmvtab table, the first column returned ** by the <sql statement> must return a path or URI that can be used to open ** the database file containing the source table. The <callback> option ** is optional. If included, it is the name of an application-defined ** SQL function that is invoked with the URI of the file, if the file ** does not already exist on disk. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Largest and smallest possible 64-bit signed integers. These macros ** copied from sqliteInt.h. */ #ifndef LARGEST_INT64 # define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) #endif #ifndef SMALLEST_INT64 # define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) #endif /* ** The following is also copied from sqliteInt.h. To facilitate coverage ** testing. */ #ifndef ALWAYS # if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define ALWAYS(X) (1) # define NEVER(X) (0) # elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) # else # define ALWAYS(X) (X) # define NEVER(X) (X) # endif #endif /* ** The swarmvtab module attempts to keep the number of open database files ** at or below this limit. This may not be possible if there are too many ** simultaneous queries. */ #define SWARMVTAB_MAX_OPEN 9 typedef struct UnionCsr UnionCsr; typedef struct UnionTab UnionTab; typedef struct UnionSrc UnionSrc; /* ** Each source table (row returned by the initialization query) is ** represented by an instance of the following structure stored in the ** UnionTab.aSrc[] array. */ struct UnionSrc { char *zDb; /* Database containing source table */ char *zTab; /* Source table name */ sqlite3_int64 iMin; /* Minimum rowid */ sqlite3_int64 iMax; /* Maximum rowid */ /* Fields used by swarmvtab only */ char *zFile; /* Database file containing table zTab */ int nUser; /* Current number of users */ sqlite3 *db; /* Database handle */ UnionSrc *pNextClosable; /* Next in list of closable sources */ }; /* ** Virtual table type for union vtab. */ struct UnionTab { sqlite3_vtab base; /* Base class - must be first */ sqlite3 *db; /* Database handle */ int bSwarm; /* 1 for "swarmvtab", 0 for "unionvtab" */ int iPK; /* INTEGER PRIMARY KEY column, or -1 */ int nSrc; /* Number of elements in the aSrc[] array */ UnionSrc *aSrc; /* Array of source tables, sorted by rowid */ /* Used by swarmvtab only */ char *zSourceStr; /* Expected unionSourceToStr() value */ char *zNotFoundCallback; /* UDF to invoke if file not found on open */ UnionSrc *pClosable; /* First in list of closable sources */ int nOpen; /* Current number of open sources */ int nMaxOpen; /* Maximum number of open sources */ }; /* ** Virtual table cursor type for union vtab. */ struct UnionCsr { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3_stmt *pStmt; /* SQL statement to run */ /* Used by swarmvtab only */ sqlite3_int64 iMaxRowid; /* Last rowid to visit */ int iTab; /* Index of table read by pStmt */ }; /* ** Given UnionTab table pTab and UnionSrc object pSrc, return the database ** handle that should be used to access the table identified by pSrc. This ** is the main db handle for "unionvtab" tables, or the source-specific ** handle for "swarmvtab". */ #define unionGetDb(pTab, pSrc) ((pTab)->bSwarm ? (pSrc)->db : (pTab)->db) /* ** If *pRc is other than SQLITE_OK when this function is called, it ** always returns NULL. Otherwise, it attempts to allocate and return ** a pointer to nByte bytes of zeroed memory. If the memory allocation ** is attempted but fails, NULL is returned and *pRc is set to ** SQLITE_NOMEM. */ static void *unionMalloc(int *pRc, int nByte){ void *pRet; assert( nByte>0 ); if( *pRc==SQLITE_OK ){ pRet = sqlite3_malloc(nByte); if( pRet ){ memset(pRet, 0, nByte); }else{ *pRc = SQLITE_NOMEM; } }else{ pRet = 0; } return pRet; } /* ** If *pRc is other than SQLITE_OK when this function is called, it ** always returns NULL. Otherwise, it attempts to allocate and return ** a copy of the nul-terminated string passed as the second argument. ** If the allocation is attempted but fails, NULL is returned and *pRc is ** set to SQLITE_NOMEM. */ static char *unionStrdup(int *pRc, const char *zIn){ char *zRet = 0; if( zIn ){ int nByte = (int)strlen(zIn) + 1; zRet = unionMalloc(pRc, nByte); if( zRet ){ memcpy(zRet, zIn, nByte); } } return zRet; } /* ** If the first character of the string passed as the only argument to this ** function is one of the 4 that may be used as an open quote character ** in SQL, this function assumes that the input is a well-formed quoted SQL ** string. In this case the string is dequoted in place. ** ** If the first character of the input is not an open quote, then this ** function is a no-op. */ static void unionDequote(char *z){ if( z ){ char q = z[0]; /* Set stack variable q to the close-quote character */ if( q=='[' || q=='\'' || q=='"' || q=='`' ){ int iIn = 1; int iOut = 0; if( q=='[' ) q = ']'; while( ALWAYS(z[iIn]) ){ if( z[iIn]==q ){ if( z[iIn+1]!=q ){ /* Character iIn was the close quote. */ iIn++; break; }else{ /* Character iIn and iIn+1 form an escaped quote character. Skip ** the input cursor past both and copy a single quote character ** to the output buffer. */ iIn += 2; z[iOut++] = q; } }else{ z[iOut++] = z[iIn++]; } } z[iOut] = '\0'; } } } /* ** This function is a no-op if *pRc is set to other than SQLITE_OK when it ** is called. NULL is returned in this case. ** ** Otherwise, the SQL statement passed as the third argument is prepared ** against the database handle passed as the second. If the statement is ** successfully prepared, a pointer to the new statement handle is ** returned. It is the responsibility of the caller to eventually free the ** statement by calling sqlite3_finalize(). Alternatively, if statement ** compilation fails, NULL is returned, *pRc is set to an SQLite error ** code and *pzErr may be set to an error message buffer allocated by ** sqlite3_malloc(). */ static sqlite3_stmt *unionPrepare( int *pRc, /* IN/OUT: Error code */ sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement to prepare */ char **pzErr /* OUT: Error message */ ){ sqlite3_stmt *pRet = 0; assert( pzErr ); if( *pRc==SQLITE_OK ){ int rc = sqlite3_prepare_v2(db, zSql, -1, &pRet, 0); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("sql error: %s", sqlite3_errmsg(db)); *pRc = rc; } } return pRet; } /* ** Like unionPrepare(), except prepare the results of vprintf(zFmt, ...) ** instead of a constant SQL string. */ static sqlite3_stmt *unionPreparePrintf( int *pRc, /* IN/OUT: Error code */ char **pzErr, /* OUT: Error message */ sqlite3 *db, /* Database handle */ const char *zFmt, /* printf() format string */ ... /* Trailing printf args */ ){ sqlite3_stmt *pRet = 0; char *zSql; va_list ap; va_start(ap, zFmt); zSql = sqlite3_vmprintf(zFmt, ap); if( *pRc==SQLITE_OK ){ if( zSql==0 ){ *pRc = SQLITE_NOMEM; }else{ pRet = unionPrepare(pRc, db, zSql, pzErr); } } sqlite3_free(zSql); va_end(ap); return pRet; } /* ** Call sqlite3_reset() on SQL statement pStmt. If *pRc is set to ** SQLITE_OK when this function is called, then it is set to the ** value returned by sqlite3_reset() before this function exits. ** In this case, *pzErr may be set to point to an error message ** buffer allocated by sqlite3_malloc(). */ #if 0 static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){ int rc = sqlite3_reset(pStmt); if( *pRc==SQLITE_OK ){ *pRc = rc; if( rc ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(sqlite3_db_handle(pStmt))); } } } #endif /* ** Call sqlite3_finalize() on SQL statement pStmt. If *pRc is set to ** SQLITE_OK when this function is called, then it is set to the ** value returned by sqlite3_finalize() before this function exits. */ static void unionFinalize(int *pRc, sqlite3_stmt *pStmt, char **pzErr){ sqlite3 *db = sqlite3_db_handle(pStmt); int rc = sqlite3_finalize(pStmt); if( *pRc==SQLITE_OK ){ *pRc = rc; if( rc ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } } } /* ** This function is a no-op for unionvtab. For swarmvtab, it attempts to ** close open database files until at most nMax are open. An SQLite error ** code is returned if an error occurs, or SQLITE_OK otherwise. */ static void unionCloseSources(UnionTab *pTab, int nMax){ while( pTab->pClosable && pTab->nOpen>nMax ){ UnionSrc **pp; for(pp=&pTab->pClosable; (*pp)->pNextClosable; pp=&(*pp)->pNextClosable); assert( (*pp)->db ); sqlite3_close((*pp)->db); (*pp)->db = 0; *pp = 0; pTab->nOpen--; } } /* ** xDisconnect method. */ static int unionDisconnect(sqlite3_vtab *pVtab){ if( pVtab ){ UnionTab *pTab = (UnionTab*)pVtab; int i; for(i=0; i<pTab->nSrc; i++){ UnionSrc *pSrc = &pTab->aSrc[i]; sqlite3_free(pSrc->zDb); sqlite3_free(pSrc->zTab); sqlite3_free(pSrc->zFile); sqlite3_close(pSrc->db); } sqlite3_free(pTab->zSourceStr); sqlite3_free(pTab->zNotFoundCallback); sqlite3_free(pTab->aSrc); sqlite3_free(pTab); } return SQLITE_OK; } /* ** Check that the table identified by pSrc is a rowid table. If not, ** return SQLITE_ERROR and set (*pzErr) to point to an English language ** error message. If the table is a rowid table and no error occurs, ** return SQLITE_OK and leave (*pzErr) unmodified. */ static int unionIsIntkeyTable( sqlite3 *db, /* Database handle */ UnionSrc *pSrc, /* Source table to test */ char **pzErr /* OUT: Error message */ ){ int bPk = 0; const char *zType = 0; int rc; sqlite3_table_column_metadata( db, pSrc->zDb, pSrc->zTab, "_rowid_", &zType, 0, 0, &bPk, 0 ); rc = sqlite3_errcode(db); if( rc==SQLITE_ERROR || (rc==SQLITE_OK && (!bPk || sqlite3_stricmp("integer", zType))) ){ rc = SQLITE_ERROR; *pzErr = sqlite3_mprintf("no such rowid table: %s%s%s", (pSrc->zDb ? pSrc->zDb : ""), (pSrc->zDb ? "." : ""), pSrc->zTab ); } return rc; } /* ** This function is a no-op if *pRc is other than SQLITE_OK when it is ** called. In this case it returns NULL. ** ** Otherwise, this function checks that the source table passed as the ** second argument (a) exists, (b) is not a view and (c) has a column ** named "_rowid_" of type "integer" that is the primary key. ** If this is not the case, *pRc is set to SQLITE_ERROR and NULL is ** returned. ** ** Finally, if the source table passes the checks above, a nul-terminated ** string describing the column names and types belonging to the source ** table is returned. Tables with the same set of column names and types ** cause this function to return identical strings. Is is the responsibility ** of the caller to free the returned string using sqlite3_free() when ** it is no longer required. */ static char *unionSourceToStr( int *pRc, /* IN/OUT: Error code */ UnionTab *pTab, /* Virtual table object */ UnionSrc *pSrc, /* Source table to test */ char **pzErr /* OUT: Error message */ ){ char *zRet = 0; if( *pRc==SQLITE_OK ){ sqlite3 *db = unionGetDb(pTab, pSrc); int rc = unionIsIntkeyTable(db, pSrc, pzErr); sqlite3_stmt *pStmt = unionPrepare(&rc, db, "SELECT group_concat(quote(name) || '.' || quote(type)) " "FROM pragma_table_info(?, ?)", pzErr ); if( rc==SQLITE_OK ){ sqlite3_bind_text(pStmt, 1, pSrc->zTab, -1, SQLITE_STATIC); sqlite3_bind_text(pStmt, 2, pSrc->zDb, -1, SQLITE_STATIC); if( SQLITE_ROW==sqlite3_step(pStmt) ){ const char *z = (const char*)sqlite3_column_text(pStmt, 0); zRet = unionStrdup(&rc, z); } unionFinalize(&rc, pStmt, pzErr); } *pRc = rc; } return zRet; } /* ** Check that all configured source tables exist and have the same column ** names and datatypes. If this is not the case, or if some other error ** occurs, return an SQLite error code. In this case *pzErr may be set ** to point to an error message buffer allocated by sqlite3_mprintf(). ** Or, if no problems regarding the source tables are detected and no ** other error occurs, SQLITE_OK is returned. */ static int unionSourceCheck(UnionTab *pTab, char **pzErr){ int rc = SQLITE_OK; char *z0 = 0; int i; assert( *pzErr==0 ); z0 = unionSourceToStr(&rc, pTab, &pTab->aSrc[0], pzErr); for(i=1; i<pTab->nSrc; i++){ char *z = unionSourceToStr(&rc, pTab, &pTab->aSrc[i], pzErr); if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){ *pzErr = sqlite3_mprintf("source table schema mismatch"); rc = SQLITE_ERROR; } sqlite3_free(z); } sqlite3_free(z0); return rc; } /* ** Try to open the swarmvtab database. If initially unable, invoke the ** not-found callback UDF and then try again. */ static int unionOpenDatabaseInner(UnionTab *pTab, UnionSrc *pSrc, char **pzErr){ int rc = SQLITE_OK; static const int openFlags = SQLITE_OPEN_READONLY | SQLITE_OPEN_URI; rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0); if( rc==SQLITE_OK ) return rc; if( pTab->zNotFoundCallback ){ char *zSql = sqlite3_mprintf("SELECT \"%w\"(%Q);", pTab->zNotFoundCallback, pSrc->zFile); sqlite3_close(pSrc->db); pSrc->db = 0; if( zSql==0 ){ *pzErr = sqlite3_mprintf("out of memory"); return SQLITE_NOMEM; } rc = sqlite3_exec(pTab->db, zSql, 0, 0, pzErr); sqlite3_free(zSql); if( rc ) return rc; rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0); } if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pSrc->db)); } return rc; } /* ** This function may only be called for swarmvtab tables. The results of ** calling it on a unionvtab table are undefined. ** ** For a swarmvtab table, this function ensures that source database iSrc ** is open. If the database is opened successfully and the schema is as ** expected, or if it is already open when this function is called, SQLITE_OK ** is returned. ** ** Alternatively If an error occurs while opening the databases, or if the ** database schema is unsuitable, an SQLite error code is returned and (*pzErr) ** may be set to point to an English language error message. In this case it is ** the responsibility of the caller to eventually free the error message buffer ** using sqlite3_free(). */ static int unionOpenDatabase(UnionTab *pTab, int iSrc, char **pzErr){ int rc = SQLITE_OK; UnionSrc *pSrc = &pTab->aSrc[iSrc]; assert( pTab->bSwarm && iSrc<pTab->nSrc ); if( pSrc->db==0 ){ unionCloseSources(pTab, pTab->nMaxOpen-1); rc = unionOpenDatabaseInner(pTab, pSrc, pzErr); if( rc==SQLITE_OK ){ char *z = unionSourceToStr(&rc, pTab, pSrc, pzErr); if( rc==SQLITE_OK ){ if( pTab->zSourceStr==0 ){ pTab->zSourceStr = z; }else{ if( sqlite3_stricmp(z, pTab->zSourceStr) ){ *pzErr = sqlite3_mprintf("source table schema mismatch"); rc = SQLITE_ERROR; } sqlite3_free(z); } } } if( rc==SQLITE_OK ){ pSrc->pNextClosable = pTab->pClosable; pTab->pClosable = pSrc; pTab->nOpen++; }else{ sqlite3_close(pSrc->db); pSrc->db = 0; } } return rc; } /* ** This function is a no-op for unionvtab tables. For swarmvtab, increment ** the reference count for source table iTab. If the reference count was ** zero before it was incremented, also remove the source from the closable ** list. */ static void unionIncrRefcount(UnionTab *pTab, int iTab){ if( pTab->bSwarm ){ UnionSrc *pSrc = &pTab->aSrc[iTab]; assert( pSrc->nUser>=0 && pSrc->db ); if( pSrc->nUser==0 ){ UnionSrc **pp; for(pp=&pTab->pClosable; *pp!=pSrc; pp=&(*pp)->pNextClosable); *pp = pSrc->pNextClosable; pSrc->pNextClosable = 0; } pSrc->nUser++; } } /* ** Finalize the SQL statement pCsr->pStmt and return the result. ** ** If this is a swarmvtab table (not unionvtab) and pCsr->pStmt was not ** NULL when this function was called, also decrement the reference ** count on the associated source table. If this means the source tables ** refcount is now zero, add it to the closable list. */ static int unionFinalizeCsrStmt(UnionCsr *pCsr){ int rc = SQLITE_OK; if( pCsr->pStmt ){ UnionTab *pTab = (UnionTab*)pCsr->base.pVtab; UnionSrc *pSrc = &pTab->aSrc[pCsr->iTab]; rc = sqlite3_finalize(pCsr->pStmt); pCsr->pStmt = 0; if( pTab->bSwarm ){ pSrc->nUser--; assert( pSrc->nUser>=0 ); if( pSrc->nUser==0 ){ pSrc->pNextClosable = pTab->pClosable; pTab->pClosable = pSrc; } unionCloseSources(pTab, pTab->nMaxOpen); } } return rc; } /* ** xConnect/xCreate method. ** ** The argv[] array contains the following: ** ** argv[0] -> module name ("unionvtab" or "swarmvtab") ** argv[1] -> database name ** argv[2] -> table name ** argv[3] -> SQL statement ** argv[4] -> not-found callback UDF name */ static int unionConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ UnionTab *pTab = 0; int rc = SQLITE_OK; int bSwarm = (pAux==0 ? 0 : 1); const char *zVtab = (bSwarm ? "swarmvtab" : "unionvtab"); if( sqlite3_stricmp("temp", argv[1]) ){ /* unionvtab tables may only be created in the temp schema */ *pzErr = sqlite3_mprintf("%s tables must be created in TEMP schema", zVtab); rc = SQLITE_ERROR; }else if( argc!=4 && argc!=5 ){ *pzErr = sqlite3_mprintf("wrong number of arguments for %s", zVtab); rc = SQLITE_ERROR; }else{ int nAlloc = 0; /* Allocated size of pTab->aSrc[] */ sqlite3_stmt *pStmt = 0; /* Argument statement */ char *zArg = unionStrdup(&rc, argv[3]); /* Copy of argument to CVT */ /* Prepare the SQL statement. Instead of executing it directly, sort ** the results by the "minimum rowid" field. This makes it easier to ** check that there are no rowid range overlaps between source tables ** and that the UnionTab.aSrc[] array is always sorted by rowid. */ unionDequote(zArg); pStmt = unionPreparePrintf(&rc, pzErr, db, "SELECT * FROM (%z) ORDER BY 3", zArg ); /* Allocate the UnionTab structure */ pTab = unionMalloc(&rc, sizeof(UnionTab)); /* Iterate through the rows returned by the SQL statement specified ** as an argument to the CREATE VIRTUAL TABLE statement. */ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); const char *zTab = (const char*)sqlite3_column_text(pStmt, 1); sqlite3_int64 iMin = sqlite3_column_int64(pStmt, 2); sqlite3_int64 iMax = sqlite3_column_int64(pStmt, 3); UnionSrc *pSrc; /* Grow the pTab->aSrc[] array if required. */ if( nAlloc<=pTab->nSrc ){ int nNew = nAlloc ? nAlloc*2 : 8; UnionSrc *aNew = (UnionSrc*)sqlite3_realloc( pTab->aSrc, nNew*sizeof(UnionSrc) ); if( aNew==0 ){ rc = SQLITE_NOMEM; break; }else{ memset(&aNew[pTab->nSrc], 0, (nNew-pTab->nSrc)*sizeof(UnionSrc)); pTab->aSrc = aNew; nAlloc = nNew; } } /* Check for problems with the specified range of rowids */ if( iMax<iMin || (pTab->nSrc>0 && iMin<=pTab->aSrc[pTab->nSrc-1].iMax) ){ *pzErr = sqlite3_mprintf("rowid range mismatch error"); rc = SQLITE_ERROR; } if( rc==SQLITE_OK ){ pSrc = &pTab->aSrc[pTab->nSrc++]; pSrc->zTab = unionStrdup(&rc, zTab); pSrc->iMin = iMin; pSrc->iMax = iMax; if( bSwarm ){ pSrc->zFile = unionStrdup(&rc, zDb); }else{ pSrc->zDb = unionStrdup(&rc, zDb); } } } unionFinalize(&rc, pStmt, pzErr); pStmt = 0; /* Capture the not-found callback UDF name */ if( rc==SQLITE_OK && argc>=5 ){ pTab->zNotFoundCallback = unionStrdup(&rc, argv[4]); unionDequote(pTab->zNotFoundCallback); } /* It is an error if the SELECT statement returned zero rows. If only ** because there is no way to determine the schema of the virtual ** table in this case. */ if( rc==SQLITE_OK && pTab->nSrc==0 ){ *pzErr = sqlite3_mprintf("no source tables configured"); rc = SQLITE_ERROR; } /* For unionvtab, verify that all source tables exist and have ** compatible schemas. For swarmvtab, attach the first database and ** check that the first table is a rowid table only. */ if( rc==SQLITE_OK ){ pTab->db = db; pTab->bSwarm = bSwarm; pTab->nMaxOpen = SWARMVTAB_MAX_OPEN; if( bSwarm ){ rc = unionOpenDatabase(pTab, 0, pzErr); }else{ rc = unionSourceCheck(pTab, pzErr); } } /* Compose a CREATE TABLE statement and pass it to declare_vtab() */ if( rc==SQLITE_OK ){ UnionSrc *pSrc = &pTab->aSrc[0]; sqlite3 *tdb = unionGetDb(pTab, pSrc); pStmt = unionPreparePrintf(&rc, pzErr, tdb, "SELECT " "'CREATE TABLE xyz('" " || group_concat(quote(name) || ' ' || type, ', ')" " || ')'," "max((cid+1) * (type='INTEGER' COLLATE nocase AND pk=1))-1 " "FROM pragma_table_info(%Q, ?)", pSrc->zTab, pSrc->zDb ); } if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zDecl = (const char*)sqlite3_column_text(pStmt, 0); rc = sqlite3_declare_vtab(db, zDecl); pTab->iPK = sqlite3_column_int(pStmt, 1); } unionFinalize(&rc, pStmt, pzErr); } if( rc!=SQLITE_OK ){ unionDisconnect((sqlite3_vtab*)pTab); pTab = 0; } *ppVtab = (sqlite3_vtab*)pTab; return rc; } /* ** xOpen */ static int unionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ UnionCsr *pCsr; int rc = SQLITE_OK; (void)p; /* Suppress harmless warning */ pCsr = (UnionCsr*)unionMalloc(&rc, sizeof(UnionCsr)); *ppCursor = &pCsr->base; return rc; } /* ** xClose */ static int unionClose(sqlite3_vtab_cursor *cur){ UnionCsr *pCsr = (UnionCsr*)cur; unionFinalizeCsrStmt(pCsr); sqlite3_free(pCsr); return SQLITE_OK; } /* ** This function does the work of the xNext() method. Except that, if it ** returns SQLITE_ROW, it should be called again within the same xNext() ** method call. See unionNext() for details. */ static int doUnionNext(UnionCsr *pCsr){ int rc = SQLITE_OK; assert( pCsr->pStmt ); if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){ UnionTab *pTab = (UnionTab*)pCsr->base.pVtab; rc = unionFinalizeCsrStmt(pCsr); if( rc==SQLITE_OK && pTab->bSwarm ){ pCsr->iTab++; if( pCsr->iTab<pTab->nSrc ){ UnionSrc *pSrc = &pTab->aSrc[pCsr->iTab]; if( pCsr->iMaxRowid>=pSrc->iMin ){ /* It is necessary to scan the next table. */ rc = unionOpenDatabase(pTab, pCsr->iTab, &pTab->base.zErrMsg); pCsr->pStmt = unionPreparePrintf(&rc, &pTab->base.zErrMsg, pSrc->db, "SELECT rowid, * FROM %Q %s %lld", pSrc->zTab, (pSrc->iMax>pCsr->iMaxRowid ? "WHERE _rowid_ <=" : "-- "), pCsr->iMaxRowid ); if( rc==SQLITE_OK ){ assert( pCsr->pStmt ); unionIncrRefcount(pTab, pCsr->iTab); rc = SQLITE_ROW; } } } } } return rc; } /* ** xNext */ static int unionNext(sqlite3_vtab_cursor *cur){ int rc; do { rc = doUnionNext((UnionCsr*)cur); }while( rc==SQLITE_ROW ); return rc; } /* ** xColumn */ static int unionColumn( sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i ){ UnionCsr *pCsr = (UnionCsr*)cur; sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1)); return SQLITE_OK; } /* ** xRowid */ static int unionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ UnionCsr *pCsr = (UnionCsr*)cur; *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); return SQLITE_OK; } /* ** xEof */ static int unionEof(sqlite3_vtab_cursor *cur){ UnionCsr *pCsr = (UnionCsr*)cur; return pCsr->pStmt==0; } /* ** xFilter */ static int unionFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ UnionTab *pTab = (UnionTab*)(pVtabCursor->pVtab); UnionCsr *pCsr = (UnionCsr*)pVtabCursor; int rc = SQLITE_OK; int i; char *zSql = 0; int bZero = 0; sqlite3_int64 iMin = SMALLEST_INT64; sqlite3_int64 iMax = LARGEST_INT64; assert( idxNum==0 || idxNum==SQLITE_INDEX_CONSTRAINT_EQ || idxNum==SQLITE_INDEX_CONSTRAINT_LE || idxNum==SQLITE_INDEX_CONSTRAINT_GE || idxNum==SQLITE_INDEX_CONSTRAINT_LT || idxNum==SQLITE_INDEX_CONSTRAINT_GT || idxNum==(SQLITE_INDEX_CONSTRAINT_GE|SQLITE_INDEX_CONSTRAINT_LE) ); (void)idxStr; /* Suppress harmless warning */ if( idxNum==SQLITE_INDEX_CONSTRAINT_EQ ){ assert( argc==1 ); iMin = iMax = sqlite3_value_int64(argv[0]); }else{ if( idxNum & (SQLITE_INDEX_CONSTRAINT_LE|SQLITE_INDEX_CONSTRAINT_LT) ){ assert( argc>=1 ); iMax = sqlite3_value_int64(argv[0]); if( idxNum & SQLITE_INDEX_CONSTRAINT_LT ){ if( iMax==SMALLEST_INT64 ){ bZero = 1; }else{ iMax--; } } } if( idxNum & (SQLITE_INDEX_CONSTRAINT_GE|SQLITE_INDEX_CONSTRAINT_GT) ){ assert( argc>=1 ); iMin = sqlite3_value_int64(argv[argc-1]); if( idxNum & SQLITE_INDEX_CONSTRAINT_GT ){ if( iMin==LARGEST_INT64 ){ bZero = 1; }else{ iMin++; } } } } unionFinalizeCsrStmt(pCsr); if( bZero ){ return SQLITE_OK; } for(i=0; i<pTab->nSrc; i++){ UnionSrc *pSrc = &pTab->aSrc[i]; if( iMin>pSrc->iMax || iMax<pSrc->iMin ){ continue; } zSql = sqlite3_mprintf("%z%sSELECT rowid, * FROM %s%q%s%Q" , zSql , (zSql ? " UNION ALL " : "") , (pSrc->zDb ? "'" : "") , (pSrc->zDb ? pSrc->zDb : "") , (pSrc->zDb ? "'." : "") , pSrc->zTab ); if( zSql==0 ){ rc = SQLITE_NOMEM; break; } if( iMin==iMax ){ zSql = sqlite3_mprintf("%z WHERE rowid=%lld", zSql, iMin); }else{ const char *zWhere = "WHERE"; if( iMin!=SMALLEST_INT64 && iMin>pSrc->iMin ){ zSql = sqlite3_mprintf("%z WHERE rowid>=%lld", zSql, iMin); zWhere = "AND"; } if( iMax!=LARGEST_INT64 && iMax<pSrc->iMax ){ zSql = sqlite3_mprintf("%z %s rowid<=%lld", zSql, zWhere, iMax); } } if( pTab->bSwarm ){ pCsr->iTab = i; pCsr->iMaxRowid = iMax; rc = unionOpenDatabase(pTab, i, &pTab->base.zErrMsg); break; } } if( zSql==0 ){ return rc; }else{ sqlite3 *db = unionGetDb(pTab, &pTab->aSrc[pCsr->iTab]); pCsr->pStmt = unionPrepare(&rc, db, zSql, &pTab->base.zErrMsg); if( pCsr->pStmt ){ unionIncrRefcount(pTab, pCsr->iTab); } sqlite3_free(zSql); } if( rc!=SQLITE_OK ) return rc; return unionNext(pVtabCursor); } /* ** xBestIndex. ** ** This implementation searches for constraints on the rowid field. EQ, ** LE, LT, GE and GT are handled. ** ** If there is an EQ comparison, then idxNum is set to INDEX_CONSTRAINT_EQ. ** In this case the only argument passed to xFilter is the rhs of the == ** operator. ** ** Otherwise, if an LE or LT constraint is found, then the INDEX_CONSTRAINT_LE ** or INDEX_CONSTRAINT_LT (but not both) bit is set in idxNum. The first ** argument to xFilter is the rhs of the <= or < operator. Similarly, if ** an GE or GT constraint is found, then the INDEX_CONSTRAINT_GE or ** INDEX_CONSTRAINT_GT bit is set in idxNum. The rhs of the >= or > operator ** is passed as either the first or second argument to xFilter, depending ** on whether or not there is also a LT|LE constraint. */ static int unionBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ UnionTab *pTab = (UnionTab*)tab; int iEq = -1; int iLt = -1; int iGt = -1; int i; for(i=0; i<pIdxInfo->nConstraint; i++){ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i]; if( p->usable && (p->iColumn<0 || p->iColumn==pTab->iPK) ){ switch( p->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: iEq = i; break; case SQLITE_INDEX_CONSTRAINT_LE: case SQLITE_INDEX_CONSTRAINT_LT: iLt = i; break; case SQLITE_INDEX_CONSTRAINT_GE: case SQLITE_INDEX_CONSTRAINT_GT: iGt = i; break; } } } if( iEq>=0 ){ pIdxInfo->estimatedRows = 1; pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE; pIdxInfo->estimatedCost = 3.0; pIdxInfo->idxNum = SQLITE_INDEX_CONSTRAINT_EQ; pIdxInfo->aConstraintUsage[iEq].argvIndex = 1; pIdxInfo->aConstraintUsage[iEq].omit = 1; }else{ int iCons = 1; int idxNum = 0; sqlite3_int64 nRow = 1000000; if( iLt>=0 ){ nRow = nRow / 2; pIdxInfo->aConstraintUsage[iLt].argvIndex = iCons++; pIdxInfo->aConstraintUsage[iLt].omit = 1; idxNum |= pIdxInfo->aConstraint[iLt].op; } if( iGt>=0 ){ nRow = nRow / 2; pIdxInfo->aConstraintUsage[iGt].argvIndex = iCons++; pIdxInfo->aConstraintUsage[iGt].omit = 1; idxNum |= pIdxInfo->aConstraint[iGt].op; } pIdxInfo->estimatedRows = nRow; pIdxInfo->estimatedCost = 3.0 * (double)nRow; pIdxInfo->idxNum = idxNum; } return SQLITE_OK; } /* ** Register the unionvtab virtual table module with database handle db. */ static int createUnionVtab(sqlite3 *db){ static sqlite3_module unionModule = { 0, /* iVersion */ unionConnect, unionConnect, unionBestIndex, /* xBestIndex - query planner */ unionDisconnect, unionDisconnect, unionOpen, /* xOpen - open a cursor */ unionClose, /* xClose - close a cursor */ unionFilter, /* xFilter - configure scan constraints */ unionNext, /* xNext - advance a cursor */ unionEof, /* xEof - check for end of scan */ unionColumn, /* xColumn - read data */ unionRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0 /* xRollbackTo */ }; int rc; rc = sqlite3_create_module(db, "unionvtab", &unionModule, 0); if( rc==SQLITE_OK ){ rc = sqlite3_create_module(db, "swarmvtab", &unionModule, (void*)db); } return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_unionvtab_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); (void)pzErrMsg; /* Suppress harmless warning */ #ifndef SQLITE_OMIT_VIRTUALTABLE rc = createUnionVtab(db); #endif return rc; } |
Added ext/misc/vtablog.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 | /* ** 2017-08-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 a virtual table that prints diagnostic information ** on stdout when its key interfaces are called. This is intended for ** interactive analysis and debugging of virtual table interfaces. ** ** Usage example: ** ** .load ./vtablog ** CREATE VIRTUAL TABLE temp.log USING vtablog( ** schema='CREATE TABLE x(a,b,c)', ** rows=25 ** ); ** SELECT * FROM log; */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> #include <ctype.h> /* vtablog_vtab is a subclass of sqlite3_vtab which will ** serve as the underlying representation of a vtablog virtual table */ typedef struct vtablog_vtab vtablog_vtab; struct vtablog_vtab { sqlite3_vtab base; /* Base class - must be first */ int nRow; /* Number of rows in the table */ int iInst; /* Instance number for this vtablog table */ int nCursor; /* Number of cursors created */ }; /* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct vtablog_cursor vtablog_cursor; struct vtablog_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ int iCursor; /* Cursor number */ sqlite3_int64 iRowid; /* The rowid */ }; /* Skip leading whitespace. Return a pointer to the first non-whitespace ** character, or to the zero terminator if the string has only whitespace */ static const char *vtablog_skip_whitespace(const char *z){ while( isspace((unsigned char)z[0]) ) z++; return z; } /* Remove trailing whitespace from the end of string z[] */ static void vtablog_trim_whitespace(char *z){ size_t n = strlen(z); while( n>0 && isspace((unsigned char)z[n]) ) n--; z[n] = 0; } /* Dequote the string */ static void vtablog_dequote(char *z){ int j; char cQuote = z[0]; size_t i, n; if( cQuote!='\'' && cQuote!='"' ) return; n = strlen(z); if( n<2 || z[n-1]!=z[0] ) return; for(i=1, j=0; i<n-1; i++){ if( z[i]==cQuote && z[i+1]==cQuote ) i++; z[j++] = z[i]; } z[j] = 0; } /* Check to see if the string is of the form: "TAG = VALUE" with optional ** whitespace before and around tokens. If it is, return a pointer to the ** first character of VALUE. If it is not, return NULL. */ static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){ z = vtablog_skip_whitespace(z); if( strncmp(zTag, z, nTag)!=0 ) return 0; z = vtablog_skip_whitespace(z+nTag); if( z[0]!='=' ) return 0; return vtablog_skip_whitespace(z+1); } /* Decode a parameter that requires a dequoted string. ** ** Return non-zero on an error. */ static int vtablog_string_parameter( char **pzErr, /* Leave the error message here, if there is one */ const char *zParam, /* Parameter we are checking for */ const char *zArg, /* Raw text of the virtual table argment */ char **pzVal /* Write the dequoted string value here */ ){ const char *zValue; zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg); if( zValue==0 ) return 0; if( *pzVal ){ *pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam); return 1; } *pzVal = sqlite3_mprintf("%s", zValue); if( *pzVal==0 ){ *pzErr = sqlite3_mprintf("out of memory"); return 1; } vtablog_trim_whitespace(*pzVal); vtablog_dequote(*pzVal); return 0; } #if 0 /* not used - yet */ /* Return 0 if the argument is false and 1 if it is true. Return -1 if ** we cannot really tell. */ static int vtablog_boolean(const char *z){ if( sqlite3_stricmp("yes",z)==0 || sqlite3_stricmp("on",z)==0 || sqlite3_stricmp("true",z)==0 || (z[0]=='1' && z[1]==0) ){ return 1; } if( sqlite3_stricmp("no",z)==0 || sqlite3_stricmp("off",z)==0 || sqlite3_stricmp("false",z)==0 || (z[0]=='0' && z[1]==0) ){ return 0; } return -1; } #endif /* ** The vtablogConnect() method is invoked to create a new ** vtablog_vtab that describes the vtablog virtual table. ** ** Think of this routine as the constructor for vtablog_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the vtablog_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against vtablog will look like. */ static int vtablogConnectCreate( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr, int isCreate ){ static int nInst = 0; vtablog_vtab *pNew; int i; int rc; int iInst = ++nInst; char *zSchema = 0; char *zNRow = 0; printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst); printf(" argc=%d\n", argc); for(i=0; i<argc; i++){ printf(" argv[%d] = ", i); if( argv[i] ){ printf("[%s]\n", argv[i]); }else{ printf("NULL\n"); } } for(i=3; i<argc; i++){ const char *z = argv[i]; if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){ return SQLITE_ERROR; } if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){ return SQLITE_ERROR; } } if( zSchema==0 ){ *pzErr = sqlite3_mprintf("no schema defined"); return SQLITE_ERROR; } rc = sqlite3_declare_vtab(db, zSchema); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); pNew->nRow = 10; if( zNRow ) pNew->nRow = atoi(zNRow); pNew->iInst = iInst; } return rc; } static int vtablogCreate( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1); } static int vtablogConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0); } /* ** This method is the destructor for vtablog_cursor objects. */ static int vtablogDisconnect(sqlite3_vtab *pVtab){ vtablog_vtab *pTab = (vtablog_vtab*)pVtab; printf("vtablogDisconnect(%d)\n", pTab->iInst); sqlite3_free(pVtab); return SQLITE_OK; } /* ** This method is the destructor for vtablog_cursor objects. */ static int vtablogDestroy(sqlite3_vtab *pVtab){ vtablog_vtab *pTab = (vtablog_vtab*)pVtab; printf("vtablogDestroy(%d)\n", pTab->iInst); sqlite3_free(pVtab); return SQLITE_OK; } /* ** Constructor for a new vtablog_cursor object. */ static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ vtablog_vtab *pTab = (vtablog_vtab*)p; vtablog_cursor *pCur; printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor); pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->iCursor = pTab->nCursor; *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Destructor for a vtablog_cursor. */ static int vtablogClose(sqlite3_vtab_cursor *cur){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor); sqlite3_free(cur); return SQLITE_OK; } /* ** Advance a vtablog_cursor to its next row of output. */ static int vtablogNext(sqlite3_vtab_cursor *cur){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n", pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1); pCur->iRowid++; return SQLITE_OK; } /* ** Return values of columns for the row at which the vtablog_cursor ** is currently pointing. */ static int vtablogColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; char zVal[50]; if( i<26 ){ sqlite3_snprintf(sizeof(zVal),zVal,"%c%d", "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid); }else{ sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid); } printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n", pTab->iInst, pCur->iCursor, i, zVal); sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT); return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; printf("vtablogRowid(tab=%d, cursor=%d): %d\n", pTab->iInst, pCur->iCursor, (int)pCur->iRowid); *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int vtablogEof(sqlite3_vtab_cursor *cur){ vtablog_cursor *pCur = (vtablog_cursor*)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; int rc = pCur->iRowid >= pTab->nRow; printf("vtablogEof(tab=%d, cursor=%d): %d\n", pTab->iInst, pCur->iCursor, rc); return rc; } /* ** Output an sqlite3_value object's value as an SQL literal. */ static void vtablogQuote(sqlite3_value *p){ char z[50]; switch( sqlite3_value_type(p) ){ case SQLITE_NULL: { printf("NULL"); break; } case SQLITE_INTEGER: { sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p)); printf("%s", z); break; } case SQLITE_FLOAT: { sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p)); printf("%s", z); break; } case SQLITE_BLOB: { int n = sqlite3_value_bytes(p); const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p); int i; printf("x'"); for(i=0; i<n; i++) printf("%02x", z[i]); printf("'"); break; } case SQLITE_TEXT: { const char *z = (const char*)sqlite3_value_text(p); int i; char c; for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c==0 ){ printf("'%s'",z); }else{ printf("'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c=='\'' ) i++; if( i ){ printf("%.*s", i, z); z += i; } if( c=='\'' ){ printf("'"); continue; } if( c==0 ){ break; } z++; } printf("'"); } break; } } } /* ** This method is called to "rewind" the vtablog_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to vtablogColumn() or vtablogRowid() or ** vtablogEof(). */ static int vtablogFilter( sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ vtablog_cursor *pCur = (vtablog_cursor *)cur; vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor); pCur->iRowid = 0; return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the vtablog virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. */ static int vtablogBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ vtablog_vtab *pTab = (vtablog_vtab*)tab; printf("vtablogBestIndex(tab=%d):\n", pTab->iInst); pIdxInfo->estimatedCost = (double)500; pIdxInfo->estimatedRows = 500; return SQLITE_OK; } /* ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from ** the table. ** ** This implementation does not actually make any changes to the table ** content. It merely logs the fact that the method was invoked */ static int vtablogUpdate( sqlite3_vtab *tab, int argc, sqlite3_value **argv, sqlite_int64 *pRowid ){ vtablog_vtab *pTab = (vtablog_vtab*)tab; int i; printf("vtablogUpdate(tab=%d):\n", pTab->iInst); printf(" argc=%d\n", argc); for(i=0; i<argc; i++){ printf(" argv[%d]=", i); vtablogQuote(argv[i]); printf("\n"); } return SQLITE_OK; } /* ** This following structure defines all the methods for the ** vtablog virtual table. */ static sqlite3_module vtablogModule = { 0, /* iVersion */ vtablogCreate, /* xCreate */ vtablogConnect, /* xConnect */ vtablogBestIndex, /* xBestIndex */ vtablogDisconnect, /* xDisconnect */ vtablogDestroy, /* xDestroy */ vtablogOpen, /* xOpen - open a cursor */ vtablogClose, /* xClose - close a cursor */ vtablogFilter, /* xFilter - configure scan constraints */ vtablogNext, /* xNext - advance a cursor */ vtablogEof, /* xEof - check for end of scan */ vtablogColumn, /* xColumn - read data */ vtablogRowid, /* xRowid - read data */ vtablogUpdate, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ }; #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_vtablog_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc; SQLITE_EXTENSION_INIT2(pApi); rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0); return rc; } |
Changes to ext/misc/vtshim.c.
︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 100 101 102 103 104 | if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); rc = pAux->pMod->xCreate(db, pAux->pChildAux, argc, argv, &pNew->pChild, pzErr); if( rc ){ sqlite3_free(pNew); *ppVtab = 0; } pNew->pAux = pAux; pNew->ppPrev = &pAux->pAllVtab; pNew->pNext = pAux->pAllVtab; if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext; pAux->pAllVtab = pNew; return rc; | > | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); rc = pAux->pMod->xCreate(db, pAux->pChildAux, argc, argv, &pNew->pChild, pzErr); if( rc ){ sqlite3_free(pNew); *ppVtab = 0; return rc; } pNew->pAux = pAux; pNew->ppPrev = &pAux->pAllVtab; pNew->pNext = pAux->pAllVtab; if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext; pAux->pAllVtab = pNew; return rc; |
︙ | ︙ | |||
129 130 131 132 133 134 135 136 137 138 139 140 141 142 | if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); rc = pAux->pMod->xConnect(db, pAux->pChildAux, argc, argv, &pNew->pChild, pzErr); if( rc ){ sqlite3_free(pNew); *ppVtab = 0; } pNew->pAux = pAux; pNew->ppPrev = &pAux->pAllVtab; pNew->pNext = pAux->pAllVtab; if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext; pAux->pAllVtab = pNew; return rc; | > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); rc = pAux->pMod->xConnect(db, pAux->pChildAux, argc, argv, &pNew->pChild, pzErr); if( rc ){ sqlite3_free(pNew); *ppVtab = 0; return rc; } pNew->pAux = pAux; pNew->ppPrev = &pAux->pAllVtab; pNew->pNext = pAux->pAllVtab; if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext; pAux->pAllVtab = pNew; return rc; |
︙ | ︙ |
Changes to ext/rbu/rbu.c.
︙ | ︙ | |||
78 79 80 81 82 83 84 | char zBuf[200]; /* Buffer for printf() */ char *zErrmsg; /* Error message, if any */ sqlite3rbu *pRbu; /* RBU handle */ int nStep = 0; /* Maximum number of step() calls */ int bVacuum = 0; int rc; sqlite3_int64 nProgress = 0; | | | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | char zBuf[200]; /* Buffer for printf() */ char *zErrmsg; /* Error message, if any */ sqlite3rbu *pRbu; /* RBU handle */ int nStep = 0; /* Maximum number of step() calls */ int bVacuum = 0; int rc; sqlite3_int64 nProgress = 0; int nArgc = argc-2; if( argc<3 ) usage(argv[0]); for(i=1; i<nArgc; i++){ const char *zArg = argv[i]; int nArg = strlen(zArg); if( nArg>1 && nArg<=8 && 0==memcmp(zArg, "-vacuum", nArg) ){ bVacuum = 1; }else if( nArg>1 && nArg<=5 && 0==memcmp(zArg, "-step", nArg) && i<nArg-1 ){ i++; nStep = atoi(argv[i]); |
︙ | ︙ |
Changes to ext/rbu/rbu10.test.
︙ | ︙ | |||
110 111 112 113 114 115 116 | do_test 3.1 { list [catch { apply_rbu { CREATE TABLE data_xt(a, xt, rbu_rowid, rbu_control); INSERT INTO data_xt VALUES('a', 'b', 1, 0); } } msg] $msg | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | do_test 3.1 { list [catch { apply_rbu { CREATE TABLE data_xt(a, xt, rbu_rowid, rbu_control); INSERT INTO data_xt VALUES('a', 'b', 1, 0); } } msg] $msg } {1 {SQLITE_ERROR - SQL logic error}} } #-------------------------------------------------------------------- # Test that it is not possible to violate a NOT NULL constraint by # applying an RBU update. # do_execsql_test 4.1 { |
︙ | ︙ |
Changes to ext/rbu/rbuA.test.
︙ | ︙ | |||
66 67 68 69 70 71 72 | rbu close } {SQLITE_OK} do_test 2.1 { sqlite3 db test.db db eval {PRAGMA journal_mode = wal} db close | < | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | rbu close } {SQLITE_OK} do_test 2.1 { sqlite3 db test.db db eval {PRAGMA journal_mode = wal} db close sqlite3rbu rbu test.db rbu.db rbu step } {SQLITE_ERROR} do_test 2.2 { list [catch { rbu close } msg] $msg } {1 {SQLITE_ERROR - cannot update wal mode database}} |
︙ | ︙ |
Changes to ext/rbu/rbucrash.test.
︙ | ︙ | |||
49 50 51 52 53 54 55 | # update using many calls to sqlite3rbu_step() on a single rbu handle # as required to apply it using a series of rbu handles, on each of # which sqlite3rbu_step() is called once. # do_test 1.1 { db_restore sqlite3rbu rbu test.db test.db2 | < | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # update using many calls to sqlite3rbu_step() on a single rbu handle # as required to apply it using a series of rbu handles, on each of # which sqlite3rbu_step() is called once. # do_test 1.1 { db_restore sqlite3rbu rbu test.db test.db2 set nStep 0 while {[rbu step]=="SQLITE_OK"} { incr nStep } rbu close } {SQLITE_DONE} set rbu_num_steps $nStep do_test 1.2 { db_restore |
︙ | ︙ |
Added ext/rbu/rbucrash2.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 | # 2017 March 02 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl set ::testprefix rbucrash2 db close forcedelete test.db-oal rbu.db sqlite3_shutdown sqlite3_config_uri 1 reset_db # Set up a target database and an rbu update database. The target # db is the usual "test.db", the rbu db is "test.db2". # forcedelete test.db2 do_execsql_test 1.0 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a), UNIQUE(b)); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); INSERT INTO t1 VALUES(7, 8, 9); ATTACH 'test.db2' AS rbu; CREATE TABLE rbu.data_t1(a, b, c, rbu_control); INSERT INTO data_t1 VALUES('one', randomblob(3500), NULL, 0); INSERT INTO data_t1 VALUES('two', randomblob(3500), NULL, 0); INSERT INTO data_t1 VALUES('three', randomblob(3500), NULL, 0); INSERT INTO data_t1 VALUES('four', randomblob(3500), NULL, 0); INSERT INTO data_t1 VALUES('five', randomblob(3500), NULL, 0); INSERT INTO data_t1 VALUES('six', randomblob(3500), NULL, 0); } db_save_and_close proc do_rbu_crash_test2 {tn script} { foreach {f blksz} { test.db 512 test.db2 512 test.db 4096 test.db2 4096 } { set bDone 0 for {set iDelay 1} {$bDone==0} {incr iDelay} { forcedelete test.db2 test.db2-journal test.db test.db-oal test.db-wal db_restore set res [ crashsql -file $f -delay $iDelay -tclbody $script -dflt 1 -opendb {} \ -blocksize $blksz {} ] set bDone 1 if {$res == "1 {child process exited abnormally}"} { set bDone 0 } elseif {$res != "0 {}"} { error "unexected catchsql result: $res" } sqlite3rbu rbu test.db test.db2 while {[rbu step]=="SQLITE_OK"} {} rbu close sqlite3 db test.db do_execsql_test $tn.delay=$iDelay.f=$f.blksz=$blksz { PRAGMA integrity_check; } {ok} db close } } } for {set x 1} {$x < 10} {incr x} { do_rbu_crash_test2 1.$x { sqlite3rbu rbu test.db test.db2 while {[rbu step]=="SQLITE_OK"} { rbu savestate } rbu close } } for {set x 1} {$x < 2} {incr x} { do_rbu_crash_test2 2.$x { sqlite3rbu rbu test.db test.db2 while {[rbu step]=="SQLITE_OK"} { rbu close sqlite3rbu rbu test.db test.db2 } rbu close } } finish_test |
Added ext/rbu/rbudor.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 | # 2016 October 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 test file focuses on interactions between RBU and the feature # enabled by SQLITE_DIRECT_OVERFLOW_READ - Direct Overflow Read. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl set ::testprefix rbudor set bigA [string repeat a 5000] set bigB [string repeat b 5000] do_execsql_test 1.0 { PRAGMA page_size = 1024; CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB); INSERT INTO t1 VALUES(1, $bigA); } {} do_test 1.1 { forcedelete rbu.db sqlite3 rbu rbu.db rbu eval { CREATE TABLE data_t1(a, b, rbu_control); INSERT INTO data_t1 VALUES(2, $bigB, 0); } rbu close } {} do_test 1.2 { sqlite3rbu rbu test.db rbu.db while {[rbu state]!="checkpoint"} { rbu step } rbu step db eval { SELECT * FROM t1 } } [list 1 $bigA 2 $bigB] do_test 1.3 { while {[rbu step]=="SQLITE_OK"} {} rbu close } {SQLITE_DONE} do_execsql_test 1.4 { SELECT * FROM t1 } [list 1 $bigA 2 $bigB] finish_test |
Changes to ext/rbu/rbufault.test.
︙ | ︙ | |||
121 122 123 124 125 126 127 | 2 ioerr-* { {0 SQLITE_DONE} {1 {SQLITE_IOERR - disk I/O error}} {1 SQLITE_IOERR} {1 SQLITE_IOERR_WRITE} {1 SQLITE_IOERR_READ} {1 SQLITE_IOERR_FSYNC} | | | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | 2 ioerr-* { {0 SQLITE_DONE} {1 {SQLITE_IOERR - disk I/O error}} {1 SQLITE_IOERR} {1 SQLITE_IOERR_WRITE} {1 SQLITE_IOERR_READ} {1 SQLITE_IOERR_FSYNC} {1 {SQLITE_ERROR - SQL logic error}} {1 {SQLITE_ERROR - unable to open database: rbu.db}} {1 {SQLITE_IOERR - unable to open database: rbu.db}} } 3 shmerr-* { {0 SQLITE_DONE} {1 {SQLITE_IOERR - disk I/O error}} |
︙ | ︙ |
Changes to ext/rbu/rbufault3.test.
︙ | ︙ | |||
27 28 29 30 31 32 33 | {1 {SQLITE_IOERR - disk I/O error}} {1 SQLITE_IOERR} {1 SQLITE_IOERR_WRITE} {1 SQLITE_IOERR_FSYNC} {1 SQLITE_IOERR_READ} {1 {SQLITE_IOERR - unable to open database: test.db2}} {1 {SQLITE_ERROR - unable to open database: test.db2}} | | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | {1 {SQLITE_IOERR - disk I/O error}} {1 SQLITE_IOERR} {1 SQLITE_IOERR_WRITE} {1 SQLITE_IOERR_FSYNC} {1 SQLITE_IOERR_READ} {1 {SQLITE_IOERR - unable to open database: test.db2}} {1 {SQLITE_ERROR - unable to open database: test.db2}} {1 {SQLITE_ERROR - SQL logic error}} } cantopen* { {1 {SQLITE_CANTOPEN - unable to open database: test.db2}} {1 {SQLITE_CANTOPEN - unable to open database: test.db2}} {1 {SQLITE_CANTOPEN - unable to open database file}} {1 SQLITE_CANTOPEN} |
︙ | ︙ |
Added ext/rbu/rbufault4.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 | # 2014 October 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. # #*********************************************************************** # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl source $testdir/malloc_common.tcl set ::testprefix rbufault4 for {set tn 1} {1} {incr tn} { reset_db do_execsql_test 1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); CREATE INDEX i1b ON t1(b); CREATE INDEX i1c ON t1(c); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); } forcedelete test.db2 sqlite3rbu_vacuum rbu test.db test.db2 for {set i 0} {$i < $tn} {incr i} { rbu step } set rc [rbu close] if {$rc!="SQLITE_OK"} { if {$rc!="SQLITE_DONE"} {error $rc} break } faultsim_save do_faultsim_test $tn -faults oom-t* -prep { faultsim_restore } -body { sqlite3rbu_vacuum rbu test.db test.db2 while 1 { set rc [rbu step] if {$rc=="SQLITE_DONE"} break if {$rc!="SQLITE_OK"} { error $rc } } } -test { catch {rbu close} faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM} sqlite3rbu_vacuum rbu test.db test.db2 while {[rbu step]=="SQLITE_OK"} {} set trc [rbu close] if {$trc!="SQLITE_DONE"} { error "Got $trc instead of SQLITE_DONE!" } set rc [db one {PRAGMA integrity_check}] if {$rc!="ok"} { error "Got $rc instead of ok!" } } } finish_test |
Changes to ext/rbu/rbufts.test.
︙ | ︙ | |||
115 116 117 118 119 120 121 | } do_test 3.2 { list [catch { apply_rbu_update test.db { CREATE TABLE data_ft(x, rbu_rowid, rbu_control); INSERT INTO data_ft VALUES(NULL, 2, 1); } } msg] $msg] | | | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | } do_test 3.2 { list [catch { apply_rbu_update test.db { CREATE TABLE data_ft(x, rbu_rowid, rbu_control); INSERT INTO data_ft VALUES(NULL, 2, 1); } } msg] $msg] } {1 {SQLITE_ERROR - SQL logic error]}} do_test 3.3 { list [catch { apply_rbu_update test.db { CREATE TABLE data_ft(x, rbu_rowid, rbu_control); INSERT INTO data_ft VALUES('7 8 9', 1, 'x'); } } msg] $msg] } {1 {SQLITE_ERROR - SQL logic error]}} finish_test |
Changes to ext/rbu/rbuprogress.test.
︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 | INSERT INTO rbu_count VALUES('data_t1', 3); } return $filename } do_execsql_test 1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); } do_test 1.1 { create_rbu1 rbu.db sqlite3rbu rbu test.db rbu.db rbu bp_progress | > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | INSERT INTO rbu_count VALUES('data_t1', 3); } return $filename } do_execsql_test 1.0 { PRAGMA page_size = 4096; CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); } do_test 1.1 { create_rbu1 rbu.db sqlite3rbu rbu test.db rbu.db rbu bp_progress |
︙ | ︙ | |||
262 263 264 265 266 267 268 269 270 271 272 273 274 275 | }] {SQLITE_DONE}] } foreach bReopen {0 1} { do_test 3.$bReopen.1.0 { reset_db execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b); CREATE TABLE t2(a INTEGER PRIMARY KEY, b); CREATE TABLE t3(a INTEGER PRIMARY KEY, b); CREATE TABLE t4(a INTEGER PRIMARY KEY, b); } create_db_file rbu.db { CREATE TABLE data_t1(a, b, rbu_control); | > | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | }] {SQLITE_DONE}] } foreach bReopen {0 1} { do_test 3.$bReopen.1.0 { reset_db execsql { PRAGMA page_size = 4096; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); CREATE TABLE t2(a INTEGER PRIMARY KEY, b); CREATE TABLE t3(a INTEGER PRIMARY KEY, b); CREATE TABLE t4(a INTEGER PRIMARY KEY, b); } create_db_file rbu.db { CREATE TABLE data_t1(a, b, rbu_control); |
︙ | ︙ |
Added ext/rbu/rburesume.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 | # 2017 January 13 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains tests for resumption of RBU operations in the # case where the previous RBU process crashed. # source [file join [file dirname [info script]] rbu_common.tcl] set ::testprefix rburesume forcedelete test.db-shm test.db-oal do_execsql_test 1.0 { CREATE TABLE t1(a PRIMARY KEY, b, c); CREATE INDEX t1a ON t1(a); CREATE INDEX t1b ON t1(b); CREATE INDEX t1c ON t1(c); WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<50 ) INSERT INTO t1 SELECT randomblob(50), randomblob(75), randomblob(100) FROM s; } db_save_and_close do_test 1.1 { list [file exists test.db] \ [file exists test.db-wal] \ [file exists test.db-shm] \ [file exists test.db-oal] } {1 0 0 0} # Each iteration of the following loop: # # 1. Restores the db to the state it was in following test case 1.0 # 2. Opens an RBU vacuum and steps it $n times. # 3. Closes the RBU vacuum handled opened in (2). # 4. Opens a second RBU vacuum handle, resumes and completes the vacuum op. # # The loop runs until $n is large enough that step (2) vacuums the entire # database. # for {set n 1} {$n < 5000} {incr n} { db_restore forcedelete state.db sqlite3rbu_vacuum rbu test.db state.db for {set i 0} {$i<$n} {incr i} { set rc [rbu step] if {$rc == "SQLITE_DONE"} break } rbu close if {$rc == "SQLITE_DONE"} break do_test 1.2.$n.1 { sqlite3rbu_vacuum rbu test.db state.db while {[rbu step]=="SQLITE_OK"} {} rbu close } {SQLITE_DONE} do_test 1.2.$n.2 { sqlite3 db2 test.db db2 eval { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {50 ok} db2 close } # Each iteration of this loop: # # 1. Restores the db to the state it was in following test case 1.0 # 2. Opens an RBU vacuum and steps it $n times. # 3. Takes a copy of all database files and the state db. # 4. Opens a second RBU vacuum handle on the copy, resumes and completes the # vacuum op. # # The loop runs until $n is large enough that step (2) vacuums the entire # database. # for {set n 1} {$n < 5000} {incr n} { db_restore forcedelete state.db state.db-shm state.db-oal state.db-wal sqlite3rbu_vacuum rbu test.db state.db for {set i 0} {$i<$n} {incr i} { set rc [rbu step] if {$rc == "SQLITE_DONE"} break } if {$rc == "SQLITE_DONE"} { rbu close break } foreach f {test.db test.db-oal test.db-wal test.db-shm test.db-vacuum} { set f2 [string map [list test.db test.db2] $f] if {[file exists $f]} { forcecopy $f $f2 } else { forcedelete $f2 } } forcecopy state.db state.db2 rbu close do_test 1.3.$n.1 { sqlite3rbu_vacuum rbu test.db2 state.db2 while {[rbu step]=="SQLITE_OK"} {} rbu close } {SQLITE_DONE} do_test 1.3.$n.2 { sqlite3 db2 test.db2 db2 eval { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {50 ok} db2 close } # Each iteration of this loop: # # 1. Restores the db to the state it was in following test case 1.0 # 2. Opens an RBU vacuum and steps it 10 times. Then closes it. # 2. Opens an RBU vacuum and steps it $n times. # 3. Takes a copy of all database files and the state db. # 4. Opens a second RBU vacuum handle on the copy, resumes and completes the # vacuum op. # # The loop runs until $n is large enough that step (3) vacuums the entire # database. # for {set n 1} {$n < 5000} {incr n} { db_restore forcedelete state.db state.db-shm state.db-oal state.db-wal sqlite3rbu_vacuum rbu test.db state.db for {set i 0} {$i<10} {incr i} { rbu step } rbu close sqlite3rbu_vacuum rbu test.db state.db for {set i 0} {$i<$n} {incr i} { set rc [rbu step] if {$rc == "SQLITE_DONE"} break } if {$rc == "SQLITE_DONE"} { rbu close break } foreach f {test.db test.db-oal test.db-wal test.db-shm test.db-vacuum} { set f2 [string map [list test.db test.db2] $f] if {[file exists $f]} { forcecopy $f $f2 } else { forcedelete $f2 } } forcecopy state.db state.db2 rbu close do_test 1.4.$n.1 { sqlite3rbu_vacuum rbu test.db2 state.db2 while {[rbu step]=="SQLITE_OK"} {} rbu close } {SQLITE_DONE} do_test 1.4.$n.2 { sqlite3 db2 test.db2 db2 eval { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {50 ok} db2 close } forcedelete rbu.db do_test 2.0 { sqlite3 db2 rbu.db db2 eval { CREATE TABLE data_t1(a, b, c, rbu_control); WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<10 ) INSERT INTO data_t1 SELECT randomblob(50), randomblob(75), randomblob(100), 0 FROM s; } db2 close } {} # Each iteration of this loop: # # 1. Restores the db to the state it was in following test case 1.0 # 2. Opens an RBU handle to apply the RBU update created in test case 2.0. # 3. Steps the RBU handle $n times. # 4. Takes a copy of all database files and the state db. # 5. Opens a second RBU handle on the copy, resumes and completes the # RBU op. Checks it worked as expected. # # The loop runs until $n is large enough that step (3) applies the entire # update. # for {set n 1} {$n < 5000} {incr n} { db_restore forcedelete state.db state.db-shm state.db-oal state.db-wal sqlite3rbu rbu test.db rbu.db state.db for {set i 0} {$i<$n} {incr i} { set rc [rbu step] if {$rc == "SQLITE_DONE"} break } if {$rc == "SQLITE_DONE"} { rbu close break } foreach f {test.db test.db-oal test.db-wal test.db-shm test.db-vacuum} { set f2 [string map [list test.db test.db2] $f] if {[file exists $f]} { forcecopy $f $f2 } else { forcedelete $f2 } } forcecopy state.db state.db2 rbu close do_test 2.$n.1 { sqlite3rbu rbu test.db2 rbu.db state.db2 while {[rbu step]=="SQLITE_OK"} {} rbu close } {SQLITE_DONE} do_test 2.$n.2 { sqlite3 db2 test.db2 db2 eval { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {60 ok} db2 close } finish_test |
Added ext/rbu/rbutemplimit.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 | # 2014 August 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. # #*********************************************************************** # source [file join [file dirname [info script]] rbu_common.tcl] set ::testprefix rbutemplimit db close sqlite3_shutdown sqlite3_config_uri 1 proc setup_databases {} { forcedelete test.db2 forcedelete test.db sqlite3 db test.db execsql { -- Create target database schema. -- CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB(100), c BLOB(100)); CREATE TABLE t2(a INTEGER PRIMARY KEY, b BLOB(100), c BLOB(100)); CREATE INDEX i1b ON t1(b); CREATE INDEX i1c ON t1(c); CREATE INDEX i2b ON t2(b); CREATE INDEX i2c ON t2(c); -- Create a large RBU database. -- ATTACH 'test.db2' AS rbu; CREATE TABLE rbu.data_t1(a, b, c, rbu_control); WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<10000 ) INSERT INTO data_t1 SELECT i, randomblob(100), randomblob(100), 0 FROM s; CREATE TABLE rbu.data_t2(a, b, c, rbu_control); WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<15000 ) INSERT INTO data_t2 SELECT i, randomblob(100), randomblob(100), 0 FROM s; } db close } proc run_rbu_cachesize {target rbu cachesize temp_limit} { sqlite3rbu rbu $target $rbu rbu temp_size_limit $temp_limit sqlite3_exec_nr [rbu db 1] "PRAGMA cache_size = $cachesize" while 1 { set rc [rbu step] set ::A([rbu temp_size]) 1 if {$rc!="SQLITE_OK"} break } list [catch {rbu close} msg] $msg } proc step_rbu_cachesize {target rbu stepsize cachesize temp_limit} { set res "" while 1 { sqlite3rbu rbu $target $rbu rbu temp_size_limit $temp_limit sqlite3_exec_nr [rbu db 1] "PRAGMA cache_size = $cachesize" for {set i 0} {$i < $stepsize} {incr i} { set rc [rbu step] set ::A([rbu temp_size]) 1 if {$rc!="SQLITE_OK"} break } set res [list [catch {rbu close} msg] $msg] if {$res != "0 SQLITE_OK"} break } set res } do_test 1.1.0 { setup_databases } {} do_test 1.1.1 { unset -nocomplain ::A run_rbu_cachesize test.db test.db2 10 0 } {0 SQLITE_DONE} do_test 1.1.2 { llength [array names ::A] } 3 do_test 1.1.3 { foreach {a0 a1 a2} [lsort -integer [array names ::A]] {} list [expr $a0==0] \ [expr $a1>1048576] [expr $a1<1200000] \ [expr $a2>1500000] [expr $a2<1700000] } {1 1 1 1 1} do_test 1.2.1 { setup_databases run_rbu_cachesize test.db test.db2 10 1000000 } {1 SQLITE_FULL} do_test 1.2.2 { info commands rbu } {} do_test 1.3.1 { setup_databases run_rbu_cachesize test.db test.db2 10 1300000 } {1 SQLITE_FULL} do_test 1.3.2 { info commands rbu } {} do_test 1.4.1 { setup_databases run_rbu_cachesize test.db test.db2 10 1800000 } {0 SQLITE_DONE} do_test 1.4.2 { info commands rbu } {} do_test 1.5.1 { setup_databases unset -nocomplain ::A step_rbu_cachesize test.db test.db2 1000 10 2400000 } {0 SQLITE_DONE} do_test 1.5.2 { info commands rbu } {} do_test 1.6.1 { setup_databases unset -nocomplain ::A step_rbu_cachesize test.db test.db2 1000 10 1400000 } {1 SQLITE_FULL} do_test 1.6.2 { info commands rbu } {} finish_test |
Changes to ext/rbu/rbuvacuum.test.
︙ | ︙ | |||
294 295 296 297 298 299 300 301 302 303 304 305 306 307 | sqlite3rbu_vacuum rbu test.db state.db rbu step } {SQLITE_ERROR} do_test 2.1.2 { list [catch { rbu close } msg] $msg } {1 {SQLITE_ERROR - cannot vacuum wal mode database}} reset_db do_execsql_test 2.2.0 { CREATE TABLE tx(a PRIMARY KEY, b BLOB); INSERT INTO tx VALUES(1, randomblob(900)); INSERT INTO tx SELECT a+1, randomblob(900) FROM tx; INSERT INTO tx SELECT a+2, randomblob(900) FROM tx; INSERT INTO tx SELECT a+4, randomblob(900) FROM tx; | > > > > > > > > | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | sqlite3rbu_vacuum rbu test.db state.db rbu step } {SQLITE_ERROR} do_test 2.1.2 { list [catch { rbu close } msg] $msg } {1 {SQLITE_ERROR - cannot vacuum wal mode database}} do_test 2.1.3 { sqlite3rbu_vacuum rbu test.db state.db rbu step } {SQLITE_ERROR} do_test 2.1.4 { list [catch { rbu close_no_error } msg] $msg } {1 SQLITE_ERROR} reset_db do_execsql_test 2.2.0 { CREATE TABLE tx(a PRIMARY KEY, b BLOB); INSERT INTO tx VALUES(1, randomblob(900)); INSERT INTO tx SELECT a+1, randomblob(900) FROM tx; INSERT INTO tx SELECT a+2, randomblob(900) FROM tx; INSERT INTO tx SELECT a+4, randomblob(900) FROM tx; |
︙ | ︙ |
Changes to ext/rbu/rbuvacuum2.test.
︙ | ︙ | |||
195 196 197 198 199 200 201 202 203 204 | do_test 5.$tn.2 { file exists test.db-vacuum } 1 do_test 5.$tn.3 { file attributes test.db-vacuum -permissions} $perm rbu close } } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_test 5.$tn.2 { file exists test.db-vacuum } 1 do_test 5.$tn.3 { file attributes test.db-vacuum -permissions} $perm rbu close } } #------------------------------------------------------------------------- # Test the outcome of some other connection running a checkpoint while # the incremental checkpoint is suspended. # reset_db do_execsql_test 6.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); CREATE INDEX i1b ON t1(b); CREATE INDEX i1c ON t1(c); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); } forcedelete test.db2 do_test 6.1 { sqlite3rbu_vacuum rbu test.db test.db2 while {[rbu state]!="checkpoint"} { rbu step } rbu close } {SQLITE_OK} do_execsql_test 6.2 { SELECT 1 FROM sqlite_master LIMIT 1; PRAGMA wal_checkpoint; } {1 0 4 4} do_test 6.3 { sqlite3rbu_vacuum rbu test.db test.db2 while {[rbu step]!="SQLITE_DONE"} { rbu step } rbu close execsql { PRAGMA integrity_check } } {ok} finish_test |
Changes to ext/rbu/sqlite3rbu.c.
︙ | ︙ | |||
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 | int rc; /* Value returned by last rbu_step() call */ char *zErrmsg; /* Error message if rc!=SQLITE_OK */ int nStep; /* Rows processed for current object */ int nProgress; /* Rows processed for all objects */ RbuObjIter objiter; /* Iterator for skipping through tbl/idx */ const char *zVfsName; /* Name of automatically created rbu vfs */ rbu_file *pTargetFd; /* File handle open on target db */ i64 iOalSz; i64 nPhaseOneStep; /* The following state variables are used as part of the incremental ** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding ** function rbuSetupCheckpoint() for details. */ u32 iMaxFrame; /* Largest iWalFrame value in aFrame[] */ u32 mLock; int nFrame; /* Entries in aFrame[] array */ int nFrameAlloc; /* Allocated size of aFrame[] array */ RbuFrame *aFrame; int pgsz; u8 *aBuf; i64 iWalCksum; /* Used in RBU vacuum mode only */ int nRbu; /* Number of RBU VFS in the stack */ rbu_file *pRbuFd; /* Fd for main db of dbRbu */ }; /* ** An rbu VFS is implemented using an instance of this structure. */ struct rbu_vfs { sqlite3_vfs base; /* rbu VFS shim methods */ sqlite3_vfs *pRealVfs; /* Underlying VFS */ sqlite3_mutex *mutex; /* Mutex to protect pMain */ rbu_file *pMain; /* Linked list of main db files */ }; /* ** Each file opened by an rbu VFS is represented by an instance of ** the following structure. */ struct rbu_file { sqlite3_file base; /* sqlite3_file methods */ sqlite3_file *pReal; /* Underlying file handle */ rbu_vfs *pRbuVfs; /* Pointer to the rbu_vfs object */ sqlite3rbu *pRbu; /* Pointer to rbu object (rbu target only) */ int openFlags; /* Flags this file was opened with */ u32 iCookie; /* Cookie value for main db files */ u8 iWriteVer; /* "write-version" value for main db files */ u8 bNolock; /* True to fail EXCLUSIVE locks */ int nShm; /* Number of entries in apShm[] array */ | > > > > > > > > > > > > > | 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 | int rc; /* Value returned by last rbu_step() call */ char *zErrmsg; /* Error message if rc!=SQLITE_OK */ int nStep; /* Rows processed for current object */ int nProgress; /* Rows processed for all objects */ RbuObjIter objiter; /* Iterator for skipping through tbl/idx */ const char *zVfsName; /* Name of automatically created rbu vfs */ rbu_file *pTargetFd; /* File handle open on target db */ int nPagePerSector; /* Pages per sector for pTargetFd */ i64 iOalSz; i64 nPhaseOneStep; /* The following state variables are used as part of the incremental ** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding ** function rbuSetupCheckpoint() for details. */ u32 iMaxFrame; /* Largest iWalFrame value in aFrame[] */ u32 mLock; int nFrame; /* Entries in aFrame[] array */ int nFrameAlloc; /* Allocated size of aFrame[] array */ RbuFrame *aFrame; int pgsz; u8 *aBuf; i64 iWalCksum; i64 szTemp; /* Current size of all temp files in use */ i64 szTempLimit; /* Total size limit for temp files */ /* Used in RBU vacuum mode only */ int nRbu; /* Number of RBU VFS in the stack */ rbu_file *pRbuFd; /* Fd for main db of dbRbu */ }; /* ** An rbu VFS is implemented using an instance of this structure. ** ** Variable pRbu is only non-NULL for automatically created RBU VFS objects. ** It is NULL for RBU VFS objects created explicitly using ** sqlite3rbu_create_vfs(). It is used to track the total amount of temp ** space used by the RBU handle. */ struct rbu_vfs { sqlite3_vfs base; /* rbu VFS shim methods */ sqlite3_vfs *pRealVfs; /* Underlying VFS */ sqlite3_mutex *mutex; /* Mutex to protect pMain */ sqlite3rbu *pRbu; /* Owner RBU object */ rbu_file *pMain; /* Linked list of main db files */ }; /* ** Each file opened by an rbu VFS is represented by an instance of ** the following structure. ** ** If this is a temporary file (pRbu!=0 && flags&DELETE_ON_CLOSE), variable ** "sz" is set to the current size of the database file. */ struct rbu_file { sqlite3_file base; /* sqlite3_file methods */ sqlite3_file *pReal; /* Underlying file handle */ rbu_vfs *pRbuVfs; /* Pointer to the rbu_vfs object */ sqlite3rbu *pRbu; /* Pointer to rbu object (rbu target only) */ i64 sz; /* Size of file in bytes (temp only) */ int openFlags; /* Flags this file was opened with */ u32 iCookie; /* Cookie value for main db files */ u8 iWriteVer; /* "write-version" value for main db files */ u8 bNolock; /* True to fail EXCLUSIVE locks */ int nShm; /* Number of entries in apShm[] array */ |
︙ | ︙ | |||
2329 2330 2331 2332 2333 2334 2335 | } /* ** Open the database handle and attach the RBU database as "rbu". If an ** error occurs, leave an error code and message in the RBU handle. */ | | | 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 | } /* ** Open the database handle and attach the RBU database as "rbu". If an ** error occurs, leave an error code and message in the RBU handle. */ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){ assert( p->rc || (p->dbMain==0 && p->dbRbu==0) ); assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 ); /* Open the RBU database */ p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1); if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){ |
︙ | ︙ | |||
2404 2405 2406 2407 2408 2409 2410 | rc = sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p); if( rc!=SQLITE_NOTFOUND ) p->rc = rc; if( p->eStage>=RBU_STAGE_MOVE ){ bOpen = 1; }else{ RbuState *pState = rbuLoadState(p); if( pState ){ | | > > > > > > > > > | 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 | rc = sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p); if( rc!=SQLITE_NOTFOUND ) p->rc = rc; if( p->eStage>=RBU_STAGE_MOVE ){ bOpen = 1; }else{ RbuState *pState = rbuLoadState(p); if( pState ){ bOpen = (pState->eStage>=RBU_STAGE_MOVE); rbuFreeState(pState); } } if( bOpen ) p->dbMain = rbuOpenDbhandle(p, p->zRbu, p->nRbu<=1); } p->eStage = 0; if( p->rc==SQLITE_OK && p->dbMain==0 ){ if( !rbuIsVacuum(p) ){ p->dbMain = rbuOpenDbhandle(p, p->zTarget, 1); }else if( p->pRbuFd->pWalFd ){ if( pbRetry ){ p->pRbuFd->bNolock = 0; sqlite3_close(p->dbRbu); sqlite3_close(p->dbMain); p->dbMain = 0; p->dbRbu = 0; *pbRetry = 1; return; } p->rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf("cannot vacuum wal mode database"); }else{ char *zTarget; char *zExtra = 0; if( strlen(p->zRbu)>=5 && 0==memcmp("file:", p->zRbu, 5) ){ zExtra = &p->zRbu[5]; |
︙ | ︙ | |||
2596 2597 2598 2599 2600 2601 2602 | if( p->rc==SQLITE_OK ){ int rc2; p->eStage = RBU_STAGE_CAPTURE; rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0); if( rc2!=SQLITE_INTERNAL ) p->rc = rc2; } | | | > | | > > > > > > > > > > > > > > > > > > | 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 | if( p->rc==SQLITE_OK ){ int rc2; p->eStage = RBU_STAGE_CAPTURE; rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0); if( rc2!=SQLITE_INTERNAL ) p->rc = rc2; } if( p->rc==SQLITE_OK && p->nFrame>0 ){ p->eStage = RBU_STAGE_CKPT; p->nStep = (pState ? pState->nRow : 0); p->aBuf = rbuMalloc(p, p->pgsz); p->iWalCksum = rbuShmChecksum(p); } if( p->rc==SQLITE_OK ){ if( p->nFrame==0 || (pState && pState->iWalCksum!=p->iWalCksum) ){ p->rc = SQLITE_DONE; p->eStage = RBU_STAGE_DONE; }else{ int nSectorSize; sqlite3_file *pDb = p->pTargetFd->pReal; sqlite3_file *pWal = p->pTargetFd->pWalFd->pReal; assert( p->nPagePerSector==0 ); nSectorSize = pDb->pMethods->xSectorSize(pDb); if( nSectorSize>p->pgsz ){ p->nPagePerSector = nSectorSize / p->pgsz; }else{ p->nPagePerSector = 1; } /* Call xSync() on the wal file. This causes SQLite to sync the ** directory in which the target database and the wal file reside, in ** case it has not been synced since the rename() call in ** rbuMoveOalFile(). */ p->rc = pWal->pMethods->xSync(pWal, SQLITE_SYNC_NORMAL); } } } /* ** Called when iAmt bytes are read from offset iOff of the wal file while ** the rbu object is in capture mode. Record the frame number of the frame ** being read in the aFrame[] array. |
︙ | ︙ | |||
2778 2779 2780 2781 2782 2783 2784 | } } #else p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK; #endif if( p->rc==SQLITE_OK ){ | | | 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 | } } #else p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK; #endif if( p->rc==SQLITE_OK ){ rbuOpenDatabase(p, 0); rbuSetupCheckpoint(p, 0); } } } sqlite3_free(zWal); sqlite3_free(zOal); |
︙ | ︙ | |||
3260 3261 3262 3263 3264 3265 3266 | } if( p->rc==SQLITE_OK ){ p->eStage = RBU_STAGE_DONE; p->rc = SQLITE_DONE; } }else{ | > > > > > > > > > > > > | > | | > > > > | 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 | } if( p->rc==SQLITE_OK ){ p->eStage = RBU_STAGE_DONE; p->rc = SQLITE_DONE; } }else{ /* At one point the following block copied a single frame from the ** wal file to the database file. So that one call to sqlite3rbu_step() ** checkpointed a single frame. ** ** However, if the sector-size is larger than the page-size, and the ** application calls sqlite3rbu_savestate() or close() immediately ** after this step, then rbu_step() again, then a power failure occurs, ** then the database page written here may be damaged. Work around ** this by checkpointing frames until the next page in the aFrame[] ** lies on a different disk sector to the current one. */ u32 iSector; do{ RbuFrame *pFrame = &p->aFrame[p->nStep]; iSector = (pFrame->iDbPage-1) / p->nPagePerSector; rbuCheckpointFrame(p, pFrame); p->nStep++; }while( p->nStep<p->nFrame && iSector==((p->aFrame[p->nStep].iDbPage-1) / p->nPagePerSector) && p->rc==SQLITE_OK ); } p->nProgress++; } break; } default: |
︙ | ︙ | |||
3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 | sqlite3_randomness(sizeof(int), (void*)&rnd); sqlite3_snprintf(sizeof(zRnd), zRnd, "rbu_vfs_%d", rnd); p->rc = sqlite3rbu_create_vfs(zRnd, 0); if( p->rc==SQLITE_OK ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(zRnd); assert( pVfs ); p->zVfsName = pVfs->zName; } } /* ** Destroy the private VFS created for the rbu handle passed as the only ** argument by an earlier call to rbuCreateVfs(). */ | > | 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 | sqlite3_randomness(sizeof(int), (void*)&rnd); sqlite3_snprintf(sizeof(zRnd), zRnd, "rbu_vfs_%d", rnd); p->rc = sqlite3rbu_create_vfs(zRnd, 0); if( p->rc==SQLITE_OK ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(zRnd); assert( pVfs ); p->zVfsName = pVfs->zName; ((rbu_vfs*)pVfs)->pRbu = p; } } /* ** Destroy the private VFS created for the rbu handle passed as the only ** argument by an earlier call to rbuCreateVfs(). */ |
︙ | ︙ | |||
3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 | /* Create the custom VFS. */ memset(p, 0, sizeof(sqlite3rbu)); rbuCreateVfs(p); /* Open the target, RBU and state databases */ if( p->rc==SQLITE_OK ){ char *pCsr = (char*)&p[1]; if( zTarget ){ p->zTarget = pCsr; memcpy(p->zTarget, zTarget, nTarget+1); pCsr += nTarget+1; } p->zRbu = pCsr; memcpy(p->zRbu, zRbu, nRbu+1); pCsr += nRbu+1; if( zState ){ p->zState = rbuMPrintf(p, "%s", zState); } | > > > > > > > > > | > > > | 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 | /* Create the custom VFS. */ memset(p, 0, sizeof(sqlite3rbu)); rbuCreateVfs(p); /* Open the target, RBU and state databases */ if( p->rc==SQLITE_OK ){ char *pCsr = (char*)&p[1]; int bRetry = 0; if( zTarget ){ p->zTarget = pCsr; memcpy(p->zTarget, zTarget, nTarget+1); pCsr += nTarget+1; } p->zRbu = pCsr; memcpy(p->zRbu, zRbu, nRbu+1); pCsr += nRbu+1; if( zState ){ p->zState = rbuMPrintf(p, "%s", zState); } /* If the first attempt to open the database file fails and the bRetry ** flag it set, this means that the db was not opened because it seemed ** to be a wal-mode db. But, this may have happened due to an earlier ** RBU vacuum operation leaving an old wal file in the directory. ** If this is the case, it will have been checkpointed and deleted ** when the handle was closed and a second attempt to open the ** database may succeed. */ rbuOpenDatabase(p, &bRetry); if( bRetry ){ rbuOpenDatabase(p, 0); } } if( p->rc==SQLITE_OK ){ pState = rbuLoadState(p); assert( pState || p->rc!=SQLITE_OK ); if( p->rc==SQLITE_OK ){ |
︙ | ︙ | |||
3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 | int rc; if( p ){ /* Commit the transaction to the *-oal file. */ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){ p->rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, &p->zErrmsg); } rbuSaveState(p, p->eStage); if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg); } | > > > > > > | 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 | int rc; if( p ){ /* Commit the transaction to the *-oal file. */ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){ p->rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, &p->zErrmsg); } /* Sync the db file if currently doing an incremental checkpoint */ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_CKPT ){ sqlite3_file *pDb = p->pTargetFd->pReal; p->rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL); } rbuSaveState(p, p->eStage); if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg); } |
︙ | ︙ | |||
3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 | int rc2 = sqlite3_exec(p->dbRbu, "DELETE FROM stat.rbu_state", 0, 0, 0); if( p->rc==SQLITE_DONE && rc2!=SQLITE_OK ) p->rc = rc2; } /* Close the open database handle and VFS object. */ sqlite3_close(p->dbRbu); sqlite3_close(p->dbMain); rbuDeleteVfs(p); sqlite3_free(p->aBuf); sqlite3_free(p->aFrame); rbuEditErrmsg(p); rc = p->rc; | > > | > > > | 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 | int rc2 = sqlite3_exec(p->dbRbu, "DELETE FROM stat.rbu_state", 0, 0, 0); if( p->rc==SQLITE_DONE && rc2!=SQLITE_OK ) p->rc = rc2; } /* Close the open database handle and VFS object. */ sqlite3_close(p->dbRbu); sqlite3_close(p->dbMain); assert( p->szTemp==0 ); rbuDeleteVfs(p); sqlite3_free(p->aBuf); sqlite3_free(p->aFrame); rbuEditErrmsg(p); rc = p->rc; if( pzErrmsg ){ *pzErrmsg = p->zErrmsg; }else{ sqlite3_free(p->zErrmsg); } sqlite3_free(p->zState); sqlite3_free(p); }else{ rc = SQLITE_NOMEM; *pzErrmsg = 0; } return rc; |
︙ | ︙ | |||
3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 | if( rc==SQLITE_DONE ) return SQLITE_OK; assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE ); if( p->eStage==RBU_STAGE_OAL ){ assert( rc!=SQLITE_DONE ); if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, 0); } p->rc = rc; rbuSaveState(p, p->eStage); rc = p->rc; if( p->eStage==RBU_STAGE_OAL ){ assert( rc!=SQLITE_DONE ); | > > > > > > | 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 | if( rc==SQLITE_DONE ) return SQLITE_OK; assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE ); if( p->eStage==RBU_STAGE_OAL ){ assert( rc!=SQLITE_DONE ); if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, 0); } /* Sync the db file */ if( rc==SQLITE_OK && p->eStage==RBU_STAGE_CKPT ){ sqlite3_file *pDb = p->pTargetFd->pReal; rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL); } p->rc = rc; rbuSaveState(p, p->eStage); rc = p->rc; if( p->eStage==RBU_STAGE_OAL ){ assert( rc!=SQLITE_DONE ); |
︙ | ︙ | |||
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 | ** database file are recorded. xShmLock() calls to unlock the same ** locks are no-ops (so that once obtained, these locks are never ** relinquished). Finally, calls to xSync() on the target database ** file fail with SQLITE_INTERNAL errors. */ static void rbuUnlockShm(rbu_file *p){ if( p->pRbu ){ int (*xShmLock)(sqlite3_file*,int,int,int) = p->pReal->pMethods->xShmLock; int i; for(i=0; i<SQLITE_SHM_NLOCK;i++){ if( (1<<i) & p->pRbu->mLock ){ xShmLock(p->pReal, i, 1, SQLITE_SHM_UNLOCK|SQLITE_SHM_EXCLUSIVE); } } p->pRbu->mLock = 0; } } /* ** Close an rbu file. */ static int rbuVfsClose(sqlite3_file *pFile){ rbu_file *p = (rbu_file*)pFile; int rc; | > > > > > > > > > > > > > | 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 | ** database file are recorded. xShmLock() calls to unlock the same ** locks are no-ops (so that once obtained, these locks are never ** relinquished). Finally, calls to xSync() on the target database ** file fail with SQLITE_INTERNAL errors. */ static void rbuUnlockShm(rbu_file *p){ assert( p->openFlags & SQLITE_OPEN_MAIN_DB ); if( p->pRbu ){ int (*xShmLock)(sqlite3_file*,int,int,int) = p->pReal->pMethods->xShmLock; int i; for(i=0; i<SQLITE_SHM_NLOCK;i++){ if( (1<<i) & p->pRbu->mLock ){ xShmLock(p->pReal, i, 1, SQLITE_SHM_UNLOCK|SQLITE_SHM_EXCLUSIVE); } } p->pRbu->mLock = 0; } } /* */ static int rbuUpdateTempSize(rbu_file *pFd, sqlite3_int64 nNew){ sqlite3rbu *pRbu = pFd->pRbu; i64 nDiff = nNew - pFd->sz; pRbu->szTemp += nDiff; pFd->sz = nNew; assert( pRbu->szTemp>=0 ); if( pRbu->szTempLimit && pRbu->szTemp>pRbu->szTempLimit ) return SQLITE_FULL; return SQLITE_OK; } /* ** Close an rbu file. */ static int rbuVfsClose(sqlite3_file *pFile){ rbu_file *p = (rbu_file*)pFile; int rc; |
︙ | ︙ | |||
3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 | rbu_file **pp; sqlite3_mutex_enter(p->pRbuVfs->mutex); for(pp=&p->pRbuVfs->pMain; *pp!=p; pp=&((*pp)->pMainNext)); *pp = p->pMainNext; sqlite3_mutex_leave(p->pRbuVfs->mutex); rbuUnlockShm(p); p->pReal->pMethods->xShmUnmap(p->pReal, 0); } /* Close the underlying file handle */ rc = p->pReal->pMethods->xClose(p->pReal); return rc; } | > > > | 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 | rbu_file **pp; sqlite3_mutex_enter(p->pRbuVfs->mutex); for(pp=&p->pRbuVfs->pMain; *pp!=p; pp=&((*pp)->pMainNext)); *pp = p->pMainNext; sqlite3_mutex_leave(p->pRbuVfs->mutex); rbuUnlockShm(p); p->pReal->pMethods->xShmUnmap(p->pReal, 0); } else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){ rbuUpdateTempSize(p, 0); } /* Close the underlying file handle */ rc = p->pReal->pMethods->xClose(p->pReal); return rc; } |
︙ | ︙ | |||
4044 4045 4046 4047 4048 4049 4050 | sqlite3rbu *pRbu = p->pRbu; int rc; if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){ assert( p->openFlags & SQLITE_OPEN_MAIN_DB ); rc = rbuCaptureDbWrite(p->pRbu, iOfst); }else{ | > | | | | | > > > > > > > > > > > | 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 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 | sqlite3rbu *pRbu = p->pRbu; int rc; if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){ assert( p->openFlags & SQLITE_OPEN_MAIN_DB ); rc = rbuCaptureDbWrite(p->pRbu, iOfst); }else{ if( pRbu ){ if( pRbu->eStage==RBU_STAGE_OAL && (p->openFlags & SQLITE_OPEN_WAL) && iOfst>=pRbu->iOalSz ){ pRbu->iOalSz = iAmt + iOfst; }else if( p->openFlags & SQLITE_OPEN_DELETEONCLOSE ){ i64 szNew = iAmt+iOfst; if( szNew>p->sz ){ rc = rbuUpdateTempSize(p, szNew); if( rc!=SQLITE_OK ) return rc; } } } rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){ /* These look like magic numbers. But they are stable, as they are part ** of the definition of the SQLite file format, which may not change. */ u8 *pBuf = (u8*)zBuf; p->iCookie = rbuGetU32(&pBuf[24]); p->iWriteVer = pBuf[19]; } } return rc; } /* ** Truncate an rbuVfs-file. */ static int rbuVfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ rbu_file *p = (rbu_file*)pFile; if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){ int rc = rbuUpdateTempSize(p, size); if( rc!=SQLITE_OK ) return rc; } return p->pReal->pMethods->xTruncate(p->pReal, size); } /* ** Sync an rbuVfs-file. */ static int rbuVfsSync(sqlite3_file *pFile, int flags){ |
︙ | ︙ | |||
4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 | rc = SQLITE_NOMEM; } pFd->pRbu = pDb->pRbu; } pDb->pWalFd = pFd; } } } if( oflags & SQLITE_OPEN_MAIN_DB && sqlite3_uri_boolean(zName, "rbu_memory", 0) ){ assert( oflags & SQLITE_OPEN_MAIN_DB ); oflags = SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | | > > | 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 | rc = SQLITE_NOMEM; } pFd->pRbu = pDb->pRbu; } pDb->pWalFd = pFd; } } }else{ pFd->pRbu = pRbuVfs->pRbu; } if( oflags & SQLITE_OPEN_MAIN_DB && sqlite3_uri_boolean(zName, "rbu_memory", 0) ){ assert( oflags & SQLITE_OPEN_MAIN_DB ); oflags = SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | |
︙ | ︙ | |||
4532 4533 4534 4535 4536 4537 4538 | */ if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath); if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ if( *pResOut ){ rc = SQLITE_CANTOPEN; }else{ | > > | | 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 | */ if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath); if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ if( *pResOut ){ rc = SQLITE_CANTOPEN; }else{ sqlite3_int64 sz = 0; rc = rbuVfsFileSize(&pDb->base, &sz); *pResOut = (sz>0); } } } return rc; } |
︙ | ︙ | |||
4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 | sqlite3_mutex_free(pNew->mutex); sqlite3_free(pNew); } } return rc; } /**************************************************************************/ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */ | > > > > > > > > > > > > > > | 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 | sqlite3_mutex_free(pNew->mutex); sqlite3_free(pNew); } } return rc; } /* ** Configure the aggregate temp file size limit for this RBU handle. */ sqlite3_int64 sqlite3rbu_temp_size_limit(sqlite3rbu *pRbu, sqlite3_int64 n){ if( n>=0 ){ pRbu->szTempLimit = n; } return pRbu->szTempLimit; } sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu){ return pRbu->szTemp; } /**************************************************************************/ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */ |
Changes to ext/rbu/sqlite3rbu.h.
︙ | ︙ | |||
304 305 306 307 308 309 310 | ** "vfs=..." option may be passed as the zTarget option. ** ** IMPORTANT NOTE FOR ZIPVFS USERS: The RBU extension works with all of ** SQLite's built-in VFSs, including the multiplexor VFS. However it does ** not work out of the box with zipvfs. Refer to the comment describing ** the zipvfs_create_vfs() API below for details on using RBU with zipvfs. */ | | | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | ** "vfs=..." option may be passed as the zTarget option. ** ** IMPORTANT NOTE FOR ZIPVFS USERS: The RBU extension works with all of ** SQLite's built-in VFSs, including the multiplexor VFS. However it does ** not work out of the box with zipvfs. Refer to the comment describing ** the zipvfs_create_vfs() API below for details on using RBU with zipvfs. */ SQLITE_API sqlite3rbu *sqlite3rbu_open( const char *zTarget, const char *zRbu, const char *zState ); /* ** Open an RBU handle to perform an RBU vacuum on database file zTarget. |
︙ | ︙ | |||
343 344 345 346 347 348 349 | ** new RBU vacuum operation. ** ** As with sqlite3rbu_open(), Zipvfs users should rever to the comment ** describing the sqlite3rbu_create_vfs() API function below for ** a description of the complications associated with using RBU with ** zipvfs databases. */ | | > > > > > > > > > > > > > > > > > > > > > > | 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 | ** new RBU vacuum operation. ** ** As with sqlite3rbu_open(), Zipvfs users should rever to the comment ** describing the sqlite3rbu_create_vfs() API function below for ** a description of the complications associated with using RBU with ** zipvfs databases. */ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( const char *zTarget, const char *zState ); /* ** Configure a limit for the amount of temp space that may be used by ** the RBU handle passed as the first argument. The new limit is specified ** in bytes by the second parameter. If it is positive, the limit is updated. ** If the second parameter to this function is passed zero, then the limit ** is removed entirely. If the second parameter is negative, the limit is ** not modified (this is useful for querying the current limit). ** ** In all cases the returned value is the current limit in bytes (zero ** indicates unlimited). ** ** If the temp space limit is exceeded during operation, an SQLITE_FULL ** error is returned. */ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size_limit(sqlite3rbu*, sqlite3_int64); /* ** Return the current amount of temp file space, in bytes, currently used by ** the RBU handle passed as the only argument. */ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu*); /* ** Internally, each RBU connection uses a separate SQLite database ** connection to access the target and rbu update databases. This ** API allows the application direct access to these database handles. ** ** The first argument passed to this function must be a valid, open, RBU ** handle. The second argument should be passed zero to access the target |
︙ | ︙ | |||
379 380 381 382 383 384 385 | ** If an error has occurred, either while opening or stepping the RBU object, ** this function may return NULL. The error code and message may be collected ** when sqlite3rbu_close() is called. ** ** Database handles returned by this function remain valid until the next ** call to any sqlite3rbu_xxx() function other than sqlite3rbu_db(). */ | | | | | | | | | | | 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 | ** If an error has occurred, either while opening or stepping the RBU object, ** this function may return NULL. The error code and message may be collected ** when sqlite3rbu_close() is called. ** ** Database handles returned by this function remain valid until the next ** call to any sqlite3rbu_xxx() function other than sqlite3rbu_db(). */ SQLITE_API sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu); /* ** Do some work towards applying the RBU update to the target db. ** ** Return SQLITE_DONE if the update has been completely applied, or ** SQLITE_OK if no error occurs but there remains work to do to apply ** the RBU update. If an error does occur, some other error code is ** returned. ** ** Once a call to sqlite3rbu_step() has returned a value other than ** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops ** that immediately return the same value. */ SQLITE_API int sqlite3rbu_step(sqlite3rbu *pRbu); /* ** Force RBU to save its state to disk. ** ** If a power failure or application crash occurs during an update, following ** system recovery RBU may resume the update from the point at which the state ** was last saved. In other words, from the most recent successful call to ** sqlite3rbu_close() or this function. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu); /* ** Close an RBU handle. ** ** If the RBU update has been completely applied, mark the RBU database ** as fully applied. Otherwise, assuming no error has occurred, save the ** current state of the RBU update appliation to the RBU database. ** ** If an error has already occurred as part of an sqlite3rbu_step() ** or sqlite3rbu_open() call, or if one occurs within this function, an ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL, ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted ** English language error message. It is the responsibility of the caller to ** eventually free any such buffer using sqlite3_free(). ** ** Otherwise, if no error occurs, this function returns SQLITE_OK if the ** update has been partially applied, or SQLITE_DONE if it has been ** completely applied. */ SQLITE_API int sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg); /* ** Return the total number of key-value operations (inserts, deletes or ** updates) that have been performed on the target database since the ** current RBU update was started. */ SQLITE_API sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu); /* ** Obtain permyriadage (permyriadage is to 10000 as percentage is to 100) ** progress indications for the two stages of an RBU update. This API may ** be useful for driving GUI progress indicators and similar. ** ** An RBU update is divided into two stages: |
︙ | ︙ | |||
474 475 476 477 478 479 480 | ** If the rbu_count table is present and populated correctly and this ** API is called during stage 1, the *pnOne output variable is set to the ** permyriadage progress of the same stage. If the rbu_count table does ** not exist, then (*pnOne) is set to -1 during stage 1. If the rbu_count ** table exists but is not correctly populated, the value of the *pnOne ** output variable during stage 1 is undefined. */ | | | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | ** If the rbu_count table is present and populated correctly and this ** API is called during stage 1, the *pnOne output variable is set to the ** permyriadage progress of the same stage. If the rbu_count table does ** not exist, then (*pnOne) is set to -1 during stage 1. If the rbu_count ** table exists but is not correctly populated, the value of the *pnOne ** output variable during stage 1 is undefined. */ SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo); /* ** Obtain an indication as to the current stage of an RBU update or vacuum. ** This function always returns one of the SQLITE_RBU_STATE_XXX constants ** defined in this file. Return values should be interpreted as follows: ** ** SQLITE_RBU_STATE_OAL: |
︙ | ︙ | |||
512 513 514 515 516 517 518 | */ #define SQLITE_RBU_STATE_OAL 1 #define SQLITE_RBU_STATE_MOVE 2 #define SQLITE_RBU_STATE_CHECKPOINT 3 #define SQLITE_RBU_STATE_DONE 4 #define SQLITE_RBU_STATE_ERROR 5 | | | 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 | */ #define SQLITE_RBU_STATE_OAL 1 #define SQLITE_RBU_STATE_MOVE 2 #define SQLITE_RBU_STATE_CHECKPOINT 3 #define SQLITE_RBU_STATE_DONE 4 #define SQLITE_RBU_STATE_ERROR 5 SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu); /* ** Create an RBU VFS named zName that accesses the underlying file-system ** via existing VFS zParent. Or, if the zParent parameter is passed NULL, ** then the new RBU VFS uses the default system VFS to access the file-system. ** The new object is registered as a non-default VFS with SQLite before ** returning. |
︙ | ︙ | |||
556 557 558 559 560 561 562 | ** that does not include the RBU layer results in an error. ** ** The overhead of adding the "rbu" VFS to the system is negligible for ** non-RBU users. There is no harm in an application accessing the ** file-system via "rbu" all the time, even if it only uses RBU functionality ** occasionally. */ | | | | 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 | ** that does not include the RBU layer results in an error. ** ** The overhead of adding the "rbu" VFS to the system is negligible for ** non-RBU users. There is no harm in an application accessing the ** file-system via "rbu" all the time, even if it only uses RBU functionality ** occasionally. */ SQLITE_API int sqlite3rbu_create_vfs(const char *zName, const char *zParent); /* ** Deregister and destroy an RBU vfs created by an earlier call to ** sqlite3rbu_create_vfs(). ** ** VFS objects are not reference counted. If a VFS object is destroyed ** before all database handles that use it have been closed, the results ** are undefined. */ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName); #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* _SQLITE3RBU_H */ |
Changes to ext/rbu/test_rbu.c.
︙ | ︙ | |||
65 66 67 68 69 70 71 | int ret = TCL_OK; sqlite3rbu *pRbu = (sqlite3rbu*)clientData; struct RbuCmd { const char *zName; int nArg; const char *zUsage; } aCmd[] = { | | | | | | | | | | > > > | 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 | int ret = TCL_OK; sqlite3rbu *pRbu = (sqlite3rbu*)clientData; struct RbuCmd { const char *zName; int nArg; const char *zUsage; } aCmd[] = { {"step", 2, ""}, /* 0 */ {"close", 2, ""}, /* 1 */ {"create_rbu_delta", 2, ""}, /* 2 */ {"savestate", 2, ""}, /* 3 */ {"dbMain_eval", 3, "SQL"}, /* 4 */ {"bp_progress", 2, ""}, /* 5 */ {"db", 3, "RBU"}, /* 6 */ {"state", 2, ""}, /* 7 */ {"progress", 2, ""}, /* 8 */ {"close_no_error", 2, ""}, /* 9 */ {"temp_size_limit", 3, "LIMIT"}, /* 10 */ {"temp_size", 2, ""}, /* 11 */ {0,0,0} }; int iCmd; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "METHOD"); return TCL_ERROR; |
︙ | ︙ | |||
98 99 100 101 102 103 104 105 106 107 108 | switch( iCmd ){ case 0: /* step */ { int rc = sqlite3rbu_step(pRbu); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); break; } case 1: /* close */ { char *zErrmsg = 0; int rc; Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); | > > | > > > | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | switch( iCmd ){ case 0: /* step */ { int rc = sqlite3rbu_step(pRbu); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); break; } case 9: /* close_no_error */ case 1: /* close */ { char *zErrmsg = 0; int rc; Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); if( iCmd==1 ){ rc = sqlite3rbu_close(pRbu, &zErrmsg); }else{ rc = sqlite3rbu_close(pRbu, 0); } if( rc==SQLITE_OK || rc==SQLITE_DONE ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); assert( zErrmsg==0 ); }else{ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); if( zErrmsg ){ Tcl_AppendResult(interp, " - ", zErrmsg, 0); |
︙ | ︙ | |||
182 183 184 185 186 187 188 189 190 191 192 193 194 195 | Tcl_SetResult(interp, (char*)aRes[eState], TCL_STATIC); break; } case 8: /* progress */ { sqlite3_int64 nStep = sqlite3rbu_progress(pRbu); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nStep)); break; } default: /* seems unlikely */ assert( !"cannot happen" ); break; } | > > > > > > > > > > > > > > > > | 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 | Tcl_SetResult(interp, (char*)aRes[eState], TCL_STATIC); break; } case 8: /* progress */ { sqlite3_int64 nStep = sqlite3rbu_progress(pRbu); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nStep)); break; } case 10: /* temp_size_limit */ { sqlite3_int64 nLimit; if( Tcl_GetWideIntFromObj(interp, objv[2], &nLimit) ){ ret = TCL_ERROR; }else{ nLimit = sqlite3rbu_temp_size_limit(pRbu, nLimit); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nLimit)); } break; } case 11: /* temp_size */ { sqlite3_int64 sz = sqlite3rbu_temp_size(pRbu); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sz)); break; } default: /* seems unlikely */ assert( !"cannot happen" ); break; } |
︙ | ︙ |
Changes to ext/rtree/rtree.c.
︙ | ︙ | |||
64 65 66 67 68 69 70 71 72 73 74 75 76 77 | #include <string.h> #include <assert.h> #include <stdio.h> #ifndef SQLITE_AMALGAMATION #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; #endif /* The following macro is used to suppress compiler warnings. */ | > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #include <string.h> #include <assert.h> #include <stdio.h> #ifndef SQLITE_AMALGAMATION #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; #endif /* The following macro is used to suppress compiler warnings. */ |
︙ | ︙ | |||
112 113 114 115 116 117 118 119 120 121 122 123 | ** An rtree virtual-table object. */ struct Rtree { sqlite3_vtab base; /* Base class. Must be first */ sqlite3 *db; /* Host database connection */ int iNodeSize; /* Size in bytes of each node in the node table */ u8 nDim; /* Number of dimensions */ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ u8 nBytesPerCell; /* Bytes consumed per cell */ int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ | > > | > > > > < | 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 | ** An rtree virtual-table object. */ struct Rtree { sqlite3_vtab base; /* Base class. Must be first */ sqlite3 *db; /* Host database connection */ int iNodeSize; /* Size in bytes of each node in the node table */ u8 nDim; /* Number of dimensions */ u8 nDim2; /* Twice the number of dimensions */ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ u32 nBusy; /* Current number of users of this structure */ i64 nRowEst; /* Estimated number of rows in this table */ u32 nCursor; /* Number of open cursors */ /* List of nodes removed during a CondenseTree operation. List is ** linked together via the pointer normally used for hash chains - ** RtreeNode.pNext. RtreeNode.iNode stores the depth of the sub-tree ** headed by the node (leaf nodes have RtreeNode.iNode==0). */ RtreeNode *pDeleted; int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */ /* Blob I/O on xxx_node */ sqlite3_blob *pNodeBlob; /* Statements to read/write/delete a record from xxx_node */ sqlite3_stmt *pWriteNode; sqlite3_stmt *pDeleteNode; /* Statements to read/write/delete a record from xxx_rowid */ sqlite3_stmt *pReadRowid; sqlite3_stmt *pWriteRowid; sqlite3_stmt *pDeleteRowid; |
︙ | ︙ | |||
329 330 331 332 333 334 335 | struct RtreeGeomCallback { int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*); int (*xQueryFunc)(sqlite3_rtree_query_info*); void (*xDestructor)(void*); void *pContext; }; | < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | | | | | | | > | < > > > > > > > > > > > > > > > > > > > > > > | 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 | struct RtreeGeomCallback { int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*); int (*xQueryFunc)(sqlite3_rtree_query_info*); void (*xDestructor)(void*); void *pContext; }; /* ** An instance of this structure (in the form of a BLOB) is returned by ** the SQL functions that sqlite3_rtree_geometry_callback() and ** sqlite3_rtree_query_callback() create, and is read as the right-hand ** operand to the MATCH operator of an R-Tree. */ struct RtreeMatchArg { u32 iSize; /* Size of this object */ RtreeGeomCallback cb; /* Info about the callback functions */ int nParam; /* Number of parameters to the SQL function */ sqlite3_value **apSqlParam; /* Original SQL parameter values */ RtreeDValue aParam[1]; /* Values for parameters to the SQL function */ }; #ifndef MAX # define MAX(x,y) ((x) < (y) ? (y) : (x)) #endif #ifndef MIN # define MIN(x,y) ((x) > (y) ? (y) : (x)) #endif /* What version of GCC is being used. 0 means GCC is not being used . ** Note that the GCC_VERSION macro will also be set correctly when using ** clang, since clang works hard to be gcc compatible. So the gcc ** optimizations will also work when compiling with clang. */ #ifndef GCC_VERSION #if defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC) # define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__) #else # define GCC_VERSION 0 #endif #endif /* The testcase() macro should already be defined in the amalgamation. If ** it is not, make it a no-op. */ #ifndef SQLITE_AMALGAMATION # define testcase(X) #endif /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** ** For best performance, an attempt is made to guess at the byte-order ** using C-preprocessor macros. If that is unsuccessful, or if ** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined ** at run-time. */ #ifndef SQLITE_BYTEORDER #if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__arm__) # define SQLITE_BYTEORDER 1234 #elif defined(sparc) || defined(__ppc__) # define SQLITE_BYTEORDER 4321 #else # define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */ #endif #endif /* What version of MSVC is being used. 0 means MSVC is not being used */ #ifndef MSVC_VERSION #if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC) # define MSVC_VERSION _MSC_VER #else # define MSVC_VERSION 0 #endif #endif /* ** Functions to deserialize a 16 bit integer, 32 bit real number and ** 64 bit integer. The deserialized value is returned. */ static int readInt16(u8 *p){ return (p[0]<<8) + p[1]; } static void readCoord(u8 *p, RtreeCoord *pCoord){ assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */ #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 pCoord->u = _byteswap_ulong(*(u32*)p); #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 pCoord->u = __builtin_bswap32(*(u32*)p); #elif SQLITE_BYTEORDER==4321 pCoord->u = *(u32*)p; #else pCoord->u = ( (((u32)p[0]) << 24) + (((u32)p[1]) << 16) + (((u32)p[2]) << 8) + (((u32)p[3]) << 0) ); #endif } static i64 readInt64(u8 *p){ #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 u64 x; memcpy(&x, p, 8); return (i64)_byteswap_uint64(x); #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 u64 x; memcpy(&x, p, 8); return (i64)__builtin_bswap64(x); #elif SQLITE_BYTEORDER==4321 i64 x; memcpy(&x, p, 8); return x; #else return (i64)( (((u64)p[0]) << 56) + (((u64)p[1]) << 48) + (((u64)p[2]) << 40) + (((u64)p[3]) << 32) + (((u64)p[4]) << 24) + (((u64)p[5]) << 16) + (((u64)p[6]) << 8) + (((u64)p[7]) << 0) ); #endif } /* ** Functions to serialize a 16 bit integer, 32 bit real number and ** 64 bit integer. The value returned is the number of bytes written ** to the argument buffer (always 2, 4 and 8 respectively). */ static void writeInt16(u8 *p, int i){ p[0] = (i>> 8)&0xFF; p[1] = (i>> 0)&0xFF; } static int writeCoord(u8 *p, RtreeCoord *pCoord){ u32 i; assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */ assert( sizeof(RtreeCoord)==4 ); assert( sizeof(u32)==4 ); #if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 i = __builtin_bswap32(pCoord->u); memcpy(p, &i, 4); #elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 i = _byteswap_ulong(pCoord->u); memcpy(p, &i, 4); #elif SQLITE_BYTEORDER==4321 i = pCoord->u; memcpy(p, &i, 4); #else i = pCoord->u; p[0] = (i>>24)&0xFF; p[1] = (i>>16)&0xFF; p[2] = (i>> 8)&0xFF; p[3] = (i>> 0)&0xFF; #endif return 4; } static int writeInt64(u8 *p, i64 i){ #if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 i = (i64)__builtin_bswap64((u64)i); memcpy(p, &i, 8); #elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 i = (i64)_byteswap_uint64((u64)i); memcpy(p, &i, 8); #elif SQLITE_BYTEORDER==4321 memcpy(p, &i, 8); #else p[0] = (i>>56)&0xFF; p[1] = (i>>48)&0xFF; p[2] = (i>>40)&0xFF; p[3] = (i>>32)&0xFF; p[4] = (i>>24)&0xFF; p[5] = (i>>16)&0xFF; p[6] = (i>> 8)&0xFF; p[7] = (i>> 0)&0xFF; #endif return 8; } /* ** Increment the reference count of node p. */ static void nodeReference(RtreeNode *p){ |
︙ | ︙ | |||
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | pNode->nRef = 1; pNode->pParent = pParent; pNode->isDirty = 1; nodeReference(pParent); } return pNode; } /* ** 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 */ ){ | > > > > > > > > > > > | < | > > | | > > > | > > > > > > | > > > > > > > > | | | | | | | | | | | > | | | | < < < | 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 | pNode->nRef = 1; pNode->pParent = pParent; pNode->isDirty = 1; nodeReference(pParent); } return pNode; } /* ** Clear the Rtree.pNodeBlob object */ static void nodeBlobReset(Rtree *pRtree){ if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){ sqlite3_blob *pBlob = pRtree->pNodeBlob; pRtree->pNodeBlob = 0; sqlite3_blob_close(pBlob); } } /* ** Obtain a reference to an r-tree node. */ 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)) ){ assert( !pParent || !pNode->pParent || pNode->pParent==pParent ); if( pParent && !pNode->pParent ){ nodeReference(pParent); pNode->pParent = pParent; } pNode->nRef++; *ppNode = pNode; return SQLITE_OK; } if( pRtree->pNodeBlob ){ sqlite3_blob *pBlob = pRtree->pNodeBlob; pRtree->pNodeBlob = 0; rc = sqlite3_blob_reopen(pBlob, iNode); pRtree->pNodeBlob = pBlob; if( rc ){ nodeBlobReset(pRtree); if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM; } } if( pRtree->pNodeBlob==0 ){ char *zTab = sqlite3_mprintf("%s_node", pRtree->zName); if( zTab==0 ) return SQLITE_NOMEM; rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0, &pRtree->pNodeBlob); sqlite3_free(zTab); } if( rc ){ nodeBlobReset(pRtree); *ppNode = 0; /* If unable to open an sqlite3_blob on the desired row, that can only ** be because the shadow tables hold erroneous data. */ if( rc==SQLITE_ERROR ) rc = SQLITE_CORRUPT_VTAB; }else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){ pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize); if( !pNode ){ rc = SQLITE_NOMEM; }else{ pNode->pParent = pParent; pNode->zData = (u8 *)&pNode[1]; pNode->nRef = 1; pNode->iNode = iNode; pNode->isDirty = 0; pNode->pNext = 0; rc = sqlite3_blob_read(pRtree->pNodeBlob, pNode->zData, pRtree->iNodeSize, 0); nodeReference(pParent); } } /* If the root node was just loaded, set pRtree->iDepth to the height ** of the r-tree structure. A height of zero means all data is stored on ** the root node. A height of one means the children of the root node ** are the leaves, and so on. If the depth as specified on the root node ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. */ |
︙ | ︙ | |||
598 599 600 601 602 603 604 | RtreeNode *pNode, /* The node into which the cell is to be written */ RtreeCell *pCell, /* The cell to write */ int iCell /* Index into pNode into which pCell is written */ ){ int ii; u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; p += writeInt64(p, pCell->iRowid); | | | 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 | RtreeNode *pNode, /* The node into which the cell is to be written */ RtreeCell *pCell, /* The cell to write */ int iCell /* Index into pNode into which pCell is written */ ){ int ii; u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; p += writeInt64(p, pCell->iRowid); for(ii=0; ii<pRtree->nDim2; ii++){ p += writeCoord(p, &pCell->aCoord[ii]); } pNode->isDirty = 1; } /* ** Remove the cell with index iCell from node pNode. |
︙ | ︙ | |||
732 733 734 735 736 737 738 | Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* The node containing the cell to be read */ int iCell, /* Index of the cell within the node */ RtreeCell *pCell /* OUT: Write the cell contents here */ ){ u8 *pData; RtreeCoord *pCoord; | | > > > > > | < < | 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 | Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* The node containing the cell to be read */ int iCell, /* Index of the cell within the node */ RtreeCell *pCell /* OUT: Write the cell contents here */ ){ u8 *pData; RtreeCoord *pCoord; int ii = 0; pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell); pCoord = pCell->aCoord; do{ readCoord(pData, &pCoord[ii]); readCoord(pData+4, &pCoord[ii+1]); pData += 8; ii += 2; }while( ii<pRtree->nDim2 ); } /* Forward declaration for the function that does the work of ** the virtual table module xCreate() and xConnect() methods. */ static int rtreeInit( |
︙ | ︙ | |||
789 790 791 792 793 794 795 | /* ** Decrement the r-tree reference count. When the reference count reaches ** zero the structure is deleted. */ static void rtreeRelease(Rtree *pRtree){ pRtree->nBusy--; if( pRtree->nBusy==0 ){ | > | > | 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 | /* ** Decrement the r-tree reference count. When the reference count reaches ** zero the structure is deleted. */ static void rtreeRelease(Rtree *pRtree){ pRtree->nBusy--; if( pRtree->nBusy==0 ){ pRtree->inWrTrans = 0; pRtree->nCursor = 0; nodeBlobReset(pRtree); sqlite3_finalize(pRtree->pWriteNode); sqlite3_finalize(pRtree->pDeleteNode); sqlite3_finalize(pRtree->pReadRowid); sqlite3_finalize(pRtree->pWriteRowid); sqlite3_finalize(pRtree->pDeleteRowid); sqlite3_finalize(pRtree->pReadParent); sqlite3_finalize(pRtree->pWriteParent); |
︙ | ︙ | |||
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 | pRtree->zDb, pRtree->zName, pRtree->zDb, pRtree->zName, pRtree->zDb, pRtree->zName ); if( !zCreate ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_exec(pRtree->db, zCreate, 0, 0, 0); sqlite3_free(zCreate); } if( rc==SQLITE_OK ){ rtreeRelease(pRtree); } return rc; } /* ** Rtree virtual table module xOpen method. */ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ int rc = SQLITE_NOMEM; RtreeCursor *pCsr; pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor)); if( pCsr ){ memset(pCsr, 0, sizeof(RtreeCursor)); pCsr->base.pVtab = pVTab; rc = SQLITE_OK; } *ppCursor = (sqlite3_vtab_cursor *)pCsr; return rc; } | > > > | 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 | pRtree->zDb, pRtree->zName, pRtree->zDb, pRtree->zName, pRtree->zDb, pRtree->zName ); if( !zCreate ){ rc = SQLITE_NOMEM; }else{ nodeBlobReset(pRtree); rc = sqlite3_exec(pRtree->db, zCreate, 0, 0, 0); sqlite3_free(zCreate); } if( rc==SQLITE_OK ){ rtreeRelease(pRtree); } return rc; } /* ** Rtree virtual table module xOpen method. */ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ int rc = SQLITE_NOMEM; Rtree *pRtree = (Rtree *)pVTab; RtreeCursor *pCsr; pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor)); if( pCsr ){ memset(pCsr, 0, sizeof(RtreeCursor)); pCsr->base.pVtab = pVTab; rc = SQLITE_OK; pRtree->nCursor++; } *ppCursor = (sqlite3_vtab_cursor *)pCsr; return rc; } |
︙ | ︙ | |||
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 | /* ** Rtree virtual table module xClose method. */ static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int ii; RtreeCursor *pCsr = (RtreeCursor *)cur; freeCursorConstraints(pCsr); sqlite3_free(pCsr->aPoint); for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]); sqlite3_free(pCsr); return SQLITE_OK; } /* ** Rtree virtual table module xEof method. ** ** Return non-zero if the cursor does not currently point to a valid | > > > | 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 | /* ** Rtree virtual table module xClose method. */ static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int ii; RtreeCursor *pCsr = (RtreeCursor *)cur; assert( pRtree->nCursor>0 ); freeCursorConstraints(pCsr); sqlite3_free(pCsr->aPoint); for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]); sqlite3_free(pCsr); pRtree->nCursor--; nodeBlobReset(pRtree); return SQLITE_OK; } /* ** Rtree virtual table module xEof method. ** ** Return non-zero if the cursor does not currently point to a valid |
︙ | ︙ | |||
907 908 909 910 911 912 913 | ** Convert raw bits from the on-disk RTree record into a coordinate value. ** The on-disk format is big-endian and needs to be converted for little- ** endian platforms. The on-disk record stores integer coordinates if ** eInt is true and it stores 32-bit floating point records if eInt is ** false. a[] is the four bytes of the on-disk record to be decoded. ** Store the results in "r". ** | | < < < | < < > > > > > > > > > > > > | | | 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 | ** Convert raw bits from the on-disk RTree record into a coordinate value. ** The on-disk format is big-endian and needs to be converted for little- ** endian platforms. The on-disk record stores integer coordinates if ** eInt is true and it stores 32-bit floating point records if eInt is ** false. a[] is the four bytes of the on-disk record to be decoded. ** Store the results in "r". ** ** There are five versions of this macro. The last one is generic. The ** other four are various architectures-specific optimizations. */ #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 #define RTREE_DECODE_COORD(eInt, a, r) { \ RtreeCoord c; /* Coordinate decoded */ \ c.u = _byteswap_ulong(*(u32*)a); \ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ } #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 #define RTREE_DECODE_COORD(eInt, a, r) { \ RtreeCoord c; /* Coordinate decoded */ \ c.u = __builtin_bswap32(*(u32*)a); \ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ } #elif SQLITE_BYTEORDER==1234 #define RTREE_DECODE_COORD(eInt, a, r) { \ RtreeCoord c; /* Coordinate decoded */ \ memcpy(&c.u,a,4); \ c.u = ((c.u>>24)&0xff)|((c.u>>8)&0xff00)| \ ((c.u&0xff)<<24)|((c.u&0xff00)<<8); \ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ } #elif SQLITE_BYTEORDER==4321 #define RTREE_DECODE_COORD(eInt, a, r) { \ RtreeCoord c; /* Coordinate decoded */ \ memcpy(&c.u,a,4); \ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ } #else #define RTREE_DECODE_COORD(eInt, a, r) { \ |
︙ | ︙ | |||
950 951 952 953 954 955 956 | RtreeConstraint *pConstraint, /* The constraint to test */ int eInt, /* True if RTree holding integer coordinates */ u8 *pCellData, /* Raw cell content */ RtreeSearchPoint *pSearch, /* Container of this cell */ sqlite3_rtree_dbl *prScore, /* OUT: score for the cell */ int *peWithin /* OUT: visibility of the cell */ ){ | < > > > | > > > > > > > > > | | > > > > > > > > > > > > > > > > > | | | 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 | RtreeConstraint *pConstraint, /* The constraint to test */ int eInt, /* True if RTree holding integer coordinates */ u8 *pCellData, /* Raw cell content */ RtreeSearchPoint *pSearch, /* Container of this cell */ sqlite3_rtree_dbl *prScore, /* OUT: score for the cell */ int *peWithin /* OUT: visibility of the cell */ ){ sqlite3_rtree_query_info *pInfo = pConstraint->pInfo; /* Callback info */ int nCoord = pInfo->nCoord; /* No. of coordinates */ int rc; /* Callback return code */ RtreeCoord c; /* Translator union */ sqlite3_rtree_dbl aCoord[RTREE_MAX_DIMENSIONS*2]; /* Decoded coordinates */ assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY ); assert( nCoord==2 || nCoord==4 || nCoord==6 || nCoord==8 || nCoord==10 ); if( pConstraint->op==RTREE_QUERY && pSearch->iLevel==1 ){ pInfo->iRowid = readInt64(pCellData); } pCellData += 8; #ifndef SQLITE_RTREE_INT_ONLY if( eInt==0 ){ switch( nCoord ){ case 10: readCoord(pCellData+36, &c); aCoord[9] = c.f; readCoord(pCellData+32, &c); aCoord[8] = c.f; case 8: readCoord(pCellData+28, &c); aCoord[7] = c.f; readCoord(pCellData+24, &c); aCoord[6] = c.f; case 6: readCoord(pCellData+20, &c); aCoord[5] = c.f; readCoord(pCellData+16, &c); aCoord[4] = c.f; case 4: readCoord(pCellData+12, &c); aCoord[3] = c.f; readCoord(pCellData+8, &c); aCoord[2] = c.f; default: readCoord(pCellData+4, &c); aCoord[1] = c.f; readCoord(pCellData, &c); aCoord[0] = c.f; } }else #endif { switch( nCoord ){ case 10: readCoord(pCellData+36, &c); aCoord[9] = c.i; readCoord(pCellData+32, &c); aCoord[8] = c.i; case 8: readCoord(pCellData+28, &c); aCoord[7] = c.i; readCoord(pCellData+24, &c); aCoord[6] = c.i; case 6: readCoord(pCellData+20, &c); aCoord[5] = c.i; readCoord(pCellData+16, &c); aCoord[4] = c.i; case 4: readCoord(pCellData+12, &c); aCoord[3] = c.i; readCoord(pCellData+8, &c); aCoord[2] = c.i; default: readCoord(pCellData+4, &c); aCoord[1] = c.i; readCoord(pCellData, &c); aCoord[0] = c.i; } } if( pConstraint->op==RTREE_MATCH ){ int eWithin = 0; rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pInfo, nCoord, aCoord, &eWithin); if( eWithin==0 ) *peWithin = NOT_WITHIN; *prScore = RTREE_ZERO; }else{ pInfo->aCoord = aCoord; pInfo->iLevel = pSearch->iLevel - 1; pInfo->rScore = pInfo->rParentScore = pSearch->rScore; pInfo->eWithin = pInfo->eParentWithin = pSearch->eWithin; rc = pConstraint->u.xQueryFunc(pInfo); |
︙ | ︙ | |||
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 | /* p->iCoord might point to either a lower or upper bound coordinate ** in a coordinate pair. But make pCellData point to the lower bound. */ pCellData += 8 + 4*(p->iCoord&0xfe); assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ ); switch( p->op ){ case RTREE_LE: case RTREE_LT: case RTREE_EQ: RTREE_DECODE_COORD(eInt, pCellData, val); /* val now holds the lower bound of the coordinate pair */ if( p->u.rValue>=val ) return; | > | 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 | /* p->iCoord might point to either a lower or upper bound coordinate ** in a coordinate pair. But make pCellData point to the lower bound. */ pCellData += 8 + 4*(p->iCoord&0xfe); assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ ); assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */ switch( p->op ){ case RTREE_LE: case RTREE_LT: case RTREE_EQ: RTREE_DECODE_COORD(eInt, pCellData, val); /* val now holds the lower bound of the coordinate pair */ if( p->u.rValue>=val ) return; |
︙ | ︙ | |||
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 | int *peWithin /* Adjust downward, as appropriate */ ){ RtreeDValue xN; /* Coordinate value converted to a double */ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ ); pCellData += 8 + p->iCoord*4; RTREE_DECODE_COORD(eInt, pCellData, xN); switch( p->op ){ case RTREE_LE: if( xN <= p->u.rValue ) return; break; case RTREE_LT: if( xN < p->u.rValue ) return; break; case RTREE_GE: if( xN >= p->u.rValue ) return; break; case RTREE_GT: if( xN > p->u.rValue ) return; break; default: if( xN == p->u.rValue ) return; break; | > | 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 | int *peWithin /* Adjust downward, as appropriate */ ){ RtreeDValue xN; /* Coordinate value converted to a double */ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ ); pCellData += 8 + p->iCoord*4; assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */ RTREE_DECODE_COORD(eInt, pCellData, xN); switch( p->op ){ case RTREE_LE: if( xN <= p->u.rValue ) return; break; case RTREE_LT: if( xN < p->u.rValue ) return; break; case RTREE_GE: if( xN >= p->u.rValue ) return; break; case RTREE_GT: if( xN > p->u.rValue ) return; break; default: if( xN == p->u.rValue ) return; break; |
︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 | if( pA->rScore>pB->rScore ) return +1; if( pA->iLevel<pB->iLevel ) return -1; if( pA->iLevel>pB->iLevel ) return +1; return 0; } /* | | | 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 | if( pA->rScore>pB->rScore ) return +1; if( pA->iLevel<pB->iLevel ) return -1; if( pA->iLevel>pB->iLevel ) return +1; return 0; } /* ** Interchange two search points in a cursor. */ static void rtreeSearchPointSwap(RtreeCursor *p, int i, int j){ RtreeSearchPoint t = p->aPoint[i]; assert( i<j ); p->aPoint[i] = p->aPoint[j]; p->aPoint[j] = t; i++; j++; |
︙ | ︙ | |||
1361 1362 1363 1364 1365 1366 1367 | if( p->iCell>=nCell ){ RTREE_QUEUE_TRACE(pCur, "POP-S:"); rtreeSearchPointPop(pCur); } if( rScore<RTREE_ZERO ) rScore = RTREE_ZERO; p = rtreeSearchPointNew(pCur, rScore, x.iLevel); if( p==0 ) return SQLITE_NOMEM; | | | 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 | if( p->iCell>=nCell ){ RTREE_QUEUE_TRACE(pCur, "POP-S:"); rtreeSearchPointPop(pCur); } if( rScore<RTREE_ZERO ) rScore = RTREE_ZERO; p = rtreeSearchPointNew(pCur, rScore, x.iLevel); if( p==0 ) return SQLITE_NOMEM; p->eWithin = (u8)eWithin; p->id = x.id; p->iCell = x.iCell; RTREE_QUEUE_TRACE(pCur, "PUSH-S:"); break; } if( p->iCell>=nCell ){ RTREE_QUEUE_TRACE(pCur, "POP-Se:"); |
︙ | ︙ | |||
1420 1421 1422 1423 1424 1425 1426 | RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; if( p==0 ) return SQLITE_OK; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else{ | < | 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 | RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; if( p==0 ) return SQLITE_OK; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else{ 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); }else #endif { |
︙ | ︙ | |||
1469 1470 1471 1472 1473 1474 1475 | /* ** This function is called to configure the RtreeConstraint object passed ** as the second argument for a MATCH constraint. The value passed as the ** first argument to this function is the right-hand operand to the MATCH ** operator. */ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ | | < < < < < < | < | < < | > < | < < < < < < < | 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 | /* ** This function is called to configure the RtreeConstraint object passed ** as the second argument for a MATCH constraint. The value passed as the ** first argument to this function is the right-hand operand to the MATCH ** operator. */ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ RtreeMatchArg *pBlob, *pSrc; /* BLOB returned by geometry function */ sqlite3_rtree_query_info *pInfo; /* Callback information */ pSrc = sqlite3_value_pointer(pValue, "RtreeMatchArg"); if( pSrc==0 ) return SQLITE_ERROR; pInfo = (sqlite3_rtree_query_info*) sqlite3_malloc64( sizeof(*pInfo)+pSrc->iSize ); if( !pInfo ) return SQLITE_NOMEM; memset(pInfo, 0, sizeof(*pInfo)); pBlob = (RtreeMatchArg*)&pInfo[1]; memcpy(pBlob, pSrc, pSrc->iSize); pInfo->pContext = pBlob->cb.pContext; pInfo->nParam = pBlob->nParam; pInfo->aParam = pBlob->aParam; pInfo->apSqlParam = pBlob->apSqlParam; if( pBlob->cb.xGeom ){ pCons->u.xGeom = pBlob->cb.xGeom; |
︙ | ︙ | |||
1549 1550 1551 1552 1553 1554 1555 | if( rc==SQLITE_OK && pLeaf!=0 ){ p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0); assert( p!=0 ); /* Always returns pCsr->sPoint */ pCsr->aNode[0] = pLeaf; p->id = iNode; p->eWithin = PARTLY_WITHIN; rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell); | | | 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 | if( rc==SQLITE_OK && pLeaf!=0 ){ p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0); assert( p!=0 ); /* Always returns pCsr->sPoint */ pCsr->aNode[0] = pLeaf; p->id = iNode; p->eWithin = PARTLY_WITHIN; rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell); p->iCell = (u8)iCell; RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:"); }else{ pCsr->atEOF = 1; } }else{ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array ** with the configured constraints. |
︙ | ︙ | |||
1582 1583 1584 1585 1586 1587 1588 | ** can be cast into an RtreeMatchArg object. One created using ** an sqlite3_rtree_geometry_callback() SQL user function. */ rc = deserializeGeometry(argv[ii], p); if( rc!=SQLITE_OK ){ break; } | | | < < < < < < < < < < < < < | 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 | ** can be cast into an RtreeMatchArg object. One created using ** an sqlite3_rtree_geometry_callback() SQL user function. */ rc = deserializeGeometry(argv[ii], p); if( rc!=SQLITE_OK ){ break; } p->pInfo->nCoord = pRtree->nDim2; p->pInfo->anQueue = pCsr->anQueue; p->pInfo->mxLevel = pRtree->iDepth + 1; }else{ #ifdef SQLITE_RTREE_INT_ONLY p->u.rValue = sqlite3_value_int64(argv[ii]); #else p->u.rValue = sqlite3_value_double(argv[ii]); #endif } } } } if( rc==SQLITE_OK ){ RtreeSearchPoint *pNew; pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1)); if( pNew==0 ) return SQLITE_NOMEM; pNew->id = 1; pNew->iCell = 0; pNew->eWithin = PARTLY_WITHIN; assert( pCsr->bPoint==1 ); pCsr->aNode[0] = pRoot; pRoot = 0; RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:"); rc = rtreeStepToLeaf(pCsr); } } nodeRelease(pRtree, pRoot); rtreeRelease(pRtree); return rc; } /* ** Rtree virtual table module xBestIndex method. There are three ** table scan strategies to choose from (in order from most to ** least desirable): ** ** idxNum idxStr Strategy ** ------------------------------------------------ |
︙ | ︙ | |||
1707 1708 1709 1710 1711 1712 1713 | /* This strategy involves a two rowid lookups on an B-Tree structures ** and then a linear search of an R-Tree node. This should be ** considered almost as quick as a direct rowid lookup (for which ** sqlite uses an internal cost of 0.0). It is expected to return ** a single row. */ pIdxInfo->estimatedCost = 30.0; | | | | > > > > > > > > > > > | > | | > > > > > | | | > | | | < > > < > < > > | | 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 | /* This strategy involves a two rowid lookups on an B-Tree structures ** and then a linear search of an R-Tree node. This should be ** considered almost as quick as a direct rowid lookup (for which ** sqlite uses an internal cost of 0.0). It is expected to return ** a single row. */ pIdxInfo->estimatedCost = 30.0; pIdxInfo->estimatedRows = 1; return SQLITE_OK; } if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){ u8 op; switch( p->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; default: assert( p->op==SQLITE_INDEX_CONSTRAINT_MATCH ); op = RTREE_MATCH; break; } zIdxStr[iIdx++] = op; zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); pIdxInfo->aConstraintUsage[ii].omit = 1; } } pIdxInfo->idxNum = 2; pIdxInfo->needToFreeIdxStr = 1; if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){ return SQLITE_NOMEM; } nRow = pRtree->nRowEst >> (iIdx/2); pIdxInfo->estimatedCost = (double)6.0 * (double)nRow; pIdxInfo->estimatedRows = nRow; return rc; } /* ** Return the N-dimensional volumn of the cell stored in *p. */ static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ RtreeDValue area = (RtreeDValue)1; assert( pRtree->nDim>=1 && pRtree->nDim<=5 ); #ifndef SQLITE_RTREE_INT_ONLY if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ switch( pRtree->nDim ){ case 5: area = p->aCoord[9].f - p->aCoord[8].f; case 4: area *= p->aCoord[7].f - p->aCoord[6].f; case 3: area *= p->aCoord[5].f - p->aCoord[4].f; case 2: area *= p->aCoord[3].f - p->aCoord[2].f; default: area *= p->aCoord[1].f - p->aCoord[0].f; } }else #endif { switch( pRtree->nDim ){ case 5: area = p->aCoord[9].i - p->aCoord[8].i; case 4: area *= p->aCoord[7].i - p->aCoord[6].i; case 3: area *= p->aCoord[5].i - p->aCoord[4].i; case 2: area *= p->aCoord[3].i - p->aCoord[2].i; default: area *= p->aCoord[1].i - p->aCoord[0].i; } } return area; } /* ** Return the margin length of cell p. The margin length is the sum ** of the objects size in each dimension. */ static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){ RtreeDValue margin = 0; int ii = pRtree->nDim2 - 2; do{ margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])); ii -= 2; }while( ii>=0 ); return margin; } /* ** Store the union of cells p1 and p2 in p1. */ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ int ii = 0; if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ do{ p1->aCoord[ii].f = MIN(p1->aCoord[ii].f, p2->aCoord[ii].f); p1->aCoord[ii+1].f = MAX(p1->aCoord[ii+1].f, p2->aCoord[ii+1].f); ii += 2; }while( ii<pRtree->nDim2 ); }else{ do{ p1->aCoord[ii].i = MIN(p1->aCoord[ii].i, p2->aCoord[ii].i); p1->aCoord[ii+1].i = MAX(p1->aCoord[ii+1].i, p2->aCoord[ii+1].i); ii += 2; }while( ii<pRtree->nDim2 ); } } /* ** Return true if the area covered by p2 is a subset of the area covered ** by p1. False otherwise. */ static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ int ii; int isInt = (pRtree->eCoordType==RTREE_COORD_INT32); for(ii=0; ii<pRtree->nDim2; ii+=2){ RtreeCoord *a1 = &p1->aCoord[ii]; RtreeCoord *a2 = &p2->aCoord[ii]; if( (!isInt && (a2[0].f<a1[0].f || a2[1].f>a1[1].f)) || ( isInt && (a2[0].i<a1[0].i || a2[1].i>a1[1].i)) ){ return 0; } |
︙ | ︙ | |||
1829 1830 1831 1832 1833 1834 1835 | int nCell ){ int ii; RtreeDValue overlap = RTREE_ZERO; for(ii=0; ii<nCell; ii++){ int jj; RtreeDValue o = (RtreeDValue)1; | | | 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 | int nCell ){ int ii; RtreeDValue overlap = RTREE_ZERO; for(ii=0; ii<nCell; ii++){ int jj; RtreeDValue o = (RtreeDValue)1; for(jj=0; jj<pRtree->nDim2; jj+=2){ RtreeDValue x1, x2; x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj])); x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1])); if( x2<x1 ){ o = (RtreeDValue)0; break; }else{ |
︙ | ︙ | |||
2690 2691 2692 2693 2694 2695 2696 | /* ** Remove the entry with rowid=iDelete from the r-tree structure. */ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ int rc; /* Return code */ RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */ int iCell; /* Index of iDelete cell in pLeaf */ | | | 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 | /* ** Remove the entry with rowid=iDelete from the r-tree structure. */ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ int rc; /* Return code */ RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */ int iCell; /* Index of iDelete cell in pLeaf */ RtreeNode *pRoot = 0; /* Root node of rtree structure */ /* Obtain a reference to the root node to initialize Rtree.iDepth */ rc = nodeAcquire(pRtree, 1, 0, &pRoot); /* Obtain a reference to the leaf node that contains the entry ** about to be deleted. |
︙ | ︙ | |||
2885 2886 2887 2888 2889 2890 2891 | ** ** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared ** with "column" that are interpreted as table constraints. ** Example: CREATE VIRTUAL TABLE bad USING rtree(x,y,CHECK(y>5)); ** This problem was discovered after years of use, so we silently ignore ** these kinds of misdeclared tables to avoid breaking any legacy. */ | | | 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 | ** ** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared ** with "column" that are interpreted as table constraints. ** Example: CREATE VIRTUAL TABLE bad USING rtree(x,y,CHECK(y>5)); ** This problem was discovered after years of use, so we silently ignore ** these kinds of misdeclared tables to avoid breaking any legacy. */ assert( nData<=(pRtree->nDim2 + 3) ); #ifndef SQLITE_RTREE_INT_ONLY if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ for(ii=0; ii<nData-4; ii+=2){ cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]); cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]); if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){ |
︙ | ︙ | |||
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 | } } constraint: rtreeRelease(pRtree); return rc; } /* ** The xRename method for rtree module virtual tables. */ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){ Rtree *pRtree = (Rtree *)pVtab; int rc = SQLITE_NOMEM; char *zSql = sqlite3_mprintf( "ALTER TABLE %Q.'%q_node' RENAME TO \"%w_node\";" "ALTER TABLE %Q.'%q_parent' RENAME TO \"%w_parent\";" "ALTER TABLE %Q.'%q_rowid' RENAME TO \"%w_rowid\";" , pRtree->zDb, pRtree->zName, zNewName , pRtree->zDb, pRtree->zName, zNewName , pRtree->zDb, pRtree->zName, zNewName ); if( zSql ){ rc = sqlite3_exec(pRtree->db, zSql, 0, 0, 0); sqlite3_free(zSql); } return rc; } /* ** This function populates the pRtree->nRowEst variable with an estimate ** of the number of rows in the virtual table. If possible, this is based ** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST. */ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } } constraint: rtreeRelease(pRtree); return rc; } /* ** Called when a transaction starts. */ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; assert( pRtree->inWrTrans==0 ); pRtree->inWrTrans++; 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; } /* ** The xRename method for rtree module virtual tables. */ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){ Rtree *pRtree = (Rtree *)pVtab; int rc = SQLITE_NOMEM; char *zSql = sqlite3_mprintf( "ALTER TABLE %Q.'%q_node' RENAME TO \"%w_node\";" "ALTER TABLE %Q.'%q_parent' RENAME TO \"%w_parent\";" "ALTER TABLE %Q.'%q_rowid' RENAME TO \"%w_rowid\";" , pRtree->zDb, pRtree->zName, zNewName , pRtree->zDb, pRtree->zName, zNewName , pRtree->zDb, pRtree->zName, zNewName ); if( zSql ){ nodeBlobReset(pRtree); rc = sqlite3_exec(pRtree->db, zSql, 0, 0, 0); sqlite3_free(zSql); } return rc; } /* ** The xSavepoint method. ** ** This module does not need to do anything to support savepoints. However, ** it uses this hook to close any open blob handle. This is done because a ** DROP TABLE command - which fortunately always opens a savepoint - cannot ** succeed if there are any open blob handles. i.e. if the blob handle were ** not closed here, the following would fail: ** ** BEGIN; ** INSERT INTO rtree... ** DROP TABLE <tablename>; -- Would fail with SQLITE_LOCKED ** COMMIT; */ static int rtreeSavepoint(sqlite3_vtab *pVtab, int iSavepoint){ Rtree *pRtree = (Rtree *)pVtab; int iwt = pRtree->inWrTrans; UNUSED_PARAMETER(iSavepoint); pRtree->inWrTrans = 0; nodeBlobReset(pRtree); pRtree->inWrTrans = iwt; return SQLITE_OK; } /* ** This function populates the pRtree->nRowEst variable with an estimate ** of the number of rows in the virtual table. If possible, this is based ** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST. */ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ |
︙ | ︙ | |||
3041 3042 3043 3044 3045 3046 3047 | sqlite3_free(zSql); } return rc; } static sqlite3_module rtreeModule = { | | | | | | | | | | < | 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 | sqlite3_free(zSql); } return rc; } static sqlite3_module rtreeModule = { 2, /* iVersion */ rtreeCreate, /* xCreate - create a table */ rtreeConnect, /* xConnect - connect to an existing table */ rtreeBestIndex, /* xBestIndex - Determine search strategy */ rtreeDisconnect, /* xDisconnect - Disconnect from a table */ rtreeDestroy, /* xDestroy - Drop a table */ rtreeOpen, /* xOpen - open a cursor */ rtreeClose, /* xClose - close a cursor */ rtreeFilter, /* xFilter - configure scan constraints */ rtreeNext, /* xNext - advance a cursor */ 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 */ rtreeEndTransaction, /* xRollback - rollback transaction */ 0, /* xFindFunction - function overloading */ rtreeRename, /* xRename - rename the table */ rtreeSavepoint, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ }; static int rtreeSqlInit( Rtree *pRtree, sqlite3 *db, const char *zDb, const char *zPrefix, int isCreate ){ int rc = SQLITE_OK; #define N_STATEMENT 8 static const char *azSql[N_STATEMENT] = { /* Write the xxx_node table */ "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(:1, :2)", "DELETE FROM '%q'.'%q_node' WHERE nodeno = :1", /* Read and write the xxx_rowid table */ "SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = :1", "INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(:1, :2)", "DELETE FROM '%q'.'%q_rowid' WHERE rowid = :1", |
︙ | ︙ | |||
3116 3117 3118 3119 3120 3121 3122 | rc = sqlite3_exec(db, zCreate, 0, 0, 0); sqlite3_free(zCreate); if( rc!=SQLITE_OK ){ return rc; } } | | | | | | | | | < > | | 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 | rc = sqlite3_exec(db, zCreate, 0, 0, 0); sqlite3_free(zCreate); if( rc!=SQLITE_OK ){ return rc; } } appStmt[0] = &pRtree->pWriteNode; appStmt[1] = &pRtree->pDeleteNode; appStmt[2] = &pRtree->pReadRowid; appStmt[3] = &pRtree->pWriteRowid; appStmt[4] = &pRtree->pDeleteRowid; appStmt[5] = &pRtree->pReadParent; appStmt[6] = &pRtree->pWriteParent; appStmt[7] = &pRtree->pDeleteParent; rc = rtreeQueryStat1(db, pRtree); for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){ char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix); if( zSql ){ rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT, appStmt[i], 0); }else{ rc = SQLITE_NOMEM; } sqlite3_free(zSql); } return rc; |
︙ | ︙ | |||
3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 | zSql = sqlite3_mprintf( "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", pRtree->zDb, pRtree->zName ); rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } } sqlite3_free(zSql); return rc; } | > > > > | 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 | zSql = sqlite3_mprintf( "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", pRtree->zDb, pRtree->zName ); rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); }else if( pRtree->iNodeSize<(512-64) ){ rc = SQLITE_CORRUPT_VTAB; *pzErr = sqlite3_mprintf("undersize RTree blobs in \"%q_node\"", pRtree->zName); } } sqlite3_free(zSql); return rc; } |
︙ | ︙ | |||
3262 3263 3264 3265 3266 3267 3268 | return SQLITE_NOMEM; } memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); pRtree->nBusy = 1; pRtree->base.pModule = &rtreeModule; pRtree->zDb = (char *)&pRtree[1]; pRtree->zName = &pRtree->zDb[nDb+1]; | | > | | | 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 | return SQLITE_NOMEM; } memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); pRtree->nBusy = 1; pRtree->base.pModule = &rtreeModule; pRtree->zDb = (char *)&pRtree[1]; pRtree->zName = &pRtree->zDb[nDb+1]; pRtree->nDim = (u8)((argc-4)/2); pRtree->nDim2 = pRtree->nDim*2; pRtree->nBytesPerCell = 8 + pRtree->nDim2*4; pRtree->eCoordType = (u8)eCoordType; memcpy(pRtree->zDb, argv[1], nDb); memcpy(pRtree->zName, argv[2], nName); /* Figure out the node size to use. */ rc = getNodeSize(db, pRtree, isCreate, pzErr); /* Create/Connect to the underlying relational database schema. If |
︙ | ︙ | |||
3337 3338 3339 3340 3341 3342 3343 | RtreeNode node; Rtree tree; int ii; UNUSED_PARAMETER(nArg); memset(&node, 0, sizeof(RtreeNode)); memset(&tree, 0, sizeof(Rtree)); | | > | | 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 | RtreeNode node; Rtree tree; int ii; UNUSED_PARAMETER(nArg); memset(&node, 0, sizeof(RtreeNode)); memset(&tree, 0, sizeof(Rtree)); tree.nDim = (u8)sqlite3_value_int(apArg[0]); tree.nDim2 = tree.nDim*2; tree.nBytesPerCell = 8 + 8 * tree.nDim; node.zData = (u8 *)sqlite3_value_blob(apArg[1]); for(ii=0; ii<NCELL(&node); ii++){ char zCell[512]; int nCell = 0; RtreeCell cell; int jj; nodeGetCell(&tree, &node, ii, &cell); sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid); nCell = (int)strlen(zCell); for(jj=0; jj<tree.nDim2; jj++){ #ifndef SQLITE_RTREE_INT_ONLY sqlite3_snprintf(512-nCell,&zCell[nCell], " %g", (double)cell.aCoord[jj].f); #else sqlite3_snprintf(512-nCell,&zCell[nCell], " %d", cell.aCoord[jj].i); #endif |
︙ | ︙ | |||
3475 3476 3477 3478 3479 3480 3481 | nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue) + nArg*sizeof(sqlite3_value*); pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob); if( !pBlob ){ sqlite3_result_error_nomem(ctx); }else{ int i; | | | | 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 | nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue) + nArg*sizeof(sqlite3_value*); pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob); if( !pBlob ){ sqlite3_result_error_nomem(ctx); }else{ int i; pBlob->iSize = nBlob; pBlob->cb = pGeomCtx[0]; pBlob->apSqlParam = (sqlite3_value**)&pBlob->aParam[nArg]; pBlob->nParam = nArg; for(i=0; i<nArg; i++){ pBlob->apSqlParam[i] = sqlite3_value_dup(aArg[i]); if( pBlob->apSqlParam[i]==0 ) memErr = 1; #ifdef SQLITE_RTREE_INT_ONLY pBlob->aParam[i] = sqlite3_value_int64(aArg[i]); #else pBlob->aParam[i] = sqlite3_value_double(aArg[i]); #endif } if( memErr ){ sqlite3_result_error_nomem(ctx); rtreeMatchArgFree(pBlob); }else{ sqlite3_result_pointer(ctx, pBlob, "RtreeMatchArg", rtreeMatchArgFree); } } } /* ** Register a new geometry function for use with the r-tree MATCH operator. */ |
︙ | ︙ |
Changes to ext/rtree/rtree1.test.
︙ | ︙ | |||
35 36 37 38 39 40 41 42 43 44 45 46 47 48 | # rtree-12.*: Test that on-conflict clauses are supported. # rtree-13.*: Test that bug [d2889096e7bdeac6d] has been fixed. # rtree-14.*: Test if a non-integer is inserted into the PK column of an # r-tree table, it is converted to an integer before being # inserted. Also that if a non-numeric is inserted into one # of the min/max dimension columns, it is converted to the # required type before being inserted. # ifcapable !rtree { finish_test return } | > > | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | # rtree-12.*: Test that on-conflict clauses are supported. # rtree-13.*: Test that bug [d2889096e7bdeac6d] has been fixed. # rtree-14.*: Test if a non-integer is inserted into the PK column of an # r-tree table, it is converted to an integer before being # inserted. Also that if a non-numeric is inserted into one # of the min/max dimension columns, it is converted to the # required type before being inserted. # rtree-15.*: Check that DROP TABLE works within a transaction that # writes to an r-tree table. # ifcapable !rtree { finish_test return } |
︙ | ︙ | |||
332 333 334 335 336 337 338 | # An error midway through a rename operation. do_test rtree-7.2.1 { execsql { CREATE TABLE t4_node(a); } catchsql { ALTER TABLE "abc 123" RENAME TO t4 } | | | | 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 | # An error midway through a rename operation. do_test rtree-7.2.1 { execsql { CREATE TABLE t4_node(a); } catchsql { ALTER TABLE "abc 123" RENAME TO t4 } } {1 {SQL logic error}} do_test rtree-7.2.2 { execsql_intout { SELECT * FROM "abc 123" } } {1 2 3 4 5 6 7} do_test rtree-7.2.3 { execsql { DROP TABLE t4_node; CREATE TABLE t4_rowid(a); } catchsql { ALTER TABLE "abc 123" RENAME TO t4 } } {1 {SQL logic error}} do_test rtree-7.2.4 { db close sqlite3 db test.db execsql_intout { SELECT * FROM "abc 123" } } {1 2 3 4 5 6 7} do_test rtree-7.2.5 { execsql { DROP TABLE t4_rowid } |
︙ | ︙ | |||
587 588 589 590 591 592 593 594 595 | do_execsql_test 14.5 { SELECT * FROM t10; } { 1 0 0 2 52 81 3 42 49 } finish_test | > > > > > > > > > > > > > > > | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 | do_execsql_test 14.5 { SELECT * FROM t10; } { 1 0 0 2 52 81 3 42 49 } #------------------------------------------------------------------------- # do_execsql_test 15.0 { CREATE VIRTUAL TABLE rt USING rtree(id, x1,x2, y1,y2); CREATE TEMP TABLE t13(a, b, c); } do_execsql_test 15.1 { BEGIN; INSERT INTO rt VALUES(1,2,3,4,5); } do_execsql_test 15.2 { DROP TABLE t13; COMMIT; } finish_test |
Changes to ext/rtree/rtree8.test.
︙ | ︙ | |||
125 126 127 128 129 130 131 | #------------------------------------------------------------------------- # Test that trying to use the MATCH operator with the r-tree module does # not confuse it. # populate_t1 10 do_catchsql_test rtree8-3.1 { SELECT * FROM t1 WHERE x1 MATCH '1234' | | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | #------------------------------------------------------------------------- # Test that trying to use the MATCH operator with the r-tree module does # not confuse it. # populate_t1 10 do_catchsql_test rtree8-3.1 { SELECT * FROM t1 WHERE x1 MATCH '1234' } {1 {SQL logic error}} #------------------------------------------------------------------------- # Test a couple of invalid arguments to rtreedepth(). # do_catchsql_test rtree8-4.1 { SELECT rtreedepth('hello world') } {1 {Invalid argument to rtreedepth()}} |
︙ | ︙ |
Changes to ext/rtree/rtree9.test.
︙ | ︙ | |||
65 66 67 68 69 70 71 | do_execsql_test rtree9-3.3 { SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 5.5, 1, 1, 1) ORDER BY id; } {555 556 565 566 655 656 665 666} do_catchsql_test rtree9-4.1 { SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 1, 1, 1) ORDER BY id; | | | | | 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 | do_execsql_test rtree9-3.3 { SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 5.5, 1, 1, 1) ORDER BY id; } {555 556 565 566 655 656 665 666} do_catchsql_test rtree9-4.1 { SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 1, 1, 1) ORDER BY id; } {1 {SQL logic error}} for {set x 2} {$x<200} {incr x 2} { do_catchsql_test rtree9-4.2.[expr $x/2] { SELECT id FROM rt WHERE id MATCH randomblob($x) } {1 {SQL logic error}} } do_catchsql_test rtree9-4.3 { SELECT id FROM rt WHERE id MATCH CAST( (cube(5.5, 5.5, 5.5, 1, 1, 1) || X'1234567812345678') AS blob ) } {1 {SQL logic error}} #------------------------------------------------------------------------- # Test the example 2d "circle" geometry callback. # register_circle_geom db |
︙ | ︙ |
Changes to ext/rtree/rtreeA.test.
︙ | ︙ | |||
105 106 107 108 109 110 111 | 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } do_execsql_test rtreeA-1.2.0 { DROP TABLE t1_node } {} | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } do_execsql_test rtreeA-1.2.0 { DROP TABLE t1_node } {} do_corruption_tests rtreeA-1.2 -error "database disk image is malformed" { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } #------------------------------------------------------------------------- |
︙ | ︙ | |||
211 212 213 214 215 216 217 218 219 220 | do_execsql_test rtreeA-6.1.0 { UPDATE t1_parent set parentnode = parentnode+1 } {} do_corruption_tests rtreeA-6.1 { 1 "DELETE FROM t1 WHERE rowid = 5" 2 "UPDATE t1 SET x1=x1+1, x2=x2+1" } finish_test | > > > > > > > > > > > > > > > > > | 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 | do_execsql_test rtreeA-6.1.0 { UPDATE t1_parent set parentnode = parentnode+1 } {} do_corruption_tests rtreeA-6.1 { 1 "DELETE FROM t1 WHERE rowid = 5" 2 "UPDATE t1 SET x1=x1+1, x2=x2+1" } #------------------------------------------------------------------------- # Truncated blobs in the _node table. # create_t1 populate_t1 sqlite3 db test.db do_execsql_test rtreeA-7.100 { UPDATE t1_node SET data=x'' WHERE rowid=1; } {} do_catchsql_test rtreeA-7.110 { SELECT * FROM t1 WHERE x1>0 AND x1<100 AND x2>0 AND x2<100; } {1 {undersize RTree blobs in "t1_node"}} do_test rtreeA-7.120 { sqlite3_extended_errcode db } {SQLITE_CORRUPT_VTAB} finish_test |
Changes to ext/rtree/rtreeD.test.
︙ | ︙ | |||
15 16 17 18 19 20 21 | if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] source $testdir/tester.tcl source $testdir/lock_common.tcl | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] source $testdir/tester.tcl source $testdir/lock_common.tcl ifcapable !rtree||!builtin_test { finish_test return } set testprefix rtreeD #------------------------------------------------------------------------- # Test that if an SQLITE_BUSY is encountered within the vtable |
︙ | ︙ | |||
49 50 51 52 53 54 55 | do_test 1.$tn.4 { list [catch { sql2 { SELECT * FROM rt } } msg] $msg } {1 {database is locked}} } finish_test | < < | 49 50 51 52 53 54 55 | do_test 1.$tn.4 { list [catch { sql2 { SELECT * FROM rt } } msg] $msg } {1 {database is locked}} } finish_test |
Added ext/rtree/rtreeconnect.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 | # 2017 August 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. # #*********************************************************************** # # The focus of this file is testing the r-tree extension. Specifically, # the impact of an SQLITE_SCHEMA error within the rtree module xConnect # callback. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl set testprefix rtreeconnect ifcapable !rtree { finish_test return } do_execsql_test 1.0 { CREATE VIRTUAL TABLE r1 USING rtree(id, x1, x2, y1, y2); CREATE TABLE t1(id, x1, x2, y1, y2); CREATE TABLE log(l); CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN INSERT INTO r1 VALUES(new.id, new.x1, new.x2, new.y1, new.y2); INSERT INTO log VALUES('r1: ' || new.id); END; } db close sqlite3 db test.db sqlite3 db2 test.db do_test 1.1 { db eval { INSERT INTO log VALUES('startup'); } db2 eval { CREATE TABLE newtable(x,y); } } {} do_execsql_test 1.2 { INSERT INTO t1 VALUES(1, 2, 3, 4, 5); } db2 close db close finish_test |
Changes to ext/session/session1.test.
︙ | ︙ | |||
16 17 18 19 20 21 22 | } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix session1 | > > > > > > > > > > > > > | | | | | | | | | | | | | > | | | | | | | | | | | > | | | | | | | | | | | | | | | | | 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 | } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix session1 # Run all tests in this file twice. Once with "WITHOUT ROWID", and once # with regular rowid tables. # foreach {tn trailing} { 1 "" 2 " WITHOUT ROWID " } { eval [string map [list %WR% $trailing] { db close forcedelete test.db test.db2 reset_db do_execsql_test $tn.1.0 { CREATE TABLE t1(x PRIMARY KEY, y) %WR%; INSERT INTO t1 VALUES('abc', 'def'); } #------------------------------------------------------------------------- # Test creating, attaching tables to and deleting session objects. # do_test $tn.1.1 { sqlite3session S db main } {S} do_test $tn.1.2 { S delete } {} do_test $tn.1.3 { sqlite3session S db main } {S} do_test $tn.1.4 { S attach t1 } {} do_test $tn.1.5 { S delete } {} do_test $tn.1.6 { sqlite3session S db main } {S} do_test $tn.1.7 { S attach t1 ; S attach t2 ; S attach t3 } {} do_test $tn.1.8 { S attach t1 ; S attach t2 ; S attach t3 } {} do_test $tn.1.9 { S delete } {} do_test $tn.1.10 { sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES('ghi', 'jkl') } } {} do_test $tn.1.11 { S delete } {} if {$tn==1} { do_test $tn.1.12 { sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES('mno', 'pqr') } execsql { UPDATE t1 SET x = 111 WHERE rowid = 1 } execsql { DELETE FROM t1 WHERE rowid = 2 } } {} do_test $tn.1.13 { S changeset S delete } {} } #------------------------------------------------------------------------- # Simple changeset tests. Also test the sqlite3changeset_invert() # function. # do_test $tn.2.1.1 { execsql { DELETE FROM t1 } sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') } execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') } execsql { INSERT INTO t1 VALUES(3, 'Thonburi') } } {} do_changeset_test $tn.2.1.2 S { {INSERT t1 0 X. {} {i 1 t Sukhothai}} {INSERT t1 0 X. {} {i 2 t Ayutthaya}} {INSERT t1 0 X. {} {i 3 t Thonburi}} } do_changeset_invert_test $tn.2.1.3 S { {DELETE t1 0 X. {i 1 t Sukhothai} {}} {DELETE t1 0 X. {i 2 t Ayutthaya} {}} {DELETE t1 0 X. {i 3 t Thonburi} {}} } do_test $tn.2.1.4 { S delete } {} do_test $tn.2.2.1 { sqlite3session S db main S attach t1 execsql { DELETE FROM t1 WHERE 1 } } {} do_changeset_test $tn.2.2.2 S { {DELETE t1 0 X. {i 1 t Sukhothai} {}} {DELETE t1 0 X. {i 2 t Ayutthaya} {}} {DELETE t1 0 X. {i 3 t Thonburi} {}} } do_changeset_invert_test $tn.2.2.3 S { {INSERT t1 0 X. {} {i 1 t Sukhothai}} {INSERT t1 0 X. {} {i 2 t Ayutthaya}} {INSERT t1 0 X. {} {i 3 t Thonburi}} } do_test $tn.2.2.4 { S delete } {} do_test $tn.2.3.1 { execsql { DELETE FROM t1 } sqlite3session S db main execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') } execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') } execsql { INSERT INTO t1 VALUES(3, 'Thonburi') } S attach t1 execsql { UPDATE t1 SET x = 10 WHERE x = 1; UPDATE t1 SET y = 'Surin' WHERE x = 2; UPDATE t1 SET x = 20, y = 'Thapae' WHERE x = 3; } } {} do_changeset_test $tn.2.3.2 S { {INSERT t1 0 X. {} {i 10 t Sukhothai}} {DELETE t1 0 X. {i 1 t Sukhothai} {}} {UPDATE t1 0 X. {i 2 t Ayutthaya} {{} {} t Surin}} {DELETE t1 0 X. {i 3 t Thonburi} {}} {INSERT t1 0 X. {} {i 20 t Thapae}} } do_changeset_invert_test $tn.2.3.3 S { {DELETE t1 0 X. {i 10 t Sukhothai} {}} {INSERT t1 0 X. {} {i 1 t Sukhothai}} {UPDATE t1 0 X. {i 2 t Surin} {{} {} t Ayutthaya}} {INSERT t1 0 X. {} {i 3 t Thonburi}} {DELETE t1 0 X. {i 20 t Thapae} {}} } do_test $tn.2.3.4 { S delete } {} do_test $tn.2.4.1 { sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES(100, 'Bangkok') } execsql { DELETE FROM t1 WHERE x = 100 } } {} do_changeset_test $tn.2.4.2 S {} do_changeset_invert_test $tn.2.4.3 S {} do_test $tn.2.4.4 { S delete } {} #------------------------------------------------------------------------- # Test the application of simple changesets. These tests also test that # the conflict callback is invoked correctly. For these tests, the # conflict callback always returns OMIT. # db close |
︙ | ︙ | |||
185 186 187 188 189 190 191 | proc do_db2_test {testname sql {result {}}} { uplevel do_test $testname [list "execsql {$sql} db2"] [list [list {*}$result]] } # Test INSERT changesets. # | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | < | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | proc do_db2_test {testname sql {result {}}} { uplevel do_test $testname [list "execsql {$sql} db2"] [list [list {*}$result]] } # Test INSERT changesets. # do_test $tn.3.1.0 { execsql { CREATE TABLE t1(a PRIMARY KEY, b NOT NULL) %WR% } db2 execsql { CREATE TABLE t1(a PRIMARY KEY, b) %WR%; INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); } db } {} do_db2_test $tn.3.1.1 "INSERT INTO t1 VALUES(6, 'VI')" do_conflict_test $tn.3.1.2 -tables t1 -sql { INSERT INTO t1 VALUES(3, 'three'); INSERT INTO t1 VALUES(4, 'four'); INSERT INTO t1 VALUES(5, 'five'); INSERT INTO t1 VALUES(6, 'six'); INSERT INTO t1 VALUES(7, 'seven'); INSERT INTO t1 VALUES(8, NULL); } -conflicts { {INSERT t1 CONFLICT {i 6 t six} {i 6 t VI}} {INSERT t1 CONSTRAINT {i 8 n {}}} } do_db2_test $tn.3.1.3 "SELECT * FROM t1 ORDER BY a" { 3 three 4 four 5 five 6 VI 7 seven } do_execsql_test $tn.3.1.4 "SELECT * FROM t1" { 1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 {} } # Test DELETE changesets. # do_execsql_test $tn.3.2.1 { PRAGMA foreign_keys = on; CREATE TABLE t2(a PRIMARY KEY, b)%WR%; CREATE TABLE t3(c, d REFERENCES t2); INSERT INTO t2 VALUES(1, 'one'); INSERT INTO t2 VALUES(2, 'two'); INSERT INTO t2 VALUES(3, 'three'); INSERT INTO t2 VALUES(4, 'four'); } do_db2_test $tn.3.2.2 { PRAGMA foreign_keys = on; CREATE TABLE t2(a PRIMARY KEY, b)%WR%; CREATE TABLE t3(c, d REFERENCES t2); INSERT INTO t2 VALUES(1, 'one'); INSERT INTO t2 VALUES(2, 'two'); INSERT INTO t2 VALUES(4, 'five'); INSERT INTO t3 VALUES('i', 1); } do_conflict_test $tn.3.2.3 -tables t2 -sql { DELETE FROM t2 WHERE a = 1; DELETE FROM t2 WHERE a = 2; DELETE FROM t2 WHERE a = 3; DELETE FROM t2 WHERE a = 4; } -conflicts { {DELETE t2 NOTFOUND {i 3 t three}} {DELETE t2 DATA {i 4 t four} {i 4 t five}} {FOREIGN_KEY 1} } do_execsql_test $tn.3.2.4 "SELECT * FROM t2" {} do_db2_test $tn.3.2.5 "SELECT * FROM t2" {4 five} # Test UPDATE changesets. # do_execsql_test $tn.3.3.1 { CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c))%WR%; INSERT INTO t4 VALUES(1, 2, 3); INSERT INTO t4 VALUES(4, 5, 6); INSERT INTO t4 VALUES(7, 8, 9); INSERT INTO t4 VALUES(10, 11, 12); } do_db2_test $tn.3.3.2 { CREATE TABLE t4(a NOT NULL, b, c, PRIMARY KEY(b, c))%WR%; INSERT INTO t4 VALUES(0, 2, 3); INSERT INTO t4 VALUES(4, 5, 7); INSERT INTO t4 VALUES(7, 8, 9); INSERT INTO t4 VALUES(10, 11, 12); } do_conflict_test $tn.3.3.3 -tables t4 -sql { UPDATE t4 SET a = -1 WHERE b = 2; UPDATE t4 SET a = -1 WHERE b = 5; UPDATE t4 SET a = NULL WHERE c = 9; UPDATE t4 SET a = 'x' WHERE b = 11; } -conflicts { {UPDATE t4 DATA {i 1 i 2 i 3} {i -1 {} {} {} {}} {i 0 i 2 i 3}} {UPDATE t4 NOTFOUND {i 4 i 5 i 6} {i -1 {} {} {} {}}} {UPDATE t4 CONSTRAINT {i 7 i 8 i 9} {n {} {} {} {} {}}} } do_db2_test $tn.3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12} do_execsql_test $tn.3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12} #------------------------------------------------------------------------- # This next block of tests verifies that values returned by the conflict # handler are intepreted correctly. # proc test_reset {} { db close db2 close forcedelete test.db test.db2 sqlite3 db test.db sqlite3 db2 test.db2 } proc xConflict {args} { lappend ::xConflict $args return $::conflict_return } foreach {tn2 conflict_return after} { 1 OMIT {1 2 value1 4 5 7 10 x x} 2 REPLACE {1 2 value1 4 5 value2 10 8 9} } { test_reset do_test $tn.4.$tn2.1 { foreach db {db db2} { execsql { CREATE TABLE t1(a, b, c, PRIMARY KEY(a))%WR%; INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); INSERT INTO t1 VALUES(7, 8, 9); } $db } execsql { REPLACE INTO t1 VALUES(4, 5, 7); REPLACE INTO t1 VALUES(10, 'x', 'x'); } db2 } {} do_conflict_test $tn.4.$tn2.2 -tables t1 -sql { UPDATE t1 SET c = 'value1' WHERE a = 1; -- no conflict UPDATE t1 SET c = 'value2' WHERE a = 4; -- DATA conflict UPDATE t1 SET a = 10 WHERE a = 7; -- CONFLICT conflict } -conflicts { {INSERT t1 CONFLICT {i 10 i 8 i 9} {i 10 t x t x}} {UPDATE t1 DATA {i 4 {} {} i 6} {{} {} {} {} t value2} {i 4 i 5 i 7}} } do_db2_test $tn.4.$tn2.3 "SELECT * FROM t1 ORDER BY a" $after } foreach {tn2 conflict_return} { 1 OMIT 2 REPLACE } { test_reset do_test $tn.5.$tn2.1 { # Create an identical schema in both databases. set schema { CREATE TABLE "'foolish name'"(x, y, z, PRIMARY KEY(x, y))%WR%; } execsql $schema db execsql $schema db2 # Add some rows to [db2]. These rows will cause conflicts later # on when the changeset from [db] is applied to it. execsql { INSERT INTO "'foolish name'" VALUES('one', 'one', 'ii'); INSERT INTO "'foolish name'" VALUES('one', 'two', 'i'); INSERT INTO "'foolish name'" VALUES('two', 'two', 'ii'); } db2 } {} do_conflict_test $tn.5.$tn2.2 -tables {{'foolish name'}} -sql { INSERT INTO "'foolish name'" VALUES('one', 'two', 2); } -conflicts { {INSERT {'foolish name'} CONFLICT {t one t two i 2} {t one t two t i}} } set res(REPLACE) {one one ii one two 2 two two ii} set res(OMIT) {one one ii one two i two two ii} do_db2_test $tn.5.$tn2.3 { SELECT * FROM "'foolish name'" ORDER BY x, y } $res($conflict_return) do_test $tn.5.$tn2.1 { set schema { CREATE TABLE d1("z""z" PRIMARY KEY, y)%WR%; INSERT INTO d1 VALUES(1, 'one'); INSERT INTO d1 VALUES(2, 'two'); } execsql $schema db execsql $schema db2 execsql { UPDATE d1 SET y = 'TWO' WHERE "z""z" = 2; } db2 } {} do_conflict_test $tn.5.$tn2.2 -tables d1 -sql { DELETE FROM d1 WHERE "z""z" = 2; } -conflicts { {DELETE d1 DATA {i 2 t two} {i 2 t TWO}} } set res(REPLACE) {1 one} set res(OMIT) {1 one 2 TWO} do_db2_test $tn.5.$tn2.3 "SELECT * FROM d1" $res($conflict_return) } #------------------------------------------------------------------------- # Test that two tables can be monitored by a single session object. # test_reset set schema { CREATE TABLE t1(a COLLATE nocase PRIMARY KEY, b)%WR%; CREATE TABLE t2(a, b PRIMARY KEY)%WR%; } do_test $tn.6.0 { execsql $schema db execsql $schema db2 execsql { INSERT INTO t1 VALUES('a', 'b'); INSERT INTO t2 VALUES('a', 'b'); } db2 } {} set conflict_return "" do_conflict_test $tn.6.1 -tables {t1 t2} -sql { INSERT INTO t1 VALUES('1', '2'); INSERT INTO t1 VALUES('A', 'B'); INSERT INTO t2 VALUES('A', 'B'); } -conflicts { {INSERT t1 CONFLICT {t A t B} {t a t b}} } do_db2_test $tn.6.2 "SELECT * FROM t1 ORDER BY a" {1 2 a b} do_db2_test $tn.6.3 "SELECT * FROM t2 ORDER BY a" {A B a b} #------------------------------------------------------------------------- # Test that session objects are not confused by changes to table in # other databases. # catch { db2 close } drop_all_tables forcedelete test.db2 do_iterator_test $tn.7.1 * { ATTACH 'test.db2' AS aux; CREATE TABLE main.t1(x PRIMARY KEY, y)%WR%; CREATE TABLE aux.t1(x PRIMARY KEY, y)%WR%; INSERT INTO main.t1 VALUES('one', 1); INSERT INTO main.t1 VALUES('two', 2); INSERT INTO aux.t1 VALUES('three', 3); INSERT INTO aux.t1 VALUES('four', 4); } { {INSERT t1 0 X. {} {t two i 2}} {INSERT t1 0 X. {} {t one i 1}} } #------------------------------------------------------------------------- # Test the sqlite3session_isempty() function. # do_test $tn.8.1 { execsql { CREATE TABLE t5(x PRIMARY KEY, y)%WR%; CREATE TABLE t6(x PRIMARY KEY, y)%WR%; INSERT INTO t5 VALUES('a', 'b'); INSERT INTO t6 VALUES('a', 'b'); } sqlite3session S db main S attach * S isempty } {1} do_test $tn.8.2 { execsql { DELETE FROM t5 } S isempty } {0} do_test $tn.8.3 { S delete sqlite3session S db main S attach t5 execsql { DELETE FROM t5 } S isempty } {1} do_test $tn.8.4 { S delete } {} do_test $tn.8.5 { sqlite3session S db main S attach t5 S attach t6 execsql { INSERT INTO t5 VALUES(1, 2) } S isempty } {0} do_test $tn.8.6 { S delete sqlite3session S db main S attach t5 S attach t6 execsql { INSERT INTO t6 VALUES(1, 2) } S isempty } {0} do_test $tn.8.7 { S delete } {} #------------------------------------------------------------------------- # do_execsql_test $tn.9.1 { CREATE TABLE t7(a, b, c, d, e PRIMARY KEY, f, g)%WR%; INSERT INTO t7 VALUES(1, 1, 1, 1, 1, 1, 1); } do_test $tn.9.2 { sqlite3session S db main S attach * execsql { UPDATE t7 SET b=2, d=2 } } {} do_changeset_test $tn.9.2 S {{UPDATE t7 0 ....X.. {{} {} i 1 {} {} i 1 i 1 {} {} {} {}} {{} {} i 2 {} {} i 2 {} {} {} {} {} {}}}} S delete catch { db2 close } #------------------------------------------------------------------------- # Test a really long table name. # reset_db set tblname [string repeat tblname123 100] do_test $tn.10.1.1 { execsql " CREATE TABLE $tblname (a PRIMARY KEY, b)%WR%; INSERT INTO $tblname VALUES('xyz', 'def'); " sqlite3session S db main S attach $tblname execsql " INSERT INTO $tblname VALUES('uvw', 'abc'); DELETE FROM $tblname WHERE a = 'xyz'; " } {} do_changeset_test $tn.10.1.2 S " {INSERT $tblname 0 X. {} {t uvw t abc}} {DELETE $tblname 0 X. {t xyz t def} {}} " do_test $tn.10.1.4 { S delete } {} #--------------------------------------------------------------- reset_db do_execsql_test $tn.11.1 { CREATE TABLE t1(a, b); } do_test $tn.11.2 { sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES(1, 2); } S changeset } {} S delete #------------------------------------------------------------------------- # Test a really long table name. # reset_db set tblname [string repeat tblname123 100] do_test $tn.10.1.1 { execsql " CREATE TABLE $tblname (a PRIMARY KEY, b)%WR%; INSERT INTO $tblname VALUES('xyz', 'def'); " sqlite3session S db main S attach $tblname execsql " INSERT INTO $tblname VALUES('uvw', 'abc'); DELETE FROM $tblname WHERE a = 'xyz'; " } {} do_changeset_test $tn.10.1.2 S " {INSERT $tblname 0 X. {} {t uvw t abc}} {DELETE $tblname 0 X. {t xyz t def} {}} " do_test $tn.10.1.4 { S delete } {} #------------------------------------------------------------------------- # Test the effect of updating a column from 0.0 to 0.0. # reset_db do_execsql_test $tn.11.1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b REAL)%WR%; INSERT INTO t1 VALUES(1, 0.0); } do_iterator_test $tn.11.2 * { UPDATE t1 SET b = 0.0; } { } reset_db do_execsql_test $tn.12.1 { CREATE TABLE t1(r INTEGER PRIMARY KEY, a, b)%WR%; CREATE INDEX i1 ON t1(a); INSERT INTO t1 VALUES(1, 1, 1); INSERT INTO t1 VALUES(2, 1, 2); INSERT INTO t1 VALUES(3, 1, 3); } do_iterator_test $tn.12.2 * { UPDATE t1 SET b='one' WHERE a=1; } { {UPDATE t1 0 X.. {i 1 {} {} i 1} {{} {} {} {} t one}} {UPDATE t1 0 X.. {i 2 {} {} i 2} {{} {} {} {} t one}} {UPDATE t1 0 X.. {i 3 {} {} i 3} {{} {} {} {} t one}} } }] } finish_test |
Changes to ext/session/session3.test.
︙ | ︙ | |||
59 60 61 62 63 64 65 | do_test 1.2.1 { set ::log {} do_then_apply_sql { INSERT INTO t1 VALUES(5, 6); INSERT INTO t1 VALUES(7, 8); } set ::log | | > > > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | do_test 1.2.1 { set ::log {} do_then_apply_sql { INSERT INTO t1 VALUES(5, 6); INSERT INTO t1 VALUES(7, 8); } set ::log } {} do_test 1.2.2 { db2 eval { SELECT * FROM t1 } } {5 6 {} 7 8 {}} do_test 1.3.0 { execsql { DROP TABLE t1; CREATE TABLE t1(a, b PRIMARY KEY); } db2 } {} |
︙ | ︙ |
Changes to ext/session/sessionE.test.
︙ | ︙ | |||
38 39 40 41 42 43 44 | do_execsql_test 1.0 { CREATE TABLE t1(a, b); CREATE TABLE t2(a PRIMARY KEY, b); } do_test 1.1 { sqlite3session S db main S attach * | < < | 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 | do_execsql_test 1.0 { CREATE TABLE t1(a, b); CREATE TABLE t2(a PRIMARY KEY, b); } do_test 1.1 { sqlite3session S db main S attach * execsql { INSERT INTO t1 VALUES(1, 2); INSERT INTO t2 VALUES(1, 2); } } {} do_changeset_test 1.2 S { {INSERT t2 0 X. {} {i 1 i 2}} } S delete reset_db do_execsql_test 2.0 { CREATE TABLE t1(a, b); CREATE TABLE t2(a PRIMARY KEY, b); } do_test 2.1 { sqlite3session S db main S attach t1 S attach t2 execsql { INSERT INTO t1 VALUES(3, 4); INSERT INTO t2 VALUES(3, 4); INSERT INTO t1 VALUES(5, 6); INSERT INTO t2 VALUES(5, 6); } } {} |
︙ | ︙ |
Changes to ext/session/session_common.tcl.
︙ | ︙ | |||
29 30 31 32 33 34 35 | sqlite3session_foreach c [set changeset] { lappend x [set c] } set x }]] [list $r] } proc do_conflict_test {tn args} { | < < < < < < > > > > > > > | 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 | sqlite3session_foreach c [set changeset] { lappend x [set c] } set x }]] [list $r] } proc do_conflict_test {tn args} { set O(-tables) [list] set O(-sql) [list] set O(-conflicts) [list] set O(-policy) "OMIT" array set V $args foreach key [array names V] { if {![info exists O($key)]} {error "no such option: $key"} } array set O $args proc xConflict {args} [subst -nocommands { lappend ::xConflict [set args] return $O(-policy) }] proc bgerror {args} { set ::background_error $args } sqlite3session S db main foreach t $O(-tables) { S attach $t } execsql $O(-sql) set ::xConflict [list] sqlite3changeset_apply db2 [S changeset] xConflict |
︙ | ︙ |
Added ext/session/session_speed_test.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 | /* ** 2017 January 31 ** ** 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 source code for a standalone program used to ** test the performance of the sessions module. Compile and run: ** ** ./session_speed_test -help ** ** for details. */ #include "sqlite3.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stddef.h> #include <unistd.h> /************************************************************************* ** Start of generic command line parser. */ #define CMDLINE_BARE 0 #define CMDLINE_INTEGER 1 #define CMDLINE_STRING 2 #define CMDLINE_BOOLEAN 3 typedef struct CmdLineOption CmdLineOption; struct CmdLineOption { const char *zText; /* Name of command line option */ const char *zHelp; /* Help text for option */ int eType; /* One of the CMDLINE_* values */ int iOff; /* Offset of output variable */ }; #define CMDLINE_INT32(x,y,z) {x, y, CMDLINE_INTEGER, z} #define CMDLINE_BOOL(x,y,z) {x, y, CMDLINE_BOOLEAN, z} #define CMDLINE_TEXT(x,y,z) {x, y, CMDLINE_STRING, z} #define CMDLINE_NONE(x,y,z) {x, y, CMDLINE_BARE, z} static void option_requires_argument_error(CmdLineOption *pOpt){ fprintf(stderr, "Option requires a%s argument: %s\n", pOpt->eType==CMDLINE_INTEGER ? "n integer" : pOpt->eType==CMDLINE_STRING ? " string" : " boolean", pOpt->zText ); exit(1); } static void ambiguous_option_error(const char *zArg){ fprintf(stderr, "Option is ambiguous: %s\n", zArg); exit(1); } static void unknown_option_error( const char *zArg, CmdLineOption *aOpt, const char *zHelp ){ int i; fprintf(stderr, "Unknown option: %s\n", zArg); fprintf(stderr, "\nOptions are:\n"); fprintf(stderr, " % -30sEcho command line options\n", "-cmdline:verbose"); for(i=0; aOpt[i].zText; i++){ int eType = aOpt[i].eType; char *zOpt = sqlite3_mprintf("%s %s", aOpt[i].zText, eType==CMDLINE_BARE ? "" : eType==CMDLINE_INTEGER ? "N" : eType==CMDLINE_BOOLEAN ? "BOOLEAN" : "TEXT" ); fprintf(stderr, " % -30s%s\n", zOpt, aOpt[i].zHelp); sqlite3_free(zOpt); } if( zHelp ){ fprintf(stderr, "\n%s\n", zHelp); } exit(1); } static int get_integer_option(CmdLineOption *pOpt, const char *zArg){ int i = 0; int iRet = 0; int bSign = 1; if( zArg[0]=='-' ){ bSign = -1; i = 1; } while( zArg[i] ){ if( zArg[i]<'0' || zArg[i]>'9' ) option_requires_argument_error(pOpt); iRet = iRet*10 + (zArg[i] - '0'); i++; } return (iRet*bSign); } static int get_boolean_option(CmdLineOption *pOpt, const char *zArg){ if( 0==sqlite3_stricmp(zArg, "true") ) return 1; if( 0==sqlite3_stricmp(zArg, "1") ) return 1; if( 0==sqlite3_stricmp(zArg, "0") ) return 0; if( 0==sqlite3_stricmp(zArg, "false") ) return 0; option_requires_argument_error(pOpt); return 0; } static void parse_command_line( int argc, char **argv, int iStart, CmdLineOption *aOpt, void *pStruct, const char *zHelp ){ char *pOut = (char*)pStruct; int bVerbose = 0; int iArg; for(iArg=iStart; iArg<argc; iArg++){ const char *zArg = argv[iArg]; int nArg = strlen(zArg); int nMatch = 0; int iOpt; for(iOpt=0; aOpt[iOpt].zText; iOpt++){ CmdLineOption *pOpt = &aOpt[iOpt]; if( 0==sqlite3_strnicmp(pOpt->zText, zArg, nArg) ){ if( nMatch ){ ambiguous_option_error(zArg); } nMatch++; if( pOpt->eType==CMDLINE_BARE ){ *(int*)(&pOut[pOpt->iOff]) = 1; }else{ iArg++; if( iArg==argc ){ option_requires_argument_error(pOpt); } switch( pOpt->eType ){ case CMDLINE_INTEGER: *(int*)(&pOut[pOpt->iOff]) = get_integer_option(pOpt, argv[iArg]); break; case CMDLINE_STRING: *(const char**)(&pOut[pOpt->iOff]) = argv[iArg]; break; case CMDLINE_BOOLEAN: *(int*)(&pOut[pOpt->iOff]) = get_boolean_option(pOpt, argv[iArg]); break; } } } } if( nMatch==0 && 0==sqlite3_strnicmp("-cmdline:verbose", zArg, nArg) ){ bVerbose = 1; nMatch = 1; } if( nMatch==0 ){ unknown_option_error(zArg, aOpt, zHelp); } } if( bVerbose ){ int iOpt; fprintf(stdout, "Options are: "); for(iOpt=0; aOpt[iOpt].zText; iOpt++){ CmdLineOption *pOpt = &aOpt[iOpt]; if( pOpt->eType!=CMDLINE_BARE || *(int*)(&pOut[pOpt->iOff]) ){ fprintf(stdout, "%s ", pOpt->zText); } switch( pOpt->eType ){ case CMDLINE_INTEGER: fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff])); break; case CMDLINE_BOOLEAN: fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff])); break; case CMDLINE_STRING: fprintf(stdout, "%s ", *(const char**)(&pOut[pOpt->iOff])); break; } } fprintf(stdout, "\n"); } } /* ** End of generic command line parser. *************************************************************************/ static void abort_due_to_error(int rc){ fprintf(stderr, "Error: %d\n"); exit(-1); } static void execsql(sqlite3 *db, const char *zSql){ int rc = sqlite3_exec(db, zSql, 0, 0, 0); if( rc!=SQLITE_OK ) abort_due_to_error(rc); } static int xConflict(void *pCtx, int eConflict, sqlite3_changeset_iter *p){ return SQLITE_CHANGESET_ABORT; } static void run_test( sqlite3 *db, sqlite3 *db2, int nRow, const char *zSql ){ sqlite3_session *pSession = 0; sqlite3_stmt *pStmt = 0; int rc; int i; int nChangeset; void *pChangeset; /* Attach a session object to database db */ rc = sqlite3session_create(db, "main", &pSession); if( rc!=SQLITE_OK ) abort_due_to_error(rc); /* Configure the session to capture changes on all tables */ rc = sqlite3session_attach(pSession, 0); if( rc!=SQLITE_OK ) abort_due_to_error(rc); /* Prepare the SQL statement */ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); if( rc!=SQLITE_OK ) abort_due_to_error(rc); /* Open a transaction */ execsql(db, "BEGIN"); /* Execute the SQL statement nRow times */ for(i=0; i<nRow; i++){ sqlite3_bind_int(pStmt, 1, i); sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ) abort_due_to_error(rc); } sqlite3_finalize(pStmt); /* Extract a changeset from the sessions object */ rc = sqlite3session_changeset(pSession, &nChangeset, &pChangeset); if( rc!=SQLITE_OK ) abort_due_to_error(rc); execsql(db, "COMMIT"); /* Apply the changeset to the second db */ rc = sqlite3changeset_apply(db2, nChangeset, pChangeset, 0, xConflict, 0); if( rc!=SQLITE_OK ) abort_due_to_error(rc); /* Cleanup */ sqlite3_free(pChangeset); sqlite3session_delete(pSession); } int main(int argc, char **argv){ struct Options { int nRow; int bWithoutRowid; int bInteger; int bAll; const char *zDb; }; struct Options o = { 2500, 0, 0, 0, "session_speed_test.db" }; CmdLineOption aOpt[] = { CMDLINE_INT32( "-rows", "number of rows in test", offsetof(struct Options, nRow) ), CMDLINE_BOOL("-without-rowid", "use WITHOUT ROWID tables", offsetof(struct Options, bWithoutRowid) ), CMDLINE_BOOL("-integer", "use integer data (instead of text/blobs)", offsetof(struct Options, bInteger) ), CMDLINE_NONE("-all", "Run all 4 combos of -without-rowid and -integer", offsetof(struct Options, bAll) ), CMDLINE_TEXT("-database", "prefix for database files to use", offsetof(struct Options, zDb) ), {0, 0, 0, 0} }; const char *azCreate[] = { "CREATE TABLE t1(a PRIMARY KEY, b, c, d)", "CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID", }; const char *azInsert[] = { "INSERT INTO t1 VALUES(" "printf('%.8d',?), randomblob(50), randomblob(50), randomblob(50))", "INSERT INTO t1 VALUES(?, random(), random(), random())" }; const char *azUpdate[] = { "UPDATE t1 SET d = randomblob(50) WHERE a = printf('%.8d',?)", "UPDATE t1 SET d = random() WHERE a = ?" }; const char *azDelete[] = { "DELETE FROM t1 WHERE a = printf('%.8d',?)", "DELETE FROM t1 WHERE a = ?" }; int rc; sqlite3 *db; sqlite3 *db2; char *zDb2; int bWithoutRowid; int bInteger; parse_command_line(argc, argv, 1, aOpt, (void*)&o, "This program creates two new, empty, databases each containing a single\n" "table. It then does the following:\n\n" " 1. Inserts -rows rows into the first database\n" " 2. Updates each row in the first db\n" " 3. Delete each row from the first db\n\n" "The modifications made by each step are captured in a changeset and\n" "applied to the second database.\n" ); zDb2 = sqlite3_mprintf("%s2", o.zDb); for(bWithoutRowid=0; bWithoutRowid<2; bWithoutRowid++){ for(bInteger=0; bInteger<2; bInteger++){ if( o.bAll || (o.bWithoutRowid==bWithoutRowid && o.bInteger==bInteger) ){ fprintf(stdout, "Testing %s data with %s table\n", bInteger ? "integer" : "blob/text", bWithoutRowid ? "WITHOUT ROWID" : "rowid" ); /* Open new database handles on two empty databases */ unlink(o.zDb); rc = sqlite3_open(o.zDb, &db); if( rc!=SQLITE_OK ) abort_due_to_error(rc); unlink(zDb2); rc = sqlite3_open(zDb2, &db2); if( rc!=SQLITE_OK ) abort_due_to_error(rc); /* Create the schema in both databases. */ execsql(db, azCreate[o.bWithoutRowid]); execsql(db2, azCreate[o.bWithoutRowid]); /* Run the three tests */ run_test(db, db2, o.nRow, azInsert[o.bInteger]); run_test(db, db2, o.nRow, azUpdate[o.bInteger]); run_test(db, db2, o.nRow, azDelete[o.bInteger]); /* Close the db handles */ sqlite3_close(db); sqlite3_close(db2); } } } return 0; } |
Added ext/session/sessionat.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 | # 2017 February 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 the sessions module. Specifically, that a changeset can # be applied after ALTER TABLE ADD COLUMN has been used to add # columns to tables. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix sessionat db close sqlite3_shutdown test_sqlite3_log log proc log {code msg} { lappend ::log $code $msg } proc reset_test {} { catch { db close } catch { db2 close } forcedelete test.db test.db2 sqlite3 db test.db sqlite3 db2 test.db2 } # Run all tests in this file twice. Once with "WITHOUT ROWID", and once # with regular rowid tables. # # ?.1.*: Test that PK inconsistencies are detected if one or more of the PK # columns are not present in the changeset. # # ?.2.*: Test that it is not possible to apply a changeset with N columns # to a db with fewer than N columns. # # ?.3.*: Test some INSERT, UPDATE and DELETE operations that do not # require conflict handling. # # ?.4.*: Test some INSERT, UPDATE and DELETE operations that do require # conflict handling. # # ?.5.*: Test that attempting to concat two changesets with different # numbers of columns for the same table is an error. # foreach {tn trailing} { sessionat-ipk "" sessionat-wor " WITHOUT ROWID " } { eval [string map [list %WR% $trailing] { reset_test #----------------------------------------------------------------------- do_execsql_test $tn.1.0 { CREATE TABLE t1(a, b, PRIMARY KEY(a)) %WR%; } do_execsql_test -db db2 $tn.1.1 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c)) %WR%; } do_test $tn.1.2 { set ::log {} do_then_apply_sql { INSERT INTO t1 VALUES('one', 'two') } set ::log } [list \ SQLITE_SCHEMA {sqlite3changeset_apply(): primary key mismatch for table t1} ] do_execsql_test $tn.1.3 { SELECT * FROM t1 } {one two} do_execsql_test -db db2 $tn.1.4 { SELECT * FROM t1 } {} #----------------------------------------------------------------------- do_execsql_test $tn.2.0 { CREATE TABLE t2(x, y, z, PRIMARY KEY(x)) %WR%; } do_execsql_test -db db2 $tn.2.1 { CREATE TABLE t2(x, y, PRIMARY KEY(x)) %WR%; } do_test $tn.2.2 { db cache flush set ::log {} do_then_apply_sql { INSERT INTO t2 VALUES(1, 2, 3) } set ::log } [list SQLITE_SCHEMA \ {sqlite3changeset_apply(): table t2 has 2 columns, expected 3 or more} ] do_execsql_test $tn.2.3 { SELECT * FROM t2 } {1 2 3} do_execsql_test -db db2 $tn.2.4 { SELECT * FROM t2 } {} #----------------------------------------------------------------------- do_execsql_test $tn.3.0 { CREATE TABLE t3(a, b, PRIMARY KEY(b)) %WR%; } do_execsql_test -db db2 $tn.3.1 { CREATE TABLE t3(a, b, c DEFAULT 'D', PRIMARY KEY(b)) %WR%; } do_test $tn.3.2 { do_then_apply_sql { INSERT INTO t3 VALUES(1, 2); INSERT INTO t3 VALUES(3, 4); INSERT INTO t3 VALUES(5, 6); }; db2 eval {SELECT * FROM t3} } {1 2 D 3 4 D 5 6 D} do_test $tn.3.3 { do_then_apply_sql { UPDATE t3 SET a=45 WHERE b=4; DELETE FROM t3 WHERE a=5; }; db2 eval {SELECT * FROM t3} } {1 2 D 45 4 D} #----------------------------------------------------------------------- # 4.1: INSERT statements # 4.2: DELETE statements # 4.3: UPDATE statements # do_execsql_test $tn.4.1.0 { CREATE TABLE t4(x INTEGER PRIMARY KEY, y) %WR%; } do_execsql_test -db db2 $tn.4.1.1 { CREATE TABLE t4(x INTEGER PRIMARY KEY, y, z) %WR%; INSERT INTO t4 VALUES(1, 2, 3); INSERT INTO t4 VALUES(4, 5, 6); } do_conflict_test $tn.4.1.2 -tables t4 -sql { INSERT INTO t4 VALUES(10, 20); INSERT INTO t4 VALUES(4, 11); } -conflicts { {INSERT t4 CONFLICT {i 4 i 11} {i 4 i 5}} } do_execsql_test -db db2 $tn.4.1.3 { SELECT * FROM t4 ORDER BY x } {1 2 3 4 5 6 10 20 {}} do_conflict_test $tn.4.1.4 -policy REPLACE -tables t4 -sql { INSERT INTO t4 VALUES(1, 11); } -conflicts { {INSERT t4 CONFLICT {i 1 i 11} {i 1 i 2}} } do_execsql_test -db db2 $tn.4.1.5 { SELECT * FROM t4 ORDER BY x } {1 11 {} 4 5 6 10 20 {}} do_execsql_test $tn.4.2.0 { DELETE FROM t4; INSERT INTO t4 VALUES(1, 'A'); INSERT INTO t4 VALUES(2, 'B'); INSERT INTO t4 VALUES(3, 'C'); INSERT INTO t4 VALUES(4, 'D'); } do_execsql_test -db db2 $tn.4.2.1 { DELETE FROM t4; INSERT INTO t4 VALUES(1, 'A', 'a'); INSERT INTO t4 VALUES(3, 'C', 'c'); INSERT INTO t4 VALUES(4, 'E', 'd'); } do_conflict_test $tn.4.2.2 -tables t4 -sql { DELETE FROM t4 WHERE x=2; DELETE FROM t4 WHERE x=4; } -conflicts { {DELETE t4 NOTFOUND {i 2 t B}} {DELETE t4 DATA {i 4 t D} {i 4 t E}} } do_execsql_test $tn.4.3.0 { CREATE TABLE t5(a, b, c PRIMARY KEY) %WR%; INSERT INTO t5 VALUES(1,1,1), (2,2,2), (3,3,3), (4,4,4); } do_execsql_test -db db2 $tn.4.3.1 { CREATE TABLE t5(a, b, c PRIMARY KEY, d CHECK(b!=10)) %WR%; INSERT INTO t5 VALUES (2,2,2,2), (3,8,3,3), (4,4,4,4); } do_conflict_test $tn.4.3.2 -tables t5 -sql { UPDATE t5 SET a=4 WHERE c=1; UPDATE t5 SET b=9 WHERE c=3; UPDATE t5 SET b=10 WHERE c=2; } -conflicts { {UPDATE t5 NOTFOUND {i 1 {} {} i 1} {i 4 {} {} {} {}}} {UPDATE t5 DATA {{} {} i 3 i 3} {{} {} i 9 {} {}} {i 3 i 8 i 3}} {UPDATE t5 CONSTRAINT {{} {} i 2 i 2} {{} {} i 10 {} {}}} } #----------------------------------------------------------------------- do_execsql_test $tn.5.0 { CREATE TABLE t6(a, b, c, PRIMARY KEY(a, b)) %WR%; } do_execsql_test -db db2 $tn.5.1 { CREATE TABLE t6(a, b, c, d, e, PRIMARY KEY(a, b)) %WR%; } do_test $tn.5.2 { set c1 [sql_exec_changeset db { INSERT INTO t6 VALUES(1, 1, 1); INSERT INTO t6 VALUES(2, 2, 2); }] set c2 [sql_exec_changeset db2 { INSERT INTO t6 VALUES(3, 3, 3, 3, 3); INSERT INTO t6 VALUES(4, 4, 4, 4, 4); }] list [catch { sqlite3changeset_concat $c1 $c2} msg] $msg } {1 SQLITE_SCHEMA} #----------------------------------------------------------------------- db2 close sqlite3 db2 test.db do_execsql_test $tn.6.0 { CREATE TABLE t7(a INTEGER PRIMARY KEY, b) %WR%; INSERT INTO t7 VALUES(1, 1); INSERT INTO t7 VALUES(2, 2); INSERT INTO t7 VALUES(3, 3); } do_test $tn.6.1 { set c1 [sql_exec_changeset db { INSERT INTO t7 VALUES(4, 4); DELETE FROM t7 WHERE a=1; UPDATE t7 SET b=222 WHERE a=2; }] set cinv [sqlite3changeset_invert $c1] execsql { SELECT * FROM t7 } } {2 222 3 3 4 4} do_execsql_test -db db2 $tn.6.2 { ALTER TABLE t7 ADD COLUMN c DEFAULT 'ccc' } proc xConfict {args} { return "OMIT" } do_test $tn.6.3 { sqlite3changeset_apply db $cinv xConflict execsql { SELECT * FROM t7 } } {1 1 ccc 2 2 ccc 3 3 ccc} }] } finish_test |
Added ext/session/sessiondiff.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 | # 2015-07-31 # # 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 [sqldiff --changeset] command. # # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix sessiondiff set PROG [test_find_sqldiff] db close proc sqlesc {id} { set ret "'[string map {' ''} $id]'" set ret } proc database_cksum {db1} { set txt "" sqlite3 dbtmp $db1 foreach tbl [dbtmp eval {SELECT name FROM sqlite_master WHERE type='table'}] { set cols [list] dbtmp eval "PRAGMA table_info = [sqlesc $tbl]" { lappend cols "quote( $name )" } append txt [dbtmp eval \ "SELECT [join $cols {||'.'||}] FROM [sqlesc $tbl] ORDER BY 1" ] } dbtmp close md5 $txt } proc readfile {filename} { set fd [open $filename] fconfigure $fd -translation binary -encoding binary set data [read $fd] close $fd set data } proc get_changeset {db1 db2} { exec $::PROG --changeset changeset.bin $db1 $db2 set bin [readfile changeset.bin] return $bin } proc xConflict {args} { return "" } proc do_changeset_test {tn sql1 sql2} { forcedelete test.db123 test.db124 sqlite3 db test.db123 db eval $sql1 db close sqlite3 db test.db124 db eval $sql2 set cs [get_changeset test.db124 test.db123] sqlite3changeset_apply db $cs xConflict db close set database_cksum1 [database_cksum test.db123] set database_cksum2 [database_cksum test.db124] uplevel [list \ do_test $tn [list string compare $database_cksum1 $database_cksum2] 0 ] } do_changeset_test 1.0 { CREATE TABLE t1(x PRIMARY KEY); } { CREATE TABLE t1(x PRIMARY KEY); } do_changeset_test 1.1 { CREATE TABLE t1(x PRIMARY KEY); CREATE TABLE t2(x PRIMARY KEY, y); INSERT INTO t2 VALUES(1, 2); } { CREATE TABLE t1(x PRIMARY KEY); CREATE TABLE t2(x PRIMARY KEY, y); INSERT INTO t2 VALUES(3, 4); } do_changeset_test 1.2 { CREATE TABLE t2(a, b, c, PRIMARY KEY(b, c)); INSERT INTO t2 VALUES(1, 2, 3); INSERT INTO t2 VALUES(4, 5, 6); } { CREATE TABLE t2(a, b, c, PRIMARY KEY(b, c)); INSERT INTO t2 VALUES(1, 2, 11); INSERT INTO t2 VALUES(7, 8, 9); } finish_test |
Added ext/session/sessionwor.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 | # 2017 Jan 31 # # 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 session module. Specifically, # testing support for WITHOUT ROWID tables. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl ifcapable !session {finish_test; return} set testprefix sessionwor proc test_reset {} { catch { db close } catch { db2 close } forcedelete test.db test.db2 sqlite3 db test.db sqlite3 db2 test.db2 } do_execsql_test 1.0 { CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT ROWID; } do_iterator_test 1.1 t1 { INSERT INTO t1 VALUES('one', 'two'); } { {INSERT t1 0 X. {} {t one t two}} } do_iterator_test 1.2 t1 { UPDATE t1 SET b='three' } { {UPDATE t1 0 X. {t one t two} {{} {} t three}} } do_iterator_test 1.3 t1 { DELETE FROM t1; } { {DELETE t1 0 X. {t one t three} {}} } finish_test |
Changes to ext/session/sqlite3session.c.
︙ | ︙ | |||
370 371 372 373 374 375 376 | } n = sqlite3_value_bytes(pValue); if( z==0 && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; nVarint = sessionVarintLen(n); if( aBuf ){ sessionVarintPut(&aBuf[1], n); | | < < | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | } n = sqlite3_value_bytes(pValue); if( z==0 && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; nVarint = sessionVarintLen(n); if( aBuf ){ sessionVarintPut(&aBuf[1], n); if( n ) memcpy(&aBuf[nVarint + 1], z, n); } nByte = 1 + nVarint + n; break; } } }else{ |
︙ | ︙ | |||
1788 1789 1790 1791 1792 1793 1794 | */ static void sessionAppendBlob( SessionBuffer *p, const u8 *aBlob, int nBlob, int *pRc ){ | | | 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 | */ static void sessionAppendBlob( SessionBuffer *p, const u8 *aBlob, int nBlob, int *pRc ){ if( nBlob>0 && 0==sessionBufferGrow(p, nBlob, pRc) ){ memcpy(&p->aBuf[p->nBuf], aBlob, nBlob); p->nBuf += nBlob; } } /* ** This function is a no-op if *pRc is other than SQLITE_OK when it is |
︙ | ︙ | |||
1974 1975 1976 1977 1978 1979 1980 | } } bChanged = 1; break; } default: { | | | | | | | 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 | } } bChanged = 1; break; } default: { int n; int nHdr = 1 + sessionVarintGet(&pCsr[1], &n); assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); nAdvance = nHdr + n; if( eType==sqlite3_column_type(pStmt, i) && n==sqlite3_column_bytes(pStmt, i) && (n==0 || 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), n)) ){ break; } bChanged = 1; } } |
︙ | ︙ | |||
2834 2835 2836 2837 2838 2839 2840 | return SQLITE_DONE; } sessionDiscardData(&p->in); p->in.iCurrent = p->in.iNext; op = p->in.aData[p->in.iNext++]; | | > | 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 | return SQLITE_DONE; } sessionDiscardData(&p->in); p->in.iCurrent = p->in.iNext; op = p->in.aData[p->in.iNext++]; while( op=='T' || op=='P' ){ p->bPatchset = (op=='P'); if( sessionChangesetReadTblhdr(p) ) return p->rc; if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; p->in.iCurrent = p->in.iNext; if( p->in.iNext>=p->in.nData ) return SQLITE_DONE; op = p->in.aData[p->in.iNext++]; } p->op = op; p->bIndirect = p->in.aData[p->in.iNext++]; if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ return (p->rc = SQLITE_CORRUPT_BKPT); |
︙ | ︙ | |||
3026 3027 3028 3029 3030 3031 3032 | sqlite3_changeset_iter *pIter, /* Changeset iterator */ int iVal, /* Index of conflict record value to fetch */ sqlite3_value **ppValue /* OUT: Value from conflicting row */ ){ if( !pIter->pConflict ){ return SQLITE_MISUSE; } | | | 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 | sqlite3_changeset_iter *pIter, /* Changeset iterator */ int iVal, /* Index of conflict record value to fetch */ sqlite3_value **ppValue /* OUT: Value from conflicting row */ ){ if( !pIter->pConflict ){ return SQLITE_MISUSE; } if( iVal<0 || iVal>=pIter->nCol ){ return SQLITE_RANGE; } *ppValue = sqlite3_column_value(pIter->pConflict, iVal); return SQLITE_OK; } /* |
︙ | ︙ | |||
3493 3494 3495 3496 3497 3498 3499 | ){ int rc = SQLITE_OK; int i; SessionBuffer buf = {0, 0, 0}; sessionAppendStr(&buf, "INSERT INTO main.", &rc); sessionAppendIdent(&buf, zTab, &rc); | | > > > > > > | 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 | ){ int rc = SQLITE_OK; int i; SessionBuffer buf = {0, 0, 0}; sessionAppendStr(&buf, "INSERT INTO main.", &rc); sessionAppendIdent(&buf, zTab, &rc); sessionAppendStr(&buf, "(", &rc); for(i=0; i<p->nCol; i++){ if( i!=0 ) sessionAppendStr(&buf, ", ", &rc); sessionAppendIdent(&buf, p->azCol[i], &rc); } sessionAppendStr(&buf, ") VALUES(?", &rc); for(i=1; i<p->nCol; i++){ sessionAppendStr(&buf, ", ?", &rc); } sessionAppendStr(&buf, ")", &rc); if( rc==SQLITE_OK ){ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0); |
︙ | ︙ | |||
4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 | if( zTab==0 ){ rc = SQLITE_NOMEM; break; } nTab = (int)strlen(zTab); sApply.azCol = (const char **)zTab; }else{ sqlite3changeset_pk(pIter, &abPK, 0); rc = sessionTableInfo( db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK ); if( rc!=SQLITE_OK ) break; if( sApply.nCol==0 ){ schemaMismatch = 1; sqlite3_log(SQLITE_SCHEMA, "sqlite3changeset_apply(): no such table: %s", zTab ); } | > > > > > > | | > | | > | | | | | | > | 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 | if( zTab==0 ){ rc = SQLITE_NOMEM; break; } nTab = (int)strlen(zTab); sApply.azCol = (const char **)zTab; }else{ int nMinCol = 0; int i; sqlite3changeset_pk(pIter, &abPK, 0); rc = sessionTableInfo( db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK ); if( rc!=SQLITE_OK ) break; for(i=0; i<sApply.nCol; i++){ if( sApply.abPK[i] ) nMinCol = i+1; } if( sApply.nCol==0 ){ schemaMismatch = 1; sqlite3_log(SQLITE_SCHEMA, "sqlite3changeset_apply(): no such table: %s", zTab ); } else if( sApply.nCol<nCol ){ schemaMismatch = 1; sqlite3_log(SQLITE_SCHEMA, "sqlite3changeset_apply(): table %s has %d columns, " "expected %d or more", zTab, sApply.nCol, nCol ); } else if( nCol<nMinCol || memcmp(sApply.abPK, abPK, nCol)!=0 ){ schemaMismatch = 1; sqlite3_log(SQLITE_SCHEMA, "sqlite3changeset_apply(): " "primary key mismatch for table %s", zTab ); } else{ sApply.nCol = nCol; if((rc = sessionSelectRow(db, zTab, &sApply)) || (rc = sessionUpdateRow(db, zTab, &sApply)) || (rc = sessionDeleteRow(db, zTab, &sApply)) || (rc = sessionInsertRow(db, zTab, &sApply)) ){ break; } } nTab = sqlite3Strlen30(zTab); } } /* If there is a schema mismatch on the current table, proceed to the ** next change. A log message has already been issued. */ |
︙ | ︙ |
Changes to ext/session/sqlite3session.h.
︙ | ︙ | |||
315 316 317 318 319 320 321 | ** <li> For each row (primary key) that exists in the to-table but not in ** the from-table, an INSERT record is added to the session object. ** ** <li> For each row (primary key) that exists in the to-table but not in ** the from-table, a DELETE record is added to the session object. ** ** <li> For each row (primary key) that exists in both tables, but features | | > | 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | ** <li> For each row (primary key) that exists in the to-table but not in ** the from-table, an INSERT record is added to the session object. ** ** <li> For each row (primary key) that exists in the to-table but not in ** the from-table, a DELETE record is added to the session object. ** ** <li> For each row (primary key) that exists in both tables, but features ** different non-PK values in each, an UPDATE record is added to the ** session. ** </ul> ** ** To clarify, if this function is called and then a changeset constructed ** using [sqlite3session_changeset()], then after applying that changeset to ** database zFrom the contents of the two compatible tables would be ** identical. ** |
︙ | ︙ | |||
900 901 902 903 904 905 906 | ** For each table that is not excluded by the filter callback, this function ** tests that the target database contains a compatible table. A table is ** considered compatible if all of the following are true: ** ** <ul> ** <li> The table has the same name as the name recorded in the ** changeset, and | | | 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 | ** For each table that is not excluded by the filter callback, this function ** tests that the target database contains a compatible table. A table is ** considered compatible if all of the following are true: ** ** <ul> ** <li> The table has the same name as the name recorded in the ** changeset, and ** <li> The table has at least as many columns as recorded in the ** changeset, and ** <li> The table has primary key columns in the same position as ** recorded in the changeset. ** </ul> ** ** If there is no compatible table, it is not an error, but none of the ** changes associated with the table are applied. A warning message is issued |
︙ | ︙ | |||
945 946 947 948 949 950 951 | ** original row values stored in the changeset. If it does, and the values ** stored in all non-primary key columns also match the values stored in ** the changeset the row is deleted from the target database. ** ** If a row with matching primary key values is found, but one or more of ** the non-primary key fields contains a value different from the original ** row value stored in the changeset, the conflict-handler function is | | > > > > | > > | | | | | | 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 | ** original row values stored in the changeset. If it does, and the values ** stored in all non-primary key columns also match the values stored in ** the changeset the row is deleted from the target database. ** ** If a row with matching primary key values is found, but one or more of ** the non-primary key fields contains a value different from the original ** row value stored in the changeset, the conflict-handler function is ** invoked with [SQLITE_CHANGESET_DATA] as the second argument. If the ** database table has more columns than are recorded in the changeset, ** only the values of those non-primary key fields are compared against ** the current database contents - any trailing database table columns ** are ignored. ** ** If no row with matching primary key values is found in the database, ** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND] ** passed as the second argument. ** ** If the DELETE operation is attempted, but SQLite returns SQLITE_CONSTRAINT ** (which can only happen if a foreign key constraint is violated), the ** conflict-handler function is invoked with [SQLITE_CHANGESET_CONSTRAINT] ** passed as the second argument. This includes the case where the DELETE ** operation is attempted because an earlier call to the conflict handler ** function returned [SQLITE_CHANGESET_REPLACE]. ** ** <dt>INSERT Changes<dd> ** For each INSERT change, an attempt is made to insert the new row into ** the database. If the changeset row contains fewer fields than the ** database table, the trailing fields are populated with their default ** values. ** ** If the attempt to insert the row fails because the database already ** contains a row with the same primary key values, the conflict handler ** function is invoked with the second argument set to ** [SQLITE_CHANGESET_CONFLICT]. ** ** If the attempt to insert the row fails because of some other constraint ** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is ** invoked with the second argument set to [SQLITE_CHANGESET_CONSTRAINT]. ** This includes the case where the INSERT operation is re-attempted because ** an earlier call to the conflict handler function returned ** [SQLITE_CHANGESET_REPLACE]. ** ** <dt>UPDATE Changes<dd> ** For each UPDATE change, this function checks if the target database ** contains a row with the same primary key value (or values) as the ** original row values stored in the changeset. If it does, and the values ** stored in all modified non-primary key columns also match the values ** stored in the changeset the row is updated within the target database. ** ** If a row with matching primary key values is found, but one or more of ** the modified non-primary key fields contains a value different from an ** original row value stored in the changeset, the conflict-handler function ** is invoked with [SQLITE_CHANGESET_DATA] as the second argument. Since ** UPDATE changes only contain values for non-primary key fields that are ** to be modified, only those fields need to match the original values to ** avoid the SQLITE_CHANGESET_DATA conflict-handler callback. ** ** If no row with matching primary key values is found in the database, ** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND] ** passed as the second argument. |
︙ | ︙ |
Changes to ext/userauth/sqlite3userauth.h.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** end of an SQLite amalgamation header file ("sqlite3.h"), then add ** the SQLITE_USER_AUTHENTICATION compile-time option. See the ** user-auth.txt file in the same source directory as this file for ** additional information. */ #ifdef SQLITE_USER_AUTHENTICATION /* ** If a database contains the SQLITE_USER table, then the ** sqlite3_user_authenticate() interface must be invoked with an ** appropriate username and password prior to enable read and write ** access to the database. ** ** Return SQLITE_OK on success or SQLITE_ERROR if the username/password | > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | ** end of an SQLite amalgamation header file ("sqlite3.h"), then add ** the SQLITE_USER_AUTHENTICATION compile-time option. See the ** user-auth.txt file in the same source directory as this file for ** additional information. */ #ifdef SQLITE_USER_AUTHENTICATION #ifdef __cplusplus extern "C" { #endif /* ** If a database contains the SQLITE_USER table, then the ** sqlite3_user_authenticate() interface must be invoked with an ** appropriate username and password prior to enable read and write ** access to the database. ** ** Return SQLITE_OK on success or SQLITE_ERROR if the username/password |
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 | ** the database cannot be converted into a no-authentication-required ** database. */ int sqlite3_user_delete( sqlite3 *db, /* Database connection */ const char *zUsername /* Username to remove */ ); #endif /* SQLITE_USER_AUTHENTICATION */ | > > > > | 84 85 86 87 88 89 90 91 92 93 94 95 96 | ** the database cannot be converted into a no-authentication-required ** database. */ int sqlite3_user_delete( sqlite3 *db, /* Database connection */ const char *zUsername /* Username to remove */ ); #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* SQLITE_USER_AUTHENTICATION */ |
Changes to ext/userauth/userauth.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** ** To compile with the user-authentication feature, append this file to ** end of an SQLite amalgamation, then add the SQLITE_USER_AUTHENTICATION ** compile-time option. See the user-auth.txt file in the same source ** directory as this file for additional information. */ #ifdef SQLITE_USER_AUTHENTICATION | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** ** To compile with the user-authentication feature, append this file to ** end of an SQLite amalgamation, then add the SQLITE_USER_AUTHENTICATION ** compile-time option. See the user-auth.txt file in the same source ** directory as this file for additional information. */ #ifdef SQLITE_USER_AUTHENTICATION #ifndef SQLITEINT_H # include "sqliteInt.h" #endif /* ** Prepare an SQL statement for use by the user authentication logic. ** Return a pointer to the prepared statement on success. Return a ** NULL pointer if there is an error of any kind. |
︙ | ︙ |
Changes to main.mk.
︙ | ︙ | |||
64 65 66 67 68 69 70 | fts3_write.o fts5.o func.o global.o hash.o \ icu.o insert.o json1.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_unix.o os_win.o \ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ | | > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | fts3_write.o fts5.o func.o global.o hash.o \ icu.o insert.o json1.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_unix.o os_win.o \ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o \ select.o sqlite3rbu.o status.o stmt.o \ table.o threads.o tokenize.o treeview.o trigger.o \ update.o userauth.o util.o vacuum.o \ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \ utf.o vtab.o LIBOBJ += sqlite3session.o |
︙ | ︙ | |||
230 231 232 233 234 235 236 | SRC += \ $(TOP)/ext/userauth/userauth.c \ $(TOP)/ext/userauth/sqlite3userauth.h SRC += \ $(TOP)/ext/rbu/sqlite3rbu.c \ $(TOP)/ext/rbu/sqlite3rbu.h SRC += \ | | > | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | SRC += \ $(TOP)/ext/userauth/userauth.c \ $(TOP)/ext/userauth/sqlite3userauth.h SRC += \ $(TOP)/ext/rbu/sqlite3rbu.c \ $(TOP)/ext/rbu/sqlite3rbu.h SRC += \ $(TOP)/ext/misc/json1.c \ $(TOP)/ext/misc/stmt.c # FTS5 things # FTS5_HDR = \ $(TOP)/ext/fts5/fts5.h \ $(TOP)/ext/fts5/fts5Int.h \ |
︙ | ︙ | |||
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | $(TOP)/ext/misc/carray.c \ $(TOP)/ext/misc/closure.c \ $(TOP)/ext/misc/csv.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/wholenumber.c \ $(TOP)/ext/misc/vfslog.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_tok.c | > > > | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | $(TOP)/ext/misc/carray.c \ $(TOP)/ext/misc/closure.c \ $(TOP)/ext/misc/csv.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/mmapwarm.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.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/vfslog.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_tok.c |
︙ | ︙ | |||
458 459 460 461 462 463 464 | # Databases containing fuzzer test cases # FUZZDATA = \ $(TOP)/test/fuzzdata1.db \ $(TOP)/test/fuzzdata2.db \ $(TOP)/test/fuzzdata3.db \ | | > | > > > > > | 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 | # Databases containing fuzzer test cases # FUZZDATA = \ $(TOP)/test/fuzzdata1.db \ $(TOP)/test/fuzzdata2.db \ $(TOP)/test/fuzzdata3.db \ $(TOP)/test/fuzzdata4.db \ $(TOP)/test/fuzzdata5.db # Standard options to testfixture # TESTOPTS = --verbose=file --output=test-out.txt # Extra compiler options for various shell tools # SHELL_OPT += -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 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. # all: sqlite3.h libsqlite3.a sqlite3$(EXE) libsqlite3.a: $(LIBOBJ) |
︙ | ︙ | |||
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | $(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB) dbhash$(EXE): $(TOP)/tool/dbhash.c sqlite3.c sqlite3.h $(TCCX) -o dbhash$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/dbhash.c sqlite3.c $(TLIBS) $(THREADLIB) scrub$(EXE): $(TOP)/ext/misc/scrub.c sqlite3.o $(TCC) -I. -DSCRUB_STANDALONE -o scrub$(EXE) $(TOP)/ext/misc/scrub.c sqlite3.o $(THREADLIB) srcck1$(EXE): $(TOP)/tool/srcck1.c $(BCC) -o srcck1$(EXE) $(TOP)/tool/srcck1.c sourcetest: srcck1$(EXE) sqlite3.c ./srcck1 sqlite3.c fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(FUZZERSHELL_OPT) $(TOP)/tool/fuzzershell.c sqlite3.c \ $(TLIBS) $(THREADLIB) | > > > > > > > > > | > > > > > | | 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 | $(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB) dbhash$(EXE): $(TOP)/tool/dbhash.c sqlite3.c sqlite3.h $(TCCX) -o dbhash$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/dbhash.c sqlite3.c $(TLIBS) $(THREADLIB) faststat1$(EXE): $(TOP)/tool/faststat1.c sqlite3.c sqlite3.h $(TCCX) -o faststat1$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/faststat1.c sqlite3.c $(TLIBS) $(THREADLIB) scrub$(EXE): $(TOP)/ext/misc/scrub.c sqlite3.o $(TCC) -I. -DSCRUB_STANDALONE -o scrub$(EXE) $(TOP)/ext/misc/scrub.c sqlite3.o $(THREADLIB) srcck1$(EXE): $(TOP)/tool/srcck1.c $(BCC) -o srcck1$(EXE) $(TOP)/tool/srcck1.c sourcetest: srcck1$(EXE) sqlite3.c ./srcck1 sqlite3.c fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(FUZZERSHELL_OPT) $(TOP)/tool/fuzzershell.c sqlite3.c \ $(TLIBS) $(THREADLIB) dbfuzz$(EXE): $(TOP)/test/dbfuzz.c sqlite3.c sqlite3.h $(TCCX) -o dbfuzz$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ $(DBFUZZ_OPT) $(TOP)/test/dbfuzz.c sqlite3.c \ $(TLIBS) $(THREADLIB) fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(TOP)/test/ossfuzz.c $(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) -DSQLITE_OSS_FUZZ \ $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) $(THREADLIB) ossshell$(EXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h $(TCCX) -o ossshell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) \ $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c $(TLIBS) $(THREADLIB) mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c $(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ $(TLIBS) $(THREADLIB) MPTEST1=./mptester$(EXE) mptest1.db $(TOP)/mptest/crash01.test --repeat 20 MPTEST2=./mptester$(EXE) mptest2.db $(TOP)/mptest/multiwrite01.test --repeat 20 |
︙ | ︙ | |||
582 583 584 585 586 587 588 589 590 591 592 593 594 595 | # Rules to build the LEMON compiler generator # lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c $(BCC) -o lemon $(TOP)/tool/lemon.c cp $(TOP)/tool/lempar.c . # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o # opcodes.o # %.o: %.c $(HDR) | > > > > > | 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 | # Rules to build the LEMON compiler generator # lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c $(BCC) -o lemon $(TOP)/tool/lemon.c cp $(TOP)/tool/lempar.c . # A tool to generate the source-id # mksourceid: $(TOP)/tool/mksourceid.c $(BCC) -o mksourceid $(TOP)/tool/mksourceid.c # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o # opcodes.o # %.o: %.c $(HDR) |
︙ | ︙ | |||
621 622 623 624 625 626 627 | parse.c: $(TOP)/src/parse.y lemon $(TOP)/tool/addopcodes.tcl cp $(TOP)/src/parse.y . rm -f parse.h ./lemon -s $(OPTS) parse.y mv parse.h parse.h.temp tclsh $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h | | | 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | parse.c: $(TOP)/src/parse.y lemon $(TOP)/tool/addopcodes.tcl cp $(TOP)/src/parse.y . rm -f parse.h ./lemon -s $(OPTS) parse.y mv parse.h parse.h.temp tclsh $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash >keywordhash.h |
︙ | ︙ | |||
698 699 700 701 702 703 704 705 706 707 708 709 710 711 | fts5.o: fts5.c $(TCCX) -DSQLITE_CORE -c fts5.c json1.o: $(TOP)/ext/misc/json1.c $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon cp $(TOP)/ext/fts5/fts5parse.y . | > > > | 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 | fts5.o: fts5.c $(TCCX) -DSQLITE_CORE -c fts5.c json1.o: $(TOP)/ext/misc/json1.c $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c stmt.o: $(TOP)/ext/misc/stmt.c $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon cp $(TOP)/ext/fts5/fts5parse.y . |
︙ | ︙ | |||
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 | echo "static const char *zMainloop = " >> $@ tclsh $(TOP)/tool/tostr.tcl $(TOP)/tool/spaceanal.tcl >> $@ echo "; return zMainloop; }" >> $@ sqlite3_analyzer$(EXE): sqlite3_analyzer.c $(TCCX) $(TCL_FLAGS) sqlite3_analyzer.c -o $@ $(LIBTCL) $(THREADLIB) # Rules to build the 'testfixture' application. # TESTFIXTURE_FLAGS = -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(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 \ | > > > > > | 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 | echo "static const char *zMainloop = " >> $@ tclsh $(TOP)/tool/tostr.tcl $(TOP)/tool/spaceanal.tcl >> $@ echo "; return zMainloop; }" >> $@ sqlite3_analyzer$(EXE): sqlite3_analyzer.c $(TCCX) $(TCL_FLAGS) sqlite3_analyzer.c -o $@ $(LIBTCL) $(THREADLIB) dbdump$(EXE): $(TOP)/ext/misc/dbdump.c sqlite3.o $(TCCX) -DDBDUMP_STANDALONE -o dbdump$(EXE) \ $(TOP)/ext/misc/dbdump.c sqlite3.o $(THREADLIB) # Rules to build the 'testfixture' application. # TESTFIXTURE_FLAGS = -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(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 \ |
︙ | ︙ | |||
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 | fastfuzztest: fuzzcheck$(EXE) $(FUZZDATA) ./fuzzcheck$(EXE) --limit-mem 100M $(FUZZDATA) valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA) # A very quick test using only testfixture and omitting all the slower # tests. Designed to run in under 3 minutes on a workstation. # quicktest: ./testfixture$(EXE) ./testfixture$(EXE) $(TOP)/test/extraquick.test $(TESTOPTS) # The default test case. Runs most of the faster standard TCL tests, # and fuzz tests, and sqlite3_analyzer and sqldiff tests. | > > > > > < | < | 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 | fastfuzztest: fuzzcheck$(EXE) $(FUZZDATA) ./fuzzcheck$(EXE) --limit-mem 100M $(FUZZDATA) valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA) # The veryquick.test TCL tests. # tcltest: ./testfixture$(EXE) ./testfixture$(EXE) $(TOP)/test/veryquick.test $(TESTOPTS) # A very quick test using only testfixture and omitting all the slower # tests. Designed to run in under 3 minutes on a workstation. # quicktest: ./testfixture$(EXE) ./testfixture$(EXE) $(TOP)/test/extraquick.test $(TESTOPTS) # The default test case. Runs most of the faster standard TCL tests, # and fuzz tests, and sqlite3_analyzer and sqldiff tests. test: fastfuzztest sourcetest $(TESTPROGS) tcltest # Run a test using valgrind. This can take a really long time # because valgrind is so much slower than a native machine. # valgrindtest: $(TESTPROGS) valgrindfuzz OMIT_MISUSE=1 valgrind -v \ ./testfixture$(EXE) $(TOP)/test/permutations.test valgrind $(TESTOPTS) |
︙ | ︙ | |||
872 873 874 875 876 877 878 | LogEst$(EXE): $(TOP)/tool/logest.c sqlite3.h $(TCC) -o LogEst$(EXE) $(TOP)/tool/logest.c wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o wordcount$(EXE) \ $(TOP)/test/wordcount.c sqlite3.c | | | > > > | 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 | LogEst$(EXE): $(TOP)/tool/logest.c sqlite3.h $(TCC) -o LogEst$(EXE) $(TOP)/tool/logest.c wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o wordcount$(EXE) \ $(TOP)/test/wordcount.c sqlite3.c speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.c $(TCCX) -I. $(ST_OPT) -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.c $(THREADLIB) kvtest$(EXE): $(TOP)/test/kvtest.c sqlite3.c $(TCCX) -I. $(KV_OPT) -o kvtest$(EXE) $(TOP)/test/kvtest.c sqlite3.c $(THREADLIB) rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.o $(TCC) -I. -o rbu$(EXE) $(TOP)/ext/rbu/rbu.c sqlite3.o \ $(THREADLIB) loadfts: $(TOP)/tool/loadfts.c libsqlite3.a $(TCC) $(TOP)/tool/loadfts.c libsqlite3.a -o loadfts $(THREADLIB) |
︙ | ︙ |
Changes to src/alter.c.
︙ | ︙ | |||
371 372 373 374 375 376 377 | ** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). ** If the table is a system table, this function leaves an error message ** in pParse->zErr (system tables may not be altered) and returns non-zero. ** ** Or, if zName is not a system table, zero is returned. */ static int isSystemTable(Parse *pParse, const char *zName){ | | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | ** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). ** If the table is a system table, this function leaves an error message ** in pParse->zErr (system tables may not be altered) and returns non-zero. ** ** Or, if zName is not a system table, zero is returned. */ static int isSystemTable(Parse *pParse, const char *zName){ if( 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); return 1; } return 0; } /* |
︙ | ︙ | |||
399 400 401 402 403 404 405 | int nTabName; /* Number of UTF-8 characters in zTabName */ const char *zTabName; /* Original name of the table */ Vdbe *v; #ifndef SQLITE_OMIT_TRIGGER char *zWhere = 0; /* Where clause to locate temp triggers */ #endif VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ | | | | | 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 | int nTabName; /* Number of UTF-8 characters in zTabName */ const char *zTabName; /* Original name of the table */ Vdbe *v; #ifndef SQLITE_OMIT_TRIGGER char *zWhere = 0; /* Where clause to locate temp triggers */ #endif VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ u32 savedDbFlags; /* Saved value of db->mDbFlags */ savedDbFlags = db->mDbFlags; if( NEVER(db->mallocFailed) ) goto exit_rename_table; assert( pSrc->nSrc==1 ); assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); if( !pTab ) goto exit_rename_table; iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; db->mDbFlags |= DBFLAG_PreferBuiltin; /* Get a NULL terminated version of the new table name. */ zName = sqlite3NameFromToken(db, pName); if( !zName ) goto exit_rename_table; /* Check that a table or index named 'zName' does not already exist ** in database iDb. If so, this is an error. |
︙ | ︙ | |||
500 501 502 503 504 505 506 | /* If foreign-key support is enabled, rewrite the CREATE TABLE ** statements corresponding to all child tables of foreign key constraints ** for which the renamed table is the parent table. */ if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){ sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " "sql = sqlite_rename_parent(sql, %Q, %Q) " | | | 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 | /* If foreign-key support is enabled, rewrite the CREATE TABLE ** statements corresponding to all child tables of foreign key constraints ** for which the renamed table is the parent table. */ if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){ sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " "sql = sqlite_rename_parent(sql, %Q, %Q) " "WHERE %s;", zDb, MASTER_NAME, zTabName, zName, zWhere); sqlite3DbFree(db, zWhere); } } #endif /* Modify the sqlite_master table to use the new table name. */ sqlite3NestedParse(pParse, |
︙ | ︙ | |||
524 525 526 527 528 529 530 | "name = CASE " "WHEN type='table' THEN %Q " "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " "'sqlite_autoindex_' || %Q || substr(name,%d+18) " "ELSE name END " "WHERE tbl_name=%Q COLLATE nocase AND " "(type='table' OR type='index' OR type='trigger');", | | | 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 | "name = CASE " "WHEN type='table' THEN %Q " "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " "'sqlite_autoindex_' || %Q || substr(name,%d+18) " "ELSE name END " "WHERE tbl_name=%Q COLLATE nocase AND " "(type='table' OR type='index' OR type='trigger');", zDb, MASTER_NAME, zName, zName, zName, #ifndef SQLITE_OMIT_TRIGGER zName, #endif zName, nTabName, zTabName ); #ifndef SQLITE_OMIT_AUTOINCREMENT |
︙ | ︙ | |||
575 576 577 578 579 580 581 | /* Drop and reload the internal table schema. */ reloadTableSchema(pParse, pTab, zName); exit_rename_table: sqlite3SrcListDelete(db, pSrc); sqlite3DbFree(db, zName); | | | 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 | /* Drop and reload the internal table schema. */ reloadTableSchema(pParse, pTab, zName); exit_rename_table: sqlite3SrcListDelete(db, pSrc); sqlite3DbFree(db, zName); db->mDbFlags = savedDbFlags; } /* ** This function is called after an "ALTER TABLE ... ADD" statement ** has been parsed. Argument pColDef contains the text of the new ** column definition. ** |
︙ | ︙ | |||
676 677 678 679 680 681 682 | sqlite3ValueFree(pVal); } /* Modify the CREATE TABLE statement. */ zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); if( zCol ){ char *zEnd = &zCol[pColDef->n-1]; | | | | | | 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 | sqlite3ValueFree(pVal); } /* Modify the CREATE TABLE statement. */ zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); if( zCol ){ char *zEnd = &zCol[pColDef->n-1]; u32 savedDbFlags = db->mDbFlags; while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ *zEnd-- = '\0'; } db->mDbFlags |= DBFLAG_PreferBuiltin; sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " "WHERE type = 'table' AND name = %Q", zDb, MASTER_NAME, pNew->addColOffset, zCol, pNew->addColOffset+1, zTab ); sqlite3DbFree(db, zCol); db->mDbFlags = savedDbFlags; } /* Make sure the schema version is at least 3. But do not upgrade ** from less than 3 to 4, as that will corrupt any preexisting DESC ** index. */ r1 = sqlite3GetTempReg(pParse); |
︙ | ︙ | |||
769 770 771 772 773 774 775 | ** prefix, we insure that the name will not collide with an existing ** table because user table are not allowed to have the "sqlite_" ** prefix on their name. */ pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table)); if( !pNew ) goto exit_begin_add_column; pParse->pNewTable = pNew; | | | | 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 | ** prefix, we insure that the name will not collide with an existing ** table because user table are not allowed to have the "sqlite_" ** prefix on their name. */ pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table)); if( !pNew ) goto exit_begin_add_column; pParse->pNewTable = pNew; pNew->nTabRef = 1; pNew->nCol = pTab->nCol; assert( pNew->nCol>0 ); nAlloc = (((pNew->nCol-1)/8)*8)+8; assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc); pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); if( !pNew->aCol || !pNew->zName ){ assert( db->mallocFailed ); goto exit_begin_add_column; } memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); for(i=0; i<pNew->nCol; i++){ Column *pCol = &pNew->aCol[i]; pCol->zName = sqlite3DbStrDup(db, pCol->zName); pCol->zColl = 0; pCol->pDflt = 0; } pNew->pSchema = db->aDb[iDb].pSchema; pNew->addColOffset = pTab->addColOffset; pNew->nTabRef = 1; /* Begin a transaction and increment the schema cookie. */ sqlite3BeginWriteOperation(pParse, 0, iDb); v = sqlite3GetVdbe(pParse); if( !v ) goto exit_begin_add_column; sqlite3ChangeCookie(pParse, iDb); exit_begin_add_column: sqlite3SrcListDelete(db, pSrc); return; } #endif /* SQLITE_ALTER_TABLE */ |
Changes to src/analyze.c.
︙ | ︙ | |||
286 287 288 289 290 291 292 293 294 295 296 297 298 299 | int nKeyCol; /* Number of index columns w/o the pk/rowid */ int mxSample; /* Maximum number of samples to accumulate */ Stat4Sample current; /* Current row as a Stat4Sample */ u32 iPrn; /* Pseudo-random number used for sampling */ Stat4Sample *aBest; /* Array of nCol best samples */ int iMin; /* Index in a[] of entry with minimum score */ int nSample; /* Current number of samples */ int iGet; /* Index of current sample accessed by stat_get() */ Stat4Sample *a; /* Array of mxSample Stat4Sample objects */ sqlite3 *db; /* Database connection, for malloc() */ }; /* Reclaim memory used by a Stat4Sample */ | > | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | int nKeyCol; /* Number of index columns w/o the pk/rowid */ int mxSample; /* Maximum number of samples to accumulate */ Stat4Sample current; /* Current row as a Stat4Sample */ u32 iPrn; /* Pseudo-random number used for sampling */ Stat4Sample *aBest; /* Array of nCol best samples */ int iMin; /* Index in a[] of entry with minimum score */ int nSample; /* Current number of samples */ int nMaxEqZero; /* Max leading 0 in anEq[] for any a[] entry */ int iGet; /* Index of current sample accessed by stat_get() */ Stat4Sample *a; /* Array of mxSample Stat4Sample objects */ sqlite3 *db; /* Database connection, for malloc() */ }; /* Reclaim memory used by a Stat4Sample */ |
︙ | ︙ | |||
550 551 552 553 554 555 556 557 558 559 560 561 562 563 | static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ Stat4Sample *pSample = 0; int i; assert( IsStat4 || nEqZero==0 ); #ifdef SQLITE_ENABLE_STAT4 if( pNew->isPSample==0 ){ Stat4Sample *pUpgrade = 0; assert( pNew->anEq[pNew->iCol]>0 ); /* This sample is being added because the prefix that ends in column ** iCol occurs many times in the table. However, if we have already ** added a sample that shares this prefix, there is no need to add | > > > > > > > | 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 | static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ Stat4Sample *pSample = 0; int i; assert( IsStat4 || nEqZero==0 ); #ifdef SQLITE_ENABLE_STAT4 /* Stat4Accum.nMaxEqZero is set to the maximum number of leading 0 ** values in the anEq[] array of any sample in Stat4Accum.a[]. In ** other words, if nMaxEqZero is n, then it is guaranteed that there ** are no samples with Stat4Sample.anEq[m]==0 for (m>=n). */ if( nEqZero>p->nMaxEqZero ){ p->nMaxEqZero = nEqZero; } if( pNew->isPSample==0 ){ Stat4Sample *pUpgrade = 0; assert( pNew->anEq[pNew->iCol]>0 ); /* This sample is being added because the prefix that ends in column ** iCol occurs many times in the table. However, if we have already ** added a sample that shares this prefix, there is no need to add |
︙ | ︙ | |||
647 648 649 650 651 652 653 654 | Stat4Sample *pBest = &p->aBest[i]; pBest->anEq[i] = p->current.anEq[i]; if( p->nSample<p->mxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){ sampleInsert(p, pBest, i); } } /* Update the anEq[] fields of any samples already collected. */ | > > > > > > > > | | | | | > > | 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 | Stat4Sample *pBest = &p->aBest[i]; pBest->anEq[i] = p->current.anEq[i]; if( p->nSample<p->mxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){ sampleInsert(p, pBest, i); } } /* Check that no sample contains an anEq[] entry with an index of ** p->nMaxEqZero or greater set to zero. */ for(i=p->nSample-1; i>=0; i--){ int j; for(j=p->nMaxEqZero; j<p->nCol; j++) assert( p->a[i].anEq[j]>0 ); } /* Update the anEq[] fields of any samples already collected. */ if( iChng<p->nMaxEqZero ){ for(i=p->nSample-1; i>=0; i--){ int j; for(j=iChng; j<p->nCol; j++){ if( p->a[i].anEq[j]==0 ) p->a[i].anEq[j] = p->current.anEq[j]; } } p->nMaxEqZero = iChng; } #endif #if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4) if( iChng==0 ){ tRowcnt nLt = p->current.anLt[0]; tRowcnt nEq = p->current.anEq[0]; |
︙ | ︙ | |||
792 793 794 795 796 797 798 799 800 801 802 803 804 805 | /* ** Implementation of the stat_get(P,J) SQL function. This routine is ** used to query statistical information that has been gathered into ** the Stat4Accum object by prior calls to stat_push(). The P parameter ** has type BLOB but it is really just a pointer to the Stat4Accum object. ** The content to returned is determined by the parameter J ** which is one of the STAT_GET_xxxx values defined above. ** ** If neither STAT3 nor STAT4 are enabled, then J is always ** STAT_GET_STAT1 and is hence omitted and this routine becomes ** a one-parameter function, stat_get(P), that always returns the ** stat1 table entry information. */ static void statGet( | > > > > > > | 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 | /* ** Implementation of the stat_get(P,J) SQL function. This routine is ** used to query statistical information that has been gathered into ** the Stat4Accum object by prior calls to stat_push(). The P parameter ** has type BLOB but it is really just a pointer to the Stat4Accum object. ** The content to returned is determined by the parameter J ** which is one of the STAT_GET_xxxx values defined above. ** ** The stat_get(P,J) function is not available to generic SQL. It is ** inserted as part of a manually constructed bytecode program. (See ** the callStatGet() routine below.) It is guaranteed that the P ** parameter will always be a poiner to a Stat4Accum object, never a ** NULL. ** ** If neither STAT3 nor STAT4 are enabled, then J is always ** STAT_GET_STAT1 and is hence omitted and this routine becomes ** a one-parameter function, stat_get(P), that always returns the ** stat1 table entry information. */ static void statGet( |
︙ | ︙ | |||
1176 1177 1178 1179 1180 1181 1182 | sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); }else{ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); int j, k, regKey; regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); for(j=0; j<pPk->nKeyCol; j++){ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]); | | | 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 | sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); }else{ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); int j, k, regKey; regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); for(j=0; j<pPk->nKeyCol; j++){ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]); assert( k>=0 && k<pIdx->nColumn ); sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName)); } sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); } #endif |
︙ | ︙ | |||
1360 1361 1362 1363 1364 1365 1366 | assert( pName2!=0 || pName1==0 ); if( pName1==0 ){ /* Form 1: Analyze everything */ for(i=0; i<db->nDb; i++){ if( i==1 ) continue; /* Do not analyze the TEMP database */ analyzeDatabase(pParse, i); } | | | < < | | < < < < < < < < < < < | | | | | > | 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 | assert( pName2!=0 || pName1==0 ); if( pName1==0 ){ /* Form 1: Analyze everything */ for(i=0; i<db->nDb; i++){ if( i==1 ) continue; /* Do not analyze the TEMP database */ analyzeDatabase(pParse, i); } }else if( pName2->n==0 && (iDb = sqlite3FindDb(db, pName1))>=0 ){ /* Analyze the schema named as the argument */ analyzeDatabase(pParse, iDb); }else{ /* Form 3: Analyze the table or index named as an argument */ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName); if( iDb>=0 ){ zDb = pName2->n ? db->aDb[iDb].zDbSName : 0; z = sqlite3NameFromToken(db, pTableName); if( z ){ if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){ analyzeTable(pParse, pIdx->pTable, pIdx); }else if( (pTab = sqlite3LocateTable(pParse, 0, z, zDb))!=0 ){ analyzeTable(pParse, pTab, 0); } sqlite3DbFree(db, z); } } } if( db->nSqlExec==0 && (v = sqlite3GetVdbe(pParse))!=0 ){ sqlite3VdbeAddOp0(v, OP_Expire); } } /* ** Used to pass information from the analyzer reader through to the ** callback routine. */ typedef struct analysisInfo analysisInfo; |
︙ | ︙ | |||
1522 1523 1524 1525 1526 1527 1528 | pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero(sizeof(tRowcnt) * nCol); if( pIndex->aiRowEst==0 ) sqlite3OomFault(pInfo->db); } aiRowEst = pIndex->aiRowEst; #endif pIndex->bUnordered = 0; decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex); | > | > > > > | 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 | pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero(sizeof(tRowcnt) * nCol); if( pIndex->aiRowEst==0 ) sqlite3OomFault(pInfo->db); } aiRowEst = pIndex->aiRowEst; #endif pIndex->bUnordered = 0; decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex); pIndex->hasStat1 = 1; if( pIndex->pPartIdxWhere==0 ){ pTable->nRowLogEst = pIndex->aiRowLogEst[0]; pTable->tabFlags |= TF_HasStat1; } }else{ Index fakeIdx; fakeIdx.szIdxRow = pTable->szTabRow; #ifdef SQLITE_ENABLE_COSTMULT fakeIdx.pTable = pTable; #endif decodeIntArray((char*)z, 1, 0, &pTable->nRowLogEst, &fakeIdx); pTable->szTabRow = fakeIdx.szIdxRow; pTable->tabFlags |= TF_HasStat1; } return 0; } /* ** If the Index.aSample variable is not NULL, delete the aSample[] array |
︙ | ︙ | |||
1611 1612 1613 1614 1615 1616 1617 | || aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] ){ sumEq += aSample[i].anEq[iCol]; nSum100 += 100; } } | | | 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 | || aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] ){ sumEq += aSample[i].anEq[iCol]; nSum100 += 100; } } if( nDist100>nSum100 && sumEq<nRow ){ avgEq = ((i64)100 * (nRow - sumEq))/(nDist100 - nSum100); } if( avgEq==0 ) avgEq = 1; pIdx->aAvgEq[iCol] = avgEq; } } } |
︙ | ︙ | |||
1762 1763 1764 1765 1766 1767 1768 | ** a buffer overread. */ pSample->n = sqlite3_column_bytes(pStmt, 4); pSample->p = sqlite3DbMallocZero(db, pSample->n + 2); if( pSample->p==0 ){ sqlite3_finalize(pStmt); return SQLITE_NOMEM_BKPT; } | > | > | 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 | ** a buffer overread. */ pSample->n = sqlite3_column_bytes(pStmt, 4); pSample->p = sqlite3DbMallocZero(db, pSample->n + 2); if( pSample->p==0 ){ sqlite3_finalize(pStmt); return SQLITE_NOMEM_BKPT; } if( pSample->n ){ memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n); } pIdx->nSample++; } rc = sqlite3_finalize(pStmt); if( rc==SQLITE_OK ) initAvgEq(pPrevIdx); return rc; } |
︙ | ︙ | |||
1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 | ** code may be ignored. */ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ analysisInfo sInfo; HashElem *i; char *zSql; int rc = SQLITE_OK; assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); /* Clear any prior statistics */ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); | > | > > > > | | 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 | ** code may be ignored. */ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ analysisInfo sInfo; HashElem *i; char *zSql; int rc = SQLITE_OK; Schema *pSchema = db->aDb[iDb].pSchema; assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); /* Clear any prior statistics */ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(i=sqliteHashFirst(&pSchema->tblHash); i; i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); pTab->tabFlags &= ~TF_HasStat1; } for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); pIdx->hasStat1 = 0; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 sqlite3DeleteIndexSamples(db, pIdx); pIdx->aSample = 0; #endif } /* Load new statistics out of the sqlite_stat1 table */ |
︙ | ︙ | |||
1854 1855 1856 1857 1858 1859 1860 | rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); sqlite3DbFree(db, zSql); } } /* Set appropriate defaults on all indexes not in the sqlite_stat1 table */ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); | | | | | 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 | rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); sqlite3DbFree(db, zSql); } } /* Set appropriate defaults on all indexes not in the sqlite_stat1 table */ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); if( !pIdx->hasStat1 ) sqlite3DefaultRowEst(pIdx); } /* Load the statistics from the sqlite_stat4 table. */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( rc==SQLITE_OK && OptimizationEnabled(db, SQLITE_Stat34) ){ db->lookaside.bDisable++; rc = loadStat4(db, sInfo.zDatabase); db->lookaside.bDisable--; } for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3_free(pIdx->aiRowEst); pIdx->aiRowEst = 0; } #endif if( rc==SQLITE_NOMEM ){ |
︙ | ︙ |
Changes to src/attach.c.
︙ | ︙ | |||
65 66 67 68 69 70 71 | int rc = 0; sqlite3 *db = sqlite3_context_db_handle(context); const char *zName; const char *zFile; char *zPath = 0; char *zErr = 0; unsigned int flags; | | > | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | int rc = 0; sqlite3 *db = sqlite3_context_db_handle(context); const char *zName; const char *zFile; char *zPath = 0; char *zErr = 0; unsigned int flags; Db *aNew; /* New array of Db pointers */ Db *pNew; /* Db object for the newly attached database */ char *zErrDyn = 0; sqlite3_vfs *pVfs; UNUSED_PARAMETER(NotUsed); zFile = (const char *)sqlite3_value_text(argv[0]); zName = (const char *)sqlite3_value_text(argv[1]); |
︙ | ︙ | |||
88 89 90 91 92 93 94 | */ if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", db->aLimit[SQLITE_LIMIT_ATTACHED] ); goto attach_error; } | < < < < | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | */ if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", db->aLimit[SQLITE_LIMIT_ATTACHED] ); goto attach_error; } for(i=0; i<db->nDb; i++){ char *z = db->aDb[i].zDbSName; assert( z && zName ); if( sqlite3StrICmp(z, zName)==0 ){ zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); goto attach_error; } |
︙ | ︙ | |||
113 114 115 116 117 118 119 | if( aNew==0 ) return; memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ) return; } db->aDb = aNew; | | | | > | | | | | | | | | | | | 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 | if( aNew==0 ) return; memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ) return; } db->aDb = aNew; pNew = &db->aDb[db->nDb]; memset(pNew, 0, sizeof(*pNew)); /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialized. */ flags = db->openFlags; rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); sqlite3_result_error(context, zErr, -1); sqlite3_free(zErr); return; } assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); sqlite3_free( zPath ); db->nDb++; db->skipBtreeMutex = 0; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; zErrDyn = sqlite3MPrintf(db, "database is already attached"); }else if( rc==SQLITE_OK ){ Pager *pPager; pNew->pSchema = sqlite3SchemaGet(db, pNew->pBt); if( !pNew->pSchema ){ rc = SQLITE_NOMEM_BKPT; }else if( pNew->pSchema->file_format && pNew->pSchema->enc!=ENC(db) ){ zErrDyn = sqlite3MPrintf(db, "attached databases must use the same text encoding as main database"); rc = SQLITE_ERROR; } sqlite3BtreeEnter(pNew->pBt); pPager = sqlite3BtreePager(pNew->pBt); sqlite3PagerLockingMode(pPager, db->dfltLockMode); sqlite3BtreeSecureDelete(pNew->pBt, sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); #ifndef SQLITE_OMIT_PAGER_PRAGMAS sqlite3BtreeSetPagerFlags(pNew->pBt, PAGER_SYNCHRONOUS_FULL | (db->flags & PAGER_FLAGS_MASK)); #endif sqlite3BtreeLeave(pNew->pBt); } pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; pNew->zDbSName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && pNew->zDbSName==0 ){ rc = SQLITE_NOMEM_BKPT; } #ifdef SQLITE_HAS_CODEC if( rc==SQLITE_OK ){ extern int sqlite3CodecAttach(sqlite3*, int, const void*, int); |
︙ | ︙ | |||
282 283 284 285 286 287 288 | sqlite3_snprintf(sizeof(zErr),zErr, "no such database: %s", zName); goto detach_error; } if( i<2 ){ sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName); goto detach_error; } | < < < < < | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | sqlite3_snprintf(sizeof(zErr),zErr, "no such database: %s", zName); goto detach_error; } if( i<2 ){ sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName); goto detach_error; } if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){ sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName); goto detach_error; } sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; |
︙ | ︙ | |||
321 322 323 324 325 326 327 328 329 330 331 332 333 334 | ){ int rc; NameContext sName; Vdbe *v; sqlite3* db = pParse->db; int regArgs; memset(&sName, 0, sizeof(NameContext)); sName.pParse = pParse; if( SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) | > | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | ){ int rc; NameContext sName; Vdbe *v; sqlite3* db = pParse->db; int regArgs; if( pParse->nErr ) goto attach_end; memset(&sName, 0, sizeof(NameContext)); sName.pParse = pParse; if( SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) |
︙ | ︙ |
Changes to src/auth.c.
︙ | ︙ | |||
114 115 116 117 118 119 120 | if( db->init.busy ) return SQLITE_OK; rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext #ifdef SQLITE_USER_AUTHENTICATION ,db->auth.zAuthUser #endif ); if( rc==SQLITE_DENY ){ | > | < < | < | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | if( db->init.busy ) return SQLITE_OK; rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext #ifdef SQLITE_USER_AUTHENTICATION ,db->auth.zAuthUser #endif ); if( rc==SQLITE_DENY ){ char *z = sqlite3_mprintf("%s.%s", zTab, zCol); if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z); sqlite3ErrorMsg(pParse, "access to %z is prohibited", z); pParse->rc = SQLITE_AUTH; }else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){ sqliteAuthBadReturnCode(pParse); } return rc; } |
︙ | ︙ | |||
212 213 214 215 216 217 218 219 220 221 222 223 224 225 | if( db->init.busy || IN_DECLARE_VTAB ){ return SQLITE_OK; } if( db->xAuth==0 ){ return SQLITE_OK; } rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext #ifdef SQLITE_USER_AUTHENTICATION ,db->auth.zAuthUser #endif ); if( rc==SQLITE_DENY ){ sqlite3ErrorMsg(pParse, "not authorized"); | > > > > > > > > > > > > | 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 | if( db->init.busy || IN_DECLARE_VTAB ){ return SQLITE_OK; } if( db->xAuth==0 ){ return SQLITE_OK; } /* EVIDENCE-OF: R-43249-19882 The third through sixth parameters to the ** callback are either NULL pointers or zero-terminated strings that ** contain additional details about the action to be authorized. ** ** The following testcase() macros show that any of the 3rd through 6th ** parameters can be either NULL or a string. */ testcase( zArg1==0 ); testcase( zArg2==0 ); testcase( zArg3==0 ); testcase( pParse->zAuthContext==0 ); rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext #ifdef SQLITE_USER_AUTHENTICATION ,db->auth.zAuthUser #endif ); if( rc==SQLITE_DENY ){ sqlite3ErrorMsg(pParse, "not authorized"); |
︙ | ︙ |
Changes to src/bitvec.c.
︙ | ︙ | |||
289 290 291 292 293 294 295 | ** Return the value of the iSize parameter specified when Bitvec *p ** was created. */ u32 sqlite3BitvecSize(Bitvec *p){ return p->iSize; } | | | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | ** Return the value of the iSize parameter specified when Bitvec *p ** was created. */ u32 sqlite3BitvecSize(Bitvec *p){ return p->iSize; } #ifndef SQLITE_UNTESTABLE /* ** Let V[] be an array of unsigned characters sufficient to hold ** up to N bits. Let I be an integer between 0 and N. 0<=I<N. ** Then the following macros can be used to set, clear, or test ** individual bits within V. */ #define SETBIT(V,I) V[I>>3] |= (1<<(I&7)) |
︙ | ︙ | |||
404 405 406 407 408 409 410 | /* Free allocated structure */ bitvec_end: sqlite3_free(pTmpSpace); sqlite3_free(pV); sqlite3BitvecDestroy(pBitvec); return rc; } | | | 404 405 406 407 408 409 410 411 | /* Free allocated structure */ bitvec_end: sqlite3_free(pTmpSpace); sqlite3_free(pV); sqlite3BitvecDestroy(pBitvec); return rc; } #endif /* SQLITE_UNTESTABLE */ |
Changes to src/btmutex.c.
︙ | ︙ | |||
179 180 181 182 183 184 185 | ** There is a corresponding leave-all procedures. ** ** Enter the mutexes in accending order by BtShared pointer address ** to avoid the possibility of deadlock when two threads with ** two or more btrees in common both try to lock all their btrees ** at the same instant. */ | | > > | > | | > > > > > | > > > | 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 | ** There is a corresponding leave-all procedures. ** ** Enter the mutexes in accending order by BtShared pointer address ** to avoid the possibility of deadlock when two threads with ** two or more btrees in common both try to lock all their btrees ** at the same instant. */ static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){ int i; int skipOk = 1; Btree *p; assert( sqlite3_mutex_held(db->mutex) ); for(i=0; i<db->nDb; i++){ p = db->aDb[i].pBt; if( p && p->sharable ){ sqlite3BtreeEnter(p); skipOk = 0; } } db->skipBtreeMutex = skipOk; } void sqlite3BtreeEnterAll(sqlite3 *db){ if( db->skipBtreeMutex==0 ) btreeEnterAll(db); } static void SQLITE_NOINLINE btreeLeaveAll(sqlite3 *db){ int i; Btree *p; assert( sqlite3_mutex_held(db->mutex) ); for(i=0; i<db->nDb; i++){ p = db->aDb[i].pBt; if( p ) sqlite3BtreeLeave(p); } } void sqlite3BtreeLeaveAll(sqlite3 *db){ if( db->skipBtreeMutex==0 ) btreeLeaveAll(db); } #ifndef NDEBUG /* ** Return true if the current thread holds the database connection ** mutex and all required BtShared mutexes. ** ** This routine is used inside assert() statements only. |
︙ | ︙ |
Changes to src/btree.c.
︙ | ︙ | |||
148 149 150 151 152 153 154 | BtLock *pLock; /* If this database is not shareable, or if the client is reading ** and has the read-uncommitted flag set, then no lock is required. ** Return true immediately. */ if( (pBtree->sharable==0) | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | BtLock *pLock; /* If this database is not shareable, or if the client is reading ** and has the read-uncommitted flag set, then no lock is required. ** Return true immediately. */ if( (pBtree->sharable==0) || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommit)) ){ return 1; } /* If the client is reading or writing an index and the schema is ** not loaded, then it is too difficult to actually check to see if ** the correct locks are held. So do not bother - just return true. |
︙ | ︙ | |||
225 226 227 228 229 230 231 | ** assert( !hasReadConflicts(pBtree, iRoot) ); */ static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ BtCursor *p; for(p=pBtree->pBt->pCursor; p; p=p->pNext){ if( p->pgnoRoot==iRoot && p->pBtree!=pBtree | | | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | ** assert( !hasReadConflicts(pBtree, iRoot) ); */ static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ BtCursor *p; for(p=pBtree->pBt->pCursor; p; p=p->pNext){ if( p->pgnoRoot==iRoot && p->pBtree!=pBtree && 0==(p->pBtree->db->flags & SQLITE_ReadUncommit) ){ return 1; } } return 0; } #endif /* #ifdef SQLITE_DEBUG */ |
︙ | ︙ | |||
247 248 249 250 251 252 253 | static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pIter; assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); | | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pIter; assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); assert( !(p->db->flags&SQLITE_ReadUncommit)||eLock==WRITE_LOCK||iTab==1 ); /* If requesting a write-lock, then the Btree must have an open write ** transaction on this file. And, obviously, for this to be so there ** must be an open write transaction on the file itself. */ assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); |
︙ | ︙ | |||
325 326 327 328 329 330 331 | 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_master ** table, and that lock is obtained in BtreeBeginTrans(). */ | | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | 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_master ** table, and that lock is obtained in BtreeBeginTrans(). */ assert( 0==(p->db->flags&SQLITE_ReadUncommit) || eLock==WRITE_LOCK ); /* This function should only be called on a sharable b-tree after it ** has been determined that no other b-tree holds a conflicting lock. */ assert( p->sharable ); assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); /* First search the list for an existing lock on this table. */ |
︙ | ︙ | |||
435 436 437 438 439 440 441 | pLock->eLock = READ_LOCK; } } } #endif /* SQLITE_OMIT_SHARED_CACHE */ | | > > | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | pLock->eLock = READ_LOCK; } } } #endif /* SQLITE_OMIT_SHARED_CACHE */ static void releasePage(MemPage *pPage); /* Forward reference */ static void releasePageOne(MemPage *pPage); /* Forward reference */ static void releasePageNotNull(MemPage *pPage); /* Forward reference */ /* ***** This routine is used inside of assert() only **** ** ** Verify that the cursor holds the mutex on its BtShared */ #ifdef SQLITE_DEBUG |
︙ | ︙ | |||
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | ** ** Otherwise, if argument isClearTable is false, then the row with ** rowid iRow is being replaced or deleted. In this case invalidate ** only those incrblob cursors open on that specific row. */ static void invalidateIncrblobCursors( Btree *pBtree, /* The database file to check */ i64 iRow, /* The rowid that might be changing */ int isClearTable /* True if all rows are being deleted */ ){ BtCursor *p; if( pBtree->hasIncrblobCur==0 ) return; assert( sqlite3BtreeHoldsMutex(pBtree) ); pBtree->hasIncrblobCur = 0; for(p=pBtree->pBt->pCursor; p; p=p->pNext){ if( (p->curFlags & BTCF_Incrblob)!=0 ){ pBtree->hasIncrblobCur = 1; | > | | | 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 | ** ** Otherwise, if argument isClearTable is false, then the row with ** rowid iRow is being replaced or deleted. In this case invalidate ** only those incrblob cursors open on that specific row. */ static void invalidateIncrblobCursors( Btree *pBtree, /* The database file to check */ Pgno pgnoRoot, /* The table that might be changing */ i64 iRow, /* The rowid that might be changing */ int isClearTable /* True if all rows are being deleted */ ){ BtCursor *p; if( pBtree->hasIncrblobCur==0 ) return; assert( sqlite3BtreeHoldsMutex(pBtree) ); pBtree->hasIncrblobCur = 0; for(p=pBtree->pBt->pCursor; p; p=p->pNext){ if( (p->curFlags & BTCF_Incrblob)!=0 ){ pBtree->hasIncrblobCur = 1; if( p->pgnoRoot==pgnoRoot && (isClearTable || p->info.nKey==iRow) ){ p->eState = CURSOR_INVALID; } } } } #else /* Stub function when INCRBLOB is omitted */ #define invalidateIncrblobCursors(w,x,y,z) #endif /* SQLITE_OMIT_INCRBLOB */ /* ** Set bit pgno of the BtShared.pHasContent bitvec. This is called ** when a page that previously contained data becomes a free-list leaf ** page. ** |
︙ | ︙ | |||
593 594 595 596 597 598 599 | } /* ** Release all of the apPage[] pages for a cursor. */ static void btreeReleaseAllCursorPages(BtCursor *pCur){ int i; | > | | < | > | > | 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 | } /* ** Release all of the apPage[] pages for a cursor. */ static void btreeReleaseAllCursorPages(BtCursor *pCur){ int i; if( pCur->iPage>=0 ){ for(i=0; i<pCur->iPage; i++){ releasePageNotNull(pCur->apPage[i]); } releasePageNotNull(pCur->pPage); pCur->iPage = -1; } } /* ** The cursor passed as the only argument must point to a valid entry ** when this function is called (i.e. have eState==CURSOR_VALID). This ** function saves the current cursor key in variables pCur->nKey and ** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error |
︙ | ︙ | |||
628 629 630 631 632 633 634 | pCur->nKey = sqlite3BtreeIntegerKey(pCur); }else{ /* For an index btree, save the complete key content */ void *pKey; pCur->nKey = sqlite3BtreePayloadSize(pCur); pKey = sqlite3Malloc( pCur->nKey ); if( pKey ){ | | | 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | pCur->nKey = sqlite3BtreeIntegerKey(pCur); }else{ /* For an index btree, save the complete key content */ void *pKey; pCur->nKey = sqlite3BtreePayloadSize(pCur); pKey = sqlite3Malloc( pCur->nKey ); if( pKey ){ rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey); if( rc==SQLITE_OK ){ pCur->pKey = pKey; }else{ sqlite3_free(pKey); } }else{ rc = SQLITE_NOMEM_BKPT; |
︙ | ︙ | |||
726 727 728 729 730 731 732 | if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){ if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ int rc = saveCursorPosition(p); if( SQLITE_OK!=rc ){ return rc; } }else{ | | | 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 | if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){ if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ int rc = saveCursorPosition(p); if( SQLITE_OK!=rc ){ return rc; } }else{ testcase( p->iPage>=0 ); btreeReleaseAllCursorPages(p); } } p = p->pNext; }while( p ); return SQLITE_OK; } |
︙ | ︙ | |||
759 760 761 762 763 764 765 | const void *pKey, /* Packed key if the btree is an index */ i64 nKey, /* Integer key for tables. Size of pKey for indices */ int bias, /* Bias search to the high end */ int *pRes /* Write search results here */ ){ int rc; /* Status code */ UnpackedRecord *pIdxKey; /* Unpacked index key */ | < < | < < < | > > | | | 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 | const void *pKey, /* Packed key if the btree is an index */ i64 nKey, /* Integer key for tables. Size of pKey for indices */ int bias, /* Bias search to the high end */ int *pRes /* Write search results here */ ){ int rc; /* Status code */ UnpackedRecord *pIdxKey; /* Unpacked index key */ if( pKey ){ assert( nKey==(i64)(int)nKey ); pIdxKey = sqlite3VdbeAllocUnpackedRecord(pCur->pKeyInfo); if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); if( pIdxKey->nField==0 ){ rc = SQLITE_CORRUPT_BKPT; goto moveto_done; } }else{ pIdxKey = 0; } rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); moveto_done: if( pIdxKey ){ sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); } return rc; } /* ** Restore the cursor to the position it was in (or as close to as possible) ** when saveCursorPosition() was called. Note that this call deletes the |
︙ | ︙ | |||
832 833 834 835 836 837 838 839 840 841 842 843 844 845 | ** ** Use the separate sqlite3BtreeCursorRestore() routine to restore a cursor ** back to where it ought to be if this routine returns true. */ int sqlite3BtreeCursorHasMoved(BtCursor *pCur){ return pCur->eState!=CURSOR_VALID; } /* ** This routine restores a cursor back to its original position after it ** has been moved by some outside activity (such as a btree rebalance or ** a row having been deleted out from under the cursor). ** ** On success, the *pDifferentRow parameter is false if the cursor is left | > > > > > > > > > > > | 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 | ** ** Use the separate sqlite3BtreeCursorRestore() routine to restore a cursor ** back to where it ought to be if this routine returns true. */ int sqlite3BtreeCursorHasMoved(BtCursor *pCur){ return pCur->eState!=CURSOR_VALID; } /* ** Return a pointer to a fake BtCursor object that will always answer ** false to the sqlite3BtreeCursorHasMoved() routine above. The fake ** cursor returned must not be used with any other Btree interface. */ BtCursor *sqlite3BtreeFakeValidCursor(void){ static u8 fakeCursor = CURSOR_VALID; assert( offsetof(BtCursor, eState)==0 ); return (BtCursor*)&fakeCursor; } /* ** This routine restores a cursor back to its original position after it ** has been moved by some outside activity (such as a btree rebalance or ** a row having been deleted out from under the cursor). ** ** On success, the *pDifferentRow parameter is false if the cursor is left |
︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 | } assert( offset <= (int)pBt->usableSize-5 ); assert( pEType!=0 ); *pEType = pPtrmap[offset]; if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); sqlite3PagerUnref(pDbPage); | | | 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 | } assert( offset <= (int)pBt->usableSize-5 ); assert( pEType!=0 ); *pEType = pPtrmap[offset]; if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); sqlite3PagerUnref(pDbPage); if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_PGNO(iPtrmap); return SQLITE_OK; } #else /* if defined SQLITE_OMIT_AUTOVACUUM */ #define ptrmapPut(w,x,y,z,rc) #define ptrmapGet(w,x,y,z) SQLITE_OK #define ptrmapPutOvflPtr(x, y, rc) |
︙ | ︙ | |||
1316 1317 1318 1319 1320 1321 1322 | ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); } } #endif /* | | | | | > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | > > > > > > < < < < | 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 | ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); } } #endif /* ** Defragment the page given. This routine reorganizes cells within the ** page so that there are no free-blocks on the free-block list. ** ** Parameter nMaxFrag is the maximum amount of fragmented space that may be ** present in the page after this routine returns. ** ** EVIDENCE-OF: R-44582-60138 SQLite may from time to time reorganize a ** b-tree page so that there are no freeblocks or fragment bytes, all ** unused bytes are contained in the unallocated space region, and all ** cells are packed tightly at the end of the page. */ static int defragmentPage(MemPage *pPage, int nMaxFrag){ int i; /* Loop counter */ int pc; /* Address of the i-th cell */ int hdr; /* Offset to the page header */ int size; /* Size of a cell */ int usableSize; /* Number of usable bytes on a page */ int cellOffset; /* Offset to the cell pointer array */ int cbrk; /* Offset to the cell content area */ int nCell; /* Number of cells on the page */ unsigned char *data; /* The page data */ unsigned char *temp; /* Temp area for cell content */ unsigned char *src; /* Source of content */ int iCellFirst; /* First allowable cell index */ int iCellLast; /* Last possible cell index */ assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt!=0 ); assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); assert( pPage->nOverflow==0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); temp = 0; src = data = pPage->aData; hdr = pPage->hdrOffset; cellOffset = pPage->cellOffset; nCell = pPage->nCell; assert( nCell==get2byte(&data[hdr+3]) ); iCellFirst = cellOffset + 2*nCell; usableSize = pPage->pBt->usableSize; /* This block handles pages with two or fewer free blocks and nMaxFrag ** or fewer fragmented bytes. In this case it is faster to move the ** two (or one) blocks of cells using memmove() and add the required ** offsets to each pointer in the cell-pointer array than it is to ** reconstruct the entire page. */ if( (int)data[hdr+7]<=nMaxFrag ){ int iFree = get2byte(&data[hdr+1]); if( iFree ){ int iFree2 = get2byte(&data[iFree]); /* pageFindSlot() has already verified that free blocks are sorted ** in order of offset within the page, and that no block extends ** past the end of the page. Provided the two free slots do not ** overlap, this guarantees that the memmove() calls below will not ** overwrite the usableSize byte buffer, even if the database page ** is corrupt. */ assert( iFree2==0 || iFree2>iFree ); assert( iFree+get2byte(&data[iFree+2]) <= usableSize ); assert( iFree2==0 || iFree2+get2byte(&data[iFree2+2]) <= usableSize ); if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){ u8 *pEnd = &data[cellOffset + nCell*2]; u8 *pAddr; int sz2 = 0; int sz = get2byte(&data[iFree+2]); int top = get2byte(&data[hdr+5]); if( iFree2 ){ assert( iFree+sz<=iFree2 ); /* Verified by pageFindSlot() */ sz2 = get2byte(&data[iFree2+2]); assert( iFree+sz+sz2+iFree2-(iFree+sz) <= usableSize ); memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); sz += sz2; } cbrk = top+sz; assert( cbrk+(iFree-top) <= usableSize ); memmove(&data[cbrk], &data[top], iFree-top); for(pAddr=&data[cellOffset]; pAddr<pEnd; pAddr+=2){ pc = get2byte(pAddr); if( pc<iFree ){ put2byte(pAddr, pc+sz); } else if( pc<iFree2 ){ put2byte(pAddr, pc+sz2); } } goto defragment_out; } } } cbrk = usableSize; iCellLast = usableSize - 4; for(i=0; i<nCell; i++){ u8 *pAddr; /* The i-th cell pointer */ pAddr = &data[cellOffset + i*2]; pc = get2byte(pAddr); testcase( pc==iCellFirst ); testcase( pc==iCellLast ); /* These conditions have already been verified in btreeInitPage() ** if PRAGMA cell_size_check=ON. */ if( pc<iCellFirst || pc>iCellLast ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } assert( pc>=iCellFirst && pc<=iCellLast ); size = pPage->xCellSize(pPage, &src[pc]); cbrk -= size; if( cbrk<iCellFirst || pc+size>usableSize ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); testcase( cbrk+size==usableSize ); testcase( pc+size==usableSize ); put2byte(pAddr, cbrk); if( temp==0 ){ int x; if( cbrk==pc ) continue; temp = sqlite3PagerTempSpace(pPage->pBt->pPager); x = get2byte(&data[hdr+5]); memcpy(&temp[x], &data[x], (cbrk+size) - x); src = temp; } memcpy(&data[cbrk], &src[pc], size); } data[hdr+7] = 0; defragment_out: if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } assert( cbrk>=iCellFirst ); put2byte(&data[hdr+5], cbrk); data[hdr+1] = 0; data[hdr+2] = 0; memset(&data[iCellFirst], 0, cbrk-iCellFirst); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); return SQLITE_OK; } /* ** Search the free-list on page pPg for space to store a cell nByte bytes in ** size. If one can be found, return a pointer to the space and remove it ** from the free-list. |
︙ | ︙ | |||
1423 1424 1425 1426 1427 1428 1429 1430 1431 | static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ const int hdr = pPg->hdrOffset; u8 * const aData = pPg->aData; int iAddr = hdr + 1; int pc = get2byte(&aData[iAddr]); int x; int usableSize = pPg->pBt->usableSize; assert( pc>0 ); | > < < < < | < < < | | > > | > > | 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 | static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ const int hdr = pPg->hdrOffset; u8 * const aData = pPg->aData; int iAddr = hdr + 1; int pc = get2byte(&aData[iAddr]); int x; int usableSize = pPg->pBt->usableSize; int size; /* Size of the free slot */ assert( pc>0 ); while( pc<=usableSize-4 ){ /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each ** freeblock form a big-endian integer which is the size of the freeblock ** in bytes, including the 4-byte header. */ size = get2byte(&aData[pc+2]); if( (x = size - nByte)>=0 ){ testcase( x==4 ); testcase( x==3 ); if( size+pc > usableSize ){ *pRc = SQLITE_CORRUPT_PGNO(pPg->pgno); return 0; }else if( x<4 ){ /* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total ** number of bytes in fragments may not exceed 60. */ if( aData[hdr+7]>57 ) return 0; /* Remove the slot from the free-list. Update the number of ** fragmented bytes within the page. */ memcpy(&aData[iAddr], &aData[pc], 2); aData[hdr+7] += (u8)x; }else{ /* The slot remains on the free-list. Reduce its size to account ** for the portion used by the new allocation. */ put2byte(&aData[pc+2], x); } return &aData[pc + x]; } iAddr = pc; pc = get2byte(&aData[pc]); if( pc<iAddr+size ) break; } if( pc ){ *pRc = SQLITE_CORRUPT_PGNO(pPg->pgno); } return 0; } /* ** Allocate nByte bytes of space from within the B-Tree page passed ** as the first argument. Write into *pIdx the index into pPage->aData[] |
︙ | ︙ | |||
1508 1509 1510 1511 1512 1513 1514 | ** integer, so a value of 0 is used in its place. */ top = get2byte(&data[hdr+5]); assert( top<=(int)pPage->pBt->usableSize ); /* Prevent by getAndInitPage() */ if( gap>top ){ if( top==0 && pPage->pBt->usableSize==65536 ){ top = 65536; }else{ | | | 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 | ** integer, so a value of 0 is used in its place. */ top = get2byte(&data[hdr+5]); assert( top<=(int)pPage->pBt->usableSize ); /* Prevent by getAndInitPage() */ if( gap>top ){ if( top==0 && pPage->pBt->usableSize==65536 ){ top = 65536; }else{ return SQLITE_CORRUPT_PGNO(pPage->pgno); } } /* If there is enough space between gap and top for one more cell pointer ** array entry offset, and if the freelist is not empty, then search the ** freelist looking for a free slot big enough to satisfy the request. */ |
︙ | ︙ | |||
1536 1537 1538 1539 1540 1541 1542 | /* The request could not be fulfilled using a freelist slot. Check ** to see if defragmentation is necessary. */ testcase( gap+2+nByte==top ); if( gap+2+nByte>top ){ assert( pPage->nCell>0 || CORRUPT_DB ); | | | | 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 | /* The request could not be fulfilled using a freelist slot. Check ** to see if defragmentation is necessary. */ testcase( gap+2+nByte==top ); if( gap+2+nByte>top ){ assert( pPage->nCell>0 || CORRUPT_DB ); rc = defragmentPage(pPage, MIN(4, pPage->nFree - (2+nByte))); if( rc ) return rc; top = get2byteNotZero(&data[hdr+5]); assert( gap+2+nByte<=top ); } /* Allocate memory from the gap in between the cell pointer array ** and the cell content area. The btreeInitPage() call has already ** validated the freelist. Given that the freelist is valid, there ** is no way that the allocation can extend off the end of the page. |
︙ | ︙ | |||
1575 1576 1577 1578 1579 1580 1581 | */ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ u16 iPtr; /* Address of ptr to next freeblock */ u16 iFreeBlk; /* Address of the next freeblock */ u8 hdr; /* Page header size. 0 or 100 */ u8 nFrag = 0; /* Reduction in fragmentation */ u16 iOrigSize = iSize; /* Original value of iSize */ | | | < < < < < < | > | > | | > > | | | > | > > > > > > | | < | 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 | */ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ u16 iPtr; /* Address of ptr to next freeblock */ u16 iFreeBlk; /* Address of the next freeblock */ u8 hdr; /* Page header size. 0 or 100 */ u8 nFrag = 0; /* Reduction in fragmentation */ u16 iOrigSize = iSize; /* Original value of iSize */ u16 x; /* Offset to cell content area */ u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ unsigned char *data = pPage->aData; /* Page content */ assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize ); assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( iSize>=4 ); /* Minimum cell size is 4 */ assert( iStart<=pPage->pBt->usableSize-4 ); /* The list of freeblocks must be in ascending order. Find the ** spot on the list where iStart should be inserted. */ hdr = pPage->hdrOffset; iPtr = hdr + 1; if( data[iPtr+1]==0 && data[iPtr]==0 ){ iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */ }else{ while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){ if( iFreeBlk<iPtr+4 ){ if( iFreeBlk==0 ) break; return SQLITE_CORRUPT_PGNO(pPage->pgno); } iPtr = iFreeBlk; } if( iFreeBlk>pPage->pBt->usableSize-4 ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } assert( iFreeBlk>iPtr || iFreeBlk==0 ); /* At this point: ** iFreeBlk: First freeblock after iStart, or zero if none ** iPtr: The address of a pointer to iFreeBlk ** ** Check to see if iFreeBlk should be coalesced onto the end of iStart. */ if( iFreeBlk && iEnd+3>=iFreeBlk ){ nFrag = iFreeBlk - iEnd; if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PGNO(pPage->pgno); iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); if( iEnd > pPage->pBt->usableSize ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } iSize = iEnd - iStart; iFreeBlk = get2byte(&data[iFreeBlk]); } /* If iPtr is another freeblock (that is, if iPtr is not the freelist ** pointer in the page header) then check to see if iStart should be ** coalesced onto the end of iPtr. */ if( iPtr>hdr+1 ){ int iPtrEnd = iPtr + get2byte(&data[iPtr+2]); if( iPtrEnd+3>=iStart ){ if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PGNO(pPage->pgno); nFrag += iStart - iPtrEnd; iSize = iEnd - iPtr; iStart = iPtr; } } if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PGNO(pPage->pgno); data[hdr+7] -= nFrag; } x = get2byte(&data[hdr+5]); if( iStart<=x ){ /* The new freeblock is at the beginning of the cell content area, ** so just extend the cell content area rather than create another ** freelist entry */ if( iStart<x || iPtr!=hdr+1 ) return SQLITE_CORRUPT_PGNO(pPage->pgno); put2byte(&data[hdr+1], iFreeBlk); put2byte(&data[hdr+5], iEnd); }else{ /* Insert the new freeblock into the freelist */ put2byte(&data[iPtr], iStart); } if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ /* Overwrite deleted information with zeros when the secure_delete ** option is enabled */ memset(&data[iStart], 0, iSize); } put2byte(&data[iStart], iFreeBlk); put2byte(&data[iStart+2], iSize); pPage->nFree += iOrigSize; return SQLITE_OK; } /* ** Decode the flags byte (the first byte of the header) for a page ** and initialize fields of the MemPage structure accordingly. |
︙ | ︙ | |||
1714 1715 1716 1717 1718 1719 1720 | pPage->intKeyLeaf = 0; pPage->xParseCell = btreeParseCellPtrIndex; pPage->maxLocal = pBt->maxLocal; pPage->minLocal = pBt->minLocal; }else{ /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is ** an error. */ | | > > > > > > > > > > < | < < < < < < < < < < | < | | | | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < | | > > > > > > | > > > | < | | | | > | > | | | | | | | | | | | | < | | 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 | pPage->intKeyLeaf = 0; pPage->xParseCell = btreeParseCellPtrIndex; pPage->maxLocal = pBt->maxLocal; pPage->minLocal = pBt->minLocal; }else{ /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is ** an error. */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } pPage->max1bytePayload = pBt->max1bytePayload; return SQLITE_OK; } /* ** Initialize the auxiliary information for a disk block. ** ** Return SQLITE_OK on success. If we see that the page does ** not contain a well-formed database page, then return ** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not ** guarantee that the page is well-formed. It only shows that ** we failed to detect any corruption. */ static int btreeInitPage(MemPage *pPage){ int pc; /* Address of a freeblock within pPage->aData[] */ u8 hdr; /* Offset to beginning of page header */ u8 *data; /* Equal to pPage->aData */ BtShared *pBt; /* The main btree structure */ int usableSize; /* Amount of usable space on each page */ u16 cellOffset; /* Offset from start of page to first cell pointer */ int nFree; /* Number of unused bytes on the page */ int top; /* First byte of the cell content area */ int iCellFirst; /* First allowable cell or freeblock offset */ int iCellLast; /* Last possible cell or freeblock offset */ assert( pPage->pBt!=0 ); assert( pPage->pBt->db!=0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); assert( pPage->isInit==0 ); pBt = pPage->pBt; hdr = pPage->hdrOffset; data = pPage->aData; /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating ** the b-tree page type. */ if( decodeFlags(pPage, data[hdr]) ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); pPage->maskPage = (u16)(pBt->pageSize - 1); pPage->nOverflow = 0; usableSize = pBt->usableSize; pPage->cellOffset = cellOffset = hdr + 8 + pPage->childPtrSize; pPage->aDataEnd = &data[usableSize]; pPage->aCellIdx = &data[cellOffset]; pPage->aDataOfst = &data[pPage->childPtrSize]; /* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates ** the start of the cell content area. A zero value for this integer is ** interpreted as 65536. */ top = get2byteNotZero(&data[hdr+5]); /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the ** number of cells on the page. */ pPage->nCell = get2byte(&data[hdr+3]); if( pPage->nCell>MX_CELL(pBt) ){ /* To many cells for a single page. The page must be corrupt */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } testcase( pPage->nCell==MX_CELL(pBt) ); /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only ** possible for a root page of a table that contains no rows) then the ** offset to the cell content area will equal the page size minus the ** bytes of reserved space. */ assert( pPage->nCell>0 || top==usableSize || CORRUPT_DB ); /* A malformed database page might cause us to read past the end ** of page when parsing a cell. ** ** The following block of code checks early to see if a cell extends ** past the end of a page boundary and causes SQLITE_CORRUPT to be ** returned if it does. */ iCellFirst = cellOffset + 2*pPage->nCell; iCellLast = usableSize - 4; if( pBt->db->flags & SQLITE_CellSizeCk ){ int i; /* Index into the cell pointer array */ int sz; /* Size of a cell */ if( !pPage->leaf ) iCellLast--; for(i=0; i<pPage->nCell; i++){ pc = get2byteAligned(&data[cellOffset+i*2]); testcase( pc==iCellFirst ); testcase( pc==iCellLast ); if( pc<iCellFirst || pc>iCellLast ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } sz = pPage->xCellSize(pPage, &data[pc]); testcase( pc+sz==usableSize ); if( pc+sz>usableSize ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } } if( !pPage->leaf ) iCellLast++; } /* Compute the total free space on the page ** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the ** start of the first freeblock on the page, or is zero if there are no ** freeblocks. */ pc = get2byte(&data[hdr+1]); nFree = data[hdr+7] + top; /* Init nFree to non-freeblock free space */ if( pc>0 ){ u32 next, size; if( pc<iCellFirst ){ /* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will ** always be at least one cell before the first freeblock. */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } while( 1 ){ if( pc>iCellLast ){ /* Freeblock off the end of the page */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } next = get2byte(&data[pc]); size = get2byte(&data[pc+2]); nFree = nFree + size; if( next<=pc+size+3 ) break; pc = next; } if( next>0 ){ /* Freeblock not in ascending order */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } if( pc+size>(unsigned int)usableSize ){ /* Last freeblock extends past page end */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } } /* At this point, nFree contains the sum of the offset to the start ** of the cell-content area plus the number of free bytes within ** the cell-content area. If this is greater than the usable-size ** of the page, then the page must be corrupted. This check also ** serves to verify that the offset to the start of the cell-content ** area, according to the page header, lies within the page. */ if( nFree>usableSize ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } pPage->nFree = (u16)(nFree - iCellFirst); pPage->isInit = 1; return SQLITE_OK; } /* ** Set up a raw page so that it looks like a database page holding ** no entries. */ static void zeroPage(MemPage *pPage, int flags){ unsigned char *data = pPage->aData; BtShared *pBt = pPage->pBt; u8 hdr = pPage->hdrOffset; u16 first; assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno ); assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); assert( sqlite3PagerGetData(pPage->pDbPage) == data ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pBt->mutex) ); if( pBt->btsFlags & BTS_FAST_SECURE ){ memset(&data[hdr], 0, pBt->usableSize - hdr); } data[hdr] = (char)flags; first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8); memset(&data[hdr+1], 0, 4); data[hdr+7] = 0; put2byte(&data[hdr+5], pBt->usableSize); |
︙ | ︙ | |||
1989 1990 1991 1992 1993 1994 1995 | MemPage **ppPage, /* Write the page pointer here */ BtCursor *pCur, /* Cursor to receive the page, or NULL */ int bReadOnly /* True for a read-only page */ ){ int rc; DbPage *pDbPage; assert( sqlite3_mutex_held(pBt->mutex) ); | | | 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 | MemPage **ppPage, /* Write the page pointer here */ BtCursor *pCur, /* Cursor to receive the page, or NULL */ int bReadOnly /* True for a read-only page */ ){ int rc; DbPage *pDbPage; assert( sqlite3_mutex_held(pBt->mutex) ); assert( pCur==0 || ppPage==&pCur->pPage ); assert( pCur==0 || bReadOnly==pCur->curPagerFlags ); assert( pCur==0 || pCur->iPage>0 ); if( pgno>btreePagecount(pBt) ){ rc = SQLITE_CORRUPT_BKPT; goto getAndInitPage_error; } |
︙ | ︙ | |||
2016 2017 2018 2019 2020 2021 2022 | } assert( (*ppPage)->pgno==pgno ); assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) ); /* If obtaining a child page for a cursor, we must verify that the page is ** compatible with the root page. */ if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ | | | > > > > > > > > > > > > > > > | 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 | } assert( (*ppPage)->pgno==pgno ); assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) ); /* If obtaining a child page for a cursor, we must verify that the page is ** compatible with the root page. */ if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ rc = SQLITE_CORRUPT_PGNO(pgno); releasePage(*ppPage); goto getAndInitPage_error; } return SQLITE_OK; getAndInitPage_error: if( pCur ){ pCur->iPage--; pCur->pPage = pCur->apPage[pCur->iPage]; } testcase( pgno==0 ); assert( pgno!=0 || rc==SQLITE_CORRUPT ); return rc; } /* ** Release a MemPage. This should be called once for each prior ** call to btreeGetPage. ** ** Page1 is a special case and must be released using releasePageOne(). */ static void releasePageNotNull(MemPage *pPage){ assert( pPage->aData ); assert( pPage->pBt ); assert( pPage->pDbPage!=0 ); assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); sqlite3PagerUnrefNotNull(pPage->pDbPage); } static void releasePage(MemPage *pPage){ if( pPage ) releasePageNotNull(pPage); } static void releasePageOne(MemPage *pPage){ assert( pPage!=0 ); assert( pPage->aData ); assert( pPage->pBt ); assert( pPage->pDbPage!=0 ); assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); sqlite3PagerUnrefPageOne(pPage->pDbPage); } /* ** Get an unused page. ** ** This works just like btreeGetPage() with the addition: ** |
︙ | ︙ | |||
2278 2279 2280 2281 2282 2283 2284 | pBt = sqlite3MallocZero( sizeof(*pBt) ); if( pBt==0 ){ rc = SQLITE_NOMEM_BKPT; goto btree_open_out; } rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, | | | > > | 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 | pBt = sqlite3MallocZero( sizeof(*pBt) ); if( pBt==0 ){ rc = SQLITE_NOMEM_BKPT; goto btree_open_out; } rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, sizeof(MemPage), flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ sqlite3PagerSetMmapLimit(pBt->pPager, db->szMmap); rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ goto btree_open_out; } pBt->openFlags = (u8)flags; pBt->db = db; sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; pBt->pPage1 = 0; if( sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY; #if defined(SQLITE_SECURE_DELETE) pBt->btsFlags |= BTS_SECURE_DELETE; #elif defined(SQLITE_FAST_SECURE_DELETE) pBt->btsFlags |= BTS_OVERWRITE; #endif /* EVIDENCE-OF: R-51873-39618 The page size for a database file is ** determined by the 2-byte integer located at an offset of 16 bytes from ** the beginning of the database file. */ pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16); if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ |
︙ | ︙ | |||
2743 2744 2745 2746 2747 2748 2749 | sqlite3BtreeEnter(p); n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); sqlite3BtreeLeave(p); return n; } /* | | > > | > | > > > > > > > > > > > > | | | | | 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 | sqlite3BtreeEnter(p); n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); sqlite3BtreeLeave(p); return n; } /* ** Change the values for the BTS_SECURE_DELETE and BTS_OVERWRITE flags: ** ** newFlag==0 Both BTS_SECURE_DELETE and BTS_OVERWRITE are cleared ** newFlag==1 BTS_SECURE_DELETE set and BTS_OVERWRITE is cleared ** newFlag==2 BTS_SECURE_DELETE cleared and BTS_OVERWRITE is set ** newFlag==(-1) No changes ** ** This routine acts as a query if newFlag is less than zero ** ** With BTS_OVERWRITE set, deleted content is overwritten by zeros, but ** freelist leaf pages are not written back to the database. Thus in-page ** deleted content is cleared, but freelist deleted content is not. ** ** With BTS_SECURE_DELETE, operation is like BTS_OVERWRITE with the addition ** that freelist leaf pages are written back into the database, increasing ** the amount of disk I/O. */ int sqlite3BtreeSecureDelete(Btree *p, int newFlag){ int b; if( p==0 ) return 0; sqlite3BtreeEnter(p); assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 ); assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) ); if( newFlag>=0 ){ p->pBt->btsFlags &= ~BTS_FAST_SECURE; p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag; } b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE; sqlite3BtreeLeave(p); return b; } /* ** Change the 'auto-vacuum' property of the database. If the 'autoVacuum' ** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it |
︙ | ︙ | |||
2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 | BTREE_AUTOVACUUM_INCR ); sqlite3BtreeLeave(p); return rc; #endif } /* ** Get a reference to pPage1 of the database file. This will ** also acquire a readlock on that file. ** ** SQLITE_OK is returned on success. If the file is not a ** well-formed database file, then SQLITE_CORRUPT is returned. | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | BTREE_AUTOVACUUM_INCR ); sqlite3BtreeLeave(p); return rc; #endif } /* ** If the user has not set the safety-level for this database connection ** using "PRAGMA synchronous", and if the safety-level is not already ** set to the value passed to this function as the second parameter, ** set it so. */ #if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){ sqlite3 *db; Db *pDb; if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){ while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; } if( pDb->bSyncSet==0 && pDb->safety_level!=safety_level && pDb!=&db->aDb[1] ){ pDb->safety_level = safety_level; sqlite3PagerSetFlags(pBt->pPager, pDb->safety_level | (db->flags & PAGER_FLAGS_MASK)); } } } #else # define setDefaultSyncFlag(pBt,safety_level) #endif /* ** Get a reference to pPage1 of the database file. This will ** also acquire a readlock on that file. ** ** SQLITE_OK is returned on success. If the file is not a ** well-formed database file, then SQLITE_CORRUPT is returned. |
︙ | ︙ | |||
2879 2880 2881 2882 2883 2884 2885 | */ if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){ int isOpen = 0; rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else{ | < < < < < < < < | < < < < < | > > | 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 | */ if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){ int isOpen = 0; rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else{ setDefaultSyncFlag(pBt, SQLITE_DEFAULT_WAL_SYNCHRONOUS+1); if( isOpen==0 ){ releasePageOne(pPage1); return SQLITE_OK; } } rc = SQLITE_NOTADB; }else{ setDefaultSyncFlag(pBt, SQLITE_DEFAULT_SYNCHRONOUS+1); } #endif /* EVIDENCE-OF: R-15465-20813 The maximum and minimum embedded payload ** fractions and the leaf payload fraction values must be 64, 32, and 32. ** ** The original design allowed these amounts to vary, but as of |
︙ | ︙ | |||
2939 2940 2941 2942 2943 2944 2945 | if( (u32)pageSize!=pBt->pageSize ){ /* After reading the first page of the database assuming a page size ** of BtShared.pageSize, we have discovered that the page-size is ** actually pageSize. Unlock the database, leave pBt->pPage1 at ** zero and return SQLITE_OK. The caller will call this function ** again with the correct page-size. */ | | | | 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 | if( (u32)pageSize!=pBt->pageSize ){ /* After reading the first page of the database assuming a page size ** of BtShared.pageSize, we have discovered that the page-size is ** actually pageSize. Unlock the database, leave pBt->pPage1 at ** zero and return SQLITE_OK. The caller will call this function ** again with the correct page-size. */ releasePageOne(pPage1); pBt->usableSize = usableSize; pBt->pageSize = pageSize; freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); return rc; } if( (pBt->db->flags & SQLITE_WriteSchema)==0 && nPage>nPageFile ){ rc = SQLITE_CORRUPT_BKPT; goto page1_init_failed; } /* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to ** be less than 480. In other words, if the page size is 512, then the ** reserved space size cannot exceed 32. */ if( usableSize<480 ){ |
︙ | ︙ | |||
2993 2994 2995 2996 2997 2998 2999 | } assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); pBt->pPage1 = pPage1; pBt->nPage = nPage; return SQLITE_OK; page1_init_failed: | | | 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 | } assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); pBt->pPage1 = pPage1; pBt->nPage = nPage; return SQLITE_OK; page1_init_failed: releasePageOne(pPage1); pBt->pPage1 = 0; return rc; } #ifndef NDEBUG /* ** Return the number of cursors open on pBt. This is for use |
︙ | ︙ | |||
3038 3039 3040 3041 3042 3043 3044 | assert( sqlite3_mutex_held(pBt->mutex) ); assert( countValidCursors(pBt,0)==0 || pBt->inTransaction>TRANS_NONE ); if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ MemPage *pPage1 = pBt->pPage1; assert( pPage1->aData ); assert( sqlite3PagerRefcount(pBt->pPager)==1 ); pBt->pPage1 = 0; | | | 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 | assert( sqlite3_mutex_held(pBt->mutex) ); assert( countValidCursors(pBt,0)==0 || pBt->inTransaction>TRANS_NONE ); if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ MemPage *pPage1 = pBt->pPage1; assert( pPage1->aData ); assert( sqlite3PagerRefcount(pBt->pPager)==1 ); pBt->pPage1 = 0; releasePageOne(pPage1); } } /* ** If pBt points to an empty file then convert that empty file ** into a new empty database by initializing the first page of ** the database. |
︙ | ︙ | |||
3287 3288 3289 3290 3291 3292 3293 | ** map entries for the overflow pages as well. */ static int setChildPtrmaps(MemPage *pPage){ int i; /* Counter variable */ int nCell; /* Number of cells in page pPage */ int rc; /* Return code */ BtShared *pBt = pPage->pBt; | < | | < < < < | 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 | ** map entries for the overflow pages as well. */ static int setChildPtrmaps(MemPage *pPage){ int i; /* Counter variable */ int nCell; /* Number of cells in page pPage */ int rc; /* Return code */ BtShared *pBt = pPage->pBt; Pgno pgno = pPage->pgno; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); rc = pPage->isInit ? SQLITE_OK : btreeInitPage(pPage); if( rc!=SQLITE_OK ) return rc; nCell = pPage->nCell; for(i=0; i<nCell; i++){ u8 *pCell = findCell(pPage, i); ptrmapPutOvflPtr(pPage, pCell, &rc); if( !pPage->leaf ){ Pgno childPgno = get4byte(pCell); ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); } } if( !pPage->leaf ){ Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); } return rc; } /* ** Somewhere on pPage is a pointer to page iFrom. Modify this pointer so ** that it points to iTo. Parameter eType describes the type of pointer to ** be modified, as follows: |
︙ | ︙ | |||
3338 3339 3340 3341 3342 3343 3344 | */ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); if( eType==PTRMAP_OVERFLOW2 ){ /* The pointer is always the first 4 bytes of the page in this case. */ if( get4byte(pPage->aData)!=iFrom ){ | | < | | | > > | < | | > | < < | 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 | */ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); if( eType==PTRMAP_OVERFLOW2 ){ /* The pointer is always the first 4 bytes of the page in this case. */ if( get4byte(pPage->aData)!=iFrom ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } put4byte(pPage->aData, iTo); }else{ int i; int nCell; int rc; rc = pPage->isInit ? SQLITE_OK : btreeInitPage(pPage); if( rc ) return rc; nCell = pPage->nCell; for(i=0; i<nCell; i++){ u8 *pCell = findCell(pPage, i); if( eType==PTRMAP_OVERFLOW1 ){ CellInfo info; pPage->xParseCell(pPage, pCell, &info); if( info.nLocal<info.nPayload ){ if( pCell+info.nSize > pPage->aData+pPage->pBt->usableSize ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } if( iFrom==get4byte(pCell+info.nSize-4) ){ put4byte(pCell+info.nSize-4, iTo); break; } } }else{ if( get4byte(pCell)==iFrom ){ put4byte(pCell, iTo); break; } } } if( i==nCell ){ if( eType!=PTRMAP_BTREE || get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); } } return SQLITE_OK; } /* ** Move the open database page pDbPage to location iFreePage in the |
︙ | ︙ | |||
3896 3897 3898 3899 3900 3901 3902 | BtCursor *p; int rc = SQLITE_OK; assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 ); if( pBtree ){ sqlite3BtreeEnter(pBtree); for(p=pBtree->pBt->pCursor; p; p=p->pNext){ | < < | < < | 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 | BtCursor *p; int rc = SQLITE_OK; assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 ); if( pBtree ){ sqlite3BtreeEnter(pBtree); for(p=pBtree->pBt->pCursor; p; p=p->pNext){ if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){ if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ rc = saveCursorPosition(p); if( rc!=SQLITE_OK ){ (void)sqlite3BtreeTripAllCursors(pBtree, rc, 0); break; } } }else{ sqlite3BtreeClearCursor(p); p->eState = CURSOR_FAULT; p->skipNext = errCode; } btreeReleaseAllCursorPages(p); } sqlite3BtreeLeave(pBtree); } return rc; } /* |
︙ | ︙ | |||
3970 3971 3972 3973 3974 3975 3976 | ** sure pPage1->aData is set correctly. */ if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ int nPage = get4byte(28+(u8*)pPage1->aData); testcase( nPage==0 ); if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage); testcase( pBt->nPage!=nPage ); pBt->nPage = nPage; | | | 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 | ** sure pPage1->aData is set correctly. */ if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ int nPage = get4byte(28+(u8*)pPage1->aData); testcase( nPage==0 ); if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage); testcase( pBt->nPage!=nPage ); pBt->nPage = nPage; releasePageOne(pPage1); } assert( countValidCursors(pBt, 1)==0 ); pBt->inTransaction = TRANS_READ; btreeClearHasContent(pBt); } btreeEndTransaction(p); |
︙ | ︙ | |||
4038 4039 4040 4041 4042 4043 4044 | int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ int rc = SQLITE_OK; if( p && p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); | > > > > | > | 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 | int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ int rc = SQLITE_OK; if( p && p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); if( op==SAVEPOINT_ROLLBACK ){ rc = saveAllCursors(pBt, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); } if( rc==SQLITE_OK ){ if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ pBt->nPage = 0; } rc = newDatabase(pBt); pBt->nPage = get4byte(28 + pBt->pPage1->aData); |
︙ | ︙ | |||
4207 4208 4209 4210 4211 4212 4213 | /* ** Close a cursor. The read lock on the database file is released ** when the last cursor is closed. */ int sqlite3BtreeCloseCursor(BtCursor *pCur){ Btree *pBtree = pCur->pBtree; if( pBtree ){ | < < < | < | < | < | > > > > | 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 | /* ** Close a cursor. The read lock on the database file is released ** when the last cursor is closed. */ int sqlite3BtreeCloseCursor(BtCursor *pCur){ Btree *pBtree = pCur->pBtree; if( pBtree ){ BtShared *pBt = pCur->pBt; sqlite3BtreeEnter(pBtree); assert( pBt->pCursor!=0 ); if( pBt->pCursor==pCur ){ pBt->pCursor = pCur->pNext; }else{ BtCursor *pPrev = pBt->pCursor; do{ if( pPrev->pNext==pCur ){ pPrev->pNext = pCur->pNext; break; } pPrev = pPrev->pNext; }while( ALWAYS(pPrev) ); } btreeReleaseAllCursorPages(pCur); unlockBtreeIfUnused(pBt); sqlite3_free(pCur->aOverflow); sqlite3_free(pCur->pKey); sqlite3BtreeLeave(pBtree); } return SQLITE_OK; } /* ** Make sure the BtCursor* given in the argument has a valid ** BtCursor.info structure. If it is not already valid, call ** btreeParseCell() to fill it in. ** ** BtCursor.info is a cache of the information in the current cell. ** Using this cache reduces the number of calls to btreeParseCell(). */ #ifndef NDEBUG static void assertCellInfo(BtCursor *pCur){ CellInfo info; memset(&info, 0, sizeof(info)); btreeParseCell(pCur->pPage, pCur->ix, &info); assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 ); } #else #define assertCellInfo(x) #endif static SQLITE_NOINLINE void getCellInfo(BtCursor *pCur){ if( pCur->info.nSize==0 ){ pCur->curFlags |= BTCF_ValidNKey; btreeParseCell(pCur->pPage,pCur->ix,&pCur->info); }else{ assertCellInfo(pCur); } } #ifndef NDEBUG /* The next routine used only within assert() statements */ /* ** Return true if the given BtCursor is valid. A valid cursor is one ** that is currently pointing to a row in a (non-empty) table. ** This is a verification routine is used only within assert() statements. */ int sqlite3BtreeCursorIsValid(BtCursor *pCur){ return pCur && pCur->eState==CURSOR_VALID; } #endif /* NDEBUG */ int sqlite3BtreeCursorIsValidNN(BtCursor *pCur){ assert( pCur!=0 ); return pCur->eState==CURSOR_VALID; } /* ** Return the value of the integer key or "rowid" for a table btree. ** This routine is only valid for a cursor that is pointing into a ** ordinary table btree. If the cursor points to an index btree or ** is invalid, the result of this routine is undefined. */ |
︙ | ︙ | |||
4420 4421 4422 4423 4424 4425 4426 | /* ** This function is used to read or overwrite payload information ** for the entry that the pCur cursor is pointing to. The eOp ** argument is interpreted as follows: ** ** 0: The operation is a read. Populate the overflow cache. ** 1: The operation is a write. Populate the overflow cache. | < | | | | | | < > | < < < < | | < | | < | | | < < | | | | < | < | | | | | < > > < | | | | < < < < < | < | | | | | < | | | | | | | > > > > | | | > > > > | | > | | | | | < | < < < > > | > | | < > > < < | < > > > | | < | > > < > | 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 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 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 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 | /* ** This function is used to read or overwrite payload information ** for the entry that the pCur cursor is pointing to. The eOp ** argument is interpreted as follows: ** ** 0: The operation is a read. Populate the overflow cache. ** 1: The operation is a write. Populate the overflow cache. ** ** A total of "amt" bytes are read or written beginning at "offset". ** Data is read to or from the buffer pBuf. ** ** The content being read or written might appear on the main page ** or be scattered out on multiple overflow pages. ** ** If the current cursor entry uses one or more overflow pages ** this function may allocate space for and lazily populate ** the overflow page-list cache array (BtCursor.aOverflow). ** Subsequent calls use this cache to make seeking to the supplied offset ** more efficient. ** ** Once an overflow page-list cache has been allocated, it must be ** invalidated if some other cursor writes to the same table, or if ** the cursor is moved to a different row. Additionally, in auto-vacuum ** mode, the following events may invalidate an overflow page-list cache. ** ** * An incremental vacuum, ** * A commit in auto_vacuum="full" mode, ** * Creating a table (may require moving an overflow page). */ static int accessPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ u32 offset, /* Begin reading this far into payload */ u32 amt, /* Read this many bytes */ unsigned char *pBuf, /* Write the bytes into this buffer */ int eOp /* zero to read. non-zero to write. */ ){ unsigned char *aPayload; int rc = SQLITE_OK; int iIdx = 0; MemPage *pPage = pCur->pPage; /* Btree page of current entry */ BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */ #ifdef SQLITE_DIRECT_OVERFLOW_READ unsigned char * const pBufStart = pBuf; /* Start of original out buffer */ #endif assert( pPage ); assert( eOp==0 || eOp==1 ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->ix<pPage->nCell ); assert( cursorHoldsMutex(pCur) ); getCellInfo(pCur); aPayload = pCur->info.pPayload; assert( offset+amt <= pCur->info.nPayload ); assert( aPayload > pPage->aData ); if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){ /* Trying to read or write past the end of the data is an error. The ** conditional above is really: ** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ** but is recast into its current form to avoid integer overflow problems */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } /* Check if data must be read/written to/from the btree page itself. */ if( offset<pCur->info.nLocal ){ int a = amt; if( a+offset>pCur->info.nLocal ){ a = pCur->info.nLocal - offset; } rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage); offset = 0; pBuf += a; amt -= a; }else{ offset -= pCur->info.nLocal; } if( rc==SQLITE_OK && amt>0 ){ const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ Pgno nextPage; nextPage = get4byte(&aPayload[pCur->info.nLocal]); /* If the BtCursor.aOverflow[] has not been allocated, allocate it now. ** ** The aOverflow[] array is sized at one entry for each overflow page ** in the overflow chain. The page number of the first overflow page is ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array ** 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( nOvfl>pCur->nOvflAlloc ){ Pgno *aNew = (Pgno*)sqlite3Realloc( pCur->aOverflow, nOvfl*2*sizeof(Pgno) ); if( aNew==0 ){ return SQLITE_NOMEM_BKPT; }else{ pCur->nOvflAlloc = nOvfl*2; pCur->aOverflow = aNew; } } memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); pCur->curFlags |= BTCF_ValidOvfl; }else{ /* If the overflow page-list cache has been allocated and the ** entry for the first required overflow page is valid, skip ** directly to it. */ if( pCur->aOverflow[offset/ovflSize] ){ iIdx = (offset/ovflSize); nextPage = pCur->aOverflow[iIdx]; offset = (offset%ovflSize); } } assert( rc==SQLITE_OK && amt>0 ); while( nextPage ){ /* If required, populate the overflow page-list cache. */ assert( pCur->aOverflow[iIdx]==0 || pCur->aOverflow[iIdx]==nextPage || CORRUPT_DB ); pCur->aOverflow[iIdx] = nextPage; if( offset>=ovflSize ){ /* The only reason to read this page is to obtain the page ** number for the next page in the overflow chain. The page ** data is not required. So first try to lookup the overflow ** page-list cache, if any, then fall back to the getOverflowPage() ** function. */ assert( pCur->curFlags & BTCF_ValidOvfl ); assert( pCur->pBtree->db==pBt->db ); if( pCur->aOverflow[iIdx+1] ){ nextPage = pCur->aOverflow[iIdx+1]; }else{ rc = getOverflowPage(pBt, nextPage, 0, &nextPage); } offset -= ovflSize; }else{ /* Need to read this page properly. It contains some of the ** range of data that is being read (eOp==0) or written (eOp!=0). */ #ifdef SQLITE_DIRECT_OVERFLOW_READ sqlite3_file *fd; /* File from which to do direct overflow read */ #endif int a = amt; if( a + offset > ovflSize ){ a = ovflSize - offset; } #ifdef SQLITE_DIRECT_OVERFLOW_READ /* If all the following are true: ** ** 1) this is a read operation, and ** 2) data is required from the start of this overflow page, and ** 3) there is no open write-transaction, and ** 4) the database is file-backed, and ** 5) the page is not in the WAL file ** 6) at least 4 bytes have already been read into the output buffer ** ** then data can be read directly from the database file into the ** output buffer, bypassing the page-cache altogether. This speeds ** up loading large records that span many overflow pages. */ if( eOp==0 /* (1) */ && offset==0 /* (2) */ && pBt->inTransaction==TRANS_READ /* (3) */ && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (4) */ && 0==sqlite3PagerUseWal(pBt->pPager, nextPage) /* (5) */ && &pBuf[-4]>=pBufStart /* (6) */ ){ 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; rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage, (eOp==0 ? PAGER_GET_READONLY : 0) ); if( rc==SQLITE_OK ){ aPayload = sqlite3PagerGetData(pDbPage); nextPage = get4byte(aPayload); rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); sqlite3PagerUnref(pDbPage); offset = 0; } } amt -= a; if( amt==0 ) return rc; pBuf += a; } if( rc ) break; iIdx++; } } if( rc==SQLITE_OK && amt>0 ){ /* Overflow chain ends prematurely */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } return rc; } /* ** Read part of the payload for the row at which that cursor pCur is currently ** pointing. "amt" bytes will be transferred into pBuf[]. The transfer ** begins at "offset". ** ** pCur can be pointing to either a table or an index b-tree. ** If pointing to a table btree, then the content section is read. If ** pCur is pointing to an index b-tree then the key section is read. ** ** For sqlite3BtreePayload(), the caller must ensure that pCur is pointing ** to a valid row in the table. For sqlite3BtreePayloadChecked(), the ** cursor might be invalid or might need to be restored before being read. ** ** Return SQLITE_OK on success or an error code if anything goes ** wrong. An error is returned if "offset+amt" is larger than ** the available payload. */ int sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>=0 && pCur->pPage ); assert( pCur->ix<pCur->pPage->nCell ); return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); } /* ** This variant of sqlite3BtreePayload() works even if the cursor has not ** in the CURSOR_VALID state. It is only used by the sqlite3_blob_read() ** interface. */ #ifndef SQLITE_OMIT_INCRBLOB static SQLITE_NOINLINE int accessPayloadChecked( BtCursor *pCur, u32 offset, u32 amt, void *pBuf ){ int rc; if ( pCur->eState==CURSOR_INVALID ){ return SQLITE_ABORT; } assert( cursorOwnsBtShared(pCur) ); rc = btreeRestoreCursorPosition(pCur); return rc ? rc : accessPayload(pCur, offset, amt, pBuf, 0); } int sqlite3BtreePayloadChecked(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ if( pCur->eState==CURSOR_VALID ){ assert( cursorOwnsBtShared(pCur) ); return accessPayload(pCur, offset, amt, pBuf, 0); }else{ return accessPayloadChecked(pCur, offset, amt, pBuf); } } #endif /* SQLITE_OMIT_INCRBLOB */ /* ** Return a pointer to payload information from the entry that the ** pCur cursor is pointing to. The pointer is to the beginning of ** the key if index btrees (pPage->intKey==0) and is the data for ** table btrees (pPage->intKey==1). The number of bytes of available ** key/data is written into *pAmt. If *pAmt==0, then the value |
︙ | ︙ | |||
4715 4716 4717 4718 4719 4720 4721 | ** any btree routine is called. */ static const void *fetchPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ u32 *pAmt /* Write the number of available bytes here */ ){ u32 amt; | | | | | | | 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 | ** any btree routine is called. */ static const void *fetchPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ u32 *pAmt /* Write the number of available bytes here */ ){ u32 amt; assert( pCur!=0 && pCur->iPage>=0 && pCur->pPage); assert( pCur->eState==CURSOR_VALID ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorOwnsBtShared(pCur) ); assert( pCur->ix<pCur->pPage->nCell ); assert( pCur->info.nSize>0 ); assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB ); assert( pCur->info.pPayload<pCur->pPage->aDataEnd ||CORRUPT_DB); amt = (int)(pCur->pPage->aDataEnd - pCur->info.pPayload); if( pCur->info.nLocal<amt ) amt = pCur->info.nLocal; *pAmt = amt; return (void*)pCur->info.pPayload; } /* |
︙ | ︙ | |||
4770 4771 4772 4773 4774 4775 4776 | assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); assert( pCur->iPage>=0 ); if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ return SQLITE_CORRUPT_BKPT; } pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); | > > | | | < | | 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 | assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); assert( pCur->iPage>=0 ); if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ return SQLITE_CORRUPT_BKPT; } pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); pCur->aiIdx[pCur->iPage] = pCur->ix; pCur->apPage[pCur->iPage] = pCur->pPage; pCur->ix = 0; pCur->iPage++; return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags); } #ifdef SQLITE_DEBUG /* ** Page pParent is an internal (non-leaf) tree page. This function ** asserts that page number iChild is the left-child if the iIdx'th ** cell in page pParent. Or, if iIdx is equal to the total number of ** cells in pParent, that page number iChild is the right-child of ** the page. */ |
︙ | ︙ | |||
4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 | ** ** pCur->idx is set to the cell index that contains the pointer ** to the page we are coming from. If we are coming from the ** right-most child page then pCur->idx is set to one more than ** the largest cell index. */ static void moveToParent(BtCursor *pCur){ assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>0 ); | > | | > > > | > | | < | < | < | < < < | > | | > > > | > > > > > > > | | | | > | > > | | | | 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 | ** ** pCur->idx is set to the cell index that contains the pointer ** to the page we are coming from. If we are coming from the ** right-most child page then pCur->idx is set to one more than ** the largest cell index. */ static void moveToParent(BtCursor *pCur){ MemPage *pLeaf; assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>0 ); assert( pCur->pPage ); assertParentIndex( pCur->apPage[pCur->iPage-1], pCur->aiIdx[pCur->iPage-1], pCur->pPage->pgno ); testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); pCur->ix = pCur->aiIdx[pCur->iPage-1]; pLeaf = pCur->pPage; pCur->pPage = pCur->apPage[--pCur->iPage]; releasePageNotNull(pLeaf); } /* ** Move the cursor to point to the root page of its b-tree structure. ** ** If the table has a virtual root page, then the cursor is moved to point ** to the virtual root page instead of the actual root page. A table has a ** virtual root page when the actual root page contains no cells and a ** single child page. This can only happen with the table rooted at page 1. ** ** If the b-tree structure is empty, the cursor state is set to ** CURSOR_INVALID and this routine returns SQLITE_EMPTY. Otherwise, ** the cursor is set to point to the first cell located on the root ** (or virtual root) page and the cursor state is set to CURSOR_VALID. ** ** If this function returns successfully, it may be assumed that the ** page-header flags indicate that the [virtual] root-page is the expected ** kind of b-tree page (i.e. if when opening the cursor the caller did not ** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D, ** indicating a table b-tree, or if the caller did specify a KeyInfo ** structure the flags byte is set to 0x02 or 0x0A, indicating an index ** b-tree). */ static int moveToRoot(BtCursor *pCur){ MemPage *pRoot; int rc = SQLITE_OK; assert( cursorOwnsBtShared(pCur) ); assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); assert( pCur->eState < CURSOR_REQUIRESEEK || pCur->iPage<0 ); assert( pCur->pgnoRoot>0 || pCur->iPage<0 ); if( pCur->iPage>=0 ){ if( pCur->iPage ){ releasePageNotNull(pCur->pPage); while( --pCur->iPage ){ releasePageNotNull(pCur->apPage[pCur->iPage]); } pCur->pPage = pCur->apPage[0]; goto skip_init; } }else if( pCur->pgnoRoot==0 ){ pCur->eState = CURSOR_INVALID; return SQLITE_EMPTY; }else{ assert( pCur->iPage==(-1) ); if( pCur->eState>=CURSOR_REQUIRESEEK ){ if( pCur->eState==CURSOR_FAULT ){ assert( pCur->skipNext!=SQLITE_OK ); return pCur->skipNext; } sqlite3BtreeClearCursor(pCur); } rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage, 0, pCur->curPagerFlags); if( rc!=SQLITE_OK ){ pCur->eState = CURSOR_INVALID; return rc; } pCur->iPage = 0; pCur->curIntKey = pCur->pPage->intKey; } pRoot = pCur->pPage; assert( pRoot->pgno==pCur->pgnoRoot ); /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is ** NULL, the caller expects a table b-tree. If this is not the case, ** return an SQLITE_CORRUPT error. ** ** Earlier versions of SQLite assumed that this test could not fail ** if the root page was already loaded when this function was called (i.e. ** if pCur->iPage>=0). But this is not so if the database is corrupted ** in such a way that page pRoot is linked into a second b-tree table ** (or the freelist). */ assert( pRoot->intKey==1 || pRoot->intKey==0 ); if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){ return SQLITE_CORRUPT_PGNO(pCur->pPage->pgno); } skip_init: pCur->ix = 0; pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl); pRoot = pCur->pPage; if( pRoot->nCell>0 ){ pCur->eState = CURSOR_VALID; }else if( !pRoot->leaf ){ Pgno subpage; if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT; subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); pCur->eState = CURSOR_VALID; rc = moveToChild(pCur, subpage); }else{ pCur->eState = CURSOR_INVALID; rc = SQLITE_EMPTY; } return rc; } /* ** Move the cursor down to the left-most leaf entry beneath the ** entry to which it is currently pointing. ** ** The left-most leaf is the one with the smallest key - the first ** in ascending order. */ static int moveToLeftmost(BtCursor *pCur){ Pgno pgno; int rc = SQLITE_OK; MemPage *pPage; assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState==CURSOR_VALID ); while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){ assert( pCur->ix<pPage->nCell ); pgno = get4byte(findCell(pPage, pCur->ix)); rc = moveToChild(pCur, pgno); } return rc; } /* ** Move the cursor down to the right-most leaf entry beneath the |
︙ | ︙ | |||
4953 4954 4955 4956 4957 4958 4959 | static int moveToRightmost(BtCursor *pCur){ Pgno pgno; int rc = SQLITE_OK; MemPage *pPage = 0; assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState==CURSOR_VALID ); | | | | < | | > | | | | < | 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 | static int moveToRightmost(BtCursor *pCur){ Pgno pgno; int rc = SQLITE_OK; MemPage *pPage = 0; assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState==CURSOR_VALID ); while( !(pPage = pCur->pPage)->leaf ){ pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); pCur->ix = pPage->nCell; rc = moveToChild(pCur, pgno); if( rc ) return rc; } pCur->ix = pPage->nCell-1; assert( pCur->info.nSize==0 ); assert( (pCur->curFlags & BTCF_ValidNKey)==0 ); return SQLITE_OK; } /* Move the cursor to the first entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. */ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ int rc; assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); rc = moveToRoot(pCur); if( rc==SQLITE_OK ){ assert( pCur->pPage->nCell>0 ); *pRes = 0; rc = moveToLeftmost(pCur); }else if( rc==SQLITE_EMPTY ){ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); *pRes = 1; rc = SQLITE_OK; } return rc; } /* Move the cursor to the last entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. |
︙ | ︙ | |||
5007 5008 5009 5010 5011 5012 5013 | #ifdef SQLITE_DEBUG /* This block serves to assert() that the cursor really does point ** to the last entry in the b-tree. */ int ii; for(ii=0; ii<pCur->iPage; ii++){ assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); } | | | < < < < | | | | | | | | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 | #ifdef SQLITE_DEBUG /* This block serves to assert() that the cursor really does point ** to the last entry in the b-tree. */ int ii; for(ii=0; ii<pCur->iPage; ii++){ assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); } assert( pCur->ix==pCur->pPage->nCell-1 ); assert( pCur->pPage->leaf ); #endif return SQLITE_OK; } rc = moveToRoot(pCur); if( rc==SQLITE_OK ){ assert( pCur->eState==CURSOR_VALID ); *pRes = 0; rc = moveToRightmost(pCur); if( rc==SQLITE_OK ){ pCur->curFlags |= BTCF_AtLast; }else{ pCur->curFlags &= ~BTCF_AtLast; } }else if( rc==SQLITE_EMPTY ){ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); *pRes = 1; rc = SQLITE_OK; } return rc; } /* ** Move the cursor pCur to a location within its b-tree that is ** approximately the x/1e9*nRow entry in the table, assuming the ** table contains nRow entries. So, in other words, if x==0 move ** to the first entry and if x=1e9 move to the last entry and if ** x=5e8 move to the middle entry. The final landing spot is ** approximate. ** ** Write an estimate of the number of entries in the b-tree into ** the *pnRowEst variable. ** ** This routine works by first moving the cursor to the root of the ** b-tree, then following pointers down to a leaf, selecting a pointer ** according to x. ** ** The estimated number of entries is found by multiplying the number of ** entries on the leaf page by the number of pointers at each layer of ** non-leaf pages. ** ** Return SQLITE_OK on success or an error code if problems are encountered. */ int sqlite3BtreeMovetoProportional( BtCursor *pCur, /* Cursor to reposition */ u32 x, /* approximate location to position the cursor */ sqlite3_uint64 *pnRowEst /* Write estimated entry count here */ ){ sqlite3_uint64 n = 1; int rc; Pgno chldPg; u32 mx = 1000000000; u32 perChild; u16 rx; MemPage *pPage; rc = moveToRoot(pCur); if( rc ) return rc; pPage = pCur->apPage[0]; while( !pPage->leaf ){ perChild = (mx+pPage->nCell)/(pPage->nCell+1); if( perChild<1 ) perChild = 1; rx = x/perChild; x %= perChild; mx = perChild; if( rx>=pPage->nCell ){ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); }else{ chldPg = get4byte(findCell(pPage,rx)); } n *= pPage->nCell+1; pCur->aiIdx[pCur->iPage] = rx; rc = moveToChild(pCur, chldPg); if( rc ) return rc; pPage = pCur->apPage[pCur->iPage]; } *pnRowEst = n*pPage->nCell; if( pPage->nCell==0 ){ rx = 0; }else{ perChild = mx/pPage->nCell; if( perChild<1 ) perChild = 1; rx = x/perChild; if( rx>=pPage->nCell ) rx = pPage->nCell-1; } pCur->aiIdx[pCur->iPage] = rx; return SQLITE_OK; } /* Move the cursor so that it points to an entry near the key ** specified by pIdxKey or intKey. Return a success code. ** ** For INTKEY tables, the intKey parameter is used. pIdxKey ** must be NULL. For index tables, pIdxKey is used and intKey ** is ignored. |
︙ | ︙ | |||
5088 5089 5090 5091 5092 5093 5094 | if( pIdxKey==0 && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ if( pCur->info.nKey==intKey ){ *pRes = 0; return SQLITE_OK; } | > | | | > > > > > > > > > > > > > > > > > > > | < | < < < | < | | > > | > > > > | | | > > < < | > > > | < | 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 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 | if( pIdxKey==0 && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ if( pCur->info.nKey==intKey ){ *pRes = 0; return SQLITE_OK; } if( pCur->info.nKey<intKey ){ if( (pCur->curFlags & BTCF_AtLast)!=0 ){ *pRes = -1; return SQLITE_OK; } /* If the requested key is one more than the previous key, then ** try to get there using sqlite3BtreeNext() rather than a full ** binary search. This is an optimization only. The correct answer ** is still obtained without this case, only a little more slowely */ if( pCur->info.nKey+1==intKey && !pCur->skipNext ){ *pRes = 0; rc = sqlite3BtreeNext(pCur, 0); if( rc==SQLITE_OK ){ getCellInfo(pCur); if( pCur->info.nKey==intKey ){ return SQLITE_OK; } }else if( rc==SQLITE_DONE ){ rc = SQLITE_OK; }else{ return rc; } } } } if( pIdxKey ){ xRecordCompare = sqlite3VdbeFindCompare(pIdxKey); pIdxKey->errCode = 0; assert( pIdxKey->default_rc==1 || pIdxKey->default_rc==0 || pIdxKey->default_rc==-1 ); }else{ xRecordCompare = 0; /* All keys are integers */ } rc = moveToRoot(pCur); if( rc ){ if( rc==SQLITE_EMPTY ){ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); *pRes = -1; return SQLITE_OK; } return rc; } assert( pCur->pPage ); assert( pCur->pPage->isInit ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->pPage->nCell > 0 ); assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); assert( pCur->curIntKey || pIdxKey ); for(;;){ int lwr, upr, idx, c; Pgno chldPg; MemPage *pPage = pCur->pPage; u8 *pCell; /* Pointer to current cell in pPage */ /* pPage->nCell must be greater than zero. If this is the root-page ** the cursor would have been INVALID above and this for(;;) loop ** not run. If this is not the root-page, then the moveToChild() routine ** would have already detected db corruption. Similarly, pPage must ** be the right kind (index or table) of b-tree page. Otherwise ** a moveToChild() or moveToRoot() call would have detected corruption. */ assert( pPage->nCell>0 ); assert( pPage->intKey==(pIdxKey==0) ); lwr = 0; upr = pPage->nCell-1; assert( biasRight==0 || biasRight==1 ); idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ pCur->ix = (u16)idx; if( xRecordCompare==0 ){ for(;;){ i64 nCellKey; pCell = findCellPastPtr(pPage, idx); if( pPage->intKeyLeaf ){ while( 0x80 <= *(pCell++) ){ if( pCell>=pPage->aDataEnd ){ return SQLITE_CORRUPT_PGNO(pPage->pgno); } } } getVarint(pCell, (u64*)&nCellKey); if( nCellKey<intKey ){ lwr = idx+1; if( lwr>upr ){ c = -1; break; } }else if( nCellKey>intKey ){ upr = idx-1; if( lwr>upr ){ c = +1; break; } }else{ assert( nCellKey==intKey ); pCur->ix = (u16)idx; if( !pPage->leaf ){ lwr = idx; goto moveto_next_layer; }else{ pCur->curFlags |= BTCF_ValidNKey; pCur->info.nKey = nCellKey; pCur->info.nSize = 0; *pRes = 0; return SQLITE_OK; } } assert( lwr+upr>=0 ); idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ } }else{ for(;;){ |
︙ | ︙ | |||
5217 5218 5219 5220 5221 5222 5223 | pPage->xParseCell(pPage, pCellBody, &pCur->info); nCell = (int)pCur->info.nKey; testcase( nCell<0 ); /* True if key size is 2^32 or more */ testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ testcase( nCell==2 ); /* Minimum legal index key size */ if( nCell<2 ){ | | | | > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | < > | | | < < < | | | < < | | | < | | | | | > | < < | | | | | | > > | | < > | | | < < < | | | < < < | | | | < | | | | | | | < < > | | | | | 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 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 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 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 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 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 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 5727 5728 5729 5730 5731 5732 | pPage->xParseCell(pPage, pCellBody, &pCur->info); nCell = (int)pCur->info.nKey; testcase( nCell<0 ); /* True if key size is 2^32 or more */ testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ testcase( nCell==2 ); /* Minimum legal index key size */ if( nCell<2 ){ rc = SQLITE_CORRUPT_PGNO(pPage->pgno); goto moveto_finish; } pCellKey = sqlite3Malloc( nCell+18 ); if( pCellKey==0 ){ rc = SQLITE_NOMEM_BKPT; goto moveto_finish; } pCur->ix = (u16)idx; rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); pCur->curFlags &= ~BTCF_ValidOvfl; if( rc ){ sqlite3_free(pCellKey); goto moveto_finish; } c = xRecordCompare(nCell, pCellKey, pIdxKey); sqlite3_free(pCellKey); } assert( (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) ); if( c<0 ){ lwr = idx+1; }else if( c>0 ){ upr = idx-1; }else{ assert( c==0 ); *pRes = 0; rc = SQLITE_OK; pCur->ix = (u16)idx; if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; goto moveto_finish; } if( lwr>upr ) break; assert( lwr+upr>=0 ); idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ } } assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); assert( pPage->isInit ); if( pPage->leaf ){ assert( pCur->ix<pCur->pPage->nCell ); pCur->ix = (u16)idx; *pRes = c; rc = SQLITE_OK; goto moveto_finish; } moveto_next_layer: if( lwr>=pPage->nCell ){ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); }else{ chldPg = get4byte(findCell(pPage, lwr)); } pCur->ix = (u16)lwr; rc = moveToChild(pCur, chldPg); if( rc ) break; } moveto_finish: pCur->info.nSize = 0; assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); return rc; } /* ** Return TRUE if the cursor is not pointing at an entry of the table. ** ** TRUE will be returned after a call to sqlite3BtreeNext() moves ** past the last entry in the table or sqlite3BtreePrev() moves past ** the first entry. TRUE is also returned if the table is empty. */ int sqlite3BtreeEof(BtCursor *pCur){ /* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries ** have been deleted? This API will need to change to return an error code ** as well as the boolean result value. */ return (CURSOR_VALID!=pCur->eState); } /* ** Return an estimate for the number of rows in the table that pCur is ** pointing to. Return a negative number if no estimate is currently ** available. */ 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_IfSmaller ** opcode, and it that case the cursor will always be valid and ** will always point to a leaf node. */ if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1; if( NEVER(pCur->pPage->leaf==0) ) return -1; n = pCur->pPage->nCell; for(i=0; i<pCur->iPage; i++){ n *= pCur->apPage[i]->nCell; } return n; } /* ** Advance the cursor to the next entry in the database. ** Return value: ** ** SQLITE_OK success ** SQLITE_DONE cursor is already pointing at the last element ** otherwise some kind of error occurred ** ** The main entry point is sqlite3BtreeNext(). That routine is optimized ** for the common case of merely incrementing the cell counter BtCursor.aiIdx ** to the next cell on the current page. The (slower) btreeNext() helper ** routine is called when it is necessary to move to a different page or ** to restore the cursor. ** ** If bit 0x01 of the F argument in sqlite3BtreeNext(C,F) is 1, then the ** cursor corresponds to an SQL index and this routine could have been ** skipped if the SQL index had been a unique index. The F argument ** is a hint to the implement. SQLite btree implementation does not use ** this hint, but COMDB2 does. */ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ int rc; int idx; MemPage *pPage; assert( cursorOwnsBtShared(pCur) ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); if( pCur->eState!=CURSOR_VALID ){ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); rc = restoreCursorPosition(pCur); if( rc!=SQLITE_OK ){ return rc; } if( CURSOR_INVALID==pCur->eState ){ return SQLITE_DONE; } if( pCur->skipNext ){ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT ); pCur->eState = CURSOR_VALID; if( pCur->skipNext>0 ){ pCur->skipNext = 0; return SQLITE_OK; } pCur->skipNext = 0; } } pPage = pCur->pPage; idx = ++pCur->ix; assert( pPage->isInit ); /* If the database file is corrupt, it is possible for the value of idx ** to be invalid here. This can only occur if a second cursor modifies ** the page while cursor pCur is holding a reference to it. Which can ** only happen if the database is corrupt in such a way as to link the ** page into more than one b-tree structure. */ testcase( idx>pPage->nCell ); if( idx>=pPage->nCell ){ if( !pPage->leaf ){ rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); if( rc ) return rc; return moveToLeftmost(pCur); } do{ if( pCur->iPage==0 ){ pCur->eState = CURSOR_INVALID; return SQLITE_DONE; } moveToParent(pCur); pPage = pCur->pPage; }while( pCur->ix>=pPage->nCell ); if( pPage->intKey ){ return sqlite3BtreeNext(pCur, 0); }else{ return SQLITE_OK; } } if( pPage->leaf ){ return SQLITE_OK; }else{ return moveToLeftmost(pCur); } } int sqlite3BtreeNext(BtCursor *pCur, int flags){ MemPage *pPage; UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ assert( cursorOwnsBtShared(pCur) ); assert( flags==0 || flags==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur); pPage = pCur->pPage; if( (++pCur->ix)>=pPage->nCell ){ pCur->ix--; return btreeNext(pCur); } if( pPage->leaf ){ return SQLITE_OK; }else{ return moveToLeftmost(pCur); } } /* ** Step the cursor to the back to the previous entry in the database. ** Return values: ** ** SQLITE_OK success ** SQLITE_DONE the cursor is already on the first element of the table ** otherwise some kind of error occurred ** ** The main entry point is sqlite3BtreePrevious(). That routine is optimized ** for the common case of merely decrementing the cell counter BtCursor.aiIdx ** to the previous cell on the current page. The (slower) btreePrevious() ** helper routine is called when it is necessary to move to a different page ** or to restore the cursor. ** ** If bit 0x01 of the F argument to sqlite3BtreePrevious(C,F) is 1, then ** the cursor corresponds to an SQL index and this routine could have been ** skipped if the SQL index had been a unique index. The F argument is a ** hint to the implement. The native SQLite btree implementation does not ** use this hint, but COMDB2 does. */ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ int rc; MemPage *pPage; assert( cursorOwnsBtShared(pCur) ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 ); assert( pCur->info.nSize==0 ); if( pCur->eState!=CURSOR_VALID ){ rc = restoreCursorPosition(pCur); if( rc!=SQLITE_OK ){ return rc; } if( CURSOR_INVALID==pCur->eState ){ return SQLITE_DONE; } if( pCur->skipNext ){ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT ); pCur->eState = CURSOR_VALID; if( pCur->skipNext<0 ){ pCur->skipNext = 0; return SQLITE_OK; } pCur->skipNext = 0; } } pPage = pCur->pPage; assert( pPage->isInit ); 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 ){ if( pCur->iPage==0 ){ pCur->eState = CURSOR_INVALID; return SQLITE_DONE; } moveToParent(pCur); } assert( pCur->info.nSize==0 ); assert( (pCur->curFlags & (BTCF_ValidOvfl))==0 ); pCur->ix--; pPage = pCur->pPage; if( pPage->intKey && !pPage->leaf ){ rc = sqlite3BtreePrevious(pCur, 0); }else{ rc = SQLITE_OK; } } return rc; } int sqlite3BtreePrevious(BtCursor *pCur, int flags){ assert( cursorOwnsBtShared(pCur) ); assert( flags==0 || flags==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); pCur->info.nSize = 0; if( pCur->eState!=CURSOR_VALID || pCur->ix==0 || pCur->pPage->leaf==0 ){ return btreePrevious(pCur); } pCur->ix--; return SQLITE_OK; } /* ** Allocate a new page from the database file. ** ** The new page is marked as dirty. (In other words, sqlite3PagerWrite() |
︙ | ︙ | |||
5606 5607 5608 5609 5610 5611 5612 | /* EVIDENCE-OF: R-59841-13798 The 4-byte big-endian integer at offset 32 ** stores the page number of the first page of the freelist, or zero if ** the freelist is empty. */ iTrunk = get4byte(&pPage1->aData[32]); } testcase( iTrunk==mxPage ); if( iTrunk>mxPage || nSearch++ > n ){ | | | 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 | /* EVIDENCE-OF: R-59841-13798 The 4-byte big-endian integer at offset 32 ** stores the page number of the first page of the freelist, or zero if ** the freelist is empty. */ iTrunk = get4byte(&pPage1->aData[32]); } testcase( iTrunk==mxPage ); if( iTrunk>mxPage || nSearch++ > n ){ rc = SQLITE_CORRUPT_PGNO(pPrevTrunk ? pPrevTrunk->pgno : 1); }else{ rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0); } if( rc ){ pTrunk = 0; goto end_allocate_page; } |
︙ | ︙ | |||
5635 5636 5637 5638 5639 5640 5641 | *pPgno = iTrunk; memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); *ppPage = pTrunk; pTrunk = 0; TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); }else if( k>(u32)(pBt->usableSize/4 - 2) ){ /* Value of k is out of range. Database corruption */ | | | 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 | *pPgno = iTrunk; memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); *ppPage = pTrunk; pTrunk = 0; TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); }else if( k>(u32)(pBt->usableSize/4 - 2) ){ /* Value of k is out of range. Database corruption */ rc = SQLITE_CORRUPT_PGNO(iTrunk); goto end_allocate_page; #ifndef SQLITE_OMIT_AUTOVACUUM }else if( searchList && (nearby==iTrunk || (iTrunk<nearby && eMode==BTALLOC_LE)) ){ /* The list is being searched and this trunk page is the page ** to allocate, regardless of whether it has leaves. |
︙ | ︙ | |||
5669 5670 5671 5672 5673 5674 5675 | /* The trunk page is required by the caller but it contains ** pointers to free-list leaves. The first leaf becomes a trunk ** page in this case. */ MemPage *pNewTrunk; Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); if( iNewTrunk>mxPage ){ | | | 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 | /* The trunk page is required by the caller but it contains ** pointers to free-list leaves. The first leaf becomes a trunk ** page in this case. */ MemPage *pNewTrunk; Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); if( iNewTrunk>mxPage ){ rc = SQLITE_CORRUPT_PGNO(iTrunk); goto end_allocate_page; } testcase( iNewTrunk==mxPage ); rc = btreeGetUnusedPage(pBt, iNewTrunk, &pNewTrunk, 0); if( rc!=SQLITE_OK ){ goto end_allocate_page; } |
︙ | ︙ | |||
5734 5735 5736 5737 5738 5739 5740 | }else{ closest = 0; } iPage = get4byte(&aData[8+closest*4]); testcase( iPage==mxPage ); if( iPage>mxPage ){ | | | 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 | }else{ closest = 0; } iPage = get4byte(&aData[8+closest*4]); testcase( iPage==mxPage ); if( iPage>mxPage ){ rc = SQLITE_CORRUPT_PGNO(iTrunk); goto end_allocate_page; } testcase( iPage==mxPage ); if( !searchList || (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE)) ){ int noContent; |
︙ | ︙ | |||
5990 5991 5992 5993 5994 5995 5996 | ** Free any overflow pages associated with the given Cell. Write the ** local Cell size (the number of bytes on the original page, omitting ** overflow) into *pnSize. */ static int clearCell( MemPage *pPage, /* The page that contains the Cell */ unsigned char *pCell, /* First byte of the Cell */ | | | < | < | | | > | > | | | 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 | ** Free any overflow pages associated with the given Cell. Write the ** local Cell size (the number of bytes on the original page, omitting ** overflow) into *pnSize. */ static int clearCell( MemPage *pPage, /* The page that contains the Cell */ unsigned char *pCell, /* First byte of the Cell */ CellInfo *pInfo /* Size information about the cell */ ){ BtShared *pBt; Pgno ovflPgno; int rc; int nOvfl; u32 ovflPageSize; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); pPage->xParseCell(pPage, pCell, pInfo); if( pInfo->nLocal==pInfo->nPayload ){ return SQLITE_OK; /* No overflow pages. Return without doing anything */ } if( pCell+pInfo->nSize-1 > pPage->aData+pPage->maskPage ){ /* Cell extends past end of page */ return SQLITE_CORRUPT_PGNO(pPage->pgno); } ovflPgno = get4byte(pCell + pInfo->nSize - 4); pBt = pPage->pBt; assert( pBt->usableSize > 4 ); ovflPageSize = pBt->usableSize - 4; nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1)/ovflPageSize; assert( nOvfl>0 || (CORRUPT_DB && (pInfo->nPayload + ovflPageSize)<ovflPageSize) ); while( nOvfl-- ){ Pgno iNext = 0; MemPage *pOvfl = 0; if( ovflPgno<2 || ovflPgno>btreePagecount(pBt) ){ /* 0 is not a legal page number and page 1 cannot be an ** overflow page. Therefore if ovflPgno<2 or past the end of the |
︙ | ︙ | |||
6076 6077 6078 6079 6080 6081 6082 | MemPage *pPage, /* The page that contains the cell */ unsigned char *pCell, /* Complete text of the cell */ const BtreePayload *pX, /* Payload with which to construct the cell */ int *pnSize /* Write cell size here */ ){ int nPayload; const u8 *pSrc; | | < | | | | > > > | | > > > > | > > > | | | | | | | | < > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 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 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 | MemPage *pPage, /* The page that contains the cell */ unsigned char *pCell, /* Complete text of the cell */ const BtreePayload *pX, /* Payload with which to construct the cell */ int *pnSize /* Write cell size here */ ){ int nPayload; const u8 *pSrc; int nSrc, n, rc, mn; int spaceLeft; MemPage *pToRelease; unsigned char *pPrior; unsigned char *pPayload; BtShared *pBt; Pgno pgnoOvfl; int nHeader; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); /* pPage is not necessarily writeable since pCell might be auxiliary ** buffer space that is separate from the pPage buffer area */ assert( pCell<pPage->aData || pCell>=&pPage->aData[pPage->pBt->pageSize] || sqlite3PagerIswriteable(pPage->pDbPage) ); /* Fill in the header. */ nHeader = pPage->childPtrSize; if( pPage->intKey ){ nPayload = pX->nData + pX->nZero; pSrc = pX->pData; nSrc = pX->nData; assert( pPage->intKeyLeaf ); /* fillInCell() only called for leaves */ nHeader += putVarint32(&pCell[nHeader], nPayload); nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey); }else{ assert( pX->nKey<=0x7fffffff && pX->pKey!=0 ); nSrc = nPayload = (int)pX->nKey; pSrc = pX->pKey; nHeader += putVarint32(&pCell[nHeader], nPayload); } /* Fill in the payload */ 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; *pnSize = n; assert( nSrc<=nPayload ); testcase( nSrc<nPayload ); memcpy(pPayload, pSrc, nSrc); memset(pPayload+nSrc, 0, nPayload-nSrc); return SQLITE_OK; } /* If we reach this point, it means that some of the content will need ** to spill onto overflow pages. */ mn = pPage->minLocal; n = mn + (nPayload - mn) % (pPage->pBt->usableSize - 4); testcase( n==pPage->maxLocal ); testcase( n==pPage->maxLocal+1 ); if( n > pPage->maxLocal ) n = mn; spaceLeft = n; *pnSize = n + nHeader + 4; pPrior = &pCell[nHeader+n]; pToRelease = 0; pgnoOvfl = 0; pBt = pPage->pBt; /* At this point variables should be set as follows: ** ** nPayload Total payload size in bytes ** pPayload Begin writing payload here ** spaceLeft Space available at pPayload. If nPayload>spaceLeft, ** that means content must spill into overflow pages. ** *pnSize Size of the local cell (not counting overflow pages) ** pPrior Where to write the pgno of the first overflow page ** ** Use a call to btreeParseCellPtr() to verify that the values above ** were computed correctly. */ #ifdef SQLITE_DEBUG { CellInfo info; pPage->xParseCell(pPage, pCell, &info); assert( nHeader==(int)(info.pPayload - pCell) ); assert( info.nKey==pX->nKey ); assert( *pnSize == info.nSize ); assert( spaceLeft == info.nLocal ); } #endif /* Write the payload into the local Cell and any extra into overflow pages */ while( 1 ){ n = nPayload; if( n>spaceLeft ) n = spaceLeft; /* If pToRelease is not zero than pPayload points into the data area ** of pToRelease. Make sure pToRelease is still writeable. */ assert( pToRelease==0 || sqlite3PagerIswriteable(pToRelease->pDbPage) ); /* If pPayload is part of the data area of pPage, then make sure pPage ** is still writeable */ assert( pPayload<pPage->aData || pPayload>=&pPage->aData[pBt->pageSize] || sqlite3PagerIswriteable(pPage->pDbPage) ); if( nSrc>=n ){ memcpy(pPayload, pSrc, n); }else if( nSrc>0 ){ n = nSrc; memcpy(pPayload, pSrc, n); }else{ memset(pPayload, 0, n); } nPayload -= n; if( nPayload<=0 ) break; pPayload += n; pSrc += n; nSrc -= n; spaceLeft -= n; if( spaceLeft==0 ){ MemPage *pOvfl = 0; #ifndef SQLITE_OMIT_AUTOVACUUM Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */ if( pBt->autoVacuum ){ do{ pgnoOvfl++; } while( PTRMAP_ISPAGE(pBt, pgnoOvfl) || pgnoOvfl==PENDING_BYTE_PAGE(pBt) |
︙ | ︙ | |||
6208 6209 6210 6211 6212 6213 6214 | releasePage(pToRelease); pToRelease = pOvfl; pPrior = pOvfl->aData; put4byte(pPrior, 0); pPayload = &pOvfl->aData[4]; spaceLeft = pBt->usableSize - 4; } | < < < < < < < < < < < < < < < < < < < < < < < < | 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 | releasePage(pToRelease); pToRelease = pOvfl; pPrior = pOvfl->aData; put4byte(pPrior, 0); pPayload = &pOvfl->aData[4]; spaceLeft = pBt->usableSize - 4; } } releasePage(pToRelease); return SQLITE_OK; } /* ** Remove the i-th cell from pPage. This routine effects pPage only. |
︙ | ︙ | |||
6253 6254 6255 6256 6257 6258 6259 | u32 pc; /* Offset to cell content of cell being deleted */ u8 *data; /* pPage->aData */ u8 *ptr; /* Used to move bytes around within data[] */ int rc; /* The return code */ int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ if( *pRC ) return; | < | | 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 | u32 pc; /* Offset to cell content of cell being deleted */ u8 *data; /* pPage->aData */ u8 *ptr; /* Used to move bytes around within data[] */ int rc; /* The return code */ int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ if( *pRC ) return; assert( idx>=0 && idx<pPage->nCell ); assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); data = pPage->aData; ptr = &pPage->aCellIdx[2*idx]; pc = get2byte(ptr); hdr = pPage->hdrOffset; testcase( pc==get2byte(&data[hdr+5]) ); testcase( pc+sz==pPage->pBt->usableSize ); if( pc+sz > pPage->pBt->usableSize ){ *pRC = SQLITE_CORRUPT_BKPT; return; } rc = freeSpace(pPage, pc, sz); if( rc ){ *pRC = rc; return; |
︙ | ︙ | |||
6337 6338 6339 6340 6341 6342 6343 | memcpy(pTemp, pCell, sz); pCell = pTemp; } if( iChild ){ put4byte(pCell, iChild); } j = pPage->nOverflow++; | > > > | | 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 | memcpy(pTemp, pCell, sz); pCell = pTemp; } if( iChild ){ put4byte(pCell, iChild); } j = pPage->nOverflow++; /* Comparison against ArraySize-1 since we hold back one extra slot ** as a contingency. In other words, never need more than 3 overflow ** slots but 4 are allocated, just to be safe. */ assert( j < ArraySize(pPage->apOvfl)-1 ); pPage->apOvfl[j] = pCell; pPage->aiOvfl[j] = (u16)i; /* When multiple overflows occur, they are always sequential and in ** sorted order. This invariants arise because multiple overflows can ** only occur when inserting divider cells into the parent page during ** balancing, and the dividers are adjacent and sorted. |
︙ | ︙ | |||
7077 7078 7079 7080 7081 7082 7083 | if( rc ){ memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; } nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; if( (i--)==0 ) break; | | | 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 | if( rc ){ memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; } nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; if( (i--)==0 ) break; if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ apDiv[i] = pParent->apOvfl[0]; pgno = get4byte(apDiv[i]); szNew[i] = pParent->xCellSize(pParent, apDiv[i]); pParent->nOverflow = 0; }else{ apDiv[i] = findCell(pParent, i+nxDiv-pParent->nOverflow); pgno = get4byte(apDiv[i]); |
︙ | ︙ | |||
7099 7100 7101 7102 7103 7104 7105 | ** later on. ** ** But not if we are in secure-delete mode. In secure-delete mode, ** the dropCell() routine will overwrite the entire cell with zeroes. ** In this case, temporarily copy the cell into the aOvflSpace[] ** buffer. It will be copied out again as soon as the aSpace[] buffer ** is allocated. */ | | | 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 | ** later on. ** ** But not if we are in secure-delete mode. In secure-delete mode, ** the dropCell() routine will overwrite the entire cell with zeroes. ** In this case, temporarily copy the cell into the aOvflSpace[] ** buffer. It will be copied out again as soon as the aSpace[] buffer ** is allocated. */ if( pBt->btsFlags & BTS_FAST_SECURE ){ int iOff; iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); if( (iOff+szNew[i])>(int)pBt->usableSize ){ rc = SQLITE_CORRUPT_BKPT; memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; |
︙ | ︙ | |||
7131 7132 7133 7134 7135 7136 7137 | nMaxCells*sizeof(u8*) /* b.apCell */ + nMaxCells*sizeof(u16) /* b.szCell */ + pBt->pageSize; /* aSpace1 */ /* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer ** that is more than 6 times the database page size. */ assert( szScratch<=6*(int)pBt->pageSize ); | | | 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 | nMaxCells*sizeof(u8*) /* b.apCell */ + nMaxCells*sizeof(u16) /* b.szCell */ + pBt->pageSize; /* aSpace1 */ /* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer ** that is more than 6 times the database page size. */ assert( szScratch<=6*(int)pBt->pageSize ); b.apCell = sqlite3StackAllocRaw(0, szScratch ); if( b.apCell==0 ){ rc = SQLITE_NOMEM_BKPT; goto balance_cleanup; } b.szCell = (u16*)&b.apCell[nMaxCells]; aSpace1 = (u8*)&b.szCell[nMaxCells]; assert( EIGHT_BYTE_ALIGNMENT(aSpace1) ); |
︙ | ︙ | |||
7269 7270 7271 7272 7273 7274 7275 | ** usableSpace: Number of bytes of space available on each sibling. ** */ usableSpace = pBt->usableSize - 12 + leafCorrection; for(i=0; i<nOld; i++){ MemPage *p = apOld[i]; szNew[i] = usableSpace - p->nFree; | < | 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 | ** usableSpace: Number of bytes of space available on each sibling. ** */ usableSpace = pBt->usableSize - 12 + leafCorrection; for(i=0; i<nOld; i++){ MemPage *p = apOld[i]; szNew[i] = usableSpace - p->nFree; for(j=0; j<p->nOverflow; j++){ szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]); } cntNew[i] = cntOld[i]; } k = nOld; for(i=0; i<k; i++){ |
︙ | ︙ | |||
7667 7668 7669 7670 7671 7672 7673 | ** ** It is critical that the child page be defragmented before being ** copied into the parent, because if the parent is page 1 then it will ** by smaller than the child due to the database header, and so all the ** free space needs to be up front. */ assert( nNew==1 || CORRUPT_DB ); | | | 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 | ** ** It is critical that the child page be defragmented before being ** copied into the parent, because if the parent is page 1 then it will ** by smaller than the child due to the database header, and so all the ** free space needs to be up front. */ assert( nNew==1 || CORRUPT_DB ); rc = defragmentPage(apNew[0], -1); testcase( rc!=SQLITE_OK ); assert( apNew[0]->nFree == (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2) || rc!=SQLITE_OK ); copyNodeContent(apNew[0], pParent, &rc); freePage(apNew[0], &rc); |
︙ | ︙ | |||
7710 7711 7712 7713 7714 7715 7716 | } #endif /* ** Cleanup before returning. */ balance_cleanup: | | | 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 | } #endif /* ** Cleanup before returning. */ balance_cleanup: sqlite3StackFree(0, b.apCell); for(i=0; i<nOld; i++){ releasePage(apOld[i]); } for(i=0; i<nNew; i++){ releasePage(apNew[i]); } |
︙ | ︙ | |||
7809 7810 7811 7812 7813 7814 7815 | u8 *pFree = 0; VVA_ONLY( int balance_quick_called = 0 ); VVA_ONLY( int balance_deeper_called = 0 ); do { int iPage = pCur->iPage; | | > | | > | 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 | u8 *pFree = 0; VVA_ONLY( int balance_quick_called = 0 ); VVA_ONLY( int balance_deeper_called = 0 ); do { int iPage = pCur->iPage; MemPage *pPage = pCur->pPage; if( iPage==0 ){ if( pPage->nOverflow ){ /* The root page of the b-tree is overfull. In this case call the ** balance_deeper() function to create a new child for the root-page ** and copy the current contents of the root-page to it. The ** next iteration of the do-loop will balance the child page. */ assert( balance_deeper_called==0 ); VVA_ONLY( balance_deeper_called++ ); rc = balance_deeper(pPage, &pCur->apPage[1]); if( rc==SQLITE_OK ){ pCur->iPage = 1; pCur->ix = 0; pCur->aiIdx[0] = 0; pCur->apPage[0] = pPage; pCur->pPage = pCur->apPage[1]; assert( pCur->pPage->nOverflow ); } }else{ break; } }else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){ break; }else{ |
︙ | ︙ | |||
7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 | pPage->nOverflow = 0; /* The next iteration of the do-loop balances the parent page. */ releasePage(pPage); pCur->iPage--; assert( pCur->iPage>=0 ); } }while( rc==SQLITE_OK ); if( pFree ){ sqlite3PageFree(pFree); } return rc; | > | 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 | pPage->nOverflow = 0; /* The next iteration of the do-loop balances the parent page. */ releasePage(pPage); pCur->iPage--; assert( pCur->iPage>=0 ); pCur->pPage = pCur->apPage[pCur->iPage]; } }while( rc==SQLITE_OK ); if( pFree ){ sqlite3PageFree(pFree); } return rc; |
︙ | ︙ | |||
7931 7932 7933 7934 7935 7936 7937 | ** hold the content of the row. ** ** For an index btree (used for indexes and WITHOUT ROWID tables), the ** key is an arbitrary byte sequence stored in pX.pKey,nKey. The ** pX.pData,nData,nZero fields must be zero. ** ** If the seekResult parameter is non-zero, then a successful call to | | | > | > | < < | | | > | > | > > | 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 | ** hold the content of the row. ** ** For an index btree (used for indexes and WITHOUT ROWID tables), the ** key is an arbitrary byte sequence stored in pX.pKey,nKey. The ** pX.pData,nData,nZero fields must be zero. ** ** If the seekResult parameter is non-zero, then a successful call to ** MovetoUnpacked() to seek cursor pCur to (pKey,nKey) has already ** been performed. In other words, if seekResult!=0 then the cursor ** is currently pointing to a cell that will be adjacent to the cell ** to be inserted. If seekResult<0 then pCur points to a cell that is ** smaller then (pKey,nKey). If seekResult>0 then pCur points to a cell ** that is larger than (pKey,nKey). ** ** If seekResult==0, that means pCur is pointing at some unknown location. ** In that case, this routine must seek the cursor to the correct insertion ** point for (pKey,nKey) before doing the insertion. For index btrees, ** if pX->nMem is non-zero, then pX->aMem contains pointers to the unpacked ** key values and pX->aMem can be used instead of pX->pKey to avoid having ** to decode the key. */ int sqlite3BtreeInsert( BtCursor *pCur, /* Insert data into the table of this cursor */ const BtreePayload *pX, /* Content of the row to be inserted */ int flags, /* True if this is likely an append */ int seekResult /* Result of prior MovetoUnpacked() call */ ){ int rc; int loc = seekResult; /* -1: before desired location +1: after */ int szNew = 0; int idx; MemPage *pPage; Btree *p = pCur->pBtree; BtShared *pBt = p->pBt; unsigned char *oldCell; unsigned char *newCell = 0; assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags ); if( pCur->eState==CURSOR_FAULT ){ assert( pCur->skipNext!=SQLITE_OK ); return pCur->skipNext; } assert( cursorOwnsBtShared(pCur) ); |
︙ | ︙ | |||
7997 7998 7999 8000 8001 8002 8003 | if( rc ) return rc; } if( pCur->pKeyInfo==0 ){ assert( pX->pKey==0 ); /* If this is an insert into a table b-tree, invalidate any incrblob ** cursors open on the row being replaced */ | | > > > > > | < | | | > > > > > > > > > > > > | > | | | | > > > > > > > > > > > > > > > > > | | > | 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 | if( rc ) return rc; } if( pCur->pKeyInfo==0 ){ assert( pX->pKey==0 ); /* If this is an insert into a table b-tree, invalidate any incrblob ** cursors open on the row being replaced */ invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing ** to a row with the same key as the new entry being inserted. */ assert( (flags & BTREE_SAVEPOSITION)==0 || ((pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey) ); /* If the cursor is currently on the last row and we are appending a ** new row onto the end, set the "loc" to avoid an unnecessary ** btreeMoveto() call */ if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){ loc = 0; }else if( loc==0 ){ rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc); if( rc ) return rc; } }else if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){ if( pX->nMem ){ UnpackedRecord r; r.pKeyInfo = pCur->pKeyInfo; r.aMem = pX->aMem; r.nField = pX->nMem; r.default_rc = 0; r.errCode = 0; r.r1 = 0; r.r2 = 0; r.eqSeen = 0; rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc); }else{ rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc); } if( rc ) return rc; } assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) ); pPage = pCur->pPage; assert( pPage->intKey || pX->nKey>=0 ); assert( pPage->leaf || !pPage->intKey ); TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, loc==0 ? "overwrite" : "new entry")); assert( pPage->isInit ); newCell = pBt->pTmpSpace; assert( newCell!=0 ); rc = fillInCell(pPage, newCell, pX, &szNew); if( rc ) goto end_insert; assert( szNew==pPage->xCellSize(pPage, newCell) ); assert( szNew <= MX_CELL_SIZE(pBt) ); idx = pCur->ix; if( loc==0 ){ CellInfo info; assert( idx<pPage->nCell ); rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ){ goto end_insert; } oldCell = findCell(pPage, idx); if( !pPage->leaf ){ memcpy(newCell, oldCell, 4); } rc = clearCell(pPage, oldCell, &info); if( info.nSize==szNew && info.nLocal==info.nPayload && (!ISAUTOVACUUM || szNew<pPage->minLocal) ){ /* Overwrite the old cell with the new if they are the same size. ** We could also try to do this if the old cell is smaller, then add ** the leftover space to the free list. But experiments show that ** doing that is no faster then skipping this optimization and just ** 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+szNew > pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT; memcpy(oldCell, newCell, szNew); return SQLITE_OK; } dropCell(pPage, idx, info.nSize, &rc); if( rc ) goto end_insert; }else if( loc<0 && pPage->nCell>0 ){ assert( pPage->leaf ); idx = ++pCur->ix; pCur->curFlags &= ~BTCF_ValidNKey; }else{ assert( pPage->leaf ); } insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); assert( pPage->nOverflow==0 || rc==SQLITE_OK ); assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); |
︙ | ︙ | |||
8084 8085 8086 8087 8088 8089 8090 | pCur->curFlags &= ~(BTCF_ValidNKey); rc = balance(pCur); /* Must make sure nOverflow is reset to zero even if the balance() ** fails. Internal data structure corruption will result otherwise. ** Also, set the cursor state to invalid. This stops saveCursorPosition() ** from trying to save the current position of the cursor. */ | | > > > > > > > > > | > > > > > | | 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 8383 8384 8385 8386 8387 8388 8389 | pCur->curFlags &= ~(BTCF_ValidNKey); rc = balance(pCur); /* Must make sure nOverflow is reset to zero even if the balance() ** fails. Internal data structure corruption will result otherwise. ** Also, set the cursor state to invalid. This stops saveCursorPosition() ** from trying to save the current position of the cursor. */ pCur->pPage->nOverflow = 0; pCur->eState = CURSOR_INVALID; if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){ btreeReleaseAllCursorPages(pCur); if( pCur->pKeyInfo ){ assert( pCur->pKey==0 ); pCur->pKey = sqlite3Malloc( pX->nKey ); if( pCur->pKey==0 ){ rc = SQLITE_NOMEM; }else{ memcpy(pCur->pKey, pX->pKey, pX->nKey); } } pCur->eState = CURSOR_REQUIRESEEK; pCur->nKey = pX->nKey; } } assert( pCur->iPage<0 || pCur->pPage->nOverflow==0 ); end_insert: return rc; } /* ** Delete the entry that the cursor is pointing to. |
︙ | ︙ | |||
8118 8119 8120 8121 8122 8123 8124 | Btree *p = pCur->pBtree; BtShared *pBt = p->pBt; int rc; /* Return code */ MemPage *pPage; /* Page to delete cell from */ unsigned char *pCell; /* Pointer to cell to delete */ int iCellIdx; /* Index of cell to delete */ int iCellDepth; /* Depth of node containing pCell */ | | | | | | 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 8436 | Btree *p = pCur->pBtree; BtShared *pBt = p->pBt; int rc; /* Return code */ MemPage *pPage; /* Page to delete cell from */ unsigned char *pCell; /* Pointer to cell to delete */ int iCellIdx; /* Index of cell to delete */ int iCellDepth; /* Depth of node containing pCell */ CellInfo info; /* Size of the cell being deleted */ int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */ assert( cursorOwnsBtShared(pCur) ); assert( pBt->inTransaction==TRANS_WRITE ); assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); assert( pCur->curFlags & BTCF_WriteFlag ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) ); assert( pCur->ix<pCur->pPage->nCell ); assert( pCur->eState==CURSOR_VALID ); assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; pCell = findCell(pPage, iCellIdx); /* If the bPreserve flag is set to true, 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. |
︙ | ︙ | |||
8167 8168 8169 8170 8171 8172 8173 | ** the cursor to the largest entry in the tree that is smaller than ** the entry being deleted. This cell will replace the cell being deleted ** from the internal node. The 'previous' entry is used for this instead ** of the 'next' entry, as the previous entry is always a part of the ** sub-tree headed by the child page of the cell being deleted. This makes ** balancing the tree following the delete operation easier. */ if( !pPage->leaf ){ | < | > | | | | | > > > > > | 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 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 8505 8506 8507 8508 8509 8510 8511 | ** the cursor to the largest entry in the tree that is smaller than ** the entry being deleted. This cell will replace the cell being deleted ** from the internal node. The 'previous' entry is used for this instead ** of the 'next' entry, as the previous entry is always a part of the ** sub-tree headed by the child page of the cell being deleted. This makes ** balancing the tree following the delete operation easier. */ if( !pPage->leaf ){ rc = sqlite3BtreePrevious(pCur, 0); assert( rc!=SQLITE_DONE ); if( rc ) return rc; } /* Save the positions of any other cursors open on this table before ** making any modifications. */ if( pCur->curFlags & BTCF_Multiple ){ rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); if( rc ) return rc; } /* If this is a delete operation to remove a row from a table b-tree, ** invalidate any incrblob cursors open on the row being deleted. */ if( pCur->pKeyInfo==0 ){ invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0); } /* Make the page containing the entry to be deleted writable. Then free any ** overflow pages associated with the entry and finally remove the cell ** itself from within the page. */ rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ) return rc; rc = clearCell(pPage, pCell, &info); dropCell(pPage, iCellIdx, info.nSize, &rc); if( rc ) return rc; /* If the cell deleted was not located on a leaf page, then the cursor ** is currently pointing to the largest entry in the sub-tree headed ** by the child-page of the cell that was just deleted from an internal ** node. The cell from the leaf node needs to be moved to the internal ** node to replace the deleted cell. */ if( !pPage->leaf ){ MemPage *pLeaf = pCur->pPage; int nCell; Pgno n; unsigned char *pTmp; 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_BKPT; nCell = pLeaf->xCellSize(pLeaf, pCell); assert( MX_CELL_SIZE(pBt) >= nCell ); pTmp = pBt->pTmpSpace; assert( pTmp!=0 ); rc = sqlite3PagerWrite(pLeaf->pDbPage); |
︙ | ︙ | |||
8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 | ** on the leaf node first. If the balance proceeds far enough up the ** tree that we can be sure that any problem in the internal node has ** been corrected, so be it. Otherwise, after balancing the leaf node, ** walk the cursor up the tree to the internal node and balance it as ** well. */ rc = balance(pCur); if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ while( pCur->iPage>iCellDepth ){ releasePage(pCur->apPage[pCur->iPage--]); } rc = balance(pCur); } if( rc==SQLITE_OK ){ if( bSkipnext ){ assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) ); | > > > | | > > | 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 | ** on the leaf node first. If the balance proceeds far enough up the ** tree that we can be sure that any problem in the internal node has ** been corrected, so be it. Otherwise, after balancing the leaf node, ** walk the cursor up the tree to the internal node and balance it as ** well. */ rc = balance(pCur); if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ releasePageNotNull(pCur->pPage); pCur->iPage--; while( pCur->iPage>iCellDepth ){ releasePage(pCur->apPage[pCur->iPage--]); } pCur->pPage = pCur->apPage[pCur->iPage]; rc = balance(pCur); } if( rc==SQLITE_OK ){ if( bSkipnext ){ assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) ); assert( pPage==pCur->pPage || CORRUPT_DB ); assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell ); pCur->eState = CURSOR_SKIPNEXT; if( iCellIdx>=pPage->nCell ){ pCur->skipNext = -1; pCur->ix = pPage->nCell-1; }else{ pCur->skipNext = 1; } }else{ rc = moveToRoot(pCur); if( bPreserve ){ btreeReleaseAllCursorPages(pCur); pCur->eState = CURSOR_REQUIRESEEK; } if( rc==SQLITE_EMPTY ) rc = SQLITE_OK; } } return rc; } /* ** Create a new BTree table. Write into *piTable the page |
︙ | ︙ | |||
8441 8442 8443 8444 8445 8446 8447 | int *pnChange /* Add number of Cells freed to this counter */ ){ MemPage *pPage; int rc; unsigned char *pCell; int i; int hdr; | | | | 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 | int *pnChange /* Add number of Cells freed to this counter */ ){ MemPage *pPage; int rc; unsigned char *pCell; int i; int hdr; CellInfo info; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); if( rc ) return rc; if( pPage->bBusy ){ rc = SQLITE_CORRUPT_BKPT; goto cleardatabasepage_out; } pPage->bBusy = 1; hdr = pPage->hdrOffset; for(i=0; i<pPage->nCell; i++){ pCell = findCell(pPage, i); if( !pPage->leaf ){ rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); if( rc ) goto cleardatabasepage_out; } rc = clearCell(pPage, pCell, &info); if( rc ) goto cleardatabasepage_out; } if( !pPage->leaf ){ rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange); if( rc ) goto cleardatabasepage_out; }else if( pnChange ){ assert( pPage->intKey || CORRUPT_DB ); |
︙ | ︙ | |||
8509 8510 8511 8512 8513 8514 8515 | rc = saveAllCursors(pBt, (Pgno)iTable, 0); if( SQLITE_OK==rc ){ /* Invalidate all incrblob cursors open on table iTable (assuming iTable ** is the root of a table b-tree - if it is not, the following call is ** a no-op). */ | | | 8807 8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 | rc = saveAllCursors(pBt, (Pgno)iTable, 0); if( SQLITE_OK==rc ){ /* Invalidate all incrblob cursors open on table iTable (assuming iTable ** is the root of a table b-tree - if it is not, the following call is ** a no-op). */ invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); } sqlite3BtreeLeave(p); return rc; } /* |
︙ | ︙ | |||
8552 8553 8554 8555 8556 8557 8558 | static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ int rc; MemPage *pPage = 0; BtShared *pBt = p->pBt; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->inTrans==TRANS_WRITE ); | | < < < < < < < < < < < < < < < < < < < < | 8850 8851 8852 8853 8854 8855 8856 8857 8858 8859 8860 8861 8862 8863 8864 | static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ int rc; MemPage *pPage = 0; BtShared *pBt = p->pBt; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->inTrans==TRANS_WRITE ); assert( iTable>=2 ); rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); if( rc ) return rc; rc = sqlite3BtreeClearTable(p, iTable, 0); if( rc ){ releasePage(pPage); return rc; |
︙ | ︙ | |||
8743 8744 8745 8746 8747 8748 8749 | ** Otherwise, if an error is encountered (i.e. an IO error or database ** corruption) an SQLite error code is returned. */ int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){ i64 nEntry = 0; /* Value to return in *pnEntry */ int rc; /* Return code */ | | > < | | 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 | ** Otherwise, if an error is encountered (i.e. an IO error or database ** corruption) an SQLite error code is returned. */ int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){ i64 nEntry = 0; /* Value to return in *pnEntry */ int rc; /* Return code */ rc = moveToRoot(pCur); if( rc==SQLITE_EMPTY ){ *pnEntry = 0; return SQLITE_OK; } /* Unless an error occurs, the following loop runs one iteration for each ** page in the B-Tree structure (not including overflow pages). */ while( rc==SQLITE_OK ){ int iIdx; /* Index of child node in parent */ MemPage *pPage; /* Current page of the b-tree */ /* If this is a leaf page or the tree is not an int-key tree, then ** this page contains countable entries. Increment the entry counter ** accordingly. */ pPage = pCur->pPage; if( pPage->leaf || !pPage->intKey ){ nEntry += pPage->nCell; } /* pPage is a leaf node. This loop navigates the cursor so that it ** points to the first interior cell that it points to the parent of ** the next page in the tree that has not yet been visited. The |
︙ | ︙ | |||
8783 8784 8785 8786 8787 8788 8789 | do { if( pCur->iPage==0 ){ /* All pages of the b-tree have been visited. Return successfully. */ *pnEntry = nEntry; return moveToRoot(pCur); } moveToParent(pCur); | | | | | | 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 | do { if( pCur->iPage==0 ){ /* All pages of the b-tree have been visited. Return successfully. */ *pnEntry = nEntry; return moveToRoot(pCur); } moveToParent(pCur); }while ( pCur->ix>=pCur->pPage->nCell ); pCur->ix++; pPage = pCur->pPage; } /* Descend to the child node of the cell that the cursor currently ** points at. This is the right-child if (iIdx==pPage->nCell). */ iIdx = pCur->ix; if( iIdx==pPage->nCell ){ rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); }else{ rc = moveToChild(pCur, get4byte(findCell(pPage, iIdx))); } } |
︙ | ︙ | |||
9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 | /* Check for integer primary key out of range */ if( pPage->intKey ){ if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){ checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey); } maxKey = info.nKey; } /* Check the content overflow list */ if( info.nPayload>info.nLocal ){ int nPage; /* Number of pages on the overflow chain */ Pgno pgnoOvfl; /* First page of the overflow chain */ assert( pc + info.nSize - 4 <= usableSize ); | > | 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469 | /* Check for integer primary key out of range */ if( pPage->intKey ){ if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){ checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey); } maxKey = info.nKey; keyCanBeEqual = 0; /* Only the first key on the page may ==maxKey */ } /* Check the content overflow list */ if( info.nPayload>info.nLocal ){ int nPage; /* Number of pages on the overflow chain */ Pgno pgnoOvfl; /* First page of the overflow chain */ assert( pc + info.nSize - 4 <= usableSize ); |
︙ | ︙ | |||
9626 9627 9628 9629 9630 9631 9632 | if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){ return SQLITE_READONLY; } assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 && pCsr->pBt->inTransaction==TRANS_WRITE ); assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); | | | 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 | if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){ return SQLITE_READONLY; } assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 && pCsr->pBt->inTransaction==TRANS_WRITE ); assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); assert( pCsr->pPage->intKey ); return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1); } /* ** Mark this cursor as an incremental blob cursor. */ |
︙ | ︙ |
Changes to src/btree.h.
︙ | ︙ | |||
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 | int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ int iTable, /* Index of root page */ int wrFlag, /* 1 for writing. 0 for read-only */ struct KeyInfo*, /* First argument to compare function */ BtCursor *pCursor /* Space to write cursor structure */ ); int sqlite3BtreeCursorSize(void); void sqlite3BtreeCursorZero(BtCursor*); void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); #ifdef SQLITE_ENABLE_CURSOR_HINTS void sqlite3BtreeCursorHint(BtCursor*, int, ...); #endif int sqlite3BtreeCloseCursor(BtCursor*); int sqlite3BtreeMovetoUnpacked( BtCursor*, UnpackedRecord *pUnKey, i64 intKey, int bias, int *pRes ); int sqlite3BtreeCursorHasMoved(BtCursor*); int sqlite3BtreeCursorRestore(BtCursor*, int*); int sqlite3BtreeDelete(BtCursor*, u8 flags); | > > | > | 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 | int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ int iTable, /* Index of root page */ int wrFlag, /* 1 for writing. 0 for read-only */ struct KeyInfo*, /* First argument to compare function */ BtCursor *pCursor /* Space to write cursor structure */ ); BtCursor *sqlite3BtreeFakeValidCursor(void); int sqlite3BtreeCursorSize(void); void sqlite3BtreeCursorZero(BtCursor*); void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); #ifdef SQLITE_ENABLE_CURSOR_HINTS void sqlite3BtreeCursorHint(BtCursor*, int, ...); #endif int sqlite3BtreeCloseCursor(BtCursor*); int sqlite3BtreeMovetoProportional(BtCursor*,u32,u64*); int sqlite3BtreeMovetoUnpacked( BtCursor*, UnpackedRecord *pUnKey, i64 intKey, int bias, int *pRes ); int sqlite3BtreeCursorHasMoved(BtCursor*); int sqlite3BtreeCursorRestore(BtCursor*, int*); int sqlite3BtreeDelete(BtCursor*, u8 flags); /* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */ #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */ #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */ #define BTREE_APPEND 0x08 /* Insert is likely an append */ /* An instance of the BtreePayload object describes the content of a single ** entry in either an index or table btree. ** ** Index btrees (used for indexes and also WITHOUT ROWID tables) contain ** an arbitrary key and no data. These btrees have pKey,nKey set to their ** key and pData,nData,nZero set to zero. |
︙ | ︙ | |||
271 272 273 274 275 276 277 278 279 280 281 282 | ** organized and understandable, and it also helps the resulting code to ** run a little faster by using fewer registers for parameter passing. */ struct BtreePayload { const void *pKey; /* Key content for indexes. NULL for tables */ sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */ const void *pData; /* Data for tables. NULL for indexes */ int nData; /* Size of pData. 0 if none. */ int nZero; /* Extra zero data appended after pData,nData */ }; int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload, | > > | | | | < > > > | 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 | ** organized and understandable, and it also helps the resulting code to ** run a little faster by using fewer registers for parameter passing. */ struct BtreePayload { const void *pKey; /* Key content for indexes. NULL for tables */ sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */ const void *pData; /* Data for tables. NULL for indexes */ sqlite3_value *aMem; /* First of nMem value in the unpacked pKey */ u16 nMem; /* Number of aMem[] value. Might be zero */ int nData; /* Size of pData. 0 if none. */ int nZero; /* Extra zero data appended after pData,nData */ }; int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload, int flags, int seekResult); int sqlite3BtreeFirst(BtCursor*, int *pRes); int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int flags); int sqlite3BtreeEof(BtCursor*); int sqlite3BtreePrevious(BtCursor*, int flags); i64 sqlite3BtreeIntegerKey(BtCursor*); int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); u32 sqlite3BtreePayloadSize(BtCursor*); char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); struct Pager *sqlite3BtreePager(Btree*); i64 sqlite3BtreeRowCountEst(BtCursor*); #ifndef SQLITE_OMIT_INCRBLOB int sqlite3BtreePayloadChecked(BtCursor*, u32 offset, u32 amt, void*); int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeIncrblobCursor(BtCursor *); #endif void sqlite3BtreeClearCursor(BtCursor *); int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask); int sqlite3BtreeIsReadonly(Btree *pBt); int sqlite3HeaderSizeBtree(void); #ifndef NDEBUG int sqlite3BtreeCursorIsValid(BtCursor*); #endif int sqlite3BtreeCursorIsValidNN(BtCursor*); #ifndef SQLITE_OMIT_BTREECOUNT int sqlite3BtreeCount(BtCursor *, i64 *); #endif #ifdef SQLITE_TEST int sqlite3BtreeCursorInfo(BtCursor*, int*, int); |
︙ | ︙ |
Changes to src/btreeInt.h.
︙ | ︙ | |||
255 256 257 258 259 260 261 | */ #define PTF_INTKEY 0x01 #define PTF_ZERODATA 0x02 #define PTF_LEAFDATA 0x04 #define PTF_LEAF 0x08 /* | | | | | | | < | > > > | | | < < < < < < < < | 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 | */ #define PTF_INTKEY 0x01 #define PTF_ZERODATA 0x02 #define PTF_LEAFDATA 0x04 #define PTF_LEAF 0x08 /* ** An instance of this object stores information about each a single database ** page that has been loaded into memory. The information in this object ** is derived from the raw on-disk page content. ** ** As each database page is loaded into memory, the pager allocats an ** instance of this object and zeros the first 8 bytes. (This is the ** "extra" information associated with each page of the pager.) ** ** Access to all fields of this structure is controlled by the mutex ** stored in MemPage.pBt->mutex. */ struct MemPage { u8 isInit; /* True if previously initialized. MUST BE FIRST! */ u8 bBusy; /* Prevent endless loops on corrupt database files */ u8 intKey; /* True if table b-trees. False for index b-trees */ u8 intKeyLeaf; /* True if the leaf of an intKey table */ Pgno pgno; /* Page number for this page */ /* Only the first 8 bytes (above) are zeroed by pager.c when a new page ** is allocated. All fields that follow must be initialized before use */ u8 leaf; /* True if a leaf page */ u8 hdrOffset; /* 100 for page 1. 0 otherwise */ u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ u8 max1bytePayload; /* min(maxLocal,127) */ u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ u16 cellOffset; /* Index in aData of first cell pointer */ u16 nFree; /* Number of free bytes on the page */ u16 nCell; /* Number of cells on this page, local and ovfl */ u16 maskPage; /* Mask for page offset */ u16 aiOvfl[4]; /* Insert the i-th overflow cell before the aiOvfl-th ** non-overflow cell */ u8 *apOvfl[4]; /* Pointers to the body of overflow cells */ BtShared *pBt; /* Pointer to BtShared that this page is part of */ u8 *aData; /* Pointer to disk image of the page data */ u8 *aDataEnd; /* One byte past the end of usable data */ u8 *aCellIdx; /* The cell index area */ u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */ DbPage *pDbPage; /* Pager page handle */ u16 (*xCellSize)(MemPage*,u8*); /* cellSizePtr method */ void (*xParseCell)(MemPage*,u8*,CellInfo*); /* btreeParseCell method */ }; /* ** A linked list of the following structures is stored at BtShared.pLock. ** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor ** is opened on the table with root page BtShared.iTable. Locks are removed ** from this list when a transaction is committed or rolled back, or when ** a btree handle is closed. */ |
︙ | ︙ | |||
450 451 452 453 454 455 456 | /* ** Allowed values for BtShared.btsFlags */ #define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */ #define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */ #define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */ | > > | | | | | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 | /* ** Allowed values for BtShared.btsFlags */ #define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */ #define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */ #define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */ #define BTS_OVERWRITE 0x0008 /* Overwrite deleted content with zeros */ #define BTS_FAST_SECURE 0x000c /* Combination of the previous two */ #define BTS_INITIALLY_EMPTY 0x0010 /* Database was empty at trans start */ #define BTS_NO_WAL 0x0020 /* Do not open write-ahead-log files */ #define BTS_EXCLUSIVE 0x0040 /* pWriter has an exclusive lock */ #define BTS_PENDING 0x0080 /* Waiting for read-locks to clear */ /* ** An instance of the following structure is used to hold information ** about a cell. The parseCellPtr() function fills in this structure ** based on information extract from the raw disk page. */ struct CellInfo { |
︙ | ︙ | |||
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | ** ** skipNext meaning: ** eState==SKIPNEXT && skipNext>0: Next sqlite3BtreeNext() is no-op. ** eState==SKIPNEXT && skipNext<0: Next sqlite3BtreePrevious() is no-op. ** eState==FAULT: Cursor fault with skipNext as error code. */ struct BtCursor { Btree *pBtree; /* The Btree to which this cursor belongs */ BtShared *pBt; /* The BtShared this cursor points to */ BtCursor *pNext; /* Forms a linked list of all cursors */ Pgno *aOverflow; /* Cache of overflow page locations */ CellInfo info; /* A parse of the cell we are pointing at */ i64 nKey; /* Size of pKey, or last integer key */ void *pKey; /* Saved key that was cursor last known position */ Pgno pgnoRoot; /* The root page of this tree */ | > > > > > < < < < < < | | > > | | 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 | ** ** skipNext meaning: ** eState==SKIPNEXT && skipNext>0: Next sqlite3BtreeNext() is no-op. ** eState==SKIPNEXT && skipNext<0: Next sqlite3BtreePrevious() is no-op. ** eState==FAULT: Cursor fault with skipNext as error code. */ struct BtCursor { u8 eState; /* One of the CURSOR_XXX constants (see below) */ u8 curFlags; /* zero or more BTCF_* flags defined below */ u8 curPagerFlags; /* Flags to send to sqlite3PagerGet() */ u8 hints; /* As configured by CursorSetHints() */ int nOvflAlloc; /* Allocated size of aOverflow[] array */ Btree *pBtree; /* The Btree to which this cursor belongs */ BtShared *pBt; /* The BtShared this cursor points to */ BtCursor *pNext; /* Forms a linked list of all cursors */ Pgno *aOverflow; /* Cache of overflow page locations */ CellInfo info; /* A parse of the cell we are pointing at */ i64 nKey; /* Size of pKey, or last integer key */ void *pKey; /* Saved key that was cursor last known position */ Pgno pgnoRoot; /* The root page of this tree */ int skipNext; /* Prev() is noop if negative. Next() is noop if positive. ** Error code if eState==CURSOR_FAULT */ /* All fields above are zeroed when the cursor is allocated. See ** sqlite3BtreeCursorZero(). Fields that follow must be manually ** initialized. */ i8 iPage; /* Index of current page in apPage */ u8 curIntKey; /* Value of apPage[0]->intKey */ u16 ix; /* Current index for apPage[iPage] */ u16 aiIdx[BTCURSOR_MAX_DEPTH-1]; /* Current index in apPage[i] */ struct KeyInfo *pKeyInfo; /* Arg passed to comparison function */ MemPage *pPage; /* Current page */ MemPage *apPage[BTCURSOR_MAX_DEPTH-1]; /* Stack of parents of current page */ }; /* ** Legal values for BtCursor.curFlags */ #define BTCF_WriteFlag 0x01 /* True if a write cursor */ #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ |
︙ | ︙ | |||
694 695 696 697 698 699 700 | /* ** get2byteAligned(), unlike get2byte(), requires that its argument point to a ** two-byte aligned address. get2bytea() is only used for accessing the ** cell addresses in a btree header. */ #if SQLITE_BYTEORDER==4321 # define get2byteAligned(x) (*(u16*)(x)) | | < | < | 691 692 693 694 695 696 697 698 699 700 701 702 703 704 | /* ** get2byteAligned(), unlike get2byte(), requires that its argument point to a ** two-byte aligned address. get2bytea() is only used for accessing the ** cell addresses in a btree header. */ #if SQLITE_BYTEORDER==4321 # define get2byteAligned(x) (*(u16*)(x)) #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4008000 # define get2byteAligned(x) __builtin_bswap16(*(u16*)(x)) #elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 # define get2byteAligned(x) _byteswap_ushort(*(u16*)(x)) #else # define get2byteAligned(x) ((x)[0]<<8 | (x)[1]) #endif |
Changes to src/build.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | #ifndef SQLITE_OMIT_SHARED_CACHE /* ** The TableLock structure is only used by the sqlite3TableLock() and ** codeTableLocks() functions. */ struct TableLock { | | | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #ifndef SQLITE_OMIT_SHARED_CACHE /* ** The TableLock structure is only used by the sqlite3TableLock() and ** codeTableLocks() functions. */ struct TableLock { int iDb; /* The database containing the table to be locked */ int iTab; /* The root page of the table to be locked */ u8 isWriteLock; /* True for write lock. False for a read lock */ const char *zLockName; /* Name of the table */ }; /* ** Record the fact that we want to lock a table at run-time. ** ** The table to be locked has root page iTab and is found in database iDb. ** A read or a write lock can be taken depending on isWritelock. |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); int i; int nBytes; TableLock *p; assert( iDb>=0 ); for(i=0; i<pToplevel->nTableLock; i++){ p = &pToplevel->aTableLock[i]; if( p->iDb==iDb && p->iTab==iTab ){ p->isWriteLock = (p->isWriteLock || isWriteLock); return; } } nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1); pToplevel->aTableLock = sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes); if( pToplevel->aTableLock ){ p = &pToplevel->aTableLock[pToplevel->nTableLock++]; p->iDb = iDb; p->iTab = iTab; p->isWriteLock = isWriteLock; | > > | | 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 | ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); int i; int nBytes; TableLock *p; assert( iDb>=0 ); if( iDb==1 ) return; if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; for(i=0; i<pToplevel->nTableLock; i++){ p = &pToplevel->aTableLock[i]; if( p->iDb==iDb && p->iTab==iTab ){ p->isWriteLock = (p->isWriteLock || isWriteLock); return; } } nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1); pToplevel->aTableLock = sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes); if( pToplevel->aTableLock ){ p = &pToplevel->aTableLock[pToplevel->nTableLock++]; p->iDb = iDb; p->iTab = iTab; p->isWriteLock = isWriteLock; p->zLockName = zName; }else{ pToplevel->nTableLock = 0; sqlite3OomFault(pToplevel->db); } } /* |
︙ | ︙ | |||
93 94 95 96 97 98 99 | pVdbe = sqlite3GetVdbe(pParse); assert( pVdbe!=0 ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */ for(i=0; i<pParse->nTableLock; i++){ TableLock *p = &pParse->aTableLock[i]; int p1 = p->iDb; sqlite3VdbeAddOp4(pVdbe, OP_TableLock, p1, p->iTab, p->isWriteLock, | | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | pVdbe = sqlite3GetVdbe(pParse); assert( pVdbe!=0 ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */ for(i=0; i<pParse->nTableLock; i++){ TableLock *p = &pParse->aTableLock[i]; int p1 = p->iDb; sqlite3VdbeAddOp4(pVdbe, OP_TableLock, p1, p->iTab, p->isWriteLock, p->zLockName, P4_STATIC); } } #else #define codeTableLocks(x) #endif /* |
︙ | ︙ | |||
302 303 304 305 306 307 308 | #if SQLITE_USER_AUTHENTICATION /* Only the admin user is allowed to know that the sqlite_user table ** exists */ if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){ return 0; } #endif | > | | | | | | | | > > > > > > | | 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 | #if SQLITE_USER_AUTHENTICATION /* Only the admin user is allowed to know that the sqlite_user table ** exists */ if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){ return 0; } #endif while(1){ for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){ assert( sqlite3SchemaMutexHeld(db, j, 0) ); p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName); if( p ) return p; } } /* Not found. If the name we were looking for was temp.sqlite_master ** then change the name to sqlite_temp_master and try again. */ if( sqlite3StrICmp(zName, MASTER_NAME)!=0 ) break; if( sqlite3_stricmp(zDatabase, db->aDb[1].zDbSName)!=0 ) break; zName = TEMP_MASTER_NAME; } return 0; } /* ** Locate the in-memory structure that describes a particular database ** table given the name of that table and (optionally) the name of the ** database containing the table. Return NULL if not found. Also leave an ** error message in pParse->zErrMsg. |
︙ | ︙ | |||
346 347 348 349 350 351 352 353 354 355 356 357 358 359 | const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table"; #ifndef SQLITE_OMIT_VIRTUALTABLE if( sqlite3FindDbName(pParse->db, zDbase)<1 ){ /* If zName is the not the name of a table in the schema created using ** CREATE, then check to see if it is the name of an virtual table that ** can be an eponymous virtual table. */ Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName); if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ return pMod->pEpoTab; } } #endif if( (flags & LOCATE_NOERR)==0 ){ if( zDbase ){ | > > > | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table"; #ifndef SQLITE_OMIT_VIRTUALTABLE if( sqlite3FindDbName(pParse->db, zDbase)<1 ){ /* If zName is the not the name of a table in the schema created using ** CREATE, then check to see if it is the name of an virtual table that ** can be an eponymous virtual table. */ Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName); if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ pMod = sqlite3PragmaVtabRegister(pParse->db, zName); } if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ return pMod->pEpoTab; } } #endif if( (flags & LOCATE_NOERR)==0 ){ if( zDbase ){ |
︙ | ︙ | |||
463 464 465 466 467 468 469 | while( ALWAYS(p) && p->pNext!=pIndex ){ p = p->pNext; } if( ALWAYS(p && p->pNext==pIndex) ){ p->pNext = pIndex->pNext; } } freeIndex(db, pIndex); } | | | 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | while( ALWAYS(p) && p->pNext!=pIndex ){ p = p->pNext; } if( ALWAYS(p && p->pNext==pIndex) ){ p->pNext = pIndex->pNext; } } freeIndex(db, pIndex); } db->mDbFlags |= DBFLAG_SchemaChange; } /* ** Look through the list of open database files in db->aDb[] and if ** any have been closed, remove them from the list. Reallocate the ** db->aDb[] structure to a smaller size, if possible. ** |
︙ | ︙ | |||
498 499 500 501 502 503 504 | sqlite3DbFree(db, db->aDb); db->aDb = db->aDbStatic; } } /* ** Reset the schema for the database at index iDb. Also reset the | | > | | < | | | | | < < < | | < > | | < > > > | | | 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 | sqlite3DbFree(db, db->aDb); db->aDb = db->aDbStatic; } } /* ** Reset the schema for the database at index iDb. Also reset the ** TEMP schema. The reset is deferred if db->nSchemaLock is not zero. ** Deferred resets may be run by calling with iDb<0. */ void sqlite3ResetOneSchema(sqlite3 *db, int iDb){ int i; assert( iDb<db->nDb ); if( iDb>=0 ){ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); DbSetProperty(db, iDb, DB_ResetWanted); DbSetProperty(db, 1, DB_ResetWanted); } if( db->nSchemaLock==0 ){ for(i=0; i<db->nDb; i++){ if( DbHasProperty(db, i, DB_ResetWanted) ){ sqlite3SchemaClear(db->aDb[i].pSchema); } } } } /* ** Erase all schema information from all attached databases (including ** "main" and "temp") for a single database connection. */ void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){ int i; sqlite3BtreeEnterAll(db); assert( db->nSchemaLock==0 ); for(i=0; i<db->nDb; i++){ Db *pDb = &db->aDb[i]; if( pDb->pSchema ){ sqlite3SchemaClear(pDb->pSchema); } } db->mDbFlags &= ~DBFLAG_SchemaChange; sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); sqlite3CollapseDatabaseArray(db); } /* ** This routine is called when a commit occurs. */ void sqlite3CommitInternalChanges(sqlite3 *db){ db->mDbFlags &= ~DBFLAG_SchemaChange; } /* ** Delete memory allocated for the column names of a table or view (the ** Table.aCol[] array). */ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){ |
︙ | ︙ | |||
583 584 585 586 587 588 589 | ** contains lookaside memory. (Table objects in the schema do not use ** lookaside memory, but some ephemeral Table objects do.) Or the ** db parameter can be used with db->pnBytesFreed to measure the memory ** used by the Table object. */ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ Index *pIndex, *pNext; | < > > | | > > | 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | ** contains lookaside memory. (Table objects in the schema do not use ** lookaside memory, but some ephemeral Table objects do.) Or the ** db parameter can be used with db->pnBytesFreed to measure the memory ** used by the Table object. */ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ Index *pIndex, *pNext; #ifdef SQLITE_DEBUG /* Record the number of outstanding lookaside allocations in schema Tables ** prior to doing any free() operations. Since schema Tables do not use ** lookaside, this number should not change. */ int nLookaside = 0; if( db && (pTable->tabFlags & TF_Ephemeral)==0 ){ nLookaside = sqlite3LookasideUsed(db, 0); } #endif /* Delete all indices associated with this table. */ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ pNext = pIndex->pNext; assert( pIndex->pSchema==pTable->pSchema || (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) ); if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){ |
︙ | ︙ | |||
623 624 625 626 627 628 629 | sqlite3ExprListDelete(db, pTable->pCheck); #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3VtabClear(db, pTable); #endif sqlite3DbFree(db, pTable); /* Verify that no lookaside memory was used by schema tables */ | | | | | 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 | sqlite3ExprListDelete(db, pTable->pCheck); #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3VtabClear(db, pTable); #endif sqlite3DbFree(db, pTable); /* Verify that no lookaside memory was used by schema tables */ assert( nLookaside==0 || nLookaside==sqlite3LookasideUsed(db,0) ); } void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ /* Do not delete the table until the reference count reaches zero. */ if( !pTable ) return; if( ((!db || 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. */ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){ Table *p; Db *pDb; assert( db!=0 ); assert( iDb>=0 && iDb<db->nDb ); assert( zTabName ); assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */ pDb = &db->aDb[iDb]; p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, 0); sqlite3DeleteTable(db, p); db->mDbFlags |= DBFLAG_SchemaChange; } /* ** Given a token, return a string that consists of the text of that ** token. Space to hold the returned string ** is obtained from sqliteMalloc() and must be freed by the calling ** function. |
︙ | ︙ | |||
682 683 684 685 686 687 688 | /* ** Open the sqlite_master table stored in database number iDb for ** writing. The table is opened using cursor 0. */ void sqlite3OpenMasterTable(Parse *p, int iDb){ Vdbe *v = sqlite3GetVdbe(p); | | | > > > | 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 | /* ** Open the sqlite_master table stored in database number iDb for ** writing. The table is opened using cursor 0. */ void sqlite3OpenMasterTable(Parse *p, int iDb){ Vdbe *v = sqlite3GetVdbe(p); sqlite3TableLock(p, iDb, MASTER_ROOT, 1, MASTER_NAME); sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, MASTER_ROOT, iDb, 5); if( p->nTab==0 ){ p->nTab = 1; } } /* ** Parameter zName points to a nul-terminated buffer containing the name ** of a database ("main", "temp" or the name of an attached db). This ** function returns the index of the named database in db->aDb[], or ** -1 if the named db cannot be found. */ int sqlite3FindDbName(sqlite3 *db, const char *zName){ int i = -1; /* Database number */ if( zName ){ Db *pDb; for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){ if( 0==sqlite3_stricmp(pDb->zDbSName, zName) ) break; /* "main" is always an acceptable alias for the primary database ** even if it has been renamed using SQLITE_DBCONFIG_MAINDBNAME. */ if( i==0 && 0==sqlite3_stricmp("main", zName) ) break; } } return i; } /* ** The token *pName contains the name of a database (either "main" or |
︙ | ︙ | |||
759 760 761 762 763 764 765 | *pUnqual = pName2; iDb = sqlite3FindDb(db, pName1); if( iDb<0 ){ sqlite3ErrorMsg(pParse, "unknown database %T", pName1); return -1; } }else{ | | > | 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 | *pUnqual = pName2; iDb = sqlite3FindDb(db, pName1); if( iDb<0 ){ sqlite3ErrorMsg(pParse, "unknown database %T", pName1); return -1; } }else{ assert( db->init.iDb==0 || db->init.busy || (db->mDbFlags & DBFLAG_Vacuum)!=0); iDb = db->init.iDb; *pUnqual = pName1; } return iDb; } /* |
︙ | ︙ | |||
919 920 921 922 923 924 925 | pParse->rc = SQLITE_NOMEM_BKPT; pParse->nErr++; goto begin_table_error; } pTable->zName = zName; pTable->iPKey = -1; pTable->pSchema = db->aDb[iDb].pSchema; | | > > > > | 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 | pParse->rc = SQLITE_NOMEM_BKPT; pParse->nErr++; goto begin_table_error; } pTable->zName = zName; pTable->iPKey = -1; pTable->pSchema = db->aDb[iDb].pSchema; pTable->nTabRef = 1; #ifdef SQLITE_DEFAULT_ROWEST pTable->nRowLogEst = sqlite3LogEst(SQLITE_DEFAULT_ROWEST); #else pTable->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); #endif assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; /* If this is the magic sqlite_sequence table used by autoincrement, ** then record a pointer to this table in the main database structure ** so that INSERT can find the table easily. */ |
︙ | ︙ | |||
987 988 989 990 991 992 993 | */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) if( isView || isVirtual ){ sqlite3VdbeAddOp2(v, OP_Integer, 0, reg2); }else #endif { | | > | 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 | */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) if( isView || isVirtual ){ sqlite3VdbeAddOp2(v, OP_Integer, 0, reg2); }else #endif { pParse->addrCrTab = sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY); } sqlite3OpenMasterTable(pParse, iDb); sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1); sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC); sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); sqlite3VdbeAddOp0(v, OP_Close); |
︙ | ︙ | |||
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 | ** the column currently under construction. */ void sqlite3AddNotNull(Parse *pParse, int onError){ Table *p; p = pParse->pNewTable; if( p==0 || NEVER(p->nCol<1) ) return; p->aCol[p->nCol-1].notNull = (u8)onError; } /* ** Scan the column type name zType (length nType) and return the ** associated affinity type. ** ** This routine does a case-independent search of zType for the | > | 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 | ** the column currently under construction. */ void sqlite3AddNotNull(Parse *pParse, int onError){ Table *p; p = pParse->pNewTable; if( p==0 || NEVER(p->nCol<1) ) return; p->aCol[p->nCol-1].notNull = (u8)onError; p->tabFlags |= TF_HasNotNull; } /* ** Scan the column type name zType (length nType) and return the ** associated affinity type. ** ** This routine does a case-independent search of zType for the |
︙ | ︙ | |||
1646 1647 1648 1649 1650 1651 1652 | ** This routine runs at the end of parsing a CREATE TABLE statement that ** has a WITHOUT ROWID clause. The job of this routine is to convert both ** internal schema data structures and the generated VDBE code so that they ** are appropriate for a WITHOUT ROWID table instead of a rowid table. ** Changes include: ** ** (1) Set all columns of the PRIMARY KEY schema object to be NOT NULL. | | < | | | 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 | ** This routine runs at the end of parsing a CREATE TABLE statement that ** has a WITHOUT ROWID clause. The job of this routine is to convert both ** internal schema data structures and the generated VDBE code so that they ** are appropriate for a WITHOUT ROWID table instead of a rowid table. ** Changes include: ** ** (1) Set all columns of the PRIMARY KEY schema object to be NOT NULL. ** (2) Convert P3 parameter of the OP_CreateBtree from BTREE_INTKEY ** into BTREE_BLOBKEY. ** (3) Bypass the creation of the sqlite_master table entry ** for the PRIMARY KEY as the primary key index is now ** identified by the sqlite_master table entry of the table itself. ** (4) Set the Index.tnum of the PRIMARY KEY Index object in the ** schema to the rootpage from the main table. ** (5) Add all table columns to the PRIMARY KEY Index object ** so that the PRIMARY KEY is a covering index. The surplus ** columns are part of KeyInfo.nAllField and are not used for ** sorting or lookup or uniqueness checks. ** (6) Replace the rowid tail on all automatically generated UNIQUE ** indices with the PRIMARY KEY columns. ** ** For virtual tables, only (1) is performed. */ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ |
︙ | ︙ | |||
1685 1686 1687 1688 1689 1690 1691 | } } /* The remaining transformations only apply to b-tree tables, not to ** virtual tables */ if( IN_DECLARE_VTAB ) return; | | | < | | 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 | } } /* The remaining transformations only apply to b-tree tables, not to ** virtual tables */ if( IN_DECLARE_VTAB ) return; /* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY ** into BTREE_BLOBKEY. */ if( pParse->addrCrTab ){ assert( v ); sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY); } /* Locate the PRIMARY KEY index. Or, if this table was originally ** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index. */ if( pTab->iPKey>=0 ){ ExprList *pList; |
︙ | ︙ | |||
1714 1715 1716 1717 1718 1719 1720 | SQLITE_IDXTYPE_PRIMARYKEY); if( db->mallocFailed ) return; pPk = sqlite3PrimaryKeyIndex(pTab); pTab->iPKey = -1; }else{ pPk = sqlite3PrimaryKeyIndex(pTab); | < < < < < < < < < > > > > > > > > > | 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 | SQLITE_IDXTYPE_PRIMARYKEY); if( db->mallocFailed ) return; pPk = sqlite3PrimaryKeyIndex(pTab); pTab->iPKey = -1; }else{ pPk = sqlite3PrimaryKeyIndex(pTab); /* ** Remove all redundant columns from the PRIMARY KEY. For example, change ** "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)". Later ** code assumes the PRIMARY KEY contains no repeated columns. */ for(i=j=1; i<pPk->nKeyCol; i++){ if( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) ){ pPk->nColumn--; }else{ pPk->aiColumn[j++] = pPk->aiColumn[i]; } } pPk->nKeyCol = j; } assert( pPk!=0 ); pPk->isCovering = 1; if( !db->init.imposterTable ) pPk->uniqNotNull = 1; nPk = pPk->nKeyCol; /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master ** table entry. This is only required if currently generating VDBE ** code for a CREATE TABLE (not when parsing one as part of reading ** a database schema). */ if( v && pPk->tnum>0 ){ assert( db->init.busy==0 ); sqlite3VdbeChangeOpcode(v, pPk->tnum, OP_Goto); } /* The root page of the PRIMARY KEY is the table root page */ pPk->tnum = pTab->tnum; /* Update the in-memory representation of all UNIQUE indices by converting ** the final rowid column into one or more columns of the PRIMARY KEY. */ |
︙ | ︙ | |||
1985 1986 1987 1988 1989 1990 1991 | ** SQLITE_MASTER table. We just need to update that slot with all ** the information we've collected. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s " "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " "WHERE rowid=#%d", | | | 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 | ** SQLITE_MASTER table. We just need to update that slot with all ** the information we've collected. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s " "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " "WHERE rowid=#%d", db->aDb[iDb].zDbSName, MASTER_NAME, zType, p->zName, p->zName, pParse->regRoot, zStmt, pParse->regRowid ); |
︙ | ︙ | |||
2031 2032 2033 2034 2035 2036 2037 | pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p); if( pOld ){ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ sqlite3OomFault(db); return; } pParse->pNewTable = 0; | | | 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 | pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p); if( pOld ){ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ sqlite3OomFault(db); return; } pParse->pNewTable = 0; db->mDbFlags |= DBFLAG_SchemaChange; #ifndef SQLITE_OMIT_ALTERTABLE if( !p->pSelect ){ const char *zName = (const char *)pParse->sNameToken.z; int nName; assert( !pSelect && pCons && pEnd ); if( pCons->z==0 ){ |
︙ | ︙ | |||
2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 | */ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ Table *pSelTab; /* A fake table from which we get the result set */ Select *pSel; /* Copy of the SELECT that implements the view */ int nErr = 0; /* Number of errors encountered */ int n; /* Temporarily holds the number of cursors assigned */ sqlite3 *db = pParse->db; /* Database connection for malloc errors */ #ifndef SQLITE_OMIT_AUTHORIZATION sqlite3_xauth xAuth; /* Saved xAuth pointer */ #endif assert( pTable ); #ifndef SQLITE_OMIT_VIRTUALTABLE | > > > > | > > | | 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 | */ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ Table *pSelTab; /* A fake table from which we get the result set */ Select *pSel; /* Copy of the SELECT that implements the view */ int nErr = 0; /* Number of errors encountered */ int n; /* Temporarily holds the number of cursors assigned */ sqlite3 *db = pParse->db; /* Database connection for malloc errors */ #ifndef SQLITE_OMIT_VIRTUALTABLE int rc; #endif #ifndef SQLITE_OMIT_AUTHORIZATION sqlite3_xauth xAuth; /* Saved xAuth pointer */ #endif assert( pTable ); #ifndef SQLITE_OMIT_VIRTUALTABLE db->nSchemaLock++; rc = sqlite3VtabCallConnect(pParse, pTable); db->nSchemaLock--; if( rc ){ return 1; } if( IsVirtual(pTable) ) return 0; #endif #ifndef SQLITE_OMIT_VIEW /* A positive nCol means the columns names for this view are ** already known. |
︙ | ︙ | |||
2322 2323 2324 2325 2326 2327 2328 | ** ** The "#NNN" in the SQL is a special constant that means whatever value ** is in register NNN. See grammar rules associated with the TK_REGISTER ** token for additional information. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d", | | | 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 | ** ** The "#NNN" in the SQL is a special constant that means whatever value ** is in register NNN. See grammar rules associated with the TK_REGISTER ** token for additional information. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d", pParse->db->aDb[iDb].zDbSName, MASTER_NAME, iTable, r1, r1); #endif sqlite3ReleaseTempReg(pParse, r1); } /* ** Write VDBE code to erase table pTab and all associated indices on disk. ** Code to update the sqlite_master tables and internal schema definitions |
︙ | ︙ | |||
2465 2466 2467 2468 2469 2470 2471 | ** every row that refers to a table of the same name as the one being ** dropped. Triggers are handled separately because a trigger can be ** created in the temp database that refers to a table in another ** database. */ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'", | | | 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 | ** every row that refers to a table of the same name as the one being ** dropped. Triggers are handled separately because a trigger can be ** created in the temp database that refers to a table in another ** database. */ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'", pDb->zDbSName, MASTER_NAME, pTab->zName); if( !isView && !IsVirtual(pTab) ){ destroyTable(pParse, pTab); } /* Remove the table entry from SQLite's internal schema and modify ** the schema cookie. */ |
︙ | ︙ | |||
2811 2812 2813 2814 2815 2816 2817 | sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord, pIndex->nKeyCol); VdbeCoverage(v); sqlite3UniqueConstraint(pParse, OE_Abort, pIndex); }else{ addr2 = sqlite3VdbeCurrentAddr(v); } sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx); | | | | 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord, pIndex->nKeyCol); VdbeCoverage(v); sqlite3UniqueConstraint(pParse, OE_Abort, pIndex); }else{ addr2 = sqlite3VdbeCurrentAddr(v); } sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx); sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx); sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_Close, iTab); sqlite3VdbeAddOp1(v, OP_Close, iIdx); |
︙ | ︙ | |||
3300 3301 3302 3303 3304 3305 3306 | p = sqlite3HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, pIndex); if( p ){ assert( p==pIndex ); /* Malloc must have failed */ sqlite3OomFault(db); goto exit_create_index; } | | | 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 | p = sqlite3HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, pIndex); if( p ){ assert( p==pIndex ); /* Malloc must have failed */ sqlite3OomFault(db); goto exit_create_index; } db->mDbFlags |= DBFLAG_SchemaChange; if( pTblName!=0 ){ pIndex->tnum = db->init.newTnum; } } /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then |
︙ | ︙ | |||
3336 3337 3338 3339 3340 3341 3342 | /* Create the rootpage for the index using CreateIndex. But before ** doing so, code a Noop instruction and store its address in ** Index.tnum. This is required in case this index is actually a ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In ** that case the convertToWithoutRowidTable() routine will replace ** the Noop with a Goto to jump over the VDBE code generated below. */ pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop); | | | | 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 | /* Create the rootpage for the index using CreateIndex. But before ** doing so, code a Noop instruction and store its address in ** Index.tnum. This is required in case this index is actually a ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In ** that case the convertToWithoutRowidTable() routine will replace ** the Noop with a Goto to jump over the VDBE code generated below. */ pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop); sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY); /* Gather the complete text of the CREATE INDEX statement into ** the zStmt variable */ if( pStart ){ int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n; if( pName->z[n-1]==';' ) n--; /* A named index with an explicit CREATE INDEX statement */ zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s", onError==OE_None ? "" : " UNIQUE", n, pName->z); }else{ /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ /* zStmt = sqlite3MPrintf(""); */ zStmt = 0; } /* Add an entry in sqlite_master for this index */ sqlite3NestedParse(pParse, "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);", db->aDb[iDb].zDbSName, MASTER_NAME, pIndex->zName, pTab->zName, iMem, zStmt ); sqlite3DbFree(db, zStmt); |
︙ | ︙ | |||
3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 | */ void sqlite3DefaultRowEst(Index *pIdx){ /* 10, 9, 8, 7, 6 */ LogEst aVal[] = { 33, 32, 30, 28, 26 }; LogEst *a = pIdx->aiRowLogEst; int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol); int i; /* Set the first entry (number of rows in the index) to the estimated ** number of rows in the table, or half the number of rows in the table ** for a partial index. But do not let the estimate drop below 10. */ a[0] = pIdx->pTable->nRowLogEst; if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10; assert( 10==sqlite3LogEst(2) ); if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) ); | > > > | 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 | */ void sqlite3DefaultRowEst(Index *pIdx){ /* 10, 9, 8, 7, 6 */ LogEst aVal[] = { 33, 32, 30, 28, 26 }; LogEst *a = pIdx->aiRowLogEst; int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol); int i; /* Indexes with default row estimates should not have stat1 data */ assert( !pIdx->hasStat1 ); /* Set the first entry (number of rows in the index) to the estimated ** number of rows in the table, or half the number of rows in the table ** for a partial index. But do not let the estimate drop below 10. */ a[0] = pIdx->pTable->nRowLogEst; if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10; assert( 10==sqlite3LogEst(2) ); if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) ); |
︙ | ︙ | |||
3509 3510 3511 3512 3513 3514 3515 | /* Generate code to remove the index and from the master table */ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE name=%Q AND type='index'", | | | 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 | /* Generate code to remove the index and from the master table */ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE name=%Q AND type='index'", db->aDb[iDb].zDbSName, MASTER_NAME, pIndex->zName ); sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); sqlite3ChangeCookie(pParse, iDb); destroyRootPage(pParse, pIndex->tnum, iDb); sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0); } |
︙ | ︙ | |||
3613 3614 3615 3616 3617 3618 3619 | void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ int i; if( pList==0 ) return; for(i=0; i<pList->nId; i++){ sqlite3DbFree(db, pList->a[i].zName); } sqlite3DbFree(db, pList->a); | | | 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 | void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ int i; if( pList==0 ) return; for(i=0; i<pList->nId; i++){ sqlite3DbFree(db, pList->a[i].zName); } sqlite3DbFree(db, pList->a); sqlite3DbFreeNN(db, pList); } /* ** Return the index in pList of the identifier named zId. Return -1 ** if not found. */ int sqlite3IdListIndex(IdList *pList, const char *zName){ |
︙ | ︙ | |||
3665 3666 3667 3668 3669 3670 3671 | assert( nExtra>=1 ); assert( pSrc!=0 ); assert( iStart<=pSrc->nSrc ); /* Allocate additional space if needed */ if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){ SrcList *pNew; | | | 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 | assert( nExtra>=1 ); assert( pSrc!=0 ); assert( iStart<=pSrc->nSrc ); /* Allocate additional space if needed */ if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){ SrcList *pNew; int nAlloc = pSrc->nSrc*2+nExtra; int nGot; pNew = sqlite3DbRealloc(db, pSrc, sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); if( pNew==0 ){ assert( db->mallocFailed ); return pSrc; } |
︙ | ︙ | |||
3743 3744 3745 3746 3747 3748 3749 | struct SrcList_item *pItem; assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ assert( db!=0 ); if( pList==0 ){ pList = sqlite3DbMallocRawNN(db, sizeof(SrcList) ); if( pList==0 ) return 0; pList->nAlloc = 1; | | > > | | > | | < | | | > | 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 | struct SrcList_item *pItem; assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ assert( db!=0 ); if( pList==0 ){ pList = sqlite3DbMallocRawNN(db, sizeof(SrcList) ); if( pList==0 ) return 0; pList->nAlloc = 1; pList->nSrc = 1; memset(&pList->a[0], 0, sizeof(pList->a[0])); pList->a[0].iCursor = -1; }else{ pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc); } if( db->mallocFailed ){ sqlite3SrcListDelete(db, pList); return 0; } pItem = &pList->a[pList->nSrc-1]; if( pDatabase && pDatabase->z==0 ){ pDatabase = 0; } if( pDatabase ){ pItem->zName = sqlite3NameFromToken(db, pDatabase); pItem->zDatabase = sqlite3NameFromToken(db, pTable); }else{ pItem->zName = sqlite3NameFromToken(db, pTable); pItem->zDatabase = 0; } return pList; } /* ** Assign VdbeCursor index numbers to all tables in a SrcList */ void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ |
︙ | ︙ | |||
3800 3801 3802 3803 3804 3805 3806 | if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy); if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg); sqlite3DeleteTable(db, pItem->pTab); sqlite3SelectDelete(db, pItem->pSelect); sqlite3ExprDelete(db, pItem->pOn); sqlite3IdListDelete(db, pItem->pUsing); } | | | 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 | if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy); if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg); sqlite3DeleteTable(db, pItem->pTab); sqlite3SelectDelete(db, pItem->pSelect); sqlite3ExprDelete(db, pItem->pOn); sqlite3IdListDelete(db, pItem->pUsing); } sqlite3DbFreeNN(db, pList); } /* ** This routine is called by the parser to add a new term to the ** end of a growing FROM clause. The "p" parameter is the part of ** the FROM clause that has already been constructed. "p" is NULL ** if this is the first term of the FROM clause. pTable and pDatabase |
︙ | ︙ | |||
3865 3866 3867 3868 3869 3870 3871 | /* ** Add an INDEXED BY or NOT INDEXED clause to the most recently added ** element of the source-list passed as the second argument. */ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ assert( pIndexedBy!=0 ); | | | > > | | 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 | /* ** Add an INDEXED BY or NOT INDEXED clause to the most recently added ** element of the source-list passed as the second argument. */ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ assert( pIndexedBy!=0 ); if( p && pIndexedBy->n>0 ){ struct SrcList_item *pItem; assert( p->nSrc>0 ); pItem = &p->a[p->nSrc-1]; assert( pItem->fg.notIndexed==0 ); assert( pItem->fg.isIndexedBy==0 ); assert( pItem->fg.isTabFunc==0 ); if( pIndexedBy->n==1 && !pIndexedBy->z ){ /* A "NOT INDEXED" clause was supplied. See parse.y ** construct "indexed_opt" for details. */ pItem->fg.notIndexed = 1; }else{ pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy); pItem->fg.isIndexedBy = 1; } } } /* ** Add the list of function arguments to the SrcList entry for a ** table-valued-function. |
︙ | ︙ | |||
3949 3950 3951 3952 3953 3954 3955 | sqlite3VdbeUsesBtree(v, i); } } sqlite3VdbeAddOp0(v, OP_AutoCommit); } /* | | > > | > < < < < < < < < | < < < < < | < < | > | | 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 | sqlite3VdbeUsesBtree(v, i); } } sqlite3VdbeAddOp0(v, OP_AutoCommit); } /* ** Generate VDBE code for a COMMIT or ROLLBACK statement. ** Code for ROLLBACK is generated if eType==TK_ROLLBACK. Otherwise ** code is generated for a COMMIT. */ void sqlite3EndTransaction(Parse *pParse, int eType){ Vdbe *v; int isRollback; assert( pParse!=0 ); assert( pParse->db!=0 ); assert( eType==TK_COMMIT || eType==TK_END || eType==TK_ROLLBACK ); isRollback = eType==TK_ROLLBACK; if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, isRollback ? "ROLLBACK" : "COMMIT", 0, 0) ){ return; } v = sqlite3GetVdbe(pParse); if( v ){ sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, isRollback); } } /* ** This function is called by the parser when it parses a command to create, ** release or rollback an SQL savepoint. */ |
︙ | ︙ | |||
4168 4169 4170 4171 4172 4173 4174 | sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName); }else{ for(j=0; j<pIdx->nKeyCol; j++){ char *zCol; assert( pIdx->aiColumn[j]>=0 ); zCol = pTab->aCol[pIdx->aiColumn[j]].zName; if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2); | | > > | 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 | sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName); }else{ for(j=0; j<pIdx->nKeyCol; j++){ char *zCol; assert( pIdx->aiColumn[j]>=0 ); zCol = pTab->aCol[pIdx->aiColumn[j]].zName; if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2); sqlite3StrAccumAppendAll(&errMsg, pTab->zName); sqlite3StrAccumAppend(&errMsg, ".", 1); sqlite3StrAccumAppendAll(&errMsg, zCol); } } zErr = sqlite3StrAccumFinish(&errMsg); sqlite3HaltConstraint(pParse, IsPrimaryKeyIndex(pIdx) ? SQLITE_CONSTRAINT_PRIMARYKEY : SQLITE_CONSTRAINT_UNIQUE, onError, zErr, P4_DYNAMIC, P5_ConstraintUnique); |
︙ | ︙ |
Changes to src/callback.c.
︙ | ︙ | |||
117 118 119 120 121 122 123 | ** ** If required, this routine calls the 'collation needed' callback to ** request a definition of the collating sequence. If this doesn't work, ** an equivalent collating sequence that uses a text encoding different ** from the main database is substituted, if one is available. */ int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | ** ** If required, this routine calls the 'collation needed' callback to ** request a definition of the collating sequence. If this doesn't work, ** an equivalent collating sequence that uses a text encoding different ** from the main database is substituted, if one is available. */ int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ if( pColl && pColl->xCmp==0 ){ const char *zName = pColl->zName; sqlite3 *db = pParse->db; CollSeq *p = sqlite3GetCollSeq(pParse, ENC(db), pColl, zName); if( !p ){ return SQLITE_ERROR; } assert( p==pColl ); |
︙ | ︙ | |||
153 154 155 156 157 158 159 | const char *zName, /* Name of the collating sequence */ int create /* Create a new entry if true */ ){ CollSeq *pColl; pColl = sqlite3HashFind(&db->aCollSeq, zName); if( 0==pColl && create ){ | | | < | 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 | const char *zName, /* Name of the collating sequence */ int create /* Create a new entry if true */ ){ CollSeq *pColl; pColl = sqlite3HashFind(&db->aCollSeq, zName); if( 0==pColl && create ){ int nName = sqlite3Strlen30(zName) + 1; pColl = sqlite3DbMallocZero(db, 3*sizeof(*pColl) + nName); if( pColl ){ CollSeq *pDel = 0; pColl[0].zName = (char*)&pColl[3]; pColl[0].enc = SQLITE_UTF8; pColl[1].zName = (char*)&pColl[3]; pColl[1].enc = SQLITE_UTF16LE; pColl[2].zName = (char*)&pColl[3]; pColl[2].enc = SQLITE_UTF16BE; memcpy(pColl[0].zName, zName, nName); pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, pColl); /* If a malloc() failure occurred in sqlite3HashInsert(), it will ** return the pColl pointer to be deleted (because it wasn't added ** to the hash table). */ assert( pDel==0 || pDel==pColl ); |
︙ | ︙ | |||
304 305 306 307 308 309 310 | int nDef /* Length of the apDef[] list */ ){ int i; for(i=0; i<nDef; i++){ FuncDef *pOther; const char *zName = aDef[i].zName; int nName = sqlite3Strlen30(zName); | | > | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | int nDef /* Length of the apDef[] list */ ){ int i; for(i=0; i<nDef; i++){ FuncDef *pOther; const char *zName = aDef[i].zName; int nName = sqlite3Strlen30(zName); int h = (zName[0] + nName) % SQLITE_FUNC_HASH_SZ; assert( zName[0]>='a' && zName[0]<='z' ); pOther = functionSearch(h, zName); if( pOther ){ assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] ); aDef[i].pNext = pOther->pNext; pOther->pNext = &aDef[i]; }else{ aDef[i].pNext = 0; |
︙ | ︙ | |||
370 371 372 373 374 375 376 | bestScore = score; } p = p->pNext; } /* If no match is found, search the built-in functions. ** | | | | 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 | bestScore = score; } p = p->pNext; } /* If no match is found, search the built-in functions. ** ** If the DBFLAG_PreferBuiltin flag is set, then search the built-in ** functions even if a prior app-defined function was found. And give ** priority to built-in functions. ** ** Except, if createFlag is true, that means that we are trying to ** install a new function. Whatever FuncDef structure is returned it will ** have fields overwritten with new information appropriate for the ** new function. But the FuncDefs for built-in functions are read-only. ** So we must not search for built-ins when creating a new function. */ if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){ bestScore = 0; h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ; p = functionSearch(h, zName); while( p ){ int score = matchQuality(p, nArg, enc); if( score>bestScore ){ pBest = p; |
︙ | ︙ | |||
453 454 455 456 457 458 459 | sqlite3DeleteTable(0, pTab); } sqlite3HashClear(&temp1); sqlite3HashClear(&pSchema->fkeyHash); pSchema->pSeqTab = 0; if( pSchema->schemaFlags & DB_SchemaLoaded ){ pSchema->iGeneration++; | < > | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | sqlite3DeleteTable(0, pTab); } sqlite3HashClear(&temp1); sqlite3HashClear(&pSchema->fkeyHash); pSchema->pSeqTab = 0; if( pSchema->schemaFlags & DB_SchemaLoaded ){ pSchema->iGeneration++; } pSchema->schemaFlags &= ~(DB_SchemaLoaded|DB_ResetWanted); } /* ** Find and return the schema associated with a BTree. Create ** a new one if necessary. */ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ |
︙ | ︙ |
Changes to src/ctime.c.
︙ | ︙ | |||
12 13 14 15 16 17 18 | ** ** This file implements routines used to report what compile-time options ** SQLite was built with. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS | > > > > > | > > > > > > > | | | < < | > > > > > > > > > > > > > > > | 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 | ** ** This file implements routines used to report what compile-time options ** SQLite was built with. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* ** Include the configuration header output by 'configure' if we're using the ** autoconf-based build */ #if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) #include "config.h" #define SQLITECONFIG_H 1 #endif /* These macros are provided to "stringify" the value of the define ** for those options in which the value is meaningful. */ #define CTIMEOPT_VAL_(opt) #opt #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) /* ** An array of names of all compile-time options. This array should ** be sorted A-Z. ** ** This array looks large, but in a typical installation actually uses ** only a handful of compile-time options, so most times this array is usually ** rather short and uses little memory space. */ static const char * const sqlite3azCompileOpt[] = { /* ** BEGIN CODE GENERATED BY tool/mkctime.tcl */ #if SQLITE_32BIT_ROWID "32BIT_ROWID", #endif #if SQLITE_4_BYTE_ALIGNED_MALLOC "4_BYTE_ALIGNED_MALLOC", #endif #if SQLITE_64BIT_STATS "64BIT_STATS", #endif #if SQLITE_ALLOW_COVERING_INDEX_SCAN "ALLOW_COVERING_INDEX_SCAN", #endif #if SQLITE_ALLOW_URI_AUTHORITY "ALLOW_URI_AUTHORITY", #endif #ifdef SQLITE_BITMASK_TYPE "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), #endif #if SQLITE_BUG_COMPATIBLE_20160819 "BUG_COMPATIBLE_20160819", #endif #if SQLITE_CASE_SENSITIVE_LIKE "CASE_SENSITIVE_LIKE", #endif #if SQLITE_CHECK_PAGES "CHECK_PAGES", #endif #if defined(__clang__) && defined(__clang_major__) |
︙ | ︙ | |||
56 57 58 59 60 61 62 | #endif #if SQLITE_COVERAGE_TEST "COVERAGE_TEST", #endif #if SQLITE_DEBUG "DEBUG", #endif | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #endif #if SQLITE_COVERAGE_TEST "COVERAGE_TEST", #endif #if SQLITE_DEBUG "DEBUG", #endif #if SQLITE_DEFAULT_AUTOMATIC_INDEX "DEFAULT_AUTOMATIC_INDEX", #endif #if SQLITE_DEFAULT_AUTOVACUUM "DEFAULT_AUTOVACUUM", #endif #ifdef SQLITE_DEFAULT_CACHE_SIZE "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE), #endif #if SQLITE_DEFAULT_CKPTFULLFSYNC "DEFAULT_CKPTFULLFSYNC", #endif #ifdef SQLITE_DEFAULT_FILE_FORMAT "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT), #endif #ifdef SQLITE_DEFAULT_FILE_PERMISSIONS "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS), #endif #if SQLITE_DEFAULT_FOREIGN_KEYS "DEFAULT_FOREIGN_KEYS", #endif #ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT), #endif #ifdef SQLITE_DEFAULT_LOCKING_MODE "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), #endif #ifdef SQLITE_DEFAULT_LOOKASIDE "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOOKASIDE), #endif #if SQLITE_DEFAULT_MEMSTATUS "DEFAULT_MEMSTATUS", #endif #ifdef SQLITE_DEFAULT_MMAP_SIZE "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), #endif #ifdef SQLITE_DEFAULT_PAGE_SIZE "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE), #endif #ifdef SQLITE_DEFAULT_PCACHE_INITSZ "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ), #endif #ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS), #endif #if SQLITE_DEFAULT_RECURSIVE_TRIGGERS "DEFAULT_RECURSIVE_TRIGGERS", #endif #ifdef SQLITE_DEFAULT_ROWEST "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST), #endif #ifdef SQLITE_DEFAULT_SECTOR_SIZE "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE), #endif #ifdef SQLITE_DEFAULT_SYNCHRONOUS "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS), #endif #ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT), #endif #ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS), #endif #ifdef SQLITE_DEFAULT_WORKER_THREADS "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS), #endif #if SQLITE_DIRECT_OVERFLOW_READ "DIRECT_OVERFLOW_READ", #endif #if SQLITE_DISABLE_DIRSYNC "DISABLE_DIRSYNC", #endif #if SQLITE_DISABLE_FTS3_UNICODE "DISABLE_FTS3_UNICODE", #endif #if SQLITE_DISABLE_FTS4_DEFERRED "DISABLE_FTS4_DEFERRED", #endif #if SQLITE_DISABLE_INTRINSIC "DISABLE_INTRINSIC", #endif #if SQLITE_DISABLE_LFS "DISABLE_LFS", #endif #if SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS "DISABLE_PAGECACHE_OVERFLOW_STATS", #endif #if SQLITE_DISABLE_SKIPAHEAD_DISTINCT "DISABLE_SKIPAHEAD_DISTINCT", #endif #ifdef SQLITE_ENABLE_8_3_NAMES "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), #endif #if SQLITE_ENABLE_API_ARMOR "ENABLE_API_ARMOR", #endif #if SQLITE_ENABLE_ATOMIC_WRITE "ENABLE_ATOMIC_WRITE", #endif #if SQLITE_ENABLE_BATCH_ATOMIC_WRITE "ENABLE_BATCH_ATOMIC_WRITE", #endif #if SQLITE_ENABLE_CEROD "ENABLE_CEROD", #endif #if SQLITE_ENABLE_COLUMN_METADATA "ENABLE_COLUMN_METADATA", #endif #if SQLITE_ENABLE_COLUMN_USED_MASK "ENABLE_COLUMN_USED_MASK", #endif #if SQLITE_ENABLE_COSTMULT "ENABLE_COSTMULT", #endif #if SQLITE_ENABLE_CURSOR_HINTS "ENABLE_CURSOR_HINTS", #endif #if SQLITE_ENABLE_DBSTAT_VTAB "ENABLE_DBSTAT_VTAB", #endif #if SQLITE_ENABLE_EXPENSIVE_ASSERT "ENABLE_EXPENSIVE_ASSERT", #endif #if SQLITE_ENABLE_FTS1 "ENABLE_FTS1", #endif #if SQLITE_ENABLE_FTS2 "ENABLE_FTS2", #endif #if SQLITE_ENABLE_FTS3 "ENABLE_FTS3", #endif #if SQLITE_ENABLE_FTS3_PARENTHESIS "ENABLE_FTS3_PARENTHESIS", #endif #if SQLITE_ENABLE_FTS3_TOKENIZER "ENABLE_FTS3_TOKENIZER", #endif #if SQLITE_ENABLE_FTS4 "ENABLE_FTS4", #endif #if SQLITE_ENABLE_FTS5 "ENABLE_FTS5", #endif #if SQLITE_ENABLE_HIDDEN_COLUMNS "ENABLE_HIDDEN_COLUMNS", #endif #if SQLITE_ENABLE_ICU "ENABLE_ICU", #endif #if SQLITE_ENABLE_IOTRACE "ENABLE_IOTRACE", #endif #if SQLITE_ENABLE_JSON1 "ENABLE_JSON1", #endif #if SQLITE_ENABLE_LOAD_EXTENSION "ENABLE_LOAD_EXTENSION", #endif #ifdef SQLITE_ENABLE_LOCKING_STYLE "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), #endif #if SQLITE_ENABLE_MEMORY_MANAGEMENT "ENABLE_MEMORY_MANAGEMENT", #endif #if SQLITE_ENABLE_MEMSYS3 "ENABLE_MEMSYS3", #endif #if SQLITE_ENABLE_MEMSYS5 "ENABLE_MEMSYS5", #endif #if SQLITE_ENABLE_MULTIPLEX "ENABLE_MULTIPLEX", #endif #if SQLITE_ENABLE_NULL_TRIM "ENABLE_NULL_TRIM", #endif #if SQLITE_ENABLE_OVERSIZE_CELL_CHECK "ENABLE_OVERSIZE_CELL_CHECK", #endif #if SQLITE_ENABLE_PREUPDATE_HOOK "ENABLE_PREUPDATE_HOOK", #endif #if SQLITE_ENABLE_QPSG "ENABLE_QPSG", #endif #if SQLITE_ENABLE_RBU "ENABLE_RBU", #endif #if SQLITE_ENABLE_RTREE "ENABLE_RTREE", #endif #if SQLITE_ENABLE_SELECTTRACE "ENABLE_SELECTTRACE", #endif #if SQLITE_ENABLE_SESSION "ENABLE_SESSION", #endif #if SQLITE_ENABLE_SNAPSHOT "ENABLE_SNAPSHOT", #endif #if SQLITE_ENABLE_SQLLOG "ENABLE_SQLLOG", #endif #if defined(SQLITE_ENABLE_STAT4) "ENABLE_STAT4", #elif defined(SQLITE_ENABLE_STAT3) "ENABLE_STAT3", #endif #if SQLITE_ENABLE_STMTVTAB "ENABLE_STMTVTAB", #endif #if SQLITE_ENABLE_STMT_SCANSTATUS "ENABLE_STMT_SCANSTATUS", #endif #if SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION "ENABLE_UNKNOWN_SQL_FUNCTION", #endif #if SQLITE_ENABLE_UNLOCK_NOTIFY "ENABLE_UNLOCK_NOTIFY", #endif #if SQLITE_ENABLE_UPDATE_DELETE_LIMIT "ENABLE_UPDATE_DELETE_LIMIT", #endif #if SQLITE_ENABLE_URI_00_ERROR "ENABLE_URI_00_ERROR", #endif #if SQLITE_ENABLE_VFSTRACE "ENABLE_VFSTRACE", #endif #if SQLITE_ENABLE_WHERETRACE "ENABLE_WHERETRACE", #endif #if SQLITE_ENABLE_ZIPVFS "ENABLE_ZIPVFS", #endif #if SQLITE_EXPLAIN_ESTIMATED_ROWS "EXPLAIN_ESTIMATED_ROWS", #endif #if SQLITE_EXTRA_IFNULLROW "EXTRA_IFNULLROW", #endif #ifdef SQLITE_EXTRA_INIT "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), #endif #ifdef SQLITE_EXTRA_SHUTDOWN "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), #endif #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), #endif #if SQLITE_FTS5_ENABLE_TEST_MI "FTS5_ENABLE_TEST_MI", #endif #if SQLITE_FTS5_NO_WITHOUT_ROWID "FTS5_NO_WITHOUT_ROWID", #endif #if SQLITE_HAS_CODEC "HAS_CODEC", #endif #if HAVE_ISNAN || SQLITE_HAVE_ISNAN "HAVE_ISNAN", #endif #if SQLITE_HOMEGROWN_RECURSIVE_MUTEX "HOMEGROWN_RECURSIVE_MUTEX", #endif #if SQLITE_IGNORE_AFP_LOCK_ERRORS "IGNORE_AFP_LOCK_ERRORS", #endif #if SQLITE_IGNORE_FLOCK_LOCK_ERRORS "IGNORE_FLOCK_LOCK_ERRORS", #endif #if SQLITE_INLINE_MEMCPY "INLINE_MEMCPY", #endif #if SQLITE_INT64_TYPE "INT64_TYPE", #endif #ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), #endif #if SQLITE_LIKE_DOESNT_MATCH_BLOBS "LIKE_DOESNT_MATCH_BLOBS", #endif #if SQLITE_LOCK_TRACE "LOCK_TRACE", #endif #if SQLITE_LOG_CACHE_SPILL "LOG_CACHE_SPILL", #endif #ifdef SQLITE_MALLOC_SOFT_LIMIT "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT), #endif #ifdef SQLITE_MAX_ATTACHED "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED), #endif #ifdef SQLITE_MAX_COLUMN "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN), #endif #ifdef SQLITE_MAX_COMPOUND_SELECT "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT), #endif #ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE), #endif #ifdef SQLITE_MAX_EXPR_DEPTH "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH), #endif #ifdef SQLITE_MAX_FUNCTION_ARG "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG), #endif #ifdef SQLITE_MAX_LENGTH "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH), #endif #ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH), #endif #ifdef SQLITE_MAX_MEMORY "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY), #endif #ifdef SQLITE_MAX_MMAP_SIZE "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), #endif #ifdef SQLITE_MAX_MMAP_SIZE_ "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_), #endif #ifdef SQLITE_MAX_PAGE_COUNT "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT), #endif #ifdef SQLITE_MAX_PAGE_SIZE "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE), #endif #ifdef SQLITE_MAX_SCHEMA_RETRY "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), #endif #ifdef SQLITE_MAX_SQL_LENGTH "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH), #endif #ifdef SQLITE_MAX_TRIGGER_DEPTH "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH), #endif #ifdef SQLITE_MAX_VARIABLE_NUMBER "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER), #endif #ifdef SQLITE_MAX_VDBE_OP "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP), #endif #ifdef SQLITE_MAX_WORKER_THREADS "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS), #endif #if SQLITE_MEMDEBUG "MEMDEBUG", #endif #if SQLITE_MIXED_ENDIAN_64BIT_FLOAT "MIXED_ENDIAN_64BIT_FLOAT", #endif #if SQLITE_MMAP_READWRITE "MMAP_READWRITE", #endif #if SQLITE_MUTEX_NOOP "MUTEX_NOOP", #endif #if SQLITE_MUTEX_NREF "MUTEX_NREF", #endif #if SQLITE_MUTEX_OMIT "MUTEX_OMIT", #endif #if SQLITE_MUTEX_PTHREADS "MUTEX_PTHREADS", #endif #if SQLITE_MUTEX_W32 "MUTEX_W32", #endif #if SQLITE_NEED_ERR_NAME "NEED_ERR_NAME", #endif #if SQLITE_NOINLINE "NOINLINE", #endif #if SQLITE_NO_SYNC "NO_SYNC", #endif #if SQLITE_OMIT_ALTERTABLE "OMIT_ALTERTABLE", #endif |
︙ | ︙ | |||
223 224 225 226 227 228 229 | #endif #if SQLITE_OMIT_BLOB_LITERAL "OMIT_BLOB_LITERAL", #endif #if SQLITE_OMIT_BTREECOUNT "OMIT_BTREECOUNT", #endif | < < < > > > | 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 | #endif #if SQLITE_OMIT_BLOB_LITERAL "OMIT_BLOB_LITERAL", #endif #if SQLITE_OMIT_BTREECOUNT "OMIT_BTREECOUNT", #endif #if SQLITE_OMIT_CAST "OMIT_CAST", #endif #if SQLITE_OMIT_CHECK "OMIT_CHECK", #endif #if SQLITE_OMIT_COMPLETE "OMIT_COMPLETE", #endif #if SQLITE_OMIT_COMPOUND_SELECT "OMIT_COMPOUND_SELECT", #endif #if SQLITE_OMIT_CONFLICT_CLAUSE "OMIT_CONFLICT_CLAUSE", #endif #if SQLITE_OMIT_CTE "OMIT_CTE", #endif #if SQLITE_OMIT_DATETIME_FUNCS "OMIT_DATETIME_FUNCS", #endif |
︙ | ︙ | |||
267 268 269 270 271 272 273 274 275 276 277 278 279 280 | "OMIT_FLOATING_POINT", #endif #if SQLITE_OMIT_FOREIGN_KEY "OMIT_FOREIGN_KEY", #endif #if SQLITE_OMIT_GET_TABLE "OMIT_GET_TABLE", #endif #if SQLITE_OMIT_INCRBLOB "OMIT_INCRBLOB", #endif #if SQLITE_OMIT_INTEGRITY_CHECK "OMIT_INTEGRITY_CHECK", #endif | > > > | 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | "OMIT_FLOATING_POINT", #endif #if SQLITE_OMIT_FOREIGN_KEY "OMIT_FOREIGN_KEY", #endif #if SQLITE_OMIT_GET_TABLE "OMIT_GET_TABLE", #endif #if SQLITE_OMIT_HEX_INTEGER "OMIT_HEX_INTEGER", #endif #if SQLITE_OMIT_INCRBLOB "OMIT_INCRBLOB", #endif #if SQLITE_OMIT_INTEGRITY_CHECK "OMIT_INTEGRITY_CHECK", #endif |
︙ | ︙ | |||
294 295 296 297 298 299 300 301 302 303 304 305 306 307 | "OMIT_MEMORYDB", #endif #if SQLITE_OMIT_OR_OPTIMIZATION "OMIT_OR_OPTIMIZATION", #endif #if SQLITE_OMIT_PAGER_PRAGMAS "OMIT_PAGER_PRAGMAS", #endif #if SQLITE_OMIT_PRAGMA "OMIT_PRAGMA", #endif #if SQLITE_OMIT_PROGRESS_CALLBACK "OMIT_PROGRESS_CALLBACK", #endif | > > > > > > | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 | "OMIT_MEMORYDB", #endif #if SQLITE_OMIT_OR_OPTIMIZATION "OMIT_OR_OPTIMIZATION", #endif #if SQLITE_OMIT_PAGER_PRAGMAS "OMIT_PAGER_PRAGMAS", #endif #if SQLITE_OMIT_PARSER_TRACE "OMIT_PARSER_TRACE", #endif #if SQLITE_OMIT_POPEN "OMIT_POPEN", #endif #if SQLITE_OMIT_PRAGMA "OMIT_PRAGMA", #endif #if SQLITE_OMIT_PROGRESS_CALLBACK "OMIT_PROGRESS_CALLBACK", #endif |
︙ | ︙ | |||
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | #endif #if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS "OMIT_SCHEMA_VERSION_PRAGMAS", #endif #if SQLITE_OMIT_SHARED_CACHE "OMIT_SHARED_CACHE", #endif #if SQLITE_OMIT_SUBQUERY "OMIT_SUBQUERY", #endif #if SQLITE_OMIT_TCL_VARIABLE "OMIT_TCL_VARIABLE", #endif #if SQLITE_OMIT_TEMPDB "OMIT_TEMPDB", #endif #if SQLITE_OMIT_TRACE "OMIT_TRACE", #endif #if SQLITE_OMIT_TRIGGER "OMIT_TRIGGER", #endif | > > > > > > | 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 | #endif #if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS "OMIT_SCHEMA_VERSION_PRAGMAS", #endif #if SQLITE_OMIT_SHARED_CACHE "OMIT_SHARED_CACHE", #endif #if SQLITE_OMIT_SHUTDOWN_DIRECTORIES "OMIT_SHUTDOWN_DIRECTORIES", #endif #if SQLITE_OMIT_SUBQUERY "OMIT_SUBQUERY", #endif #if SQLITE_OMIT_TCL_VARIABLE "OMIT_TCL_VARIABLE", #endif #if SQLITE_OMIT_TEMPDB "OMIT_TEMPDB", #endif #if SQLITE_OMIT_TEST_CONTROL "OMIT_TEST_CONTROL", #endif #if SQLITE_OMIT_TRACE "OMIT_TRACE", #endif #if SQLITE_OMIT_TRIGGER "OMIT_TRIGGER", #endif |
︙ | ︙ | |||
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 | "OMIT_WAL", #endif #if SQLITE_OMIT_WSD "OMIT_WSD", #endif #if SQLITE_OMIT_XFER_OPT "OMIT_XFER_OPT", #endif #if SQLITE_PERFORMANCE_TRACE "PERFORMANCE_TRACE", #endif #if SQLITE_PROXY_DEBUG "PROXY_DEBUG", #endif #if SQLITE_RTREE_INT_ONLY "RTREE_INT_ONLY", #endif #if SQLITE_SECURE_DELETE "SECURE_DELETE", #endif #if SQLITE_SMALL_STACK "SMALL_STACK", #endif #if SQLITE_SOUNDEX "SOUNDEX", #endif #if SQLITE_SYSTEM_MALLOC "SYSTEM_MALLOC", #endif #if SQLITE_TCL "TCL", #endif | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | | > > > | < < | < < | < < < < < < < < < | < < < < < < < < < < < < < < | < < < < < < | < | | 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 | "OMIT_WAL", #endif #if SQLITE_OMIT_WSD "OMIT_WSD", #endif #if SQLITE_OMIT_XFER_OPT "OMIT_XFER_OPT", #endif #if SQLITE_PCACHE_SEPARATE_HEADER "PCACHE_SEPARATE_HEADER", #endif #if SQLITE_PERFORMANCE_TRACE "PERFORMANCE_TRACE", #endif #if SQLITE_POWERSAFE_OVERWRITE "POWERSAFE_OVERWRITE", #endif #if SQLITE_PREFER_PROXY_LOCKING "PREFER_PROXY_LOCKING", #endif #if SQLITE_PROXY_DEBUG "PROXY_DEBUG", #endif #if SQLITE_REVERSE_UNORDERED_SELECTS "REVERSE_UNORDERED_SELECTS", #endif #if SQLITE_RTREE_INT_ONLY "RTREE_INT_ONLY", #endif #if SQLITE_SECURE_DELETE "SECURE_DELETE", #endif #if SQLITE_SMALL_STACK "SMALL_STACK", #endif #ifdef SQLITE_SORTER_PMASZ "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ), #endif #if SQLITE_SOUNDEX "SOUNDEX", #endif #ifdef SQLITE_STAT4_SAMPLES "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES), #endif #ifdef SQLITE_STMTJRNL_SPILL "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL), #endif #if SQLITE_SUBSTR_COMPATIBILITY "SUBSTR_COMPATIBILITY", #endif #if SQLITE_SYSTEM_MALLOC "SYSTEM_MALLOC", #endif #if SQLITE_TCL "TCL", #endif #ifdef SQLITE_TEMP_STORE "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), #endif #if SQLITE_TEST "TEST", #endif #if defined(SQLITE_THREADSAFE) "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), #elif defined(THREADSAFE) "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), #else "THREADSAFE=1", #endif #if SQLITE_UNLINK_AFTER_CLOSE "UNLINK_AFTER_CLOSE", #endif #if SQLITE_UNTESTABLE "UNTESTABLE", #endif #if SQLITE_USER_AUTHENTICATION "USER_AUTHENTICATION", #endif #if SQLITE_USE_ALLOCA "USE_ALLOCA", #endif #if SQLITE_USE_FCNTL_TRACE "USE_FCNTL_TRACE", #endif #if SQLITE_USE_URI "USE_URI", #endif #if SQLITE_VDBE_COVERAGE "VDBE_COVERAGE", #endif #if SQLITE_WIN32_MALLOC "WIN32_MALLOC", #endif #if SQLITE_ZERO_MALLOC "ZERO_MALLOC", #endif /* ** END CODE GENERATED BY tool/mkctime.tcl */ }; const char **sqlite3CompileOptions(int *pnOpt){ *pnOpt = sizeof(sqlite3azCompileOpt) / sizeof(sqlite3azCompileOpt[0]); return (const char**)sqlite3azCompileOpt; } #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ |
Changes to src/date.c.
︙ | ︙ | |||
61 62 63 64 65 66 67 | #endif /* ** A structure for holding a single date and time. */ typedef struct DateTime DateTime; struct DateTime { | | | | | | > > | | < | | > | 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 | #endif /* ** A structure for holding a single date and time. */ typedef struct DateTime DateTime; 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 rawS; /* Raw numeric value stored in s */ char validYMD; /* True (1) if Y,M,D are valid */ char validHMS; /* True (1) if h,m,s are valid */ char validTZ; /* True (1) if tz is valid */ char tzSet; /* Timezone was set explicitly */ char isError; /* An overflow has occurred */ }; /* ** Convert zDate into one or more integers according to the conversion ** specifier zFormat. ** |
︙ | ︙ | |||
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 | } ms /= rScale; } }else{ s = 0; } p->validJD = 0; p->validHMS = 1; p->h = h; p->m = m; p->s = s + ms; if( parseTimezone(zDate, p) ) return 1; p->validTZ = (p->tz!=0)?1:0; return 0; } /* ** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume ** that the YYYY-MM-DD is according to the Gregorian calendar. ** ** Reference: Meeus page 61 */ static void computeJD(DateTime *p){ int Y, M, D, A, B, X1, X2; if( p->validJD ) return; if( p->validYMD ){ Y = p->Y; M = p->M; D = p->D; }else{ Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */ M = 1; D = 1; } if( M<=2 ){ Y--; M += 12; } A = Y/100; B = 2 - A + (A/4); | > > > > > > > > > > > > > | 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 | } ms /= rScale; } }else{ s = 0; } 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; p->validTZ = (p->tz!=0)?1:0; return 0; } /* ** Put the DateTime object into its error state. */ static void datetimeError(DateTime *p){ memset(p, 0, sizeof(*p)); p->isError = 1; } /* ** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume ** that the YYYY-MM-DD is according to the Gregorian calendar. ** ** Reference: Meeus page 61 */ static void computeJD(DateTime *p){ int Y, M, D, A, B, X1, X2; if( p->validJD ) return; if( p->validYMD ){ Y = p->Y; M = p->M; D = p->D; }else{ Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */ M = 1; D = 1; } if( Y<-4713 || Y>9999 || p->rawS ){ datetimeError(p); return; } if( M<=2 ){ Y--; M += 12; } A = Y/100; B = 2 - A + (A/4); |
︙ | ︙ | |||
325 326 327 328 329 330 331 332 333 334 335 336 337 338 | if( p->iJD>0 ){ p->validJD = 1; return 0; }else{ return 1; } } /* ** Attempt to parse the given string into a julian day number. Return ** the number of errors. ** ** The following are acceptable forms for the input string: ** | > > > > > > > > > > > > > > > | 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 | if( p->iJD>0 ){ p->validJD = 1; return 0; }else{ return 1; } } /* ** Input "r" is a numeric quantity which might be a julian day number, ** or the number of seconds since 1970. If the value if r is within ** range of a julian day number, install it as such and set validJD. ** If the value is a valid unix timestamp, put it in p->s and set p->rawS. */ static void setRawDateNumber(DateTime *p, double r){ p->s = r; p->rawS = 1; if( r>=0.0 && r<5373484.5 ){ p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5); p->validJD = 1; } } /* ** Attempt to parse the given string into a julian day number. Return ** the number of errors. ** ** The following are acceptable forms for the input string: ** |
︙ | ︙ | |||
352 353 354 355 356 357 358 | DateTime *p ){ double r; if( parseYyyyMmDd(zDate,p)==0 ){ return 0; }else if( parseHhMmSs(zDate, p)==0 ){ return 0; | | | < > > > > > > > > > > > > > > > > > > > > > | 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 | DateTime *p ){ double r; if( parseYyyyMmDd(zDate,p)==0 ){ return 0; }else if( parseHhMmSs(zDate, p)==0 ){ return 0; }else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){ return setDateTimeToCurrent(context, p); }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){ setRawDateNumber(p, r); return 0; } return 1; } /* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999. ** Multiplying this by 86400000 gives 464269060799999 as the maximum value ** for DateTime.iJD. ** ** But some older compilers (ex: gcc 4.2.1 on older Macs) cannot deal with ** such a large integer literal, so we have to encode it. */ #define INT_464269060799999 ((((i64)0x1a640)<<32)|0x1072fdff) /* ** Return TRUE if the given julian day number is within range. ** ** The input is the JulianDay times 86400000. */ static int validJulianDay(sqlite3_int64 iJD){ return iJD>=0 && iJD<=INT_464269060799999; } /* ** Compute the Year, Month, and Day from the julian day number. */ static void computeYMD(DateTime *p){ int Z, A, B, C, D, E, X1; if( p->validYMD ) return; if( !p->validJD ){ p->Y = 2000; p->M = 1; p->D = 1; }else if( !validJulianDay(p->iJD) ){ datetimeError(p); return; }else{ Z = (int)((p->iJD + 43200000)/86400000); A = (int)((Z - 1867216.25)/36524.25); A = Z + 1 + A - (A/4); B = A + 1524; C = (int)((B - 122.1)/365.25); D = (36525*(C&32767))/100; |
︙ | ︙ | |||
403 404 405 406 407 408 409 410 411 412 413 414 415 416 | p->s = s/1000.0; s = (int)p->s; p->s -= s; p->h = s/3600; s -= p->h*3600; p->m = s/60; p->s += s - p->m*60; p->validHMS = 1; } /* ** Compute both YMD and HMS */ static void computeYMD_HMS(DateTime *p){ | > | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | p->s = s/1000.0; s = (int)p->s; p->s -= s; p->h = s/3600; s -= p->h*3600; p->m = s/60; p->s += s - p->m*60; p->rawS = 0; p->validHMS = 1; } /* ** Compute both YMD and HMS */ static void computeYMD_HMS(DateTime *p){ |
︙ | ︙ | |||
464 465 466 467 468 469 470 | #if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S struct tm *pX; #if SQLITE_THREADSAFE>0 sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif sqlite3_mutex_enter(mutex); pX = localtime(t); | | | | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 | #if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S struct tm *pX; #if SQLITE_THREADSAFE>0 sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif sqlite3_mutex_enter(mutex); pX = localtime(t); #ifndef SQLITE_UNTESTABLE if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0; #endif if( pX ) *pTm = *pX; sqlite3_mutex_leave(mutex); rc = pX==0; #else #ifndef SQLITE_UNTESTABLE if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; #endif #if HAVE_LOCALTIME_R rc = localtime_r(t, pTm)==0; #else rc = localtime_s(pTm, t); #endif /* HAVE_LOCALTIME_R */ |
︙ | ︙ | |||
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 | y.D = sLocal.tm_mday; y.h = sLocal.tm_hour; y.m = sLocal.tm_min; y.s = sLocal.tm_sec; y.validYMD = 1; y.validHMS = 1; y.validJD = 0; y.validTZ = 0; computeJD(&y); *pRc = SQLITE_OK; return y.iJD - x.iJD; } #endif /* SQLITE_OMIT_LOCALTIME */ /* ** Process a modifier to a date-time stamp. The modifiers are ** as follows: ** ** NNN days ** NNN hours | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | y.D = sLocal.tm_mday; y.h = sLocal.tm_hour; y.m = sLocal.tm_min; y.s = sLocal.tm_sec; y.validYMD = 1; y.validHMS = 1; y.validJD = 0; y.rawS = 0; y.validTZ = 0; y.isError = 0; computeJD(&y); *pRc = SQLITE_OK; return y.iJD - x.iJD; } #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 eType; /* Transformation type code */ u8 nName; /* Length of th name */ char *zName; /* Name of the transformation */ double rLimit; /* Maximum NNN value for this transform */ double rXform; /* Constant used for this transform */ } aXformType[] = { { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) }, { 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) }, { 0, 4, "hour", 128963628.0, 86400000.0/24.0 }, { 0, 3, "day", 5373485.0, 86400000.0 }, { 1, 5, "month", 176546.0, 30.0*86400000.0 }, { 2, 4, "year", 14713.0, 365.0*86400000.0 }, }; /* ** Process a modifier to a date-time stamp. The modifiers are ** as follows: ** ** NNN days ** NNN hours |
︙ | ︙ | |||
573 574 575 576 577 578 579 | ** 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. */ | | > > > > > < < < < | < < < | | | | > | > > > | | > | | 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 | ** 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( sqlite3_context *pCtx, /* Function context */ const char *z, /* The text of the modifier */ int n, /* Length of zMod in bytes */ DateTime *p /* The date/time value to be modified */ ){ int rc = 1; double r; switch(sqlite3UpperToLower[(u8)z[0]] ){ #ifndef SQLITE_OMIT_LOCALTIME 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) ){ computeJD(p); p->iJD += localtimeOffset(p, pCtx, &rc); clearYMD_HMS_TZ(p); } break; } #endif case 'u': { /* ** unixepoch ** ** Treat the current value of p->s as the number of ** seconds since 1970. Convert to a real julian day number. */ if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){ r = p->s*1000.0 + 210866760000000.0; if( r>=0.0 && r<464269060800000.0 ){ clearYMD_HMS_TZ(p); p->iJD = (sqlite3_int64)r; p->validJD = 1; p->rawS = 0; rc = 0; } } #ifndef SQLITE_OMIT_LOCALTIME else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ if( p->tzSet==0 ){ sqlite3_int64 c1; computeJD(p); c1 = localtimeOffset(p, pCtx, &rc); if( rc==SQLITE_OK ){ p->iJD -= c1; clearYMD_HMS_TZ(p); |
︙ | ︙ | |||
638 639 640 641 642 643 644 | /* ** 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. */ | | | 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | /* ** 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) && (n=(int)r)==r && n>=0 && r<7 ){ sqlite3_int64 Z; computeYMD_HMS(p); p->validTZ = 0; p->validJD = 0; computeJD(p); |
︙ | ︙ | |||
661 662 663 664 665 666 667 | case 's': { /* ** start of TTTTT ** ** Move the date backwards to the beginning of the current day, ** or month or year. */ | | > > | | < | > | 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 | case 's': { /* ** start of TTTTT ** ** Move the date backwards to the beginning of the current day, ** or month or year. */ if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; 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->validTZ = 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; rc = 0; }else if( sqlite3_stricmp(z,"day")==0 ){ rc = 0; } break; } case '+': case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { double rRounder; int i; for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){ rc = 1; break; } if( z[n]==':' ){ /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the |
︙ | ︙ | |||
723 724 725 726 727 728 729 730 731 732 733 | if( z[0]=='-' ) tx.iJD = -tx.iJD; computeJD(p); clearYMD_HMS_TZ(p); p->iJD += tx.iJD; rc = 0; break; } z += n; while( sqlite3Isspace(*z) ) z++; n = sqlite3Strlen30(z); if( n>10 || n<3 ) break; | > > > | | > | | < | | | < < | | | | | | | | < | | < | < > | | | | > > > > | < | > > < < | 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 | if( z[0]=='-' ) tx.iJD = -tx.iJD; computeJD(p); clearYMD_HMS_TZ(p); p->iJD += tx.iJD; rc = 0; break; } /* 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>10 || n<3 ) break; if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; computeJD(p); rc = 1; rRounder = r<0 ? -0.5 : +0.5; 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( aXformType[i].eType ){ case 1: { /* Special processing to add months */ int x; 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; p->validJD = 0; r -= (int)r; break; } case 2: { /* Special processing to add years */ int y = (int)r; computeYMD_HMS(p); p->Y += y; p->validJD = 0; r -= (int)r; break; } } computeJD(p); p->iJD += (sqlite3_int64)(r*aXformType[i].rXform + rRounder); rc = 0; break; } } clearYMD_HMS_TZ(p); break; } default: { break; } |
︙ | ︙ | |||
789 790 791 792 793 794 795 | */ static int isDate( sqlite3_context *context, int argc, sqlite3_value **argv, DateTime *p ){ | | | < > | > > | 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 | */ static int isDate( sqlite3_context *context, int argc, sqlite3_value **argv, DateTime *p ){ int i, n; const unsigned char *z; int eType; memset(p, 0, sizeof(*p)); if( argc==0 ){ return setDateTimeToCurrent(context, p); } if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT || eType==SQLITE_INTEGER ){ setRawDateNumber(p, sqlite3_value_double(argv[0])); }else{ z = sqlite3_value_text(argv[0]); if( !z || parseDateOrTime(context, (char*)z, p) ){ return 1; } } 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) ) 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. |
︙ | ︙ | |||
1142 1143 1144 1145 1146 1147 1148 | ** 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 | | | | | | | 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 | ** 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(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 ), 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/dbstat.c.
︙ | ︙ | |||
690 691 692 693 694 695 696 697 698 699 700 701 702 | 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); } #elif defined(SQLITE_ENABLE_DBSTAT_VTAB) int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; } #endif /* SQLITE_ENABLE_DBSTAT_VTAB */ | > > > | 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ }; return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); } #elif defined(SQLITE_ENABLE_DBSTAT_VTAB) int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; } #endif /* SQLITE_ENABLE_DBSTAT_VTAB */ |
Changes to src/delete.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | struct SrcList_item *pItem = pSrc->a; Table *pTab; assert( pItem && pSrc->nSrc==1 ); pTab = sqlite3LocateTableItem(pParse, 0, pItem); sqlite3DeleteTable(pParse->db, pItem->pTab); pItem->pTab = pTab; if( pTab ){ | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | struct SrcList_item *pItem = pSrc->a; Table *pTab; assert( pItem && pSrc->nSrc==1 ); pTab = sqlite3LocateTableItem(pParse, 0, pItem); sqlite3DeleteTable(pParse->db, pItem->pTab); pItem->pTab = pTab; if( pTab ){ pTab->nTabRef++; } if( sqlite3IndexedByLookup(pParse, pItem) ){ pTab = 0; } return pTab; } |
︙ | ︙ | |||
160 161 162 163 164 165 166 | ** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** becomes: ** DELETE FROM table_a WHERE rowid IN ( ** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** ); */ | | | | | 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 | ** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** becomes: ** DELETE FROM table_a WHERE rowid IN ( ** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** ); */ pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0); if( pSelectRowid == 0 ) goto limit_where_cleanup; pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid); if( pEList == 0 ) goto limit_where_cleanup; /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree ** and the SELECT subtree. */ pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0); if( pSelectSrc == 0 ) { sqlite3ExprListDelete(pParse->db, pEList); goto limit_where_cleanup; } /* generate the SELECT expression tree. */ pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0, pOrderBy,0,pLimit,pOffset); if( pSelect == 0 ) return 0; /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0); pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0) : 0; sqlite3PExprAddSelect(pParse, pInClause, pSelect); return pInClause; limit_where_cleanup: sqlite3ExprDelete(pParse->db, pWhere); sqlite3ExprListDelete(pParse->db, pOrderBy); sqlite3ExprDelete(pParse->db, pLimit); |
︙ | ︙ | |||
346 347 348 349 350 351 352 | sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); } #ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION /* Special case: A DELETE without a WHERE clause deletes everything. ** It is easier just to erase the whole table. Prior to version 3.6.5, ** this optimization caused the row change count (the value returned by | | > > > > > > > | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); } #ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION /* Special case: A DELETE without a WHERE clause deletes everything. ** It is easier just to erase the whole table. Prior to version 3.6.5, ** this optimization caused the row change count (the value returned by ** API function sqlite3_count_changes) to be set incorrectly. ** ** The "rcauth==SQLITE_OK" terms is the ** IMPLEMENTATION-OF: R-17228-37124 If the action code is SQLITE_DELETE and ** the callback returns SQLITE_IGNORE then the DELETE operation proceeds but ** the truncate optimization is disabled and all rows are deleted ** individually. */ if( rcauth==SQLITE_OK && pWhere==0 && !bComplex && !IsVirtual(pTab) #ifdef SQLITE_ENABLE_PREUPDATE_HOOK && db->xPreUpdateCallback==0 #endif |
︙ | ︙ | |||
445 446 447 448 449 450 451 | }else{ if( pPk ){ /* Add the PK key for this row to the temporary table */ iKey = ++pParse->nMem; nKey = 0; /* Zero tells OP_Found to use a composite key */ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, sqlite3IndexAffinityStr(pParse->db, pPk), nPk); | | | | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | }else{ if( pPk ){ /* Add the PK key for this row to the temporary table */ iKey = ++pParse->nMem; nKey = 0; /* Zero tells OP_Found to use a composite key */ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, sqlite3IndexAffinityStr(pParse->db, pPk), nPk); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEphCur, iKey, iPk, nPk); }else{ /* Add the rowid of the row to be deleted to the RowSet */ nKey = 1; /* OP_DeferredSeek always uses a single rowid */ sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey); } } /* If this DELETE cannot use the ONEPASS strategy, this is the ** end of the WHERE loop */ if( eOnePass!=ONEPASS_OFF ){ |
︙ | ︙ | |||
491 492 493 494 495 496 497 | if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ assert( pPk!=0 || pTab->pSelect!=0 ); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); VdbeCoverage(v); } }else if( pPk ){ addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v); | > > > | > | 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 | if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ assert( pPk!=0 || pTab->pSelect!=0 ); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); VdbeCoverage(v); } }else if( pPk ){ addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v); if( IsVirtual(pTab) ){ sqlite3VdbeAddOp3(v, OP_Column, iEphCur, 0, iKey); }else{ sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey); } assert( nKey==0 ); /* OP_Found will use a composite key */ }else{ addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey); VdbeCoverage(v); assert( nKey==1 ); } |
︙ | ︙ | |||
515 516 517 518 519 520 521 | if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){ pParse->isMultiWrite = 0; } }else #endif { int count = (pParse->nested==0); /* True to count changes */ | < < < < | < < < < < < < < | 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 | if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){ pParse->isMultiWrite = 0; } }else #endif { int count = (pParse->nested==0); /* True to count changes */ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, iKey, nKey, count, OE_Default, eOnePass, aiCurOnePass[1]); } /* End of the loop over all rowids/primary-keys. */ if( eOnePass!=ONEPASS_OFF ){ sqlite3VdbeResolveLabel(v, addrBypass); sqlite3WhereEnd(pWInfo); }else if( pPk ){ sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addrLoop); }else{ sqlite3VdbeGoto(v, addrLoop); sqlite3VdbeJumpHere(v, addrLoop); } } /* End non-truncate path */ /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if( pParse->nested==0 && pParse->pTriggerTab==0 ){ |
︙ | ︙ | |||
608 609 610 611 612 613 614 | ** ONEPASS_MULTI. If eMode is not ONEPASS_OFF, then the cursor ** iDataCur already points to the row to delete. If eMode is ONEPASS_OFF ** then this function must seek iDataCur to the entry identified by iPk ** and nPk before reading from it. ** ** If eMode is ONEPASS_MULTI, then this call is being made as part ** of a ONEPASS delete that affects multiple rows. In this case, if | | > | | < | | | > > | 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 | ** ONEPASS_MULTI. If eMode is not ONEPASS_OFF, then the cursor ** iDataCur already points to the row to delete. If eMode is ONEPASS_OFF ** then this function must seek iDataCur to the entry identified by iPk ** and nPk before reading from it. ** ** If eMode is ONEPASS_MULTI, then this call is being made as part ** of a ONEPASS delete that affects multiple rows. In this case, if ** iIdxNoSeek is a valid cursor number (>=0) and is not the same as ** iDataCur, then its position should be preserved following the delete ** operation. Or, if iIdxNoSeek is not a valid cursor number, the ** position of iDataCur should be preserved instead. ** ** iIdxNoSeek: ** If iIdxNoSeek is a valid cursor number (>=0) not equal to iDataCur, ** then it identifies an index cursor (from within array of cursors ** starting at iIdxCur) that already points to the index entry to be deleted. ** Except, this optimization is disabled if there are BEFORE triggers since ** the trigger body might have moved the cursor. */ void sqlite3GenerateRowDelete( Parse *pParse, /* Parsing context */ Table *pTab, /* Table containing the row to be deleted */ Trigger *pTrigger, /* List of triggers to (potentially) fire */ int iDataCur, /* Cursor from which column data is extracted */ int iIdxCur, /* First index cursor */ |
︙ | ︙ | |||
687 688 689 690 691 692 693 | addrStart = sqlite3VdbeCurrentAddr(v); sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel ); /* If any BEFORE triggers were coded, then seek the cursor to the ** row to be deleted again. It may be that the BEFORE triggers moved | | > > > > > | 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 | addrStart = sqlite3VdbeCurrentAddr(v); sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel ); /* If any BEFORE triggers were coded, then seek the cursor to the ** row to be deleted again. It may be that the BEFORE triggers moved ** the cursor or already deleted the row that the cursor was ** pointing to. ** ** Also disable the iIdxNoSeek optimization since the BEFORE trigger ** may have moved that cursor. */ if( addrStart<sqlite3VdbeCurrentAddr(v) ){ sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); VdbeCoverageIf(v, opSeek==OP_NotExists); VdbeCoverageIf(v, opSeek==OP_NotFound); testcase( iIdxNoSeek>=0 ); iIdxNoSeek = -1; } /* Do FK processing. This call checks that any FK constraints that ** refer to this table (i.e. constraints attached to other tables) ** are not violated by deleting this row. */ sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0); } |
︙ | ︙ | |||
716 717 718 719 720 721 722 | ** the update-hook is not invoked for rows removed by REPLACE, but the ** pre-update-hook is. */ if( pTab->pSelect==0 ){ u8 p5 = 0; sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek); sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); | > | > | | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 | ** the update-hook is not invoked for rows removed by REPLACE, but the ** pre-update-hook is. */ if( pTab->pSelect==0 ){ u8 p5 = 0; sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek); sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); if( pParse->nested==0 ){ sqlite3VdbeAppendP4(v, (char*)pTab, P4_TABLE); } if( eMode!=ONEPASS_OFF ){ sqlite3VdbeChangeP5(v, OPFLAG_AUXDELETE); } if( iIdxNoSeek>=0 && iIdxNoSeek!=iDataCur ){ sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek); } if( eMode==ONEPASS_MULTI ) p5 |= OPFLAG_SAVEPOSITION; sqlite3VdbeChangeP5(v, p5); } /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to |
︙ | ︙ | |||
844 845 846 847 848 849 850 | int j; int regBase; int nCol; if( piPartIdxLabel ){ if( pIdx->pPartIdxWhere ){ *piPartIdxLabel = sqlite3VdbeMakeLabel(v); | | > | 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | int j; int regBase; int nCol; if( piPartIdxLabel ){ if( pIdx->pPartIdxWhere ){ *piPartIdxLabel = sqlite3VdbeMakeLabel(v); pParse->iSelfTab = iDataCur + 1; sqlite3ExprCachePush(pParse); sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel, SQLITE_JUMPIFNULL); pParse->iSelfTab = 0; }else{ *piPartIdxLabel = 0; } } nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; regBase = sqlite3GetTempRange(pParse, nCol); if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0; |
︙ | ︙ | |||
874 875 876 877 878 879 880 881 882 883 884 885 886 887 | ** But we are getting ready to store this value back into an index, where ** it should be converted by to INTEGER again. So omit the OP_RealAffinity ** opcode if it is present */ sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); } if( regOut ){ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); } sqlite3ReleaseTempRange(pParse, regBase, nCol); return regBase; } /* ** If a prior call to sqlite3GenerateIndexKey() generated a jump-over label | > > > > | 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 | ** But we are getting ready to store this value back into an index, where ** it should be converted by to INTEGER again. So omit the OP_RealAffinity ** opcode if it is present */ sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); } if( regOut ){ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); if( pIdx->pTable->pSelect ){ const char *zAff = sqlite3IndexAffinityStr(pParse->db, pIdx); sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); } } sqlite3ReleaseTempRange(pParse, regBase, nCol); return regBase; } /* ** If a prior call to sqlite3GenerateIndexKey() generated a jump-over label |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 | if( op==TK_REGISTER ) op = pExpr->op2; #ifndef SQLITE_OMIT_CAST if( op==TK_CAST ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); return sqlite3AffinityType(pExpr->u.zToken, 0); } #endif | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | if( op==TK_REGISTER ) op = pExpr->op2; #ifndef SQLITE_OMIT_CAST if( op==TK_CAST ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); return sqlite3AffinityType(pExpr->u.zToken, 0); } #endif if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->pTab ){ return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn); } if( op==TK_SELECT_COLUMN ){ assert( pExpr->pLeft->flags&EP_xIsSelect ); return sqlite3ExprAffinity( pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr ); |
︙ | ︙ | |||
227 228 229 230 231 232 233 | pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT ); assert( pExpr->pLeft ); aff = sqlite3ExprAffinity(pExpr->pLeft); if( pExpr->pRight ){ aff = sqlite3CompareAffinity(pExpr->pRight, aff); }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); | | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT ); assert( pExpr->pLeft ); aff = sqlite3ExprAffinity(pExpr->pLeft); if( pExpr->pRight ){ aff = sqlite3CompareAffinity(pExpr->pRight, aff); }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); }else if( aff==0 ){ aff = SQLITE_AFF_BLOB; } return aff; } /* ** pExpr is a comparison expression, eg. '=', '<', IN(...) etc. |
︙ | ︙ | |||
348 349 350 351 352 353 354 | }else if( op==TK_SELECT ){ return pExpr->x.pSelect->pEList->nExpr; }else{ return 1; } } | < | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | }else if( op==TK_SELECT ){ return pExpr->x.pSelect->pEList->nExpr; }else{ return 1; } } /* ** Return a pointer to a subexpression of pVector that is the i-th ** column of the vector (numbered starting with 0). The caller must ** ensure that i is within range. ** ** If pVector is really a scalar (and "scalar" here includes subqueries ** that return a single column!) then return pVector unmodified. |
︙ | ︙ | |||
376 377 378 379 380 381 382 | return pVector->x.pSelect->pEList->a[i].pExpr; }else{ return pVector->x.pList->a[i].pExpr; } } return pVector; } | < < | 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | return pVector->x.pSelect->pEList->a[i].pExpr; }else{ return pVector->x.pList->a[i].pExpr; } } return pVector; } /* ** Compute and return a new Expr object which when passed to ** sqlite3ExprCode() will generate all necessary code to compute ** the iField-th column of the vector expression pVector. ** ** It is ok for pVector to be a scalar (as long as iField==0). ** In that case, this routine works like sqlite3ExprDup(). |
︙ | ︙ | |||
410 411 412 413 414 415 416 | int iField /* Which column of the vector to return */ ){ Expr *pRet; if( pVector->op==TK_SELECT ){ assert( pVector->flags & EP_xIsSelect ); /* The TK_SELECT_COLUMN Expr node: ** | | > | < | 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 | int iField /* Which column of the vector to return */ ){ Expr *pRet; if( pVector->op==TK_SELECT ){ assert( pVector->flags & EP_xIsSelect ); /* The TK_SELECT_COLUMN Expr node: ** ** pLeft: pVector containing TK_SELECT. Not deleted. ** pRight: not used. But recursively deleted. ** iColumn: Index of a column in pVector ** iTable: 0 or the number of columns on the LHS of an assignment ** pLeft->iTable: First in an array of register holding result, or 0 ** if the result is not yet computed. ** ** sqlite3ExprDelete() specifically skips the recursive delete of ** pLeft on TK_SELECT_COLUMN nodes. But pRight is followed, so pVector ** can be attached to pRight to cause this node to take ownership of ** pVector. Typically there will be multiple TK_SELECT_COLUMN nodes ** with the same pLeft pointer to the pVector, but only one of them ** will own the pVector. */ pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0); if( pRet ){ pRet->iColumn = iField; pRet->pLeft = pVector; } assert( pRet==0 || pRet->iTable==0 ); }else{ if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; pRet = sqlite3ExprDup(pParse->db, pVector, 0); } return pRet; } /* ** If expression pExpr is of type TK_SELECT, generate code to evaluate ** it. Return the register in which the result is stored (or, if the ** sub-select returns more than one column, the first in an array ** of registers in which the result is stored). ** |
︙ | ︙ | |||
523 524 525 526 527 528 529 | int nLeft = sqlite3ExprVectorSize(pLeft); int i; int regLeft = 0; int regRight = 0; u8 opx = op; int addrDone = sqlite3VdbeMakeLabel(v); | | > > > | 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 | int nLeft = sqlite3ExprVectorSize(pLeft); int i; int regLeft = 0; int regRight = 0; u8 opx = op; int addrDone = sqlite3VdbeMakeLabel(v); if( nLeft!=sqlite3ExprVectorSize(pRight) ){ sqlite3ErrorMsg(pParse, "row value misused"); return; } assert( pExpr->op==TK_EQ || pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT || pExpr->op==TK_LT || pExpr->op==TK_GT || pExpr->op==TK_LE || pExpr->op==TK_GE ); assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ) || (pExpr->op==TK_ISNOT && op==TK_NE) ); |
︙ | ︙ | |||
740 741 742 743 744 745 746 | pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra); if( pNew ){ memset(pNew, 0, sizeof(Expr)); pNew->op = (u8)op; pNew->iAgg = -1; if( pToken ){ if( nExtra==0 ){ | | | 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 | pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra); if( pNew ){ memset(pNew, 0, sizeof(Expr)); pNew->op = (u8)op; pNew->iAgg = -1; if( pToken ){ if( nExtra==0 ){ pNew->flags |= EP_IntValue|EP_Leaf; pNew->u.iValue = iValue; }else{ pNew->u.zToken = (char*)&pNew[1]; assert( pToken->z!=0 || pToken->n==0 ); if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); pNew->u.zToken[pToken->n] = 0; if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){ |
︙ | ︙ | |||
771 772 773 774 775 776 777 | Expr *sqlite3Expr( sqlite3 *db, /* Handle for sqlite3DbMallocZero() (may be null) */ int op, /* Expression opcode */ const char *zToken /* Token argument. Might be NULL */ ){ Token x; x.z = zToken; | | | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | Expr *sqlite3Expr( sqlite3 *db, /* Handle for sqlite3DbMallocZero() (may be null) */ int op, /* Expression opcode */ const char *zToken /* Token argument. Might be NULL */ ){ Token x; x.z = zToken; x.n = sqlite3Strlen30(zToken); return sqlite3ExprAlloc(db, op, &x, 0); } /* ** Attach subtrees pLeft and pRight to the Expr node pRoot. ** ** If pRoot==NULL that means that a memory allocation error has occurred. |
︙ | ︙ | |||
815 816 817 818 819 820 821 | ** Expr node. Or, if an OOM error occurs, set pParse->db->mallocFailed, ** free the subtrees and return NULL. */ Expr *sqlite3PExpr( Parse *pParse, /* Parsing context */ int op, /* Expression opcode */ Expr *pLeft, /* Left operand */ | | < > > > | > > | 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 | ** Expr node. Or, if an OOM error occurs, set pParse->db->mallocFailed, ** free the subtrees and return NULL. */ Expr *sqlite3PExpr( Parse *pParse, /* Parsing context */ int op, /* Expression opcode */ Expr *pLeft, /* Left operand */ Expr *pRight /* Right operand */ ){ Expr *p; if( op==TK_AND && pParse->nErr==0 ){ /* Take advantage of short-circuit false optimization for AND */ p = sqlite3ExprAnd(pParse->db, pLeft, pRight); }else{ p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)); if( p ){ memset(p, 0, sizeof(Expr)); p->op = op & TKFLG_MASK; p->iAgg = -1; } sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); } if( p ) { sqlite3ExprCheckHeight(pParse, p->nHeight); } return p; } |
︙ | ︙ | |||
926 927 928 929 930 931 932 | ** Assign a variable number to an expression that encodes a wildcard ** in the original SQL statement. ** ** Wildcards consisting of a single "?" are assigned the next sequential ** variable number. ** ** Wildcards of the form "?nnn" are assigned the number "nnn". We make | | > | | | > > > > > | < > > | | > > > < < < | < < < | < < | | < < < < < < < | | | > | | 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 | ** Assign a variable number to an expression that encodes a wildcard ** in the original SQL statement. ** ** Wildcards consisting of a single "?" are assigned the next sequential ** variable number. ** ** Wildcards of the form "?nnn" are assigned the number "nnn". We make ** sure "nnn" is not too big to avoid a denial of service attack when ** the SQL statement comes from an external source. ** ** Wildcards of the form ":aaa", "@aaa", or "$aaa" are assigned the same number ** as the previous instance of the same wildcard. Or if this is the first ** instance of the wildcard, the next sequential variable number is ** assigned. */ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n){ sqlite3 *db = pParse->db; const char *z; ynVar x; if( pExpr==0 ) return; assert( !ExprHasProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) ); z = pExpr->u.zToken; assert( z!=0 ); assert( z[0]!=0 ); assert( n==(u32)sqlite3Strlen30(z) ); if( z[1]==0 ){ /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); x = (ynVar)(++pParse->nVar); }else{ int doAdd = 0; if( z[0]=='?' ){ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and ** use it as the variable number */ i64 i; int bOk; if( n==2 ){ /*OPTIMIZATION-IF-TRUE*/ i = z[1]-'0'; /* The common case of ?N for a single digit N */ bOk = 1; }else{ bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8); } testcase( i==0 ); testcase( i==1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); return; } x = (ynVar)i; if( x>pParse->nVar ){ pParse->nVar = (int)x; doAdd = 1; }else if( sqlite3VListNumToName(pParse->pVList, x)==0 ){ doAdd = 1; } }else{ /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable ** number as the prior appearance of the same name, or if the name ** has never appeared before, reuse the same variable number */ x = (ynVar)sqlite3VListNameToNum(pParse->pVList, z, n); if( x==0 ){ x = (ynVar)(++pParse->nVar); doAdd = 1; } } if( doAdd ){ pParse->pVList = sqlite3VListAdd(db, pParse->pVList, z, n, x); } } pExpr->iColumn = x; if( x>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ sqlite3ErrorMsg(pParse, "too many SQL variables"); } } /* ** Recursively delete an expression tree. */ |
︙ | ︙ | |||
1021 1022 1023 1024 1025 1026 1027 | assert( p->x.pSelect==0 ); } #endif if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( p->x.pList==0 || p->pRight==0 ); if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); | > | | | | 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 | assert( p->x.pSelect==0 ); } #endif if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( p->x.pList==0 || p->pRight==0 ); if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); if( p->pRight ){ sqlite3ExprDeleteNN(db, p->pRight); }else if( ExprHasProperty(p, EP_xIsSelect) ){ sqlite3SelectDelete(db, p->x.pSelect); }else{ sqlite3ExprListDelete(db, p->x.pList); } } if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken); if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbFreeNN(db, p); } } void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p ) sqlite3ExprDeleteNN(db, p); } /* |
︙ | ︙ | |||
1087 1088 1089 1090 1091 1092 1093 | ** to enforce this constraint. */ static int dupedExprStructSize(Expr *p, int flags){ int nSize; assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ assert( EXPR_FULLSIZE<=0xfff ); assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 ); | | | 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 | ** to enforce this constraint. */ static int dupedExprStructSize(Expr *p, int flags){ int nSize; assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ assert( EXPR_FULLSIZE<=0xfff ); assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 ); if( 0==flags || p->op==TK_SELECT_COLUMN ){ nSize = EXPR_FULLSIZE; }else{ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); assert( !ExprHasProperty(p, EP_FromJoin) ); assert( !ExprHasProperty(p, EP_MemToken) ); assert( !ExprHasProperty(p, EP_NoReduce) ); if( p->pLeft || p->x.pList ){ |
︙ | ︙ | |||
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | if( pzBuffer ){ *pzBuffer = zAlloc; } }else{ if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ if( pNew->op==TK_SELECT_COLUMN ){ pNew->pLeft = p->pLeft; }else{ pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); } pNew->pRight = sqlite3ExprDup(db, p->pRight, 0); } } } | > > | 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 | if( pzBuffer ){ *pzBuffer = zAlloc; } }else{ if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ if( pNew->op==TK_SELECT_COLUMN ){ pNew->pLeft = p->pLeft; assert( p->iColumn==0 || p->pRight==0 ); assert( p->pRight==0 || p->pRight==p->pLeft ); }else{ pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); } pNew->pRight = sqlite3ExprDup(db, p->pRight, 0); } } } |
︙ | ︙ | |||
1292 1293 1294 1295 1296 1297 1298 1299 1300 | assert( flags==0 || flags==EXPRDUP_REDUCE ); return p ? exprDup(db, p, flags, 0) : 0; } ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ ExprList *pNew; struct ExprList_item *pItem, *pOldItem; int i; assert( db!=0 ); if( p==0 ) return 0; | > | | < < | < < < > > > > > > > > > > > > > > > > > | 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 | assert( flags==0 || flags==EXPRDUP_REDUCE ); return p ? exprDup(db, p, flags, 0) : 0; } ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ ExprList *pNew; struct ExprList_item *pItem, *pOldItem; int i; Expr *pPriorSelectCol = 0; assert( db!=0 ); if( p==0 ) return 0; pNew = sqlite3DbMallocRawNN(db, sqlite3DbMallocSize(db, p)); if( pNew==0 ) return 0; pNew->nExpr = p->nExpr; pItem = pNew->a; pOldItem = p->a; for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){ Expr *pOldExpr = pOldItem->pExpr; Expr *pNewExpr; pItem->pExpr = sqlite3ExprDup(db, pOldExpr, flags); if( pOldExpr && pOldExpr->op==TK_SELECT_COLUMN && (pNewExpr = pItem->pExpr)!=0 ){ assert( pNewExpr->iColumn==0 || i>0 ); if( pNewExpr->iColumn==0 ){ assert( pOldExpr->pLeft==pOldExpr->pRight ); pPriorSelectCol = pNewExpr->pLeft = pNewExpr->pRight; }else{ assert( i>0 ); assert( pItem[-1].pExpr!=0 ); assert( pNewExpr->iColumn==pItem[-1].pExpr->iColumn+1 ); assert( pPriorSelectCol==pItem[-1].pExpr->pLeft ); pNewExpr->pLeft = pPriorSelectCol; } } pItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan); pItem->sortOrder = pOldItem->sortOrder; pItem->done = 0; pItem->bSpanIsTab = pOldItem->bSpanIsTab; pItem->u = pOldItem->u; } |
︙ | ︙ | |||
1357 1358 1359 1360 1361 1362 1363 | pNewItem->pIBIndex = pOldItem->pIBIndex; if( pNewItem->fg.isTabFunc ){ pNewItem->u1.pFuncArg = sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); } pTab = pNewItem->pTab = pOldItem->pTab; if( pTab ){ | | | | > > > | > | | | | | | | | | | < | | | | | | | | | | | | > > > > > | > > > > > > > > < < | < | > | | < < | > > | | < | | > > > > > | > | > | | > | > > > | | | > > > | 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 | pNewItem->pIBIndex = pOldItem->pIBIndex; if( pNewItem->fg.isTabFunc ){ pNewItem->u1.pFuncArg = sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); } pTab = pNewItem->pTab = pOldItem->pTab; if( pTab ){ pTab->nTabRef++; } pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags); pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); pNewItem->colUsed = pOldItem->colUsed; } return pNew; } IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){ IdList *pNew; int i; assert( db!=0 ); if( p==0 ) return 0; pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) ); if( pNew==0 ) return 0; pNew->nId = p->nId; pNew->a = sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) ); if( pNew->a==0 ){ sqlite3DbFreeNN(db, pNew); return 0; } /* Note that because the size of the allocation for p->a[] is not ** necessarily a power of two, sqlite3IdListAppend() may not be called ** on the duplicate created by this function. */ for(i=0; i<p->nId; i++){ struct IdList_item *pNewItem = &pNew->a[i]; struct IdList_item *pOldItem = &p->a[i]; pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->idx = pOldItem->idx; } return pNew; } Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ Select *pRet = 0; Select *pNext = 0; Select **pp = &pRet; Select *p; assert( db!=0 ); for(p=pDup; p; p=p->pPrior){ Select *pNew = sqlite3DbMallocRawNN(db, sizeof(*p) ); if( pNew==0 ) break; pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags); pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags); pNew->pWhere = sqlite3ExprDup(db, p->pWhere, flags); pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy, flags); pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags); pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags); pNew->op = p->op; pNew->pNext = pNext; pNew->pPrior = 0; pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags); pNew->iLimit = 0; pNew->iOffset = 0; pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; pNew->nSelectRow = p->nSelectRow; pNew->pWith = withDup(db, p->pWith); sqlite3SelectSetName(pNew, p->zSelName); *pp = pNew; pp = &pNew->pPrior; pNext = pNew; } return pRet; } #else Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ assert( p==0 ); return 0; } #endif /* ** Add a new element to the end of an expression list. If pList is ** initially NULL, then create a new expression list. ** ** The pList argument must be either NULL or a pointer to an ExprList ** obtained from a prior call to sqlite3ExprListAppend(). This routine ** may not be used with an ExprList obtained from sqlite3ExprListDup(). ** Reason: This routine assumes that the number of slots in pList->a[] ** is a power of two. That is true for sqlite3ExprListAppend() returns ** but is not necessarily true from the return value of sqlite3ExprListDup(). ** ** If a memory allocation error occurs, the entire list is freed and ** NULL is returned. If non-NULL is returned, then it is guaranteed ** that the new entry was successfully appended. */ ExprList *sqlite3ExprListAppend( Parse *pParse, /* Parsing context */ ExprList *pList, /* List to which to append. Might be NULL */ Expr *pExpr /* Expression to be appended. Might be NULL */ ){ struct ExprList_item *pItem; sqlite3 *db = pParse->db; assert( db!=0 ); if( pList==0 ){ pList = sqlite3DbMallocRawNN(db, sizeof(ExprList) ); if( pList==0 ){ goto no_mem; } pList->nExpr = 0; }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ ExprList *pNew; pNew = sqlite3DbRealloc(db, pList, sizeof(*pList)+(2*pList->nExpr - 1)*sizeof(pList->a[0])); if( pNew==0 ){ goto no_mem; } pList = pNew; } pItem = &pList->a[pList->nExpr++]; assert( offsetof(struct ExprList_item,zName)==sizeof(pItem->pExpr) ); assert( offsetof(struct ExprList_item,pExpr)==0 ); memset(&pItem->zName,0,sizeof(*pItem)-offsetof(struct ExprList_item,zName)); pItem->pExpr = pExpr; return pList; no_mem: /* Avoid leaking memory if malloc has failed. */ sqlite3ExprDelete(db, pExpr); sqlite3ExprListDelete(db, pList); return 0; } /* ** pColumns and pExpr form a vector assignment which is part of the SET ** clause of an UPDATE statement. Like this: ** ** (a,b,c) = (expr1,expr2,expr3) ** Or: (a,b,c) = (SELECT x,y,z FROM ....) ** ** For each term of the vector assignment, append new entries to the ** expression list pList. In the case of a subquery on the RHS, append ** TK_SELECT_COLUMN expressions. */ ExprList *sqlite3ExprListAppendVector( Parse *pParse, /* Parsing context */ ExprList *pList, /* List to which to append. Might be NULL */ IdList *pColumns, /* List of names of LHS of the assignment */ Expr *pExpr /* Vector expression to be appended. Might be NULL */ ){ sqlite3 *db = pParse->db; int n; int i; int iFirst = pList ? pList->nExpr : 0; /* pColumns can only be NULL due to an OOM but an OOM will cause an ** exit prior to this routine being invoked */ if( NEVER(pColumns==0) ) goto vector_append_error; if( pExpr==0 ) goto vector_append_error; /* If the RHS is a vector, then we can immediately check to see that ** the size of the RHS and LHS match. But if the RHS is a SELECT, ** wildcards ("*") in the result set of the SELECT must be expanded before ** we can do the size check, so defer the size check until code generation. */ if( pExpr->op!=TK_SELECT && pColumns->nId!=(n=sqlite3ExprVectorSize(pExpr)) ){ sqlite3ErrorMsg(pParse, "%d columns assigned %d values", pColumns->nId, n); goto vector_append_error; } for(i=0; i<pColumns->nId; i++){ Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i); pList = sqlite3ExprListAppend(pParse, pList, pSubExpr); if( pList ){ assert( pList->nExpr==iFirst+i+1 ); pList->a[pList->nExpr-1].zName = pColumns->a[i].zName; pColumns->a[i].zName = 0; } } if( !db->mallocFailed && pExpr->op==TK_SELECT && ALWAYS(pList!=0) ){ Expr *pFirst = pList->a[iFirst].pExpr; assert( pFirst!=0 ); assert( pFirst->op==TK_SELECT_COLUMN ); /* Store the SELECT statement in pRight so it will be deleted when ** sqlite3ExprListDelete() is called */ pFirst->pRight = pExpr; pExpr = 0; /* Remember the size of the LHS in iTable so that we can check that ** the RHS and LHS sizes match during code generation. */ pFirst->iTable = pColumns->nId; } vector_append_error: sqlite3ExprDelete(db, pExpr); sqlite3IdListDelete(db, pColumns); return pList; } |
︙ | ︙ | |||
1612 1613 1614 1615 1616 1617 1618 | } } /* ** Delete an entire expression list. */ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){ | | | | | > | < | | 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 | } } /* ** Delete an entire expression list. */ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){ int i = pList->nExpr; struct ExprList_item *pItem = pList->a; assert( pList->nExpr>0 ); do{ sqlite3ExprDelete(db, pItem->pExpr); sqlite3DbFree(db, pItem->zName); sqlite3DbFree(db, pItem->zSpan); pItem++; }while( --i>0 ); sqlite3DbFreeNN(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 |
︙ | ︙ | |||
1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 | Expr *pExpr = pList->a[i].pExpr; assert( pExpr!=0 ); m |= pExpr->flags; } } return m; } /* ** 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. ** | > > > > > > > > > > > > > | 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 | Expr *pExpr = pList->a[i].pExpr; assert( pExpr!=0 ); m |= pExpr->flags; } } return m; } /* ** This is a SELECT-node callback for the expression walker that ** always "fails". By "fail" in this case, we mean set ** pWalker->eCode to zero and abort. ** ** This callback is used by multiple expression walkers. */ int sqlite3SelectWalkFail(Walker *pWalker, Select *NotUsed){ UNUSED_PARAMETER(NotUsed); pWalker->eCode = 0; return WRC_Abort; } /* ** 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. ** |
︙ | ︙ | |||
1700 1701 1702 1703 1704 1705 1706 | case TK_AGG_COLUMN: testcase( pExpr->op==TK_ID ); testcase( pExpr->op==TK_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); testcase( pExpr->op==TK_AGG_COLUMN ); if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){ return WRC_Continue; | > > | > | | < | | < < < < < < | > > > | 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 | case TK_AGG_COLUMN: testcase( pExpr->op==TK_ID ); testcase( pExpr->op==TK_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); testcase( pExpr->op==TK_AGG_COLUMN ); if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){ return WRC_Continue; } /* Fall through */ case TK_IF_NULL_ROW: testcase( pExpr->op==TK_IF_NULL_ROW ); pWalker->eCode = 0; return WRC_Abort; case TK_VARIABLE: if( pWalker->eCode==5 ){ /* Silently convert bound parameters that appear inside of CREATE ** statements into a NULL when parsing the CREATE statement text out ** of the sqlite_master table */ pExpr->op = TK_NULL; }else if( pWalker->eCode==4 ){ /* A bound parameter in a CREATE statement that originates from ** sqlite3_prepare() causes an error */ pWalker->eCode = 0; return WRC_Abort; } /* Fall through */ default: testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail will disallow */ testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail will disallow */ return WRC_Continue; } } static int exprIsConst(Expr *p, int initFlag, int iCur){ Walker w; w.eCode = initFlag; 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 |
︙ | ︙ | |||
1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 | ** expression must not refer to any non-deterministic function nor any ** table other than iCur. */ int sqlite3ExprIsTableConstant(Expr *p, int iCur){ return exprIsConst(p, 3, iCur); } /* ** Walk an expression tree. Return non-zero if the expression is constant ** or a function call with constant arguments. Return and 0 if there ** are any variables. ** ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** expression must not refer to any non-deterministic function nor any ** table other than iCur. */ int sqlite3ExprIsTableConstant(Expr *p, int iCur){ return exprIsConst(p, 3, iCur); } /* ** sqlite3WalkExpr() callback used by sqlite3ExprIsConstantOrGroupBy(). */ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){ ExprList *pGroupBy = pWalker->u.pGroupBy; int i; /* Check if pExpr is identical to any GROUP BY term. If so, consider ** it constant. */ for(i=0; i<pGroupBy->nExpr; i++){ Expr *p = pGroupBy->a[i].pExpr; if( sqlite3ExprCompare(0, pExpr, p, -1)<2 ){ CollSeq *pColl = sqlite3ExprCollSeq(pWalker->pParse, p); if( pColl==0 || sqlite3_stricmp("BINARY", pColl->zName)==0 ){ return WRC_Prune; } } } /* Check if pExpr is a sub-select. If so, consider it variable. */ if( ExprHasProperty(pExpr, EP_xIsSelect) ){ pWalker->eCode = 0; return WRC_Abort; } return exprNodeIsConstant(pWalker, pExpr); } /* ** Walk the expression tree passed as the first argument. Return non-zero ** if the expression consists entirely of constants or copies of terms ** in pGroupBy that sort with the BINARY collation sequence. ** ** This routine is used to determine if a term of the HAVING clause can ** be promoted into the WHERE clause. In order for such a promotion to work, ** the value of the HAVING clause term must be the same for all members of ** a "group". The requirement that the GROUP BY term must be BINARY ** assumes that no other collating sequence will have a finer-grained ** grouping than binary. In other words (A=B COLLATE binary) implies ** A=B in every other collating sequence. The requirement that the ** GROUP BY be BINARY is stricter than necessary. It would also work ** to promote HAVING clauses that use the same alternative collating ** sequence as the GROUP BY term, but that is much harder to check, ** alternative collating sequences are uncommon, and this is only an ** optimization, so we take the easy way out and simply require the ** GROUP BY to use the BINARY collating sequence. */ int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprList *pGroupBy){ Walker w; w.eCode = 1; w.xExprCallback = exprNodeIsConstantOrGroupBy; w.xSelectCallback = 0; w.u.pGroupBy = pGroupBy; w.pParse = pParse; sqlite3WalkExpr(&w, p); return w.eCode; } /* ** Walk an expression tree. Return non-zero if the expression is constant ** or a function call with constant arguments. Return and 0 if there ** are any variables. ** ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is |
︙ | ︙ | |||
1792 1793 1794 1795 1796 1797 1798 | #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. */ int sqlite3ExprContainsSubquery(Expr *p){ Walker w; | < | > > > > | 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 | #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. */ int sqlite3ExprContainsSubquery(Expr *p){ Walker w; w.eCode = 1; w.xExprCallback = sqlite3ExprWalkNoop; w.xSelectCallback = sqlite3SelectWalkFail; #ifdef SQLITE_DEBUG w.xSelectCallback2 = sqlite3SelectWalkAssert2; #endif sqlite3WalkExpr(&w, p); return w.eCode==0; } #endif /* ** If the expression p codes a constant integer that is small enough ** to fit in a 32-bit integer, return 1 and put the value of the integer ** in *pValue. If the expression is not an integer or if it is too big ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. */ int sqlite3ExprIsInteger(Expr *p, int *pValue){ int rc = 0; if( p==0 ) return 0; /* Can only happen following on OOM */ /* If an expression is an integer literal that fits in a signed 32-bit ** integer, then the EP_IntValue flag will have already been set */ assert( p->op!=TK_INTEGER || (p->flags & EP_IntValue)!=0 || sqlite3GetInt32(p->u.zToken, &rc)==0 ); if( p->flags & EP_IntValue ){ |
︙ | ︙ | |||
1864 1865 1866 1867 1868 1869 1870 | switch( op ){ case TK_INTEGER: case TK_STRING: case TK_FLOAT: case TK_BLOB: return 0; case TK_COLUMN: | < > | 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 | switch( op ){ case TK_INTEGER: case TK_STRING: case TK_FLOAT: case TK_BLOB: return 0; case TK_COLUMN: return ExprHasProperty(p, EP_CanBeNull) || p->pTab==0 || /* Reference to column of index on expression */ (p->iColumn>=0 && p->pTab->aCol[p->iColumn].notNull==0); default: return 1; } } /* |
︙ | ︙ | |||
2316 2317 2318 2319 2320 2321 2322 | static char *exprINAffinity(Parse *pParse, Expr *pExpr){ Expr *pLeft = pExpr->pLeft; int nVal = sqlite3ExprVectorSize(pLeft); Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0; char *zRet; assert( pExpr->op==TK_IN ); | | | 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 | static char *exprINAffinity(Parse *pParse, Expr *pExpr){ Expr *pLeft = pExpr->pLeft; int nVal = sqlite3ExprVectorSize(pLeft); Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0; char *zRet; assert( pExpr->op==TK_IN ); zRet = sqlite3DbMallocRaw(pParse->db, nVal+1); if( zRet ){ int i; for(i=0; i<nVal; i++){ Expr *pA = sqlite3VectorFieldSubexpr(pLeft, i); char a = sqlite3ExprAffinity(pA); if( pSelect ){ zRet[i] = sqlite3CompareAffinity(pSelect->pEList->a[i].pExpr, a); |
︙ | ︙ | |||
2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 | ** "sub-select returns N columns - expected M" */ void sqlite3SubselectError(Parse *pParse, int nActual, int nExpect){ const char *zFmt = "sub-select returns %d columns - expected %d"; sqlite3ErrorMsg(pParse, zFmt, nActual, nExpect); } #endif /* ** Generate code for scalar subqueries used as a subquery expression, EXISTS, ** or IN operators. Examples: ** ** (SELECT a FROM b) -- subquery ** EXISTS (SELECT a FROM b) -- EXISTS subquery | > > > > > > > > > > > > > > > > > > > > > > | 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 | ** "sub-select returns N columns - expected M" */ void sqlite3SubselectError(Parse *pParse, int nActual, int nExpect){ const char *zFmt = "sub-select returns %d columns - expected %d"; sqlite3ErrorMsg(pParse, zFmt, nActual, nExpect); } #endif /* ** Expression pExpr is a vector that has been used in a context where ** it is not permitted. If pExpr is a sub-select vector, this routine ** loads the Parse object with a message of the form: ** ** "sub-select returns N columns - expected 1" ** ** Or, if it is a regular scalar vector: ** ** "row value misused" */ void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ #ifndef SQLITE_OMIT_SUBQUERY if( pExpr->flags & EP_xIsSelect ){ sqlite3SubselectError(pParse, pExpr->x.pSelect->pEList->nExpr, 1); }else #endif { sqlite3ErrorMsg(pParse, "row value misused"); } } /* ** Generate code for scalar subqueries used as a subquery expression, EXISTS, ** or IN operators. Examples: ** ** (SELECT a FROM b) -- subquery ** EXISTS (SELECT a FROM b) -- EXISTS subquery |
︙ | ︙ | |||
2459 2460 2461 2462 2463 2464 2465 | /* If the LHS and RHS of the IN operator do not match, that ** error will have been caught long before we reach this point. */ if( ALWAYS(pEList->nExpr==nVal) ){ SelectDest dest; int i; sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); dest.zAffSdst = exprINAffinity(pParse, pExpr); | < | 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 | /* If the LHS and RHS of the IN operator do not match, that ** error will have been caught long before we reach this point. */ if( ALWAYS(pEList->nExpr==nVal) ){ SelectDest dest; int i; sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); dest.zAffSdst = exprINAffinity(pParse, pExpr); pSelect->iLimit = 0; testcase( pSelect->selFlags & SF_Distinct ); testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ if( sqlite3Select(pParse, pSelect, &dest) ){ sqlite3DbFree(pParse->db, dest.zAffSdst); sqlite3KeyInfoUnref(pKeyInfo); return 0; |
︙ | ︙ | |||
2534 2535 2536 2537 2538 2539 2540 | sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); }else{ sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); sqlite3ExprCacheAffinityChange(pParse, r3, 1); | | | 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 | sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); }else{ sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); sqlite3ExprCacheAffinityChange(pParse, r3, 1); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pExpr->iTable, r2, r3, 1); } } } sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r2); } if( pKeyInfo ){ |
︙ | ︙ | |||
2629 2630 2631 2632 2633 2634 2635 | int nVector = sqlite3ExprVectorSize(pIn->pLeft); if( (pIn->flags & EP_xIsSelect) ){ if( nVector!=pIn->x.pSelect->pEList->nExpr ){ sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector); return 1; } }else if( nVector!=1 ){ | < < < | < | 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 | int nVector = sqlite3ExprVectorSize(pIn->pLeft); if( (pIn->flags & EP_xIsSelect) ){ if( nVector!=pIn->x.pSelect->pEList->nExpr ){ sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector); return 1; } }else if( nVector!=1 ){ sqlite3VectorErrorMsg(pParse, pIn->pLeft); return 1; } return 0; } #endif #ifndef SQLITE_OMIT_SUBQUERY |
︙ | ︙ | |||
2938 2939 2940 2941 2942 2943 2944 | sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); }else{ int c; i64 value; const char *z = pExpr->u.zToken; assert( z!=0 ); c = sqlite3DecOrHexToI64(z, &value); | < | < < | > > > | 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 | sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); }else{ int c; i64 value; const char *z = pExpr->u.zToken; assert( z!=0 ); c = sqlite3DecOrHexToI64(z, &value); if( (c==3 && !negFlag) || (c==2) || (negFlag && value==SMALLEST_INT64)){ #ifdef SQLITE_OMIT_FLOATING_POINT sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); #else #ifndef SQLITE_OMIT_HEX_INTEGER if( sqlite3_strnicmp(z,"0x",2)==0 ){ sqlite3ErrorMsg(pParse, "hex literal too big: %s%s", negFlag?"-":"",z); }else #endif { codeReal(v, z, negFlag, iMem); } #endif }else{ if( negFlag ){ value = c==3 ? SMALLEST_INT64 : -value; } sqlite3VdbeAddOp4Dup8(v, OP_Int64, 0, iMem, 0, (u8*)&value, P4_INT64); } } } /* ** Erase column-cache entry number i */ |
︙ | ︙ | |||
3112 3113 3114 3115 3116 3117 3118 | int iIdxCol, /* The column of the index to be loaded */ int regOut /* Store the index column value in this register */ ){ i16 iTabCol = pIdx->aiColumn[iIdxCol]; if( iTabCol==XN_EXPR ){ assert( pIdx->aColExpr ); assert( pIdx->aColExpr->nExpr>iIdxCol ); | | > > > > > | 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 | int iIdxCol, /* The column of the index to be loaded */ int regOut /* Store the index column value in this register */ ){ i16 iTabCol = pIdx->aiColumn[iIdxCol]; if( iTabCol==XN_EXPR ){ assert( pIdx->aColExpr ); assert( pIdx->aColExpr->nExpr>iIdxCol ); pParse->iSelfTab = iTabCur + 1; sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut); pParse->iSelfTab = 0; }else{ sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable, iTabCur, iTabCol, regOut); } } /* ** Generate code to extract the value of the iCol-th column of a table. */ void sqlite3ExprCodeGetColumnOfTable( Vdbe *v, /* The VDBE under construction */ Table *pTab, /* The table containing the value */ int iTabCur, /* The table cursor. Or the PK cursor for WITHOUT ROWID */ int iCol, /* Index of the column to extract */ int regOut /* Extract the value into this register */ ){ if( pTab==0 ){ sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut); return; } if( iCol<0 || iCol==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); }else{ int op = IsVirtual(pTab) ? OP_VColumn : OP_Column; int x = iCol; if( !HasRowid(pTab) && !IsVirtual(pTab) ){ x = sqlite3ColumnOfIndex(sqlite3PrimaryKeyIndex(pTab), iCol); |
︙ | ︙ | |||
3203 3204 3205 3206 3207 3208 3209 | /* ** Clear all column cache entries. */ void sqlite3ExprCacheClear(Parse *pParse){ int i; | | | 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 | /* ** Clear all column cache entries. */ void sqlite3ExprCacheClear(Parse *pParse){ int i; #ifdef SQLITE_DEBUG if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ printf("CLEAR\n"); } #endif for(i=0; i<pParse->nColCache; i++){ if( pParse->aColCache[i].tempReg && pParse->nTempReg<ArraySize(pParse->aTempReg) |
︙ | ︙ | |||
3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 | int iResult; int nResult = sqlite3ExprVectorSize(p); if( nResult==1 ){ iResult = sqlite3ExprCodeTemp(pParse, p, piFreeable); }else{ *piFreeable = 0; if( p->op==TK_SELECT ){ iResult = sqlite3CodeSubselect(pParse, p, 0, 0); }else{ int i; iResult = pParse->nMem+1; pParse->nMem += nResult; for(i=0; i<nResult; i++){ | > > > > | | 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 | int iResult; int nResult = sqlite3ExprVectorSize(p); if( nResult==1 ){ iResult = sqlite3ExprCodeTemp(pParse, p, piFreeable); }else{ *piFreeable = 0; if( p->op==TK_SELECT ){ #if SQLITE_OMIT_SUBQUERY iResult = 0; #else iResult = sqlite3CodeSubselect(pParse, p, 0, 0); #endif }else{ int i; iResult = pParse->nMem+1; pParse->nMem += nResult; for(i=0; i<nResult; i++){ sqlite3ExprCodeFactorable(pParse, p->x.pList->a[i].pExpr, i+iResult); } } } return iResult; } |
︙ | ︙ | |||
3349 3350 3351 3352 3353 3354 3355 | return target; } /* Otherwise, fall thru into the TK_COLUMN case */ } case TK_COLUMN: { int iTab = pExpr->iTable; if( iTab<0 ){ | | | | | 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 | return target; } /* Otherwise, fall thru into the TK_COLUMN case */ } case TK_COLUMN: { int iTab = pExpr->iTable; if( iTab<0 ){ if( pParse->iSelfTab<0 ){ /* Generating CHECK constraints or inserting into partial index */ return pExpr->iColumn - pParse->iSelfTab; }else{ /* Coding an expression that is part of an index where column names ** in the index refer to the table to which the index belongs */ iTab = pParse->iSelfTab - 1; } } return sqlite3ExprCodeGetColumn(pParse, pExpr->pTab, pExpr->iColumn, iTab, target, pExpr->op2); } case TK_INTEGER: { |
︙ | ︙ | |||
3404 3405 3406 3407 3408 3409 3410 | #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); if( pExpr->u.zToken[1]!=0 ){ | > | | | | 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 | #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); if( pExpr->u.zToken[1]!=0 ){ const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn); assert( pExpr->u.zToken[0]=='?' || strcmp(pExpr->u.zToken, z)==0 ); pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */ sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC); } return target; } case TK_REGISTER: { return pExpr->iTable; } #ifndef SQLITE_OMIT_CAST |
︙ | ︙ | |||
3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 | const char *zId; /* The function name */ u32 constMask = 0; /* Mask of function arguments that are constant */ int i; /* Loop counter */ sqlite3 *db = pParse->db; /* The database connection */ u8 enc = ENC(db); /* The text encoding used by this database */ CollSeq *pColl = 0; /* A collating sequence */ assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); if( ExprHasProperty(pExpr, EP_TokenOnly) ){ pFarg = 0; }else{ pFarg = pExpr->x.pList; } nFarg = pFarg ? pFarg->nExpr : 0; | > > > > > | 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 | const char *zId; /* The function name */ u32 constMask = 0; /* Mask of function arguments that are constant */ int i; /* Loop counter */ sqlite3 *db = pParse->db; /* The database connection */ u8 enc = ENC(db); /* The text encoding used by this database */ CollSeq *pColl = 0; /* A collating sequence */ if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){ /* SQL functions can be expensive. So try to move constant functions ** out of the inner loop, even if that means an extra OP_Copy. */ return sqlite3ExprCodeAtInit(pParse, pExpr, -1); } assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); if( ExprHasProperty(pExpr, EP_TokenOnly) ){ pFarg = 0; }else{ pFarg = pExpr->x.pList; } nFarg = pFarg ? pFarg->nExpr : 0; |
︙ | ︙ | |||
3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 | /* The UNLIKELY() function is a no-op. The result is the value ** of the first argument. */ if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){ assert( nFarg>=1 ); return sqlite3ExprCodeTarget(pParse, pFarg->a[0].pExpr, target); } for(i=0; i<nFarg; i++){ if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){ testcase( i==31 ); constMask |= MASKBIT32(i); } if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ | > > > > > > > > > > > > > > > > | 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 | /* The UNLIKELY() function is a no-op. The result is the value ** of the first argument. */ if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){ assert( nFarg>=1 ); return sqlite3ExprCodeTarget(pParse, pFarg->a[0].pExpr, target); } #ifdef SQLITE_DEBUG /* The AFFINITY() function evaluates to a string that describes ** the type affinity of the argument. This is used for testing of ** the SQLite type logic. */ if( pDef->funcFlags & SQLITE_FUNC_AFFINITY ){ const char *azAff[] = { "blob", "text", "numeric", "integer", "real" }; char aff; assert( nFarg==1 ); aff = sqlite3ExprAffinity(pFarg->a[0].pExpr); sqlite3VdbeLoadString(v, target, aff ? azAff[aff-SQLITE_AFF_BLOB] : "none"); return target; } #endif for(i=0; i<nFarg; i++){ if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){ testcase( i==31 ); constMask |= MASKBIT32(i); } if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ |
︙ | ︙ | |||
3670 3671 3672 3673 3674 3675 3676 | pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr); } #endif if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){ if( !pColl ) pColl = db->pDfltColl; sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); } | | | > > > > > > > > | 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 | pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr); } #endif if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){ if( !pColl ) pColl = db->pDfltColl; sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); } sqlite3VdbeAddOp4(v, pParse->iSelfTab ? OP_PureFunc0 : OP_Function0, constMask, r1, target, (char*)pDef, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nFarg); if( nFarg && constMask==0 ){ sqlite3ReleaseTempRange(pParse, r1, nFarg); } return target; } #ifndef SQLITE_OMIT_SUBQUERY case TK_EXISTS: case TK_SELECT: { int nCol; testcase( op==TK_EXISTS ); testcase( op==TK_SELECT ); if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){ sqlite3SubselectError(pParse, nCol, 1); }else{ return sqlite3CodeSubselect(pParse, pExpr, 0, 0); } break; } case TK_SELECT_COLUMN: { int n; if( pExpr->pLeft->iTable==0 ){ pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft, 0, 0); } assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); if( pExpr->iTable && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft)) ){ sqlite3ErrorMsg(pParse, "%d columns assigned %d values", pExpr->iTable, n); } return pExpr->pLeft->iTable + pExpr->iColumn; } case TK_IN: { int destIfFalse = sqlite3VdbeMakeLabel(v); int destIfNull = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp2(v, OP_Null, 0, target); |
︙ | ︙ | |||
3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 | break; } case TK_VECTOR: { sqlite3ErrorMsg(pParse, "row value misused"); break; } /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END ** ** Form B: ** CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END | > > > > > > > > > > > | 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 | break; } case TK_VECTOR: { sqlite3ErrorMsg(pParse, "row value misused"); break; } case TK_IF_NULL_ROW: { int addrINR; addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable); sqlite3ExprCachePush(pParse); inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); sqlite3ExprCachePop(pParse); sqlite3VdbeJumpHere(v, addrINR); sqlite3VdbeChangeP3(v, addrINR, inReg); break; } /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END ** ** Form B: ** CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END |
︙ | ︙ | |||
3912 3913 3914 3915 3916 3917 3918 3919 | sqlite3ReleaseTempReg(pParse, regFree1); sqlite3ReleaseTempReg(pParse, regFree2); return inReg; } /* ** Factor out the code of the given expression to initialization time. */ | > > > > > > | | < > > > > > > > > > > > < > | 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 | sqlite3ReleaseTempReg(pParse, regFree1); sqlite3ReleaseTempReg(pParse, regFree2); return inReg; } /* ** Factor out the code of the given expression to initialization time. ** ** If regDest>=0 then the result is always stored in that register and the ** result is not reusable. If regDest<0 then this routine is free to ** store the value whereever it wants. The register where the expression ** is stored is returned. When regDest<0, two identical expressions will ** code to the same register. */ int sqlite3ExprCodeAtInit( Parse *pParse, /* Parsing context */ Expr *pExpr, /* The expression to code when the VDBE initializes */ int regDest /* Store the value in this register */ ){ ExprList *p; assert( ConstFactorOk(pParse) ); p = pParse->pConstExpr; if( regDest<0 && p ){ struct ExprList_item *pItem; int i; for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){ if( pItem->reusable && sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 ){ return pItem->u.iConstExprReg; } } } pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); p = sqlite3ExprListAppend(pParse, p, pExpr); if( p ){ struct ExprList_item *pItem = &p->a[p->nExpr-1]; pItem->reusable = regDest<0; if( regDest<0 ) regDest = ++pParse->nMem; pItem->u.iConstExprReg = regDest; } pParse->pConstExpr = p; return regDest; } /* ** Generate code to evaluate an expression and store the results ** into a register. Return the register number where the results ** are stored. ** |
︙ | ︙ | |||
3952 3953 3954 3955 3956 3957 3958 | int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ int r2; pExpr = sqlite3ExprSkipCollate(pExpr); if( ConstFactorOk(pParse) && pExpr->op!=TK_REGISTER && sqlite3ExprIsConstantNotJoin(pExpr) ){ | < < < < < < < < < < < | | 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 | int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ int r2; pExpr = sqlite3ExprSkipCollate(pExpr); if( ConstFactorOk(pParse) && pExpr->op!=TK_REGISTER && sqlite3ExprIsConstantNotJoin(pExpr) ){ *pReg = 0; r2 = sqlite3ExprCodeAtInit(pParse, pExpr, -1); }else{ int r1 = sqlite3GetTempReg(pParse); r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); if( r2==r1 ){ *pReg = r1; }else{ sqlite3ReleaseTempReg(pParse, r1); |
︙ | ︙ | |||
4018 4019 4020 4021 4022 4023 4024 | ** 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 && sqlite3ExprIsConstant(pExpr) ){ | | | 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 | ** 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 && sqlite3ExprIsConstant(pExpr) ){ sqlite3ExprCodeAtInit(pParse, pExpr, target); }else{ sqlite3ExprCode(pParse, pExpr, target); } } /* ** Generate code that evaluates the given expression and puts the result |
︙ | ︙ | |||
4052 4053 4054 4055 4056 4057 4058 | exprToRegister(pExpr, iMem); } /* ** Generate code that pushes the value of every element of the given ** expression list into a sequence of registers beginning at target. ** | | > > > > | > > > > | > | | 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 | exprToRegister(pExpr, iMem); } /* ** Generate code that pushes the value of every element of the given ** expression list into a sequence of registers beginning at target. ** ** Return the number of elements evaluated. The number returned will ** usually be pList->nExpr but might be reduced if SQLITE_ECEL_OMITREF ** is defined. ** ** The SQLITE_ECEL_DUP flag prevents the arguments from being ** filled using OP_SCopy. OP_Copy must be used instead. ** ** The SQLITE_ECEL_FACTOR argument allows constant arguments to be ** factored out into initialization code. ** ** The SQLITE_ECEL_REF flag means that expressions in the list with ** ExprList.a[].u.x.iOrderByCol>0 have already been evaluated and stored ** in registers at srcReg, and so the value can be copied from there. ** If SQLITE_ECEL_OMITREF is also set, then the values with u.x.iOrderByCol>0 ** are simply omitted rather than being copied from srcReg. */ int sqlite3ExprCodeExprList( Parse *pParse, /* Parsing context */ ExprList *pList, /* The expression list to be coded */ int target, /* Where to write results */ int srcReg, /* Source registers if SQLITE_ECEL_REF */ u8 flags /* SQLITE_ECEL_* flags */ ){ struct ExprList_item *pItem; int i, j, n; u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy; Vdbe *v = pParse->pVdbe; assert( pList!=0 ); assert( target>0 ); assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */ n = pList->nExpr; if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR; for(pItem=pList->a, i=0; i<n; i++, pItem++){ Expr *pExpr = pItem->pExpr; if( (flags & SQLITE_ECEL_REF)!=0 && (j = pItem->u.x.iOrderByCol)>0 ){ if( flags & SQLITE_ECEL_OMITREF ){ i--; n--; }else{ sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i); } }else if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ sqlite3ExprCodeAtInit(pParse, pExpr, target+i); }else{ int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); if( inReg!=target+i ){ VdbeOp *pOp; if( copyOp==OP_Copy && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy && pOp->p1+pOp->p3+1==inReg |
︙ | ︙ | |||
4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 | compRight.op = TK_LE; compRight.pLeft = &exprX; compRight.pRight = pExpr->x.pList->a[1].pExpr; exprToRegister(&exprX, exprCodeVector(pParse, &exprX, ®Free1)); if( xJump ){ xJump(pParse, &exprAnd, dest, jumpIfNull); }else{ exprX.flags |= EP_FromJoin; sqlite3ExprCodeTarget(pParse, &exprAnd, dest); } sqlite3ReleaseTempReg(pParse, regFree1); /* Ensure adequate test coverage */ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1==0 ); | > > > > > | 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 | compRight.op = TK_LE; compRight.pLeft = &exprX; compRight.pRight = pExpr->x.pList->a[1].pExpr; exprToRegister(&exprX, exprCodeVector(pParse, &exprX, ®Free1)); if( xJump ){ xJump(pParse, &exprAnd, dest, jumpIfNull); }else{ /* Mark the expression is being from the ON or USING clause of a join ** so that the sqlite3ExprCodeTarget() routine will not attempt to move ** it into the Parse.pConstExpr list. We should use a new bit for this, ** for clarity, but we are out of bits in the Expr.flags field so we ** have to reuse the EP_FromJoin bit. Bummer. */ exprX.flags |= EP_FromJoin; sqlite3ExprCodeTarget(pParse, &exprAnd, dest); } sqlite3ReleaseTempReg(pParse, regFree1); /* Ensure adequate test coverage */ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1==0 ); |
︙ | ︙ | |||
4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 | Expr *pCopy = sqlite3ExprDup(db, pExpr, 0); if( db->mallocFailed==0 ){ sqlite3ExprIfFalse(pParse, pCopy, dest, jumpIfNull); } sqlite3ExprDelete(db, pCopy); } /* ** Do a deep comparison of two expression trees. Return 0 if the two ** expressions are completely identical. Return 1 if they differ only ** by a COLLATE operator at the top level. Return 2 if there are differences ** other than the top-level COLLATE operator. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 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 | Expr *pCopy = sqlite3ExprDup(db, pExpr, 0); if( db->mallocFailed==0 ){ sqlite3ExprIfFalse(pParse, pCopy, dest, jumpIfNull); } sqlite3ExprDelete(db, pCopy); } /* ** Expression pVar is guaranteed to be an SQL variable. pExpr may be any ** type of expression. ** ** If pExpr is a simple SQL value - an integer, real, string, blob ** or NULL value - then the VDBE currently being prepared is configured ** to re-prepare each time a new value is bound to variable pVar. ** ** Additionally, if pExpr is a simple SQL value and the value is the ** same as that currently bound to variable pVar, non-zero is returned. ** Otherwise, if the values are not the same or if pExpr is not a simple ** SQL value, zero is returned. */ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ int res = 0; int iVar; sqlite3_value *pL, *pR = 0; sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR); if( pR ){ iVar = pVar->iColumn; sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB); if( pL ){ if( sqlite3_value_type(pL)==SQLITE_TEXT ){ sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */ } res = 0==sqlite3MemCompare(pL, pR, 0); } sqlite3ValueFree(pR); sqlite3ValueFree(pL); } return res; } /* ** Do a deep comparison of two expression trees. Return 0 if the two ** expressions are completely identical. Return 1 if they differ only ** by a COLLATE operator at the top level. Return 2 if there are differences ** other than the top-level COLLATE operator. ** |
︙ | ︙ | |||
4493 4494 4495 4496 4497 4498 4499 4500 | ** identical, we return 2 just to be safe. So if this routine ** returns 2, then you do not really know for certain if the two ** expressions are the same. But if you get a 0 or 1 return, then you ** can be sure the expressions are the same. In the places where ** this routine is used, it does not hurt to get an extra 2 - that ** just might result in some slightly slower code. But returning ** an incorrect 0 or 1 could lead to a malfunction. */ | > > > > > > > | > > > | | | | | 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 | ** identical, we return 2 just to be safe. So if this routine ** returns 2, then you do not really know for certain if the two ** expressions are the same. But if you get a 0 or 1 return, then you ** can be sure the expressions are the same. In the places where ** this routine is used, it does not hurt to get an extra 2 - that ** just might result in some slightly slower code. But returning ** an incorrect 0 or 1 could lead to a malfunction. ** ** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in ** pParse->pReprepare can be matched against literals in pB. The ** pParse->pVdbe->expmask bitmask is updated for each variable referenced. ** If pParse is NULL (the normal case) then any TK_VARIABLE term in ** Argument pParse should normally be NULL. If it is not NULL and pA or ** pB causes a return value of 2. */ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ u32 combinedFlags; if( pA==0 || pB==0 ){ return pB==pA ? 0 : 2; } if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){ return 0; } combinedFlags = pA->flags | pB->flags; if( combinedFlags & EP_IntValue ){ if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){ return 0; } return 2; } if( pA->op!=pB->op ){ if( pA->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA->pLeft,pB,iTab)<2 ){ return 1; } if( pB->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA,pB->pLeft,iTab)<2 ){ return 1; } return 2; } if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ if( pA->op==TK_FUNCTION ){ if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ return pA->op==TK_COLLATE ? 1 : 2; } } if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){ if( combinedFlags & EP_xIsSelect ) return 2; if( sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2; if( sqlite3ExprCompare(pParse, pA->pRight, pB->pRight, iTab) ) return 2; if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; if( ALWAYS((combinedFlags & EP_Reduced)==0) && pA->op!=TK_STRING ){ if( pA->iColumn!=pB->iColumn ) return 2; if( pA->iTable!=pB->iTable && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; } } |
︙ | ︙ | |||
4561 4562 4563 4564 4565 4566 4567 | if( pA==0 && pB==0 ) return 0; if( pA==0 || pB==0 ) return 1; if( pA->nExpr!=pB->nExpr ) return 1; for(i=0; i<pA->nExpr; i++){ Expr *pExprA = pA->a[i].pExpr; Expr *pExprB = pB->a[i].pExpr; if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1; | | > > > > > > > > > > > > > > > > | | | | | | | < | | 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 | if( pA==0 && pB==0 ) return 0; if( pA==0 || pB==0 ) return 1; if( pA->nExpr!=pB->nExpr ) return 1; for(i=0; i<pA->nExpr; i++){ Expr *pExprA = pA->a[i].pExpr; Expr *pExprB = pB->a[i].pExpr; if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1; if( sqlite3ExprCompare(0, pExprA, pExprB, iTab) ) return 1; } return 0; } /* ** 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 true if we can prove the pE2 will always be true if pE1 is ** true. Return false if we cannot complete the proof or if pE2 might ** be false. Examples: ** ** pE1: x==5 pE2: x==5 Result: true ** pE1: x>0 pE2: x==5 Result: false ** pE1: x=21 pE2: x=21 OR y=43 Result: true ** pE1: x!=123 pE2: x IS NOT NULL Result: true ** pE1: x!=?1 pE2: x IS NOT NULL Result: true ** pE1: x IS NULL pE2: x IS NOT NULL Result: false ** pE1: x IS ?2 pE2: x IS NOT NULL Reuslt: false ** ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has ** Expr.iTable<0 then assume a table number given by iTab. ** ** If pParse is not NULL, then the values of bound variables in pE1 are ** compared against literal values in pE2 and pParse->pVdbe->expmask is ** modified to record which bound variables are referenced. If pParse ** is NULL, then false will be returned if pE1 contains any bound variables. ** ** When in doubt, return false. Returning true might give a performance ** improvement. Returning false might cause a performance reduction, but ** it will always give the correct answer and is hence always safe. */ int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, int iTab){ if( sqlite3ExprCompare(pParse, pE1, pE2, iTab)==0 ){ return 1; } if( pE2->op==TK_OR && (sqlite3ExprImpliesExpr(pParse, pE1, pE2->pLeft, iTab) || sqlite3ExprImpliesExpr(pParse, pE1, pE2->pRight, iTab) ) ){ return 1; } if( pE2->op==TK_NOTNULL && pE1->op!=TK_ISNULL && pE1->op!=TK_IS ){ Expr *pX = sqlite3ExprSkipCollate(pE1->pLeft); testcase( pX!=pE1->pLeft ); if( sqlite3ExprCompare(pParse, pX, pE2->pLeft, iTab)==0 ) return 1; } return 0; } /* ** An instance of the following structure is used by the tree walker ** to determine if an expression can be evaluated by reference to the |
︙ | ︙ | |||
4708 4709 4710 4711 4712 4713 4714 | ** has no arguments or has only constant arguments. Return false if pExpr ** references columns but not columns of tables found in pSrcList. */ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ Walker w; struct SrcCount cnt; assert( pExpr->op==TK_AGG_FUNCTION ); | < > | 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 | ** has no arguments or has only constant arguments. Return false if pExpr ** references columns but not columns of tables found in pSrcList. */ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ Walker w; struct SrcCount cnt; assert( pExpr->op==TK_AGG_FUNCTION ); w.xExprCallback = exprSrcCount; w.xSelectCallback = 0; w.u.pSrcCount = &cnt; cnt.pSrc = pSrcList; cnt.nThis = 0; cnt.nOther = 0; sqlite3WalkExprList(&w, pExpr->x.pList); return cnt.nThis>0 || cnt.nOther==0; } |
︙ | ︙ | |||
4841 4842 4843 4844 4845 4846 4847 | && 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++){ | | | 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 | && 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++){ if( sqlite3ExprCompare(0, pItem->pExpr, pExpr, -1)==0 ){ break; } } if( i>=pAggInfo->nFunc ){ /* pExpr is original. Make a new entry in pAggInfo->aFunc[] */ u8 enc = ENC(pParse->db); |
︙ | ︙ | |||
4881 4882 4883 4884 4885 4886 4887 | return WRC_Continue; } } } return WRC_Continue; } static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ | < > > > > > < > > | 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 | return WRC_Continue; } } } return WRC_Continue; } static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ UNUSED_PARAMETER(pSelect); pWalker->walkerDepth++; return WRC_Continue; } static void analyzeAggregatesInSelectEnd(Walker *pWalker, Select *pSelect){ UNUSED_PARAMETER(pSelect); pWalker->walkerDepth--; } /* ** Analyze the pExpr expression looking for aggregate functions and ** for variables that need to be added to AggInfo object that pNC->pAggInfo ** points to. Additional entries are made on the AggInfo object as ** necessary. ** ** This routine should only be called after the expression has been ** analyzed by sqlite3ResolveExprNames(). */ void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ Walker w; w.xExprCallback = analyzeAggregate; w.xSelectCallback = analyzeAggregatesInSelect; w.xSelectCallback2 = analyzeAggregatesInSelectEnd; w.walkerDepth = 0; w.u.pNC = pNC; assert( pNC->pSrcList!=0 ); sqlite3WalkExpr(&w, pExpr); } /* ** Call sqlite3ExprAnalyzeAggregates() for every expression in an |
︙ | ︙ | |||
5000 5001 5002 5003 5004 5005 5006 | ** iFirst..iLast, inclusive. This routine is only call from within assert() ** statements. */ #ifdef SQLITE_DEBUG int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ int i; if( pParse->nRangeReg>0 | | | | 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 | ** iFirst..iLast, inclusive. This routine is only call from within assert() ** statements. */ #ifdef SQLITE_DEBUG int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ int i; if( pParse->nRangeReg>0 && pParse->iRangeReg+pParse->nRangeReg > iFirst && pParse->iRangeReg <= iLast ){ return 0; } for(i=0; i<pParse->nTempReg; i++){ if( pParse->aTempReg[i]>=iFirst && pParse->aTempReg[i]<=iLast ){ return 0; } } return 1; } #endif /* SQLITE_DEBUG */ |
Changes to src/fault.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 | ** is completely recoverable simply by not carrying out the resize. The ** hash table will continue to function normally. So a malloc failure ** during a hash table resize is a benign fault. */ #include "sqliteInt.h" | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ** is completely recoverable simply by not carrying out the resize. The ** hash table will continue to function normally. So a malloc failure ** during a hash table resize is a benign fault. */ #include "sqliteInt.h" #ifndef SQLITE_UNTESTABLE /* ** Global variables. */ typedef struct BenignMallocHooks BenignMallocHooks; static SQLITE_WSD struct BenignMallocHooks { void (*xBenignBegin)(void); |
︙ | ︙ | |||
80 81 82 83 84 85 86 | void sqlite3EndBenignMalloc(void){ wsdHooksInit; if( wsdHooks.xBenignEnd ){ wsdHooks.xBenignEnd(); } } | | | 80 81 82 83 84 85 86 87 | void sqlite3EndBenignMalloc(void){ wsdHooksInit; if( wsdHooks.xBenignEnd ){ wsdHooks.xBenignEnd(); } } #endif /* #ifndef SQLITE_UNTESTABLE */ |
Changes to src/fkey.c.
︙ | ︙ | |||
221 222 223 224 225 226 227 | assert( nCol>1 ); aiCol = (int *)sqlite3DbMallocRawNN(pParse->db, nCol*sizeof(int)); if( !aiCol ) return 1; *paiCol = aiCol; } for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){ | | | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | assert( nCol>1 ); aiCol = (int *)sqlite3DbMallocRawNN(pParse->db, nCol*sizeof(int)); if( !aiCol ) return 1; *paiCol = aiCol; } for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->nKeyCol==nCol && IsUniqueIndex(pIdx) && pIdx->pPartIdxWhere==0 ){ /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number ** of columns. If each indexed column corresponds to a foreign key ** column of pFKey, then this index is a winner. */ if( zKey==0 ){ /* If zKey is NULL, then this foreign key is implicitly mapped to ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be |
︙ | ︙ | |||
580 581 582 583 584 585 586 | iCol = pIdx ? pIdx->aiColumn[i] : -1; pLeft = exprTableRegister(pParse, pTab, regData, iCol); iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iCol>=0 ); zCol = pFKey->pFrom->aCol[iCol].zName; pRight = sqlite3Expr(db, TK_ID, zCol); | | | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | iCol = pIdx ? pIdx->aiColumn[i] : -1; pLeft = exprTableRegister(pParse, pTab, regData, iCol); iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iCol>=0 ); zCol = pFKey->pFrom->aCol[iCol].zName; pRight = sqlite3Expr(db, TK_ID, zCol); pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight); pWhere = sqlite3ExprAnd(db, pWhere, pEq); } /* If the child table is the same as the parent table, then add terms ** to the WHERE clause that prevent this entry from being scanned. ** The added WHERE clause terms are like this: ** |
︙ | ︙ | |||
602 603 604 605 606 607 608 | if( pTab==pFKey->pFrom && nIncr>0 ){ Expr *pNe; /* Expression (pLeft != pRight) */ Expr *pLeft; /* Value from parent table row */ Expr *pRight; /* Column ref to child table */ if( HasRowid(pTab) ){ pLeft = exprTableRegister(pParse, pTab, regData, -1); pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1); | | | | > | | | | > | 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 | if( pTab==pFKey->pFrom && nIncr>0 ){ Expr *pNe; /* Expression (pLeft != pRight) */ Expr *pLeft; /* Value from parent table row */ Expr *pRight; /* Column ref to child table */ if( HasRowid(pTab) ){ pLeft = exprTableRegister(pParse, pTab, regData, -1); pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1); pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight); }else{ Expr *pEq, *pAll = 0; Index *pPk = sqlite3PrimaryKeyIndex(pTab); assert( pIdx!=0 ); for(i=0; i<pPk->nKeyCol; i++){ i16 iCol = pIdx->aiColumn[i]; assert( iCol>=0 ); pLeft = exprTableRegister(pParse, pTab, regData, iCol); pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol); pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight); pAll = sqlite3ExprAnd(db, pAll, pEq); } pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0); } pWhere = sqlite3ExprAnd(db, pWhere, pNe); } /* Resolve the references in the WHERE clause. */ memset(&sNameContext, 0, sizeof(NameContext)); sNameContext.pSrcList = pSrc; sNameContext.pParse = pParse; sqlite3ResolveExprNames(&sNameContext, pWhere); /* Create VDBE to loop through the entries in pSrc that match the WHERE ** clause. For each row found, increment either the deferred or immediate ** foreign key constraint counter. */ if( pParse->nErr==0 ){ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); if( pWInfo ){ sqlite3WhereEnd(pWInfo); } } /* Clean up the WHERE clause constructed above. */ sqlite3ExprDelete(db, pWhere); if( iFkIfZero ){ sqlite3VdbeJumpHere(v, iFkIfZero); } |
︙ | ︙ | |||
1003 1004 1005 1006 1007 1008 1009 | /* Create a SrcList structure containing the child table. We need the ** child table as a SrcList for sqlite3WhereBegin() */ pSrc = sqlite3SrcListAppend(db, 0, 0, 0); if( pSrc ){ struct SrcList_item *pItem = pSrc->a; pItem->pTab = pFKey->pFrom; pItem->zName = pFKey->pFrom->zName; | | | 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 | /* Create a SrcList structure containing the child table. We need the ** child table as a SrcList for sqlite3WhereBegin() */ pSrc = sqlite3SrcListAppend(db, 0, 0, 0); if( pSrc ){ struct SrcList_item *pItem = pSrc->a; pItem->pTab = pFKey->pFrom; pItem->zName = pFKey->pFrom->zName; pItem->pTab->nTabRef++; pItem->iCursor = pParse->nTab++; if( regNew!=0 ){ fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); } if( regOld!=0 ){ int eAction = pFKey->aAction[aChange!=0]; |
︙ | ︙ | |||
1083 1084 1085 1086 1087 1088 1089 | ** to an array of size N, where N is the number of columns in table pTab. ** If the i'th column is not modified by the UPDATE, then the corresponding ** entry in the aChange[] array is set to -1. If the column is modified, ** the value is 0 or greater. Parameter chngRowid is set to true if the ** UPDATE statement modifies the rowid fields of the table. ** ** If any foreign key processing will be required, this function returns | | | > > > > > > > > > | > | > > | > > > | | 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 | ** to an array of size N, where N is the number of columns in table pTab. ** If the i'th column is not modified by the UPDATE, then the corresponding ** entry in the aChange[] array is set to -1. If the column is modified, ** the value is 0 or greater. Parameter chngRowid is set to true if the ** UPDATE statement modifies the rowid fields of the table. ** ** If any foreign key processing will be required, this function returns ** non-zero. If there is no foreign key related processing, this function ** returns zero. ** ** For an UPDATE, this function returns 2 if: ** ** * There are any FKs for which pTab is the child and the parent table, or ** * the UPDATE modifies one or more parent keys for which the action is ** not "NO ACTION" (i.e. is CASCADE, SET DEFAULT or SET NULL). ** ** Or, assuming some other foreign key processing is required, 1. */ int sqlite3FkRequired( Parse *pParse, /* Parse context */ Table *pTab, /* Table being modified */ int *aChange, /* Non-NULL for UPDATE operations */ int chngRowid /* True for UPDATE that affects rowid */ ){ int eRet = 0; if( pParse->db->flags&SQLITE_ForeignKeys ){ if( !aChange ){ /* A DELETE operation. Foreign key processing is required if the ** table in question is either the child or parent table for any ** foreign key constraint. */ eRet = (sqlite3FkReferences(pTab) || pTab->pFKey); }else{ /* This is an UPDATE. Foreign key processing is only required if the ** operation modifies one or more child or parent key columns. */ FKey *p; /* Check if any child key columns are being modified. */ for(p=pTab->pFKey; p; p=p->pNextFrom){ if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) return 2; if( fkChildIsModified(pTab, p, aChange, chngRowid) ){ eRet = 1; } } /* Check if any parent key columns are being modified. */ for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ if( fkParentIsModified(pTab, p, aChange, chngRowid) ){ if( p->aAction[1]!=OE_None ) return 2; eRet = 1; } } } } return eRet; } /* ** This function is called when an UPDATE or DELETE operation is being ** compiled on table pTab, which is the parent table of foreign-key pFKey. ** If the current operation is an UPDATE, then the pChanges parameter is ** passed a pointer to the list of columns being modified. If it is a |
︙ | ︙ | |||
1201 1202 1203 1204 1205 1206 1207 | /* Create the expression "OLD.zToCol = zFromCol". It is important ** that the "OLD.zToCol" term is on the LHS of the = operator, so ** that the affinity and collation sequence associated with the ** parent table are used for the comparison. */ pEq = sqlite3PExpr(pParse, TK_EQ, sqlite3PExpr(pParse, TK_DOT, sqlite3ExprAlloc(db, TK_ID, &tOld, 0), | | < | | < | < | | < | 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 | /* Create the expression "OLD.zToCol = zFromCol". It is important ** that the "OLD.zToCol" term is on the LHS of the = operator, so ** that the affinity and collation sequence associated with the ** parent table are used for the comparison. */ pEq = sqlite3PExpr(pParse, TK_EQ, sqlite3PExpr(pParse, TK_DOT, sqlite3ExprAlloc(db, TK_ID, &tOld, 0), sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)), sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0) ); pWhere = sqlite3ExprAnd(db, pWhere, pEq); /* For ON UPDATE, construct the next term of the WHEN clause. ** The final WHEN clause will be like this: ** ** WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN) */ if( pChanges ){ pEq = sqlite3PExpr(pParse, TK_IS, sqlite3PExpr(pParse, TK_DOT, sqlite3ExprAlloc(db, TK_ID, &tOld, 0), sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)), sqlite3PExpr(pParse, TK_DOT, sqlite3ExprAlloc(db, TK_ID, &tNew, 0), sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)) ); pWhen = sqlite3ExprAnd(db, pWhen, pEq); } if( action!=OE_Restrict && (action!=OE_Cascade || pChanges) ){ Expr *pNew; if( action==OE_Cascade ){ pNew = sqlite3PExpr(pParse, TK_DOT, sqlite3ExprAlloc(db, TK_ID, &tNew, 0), sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)); }else if( action==OE_SetDflt ){ Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt; if( pDflt ){ pNew = sqlite3ExprDup(db, pDflt, 0); }else{ pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0); } |
︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 | pStep->zTarget = (char *)&pStep[1]; memcpy((char *)pStep->zTarget, zFrom, nFrom); pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); if( pWhen ){ | | | 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 | pStep->zTarget = (char *)&pStep[1]; memcpy((char *)pStep->zTarget, zFrom, nFrom); pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); if( pWhen ){ pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0); pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); } } /* Re-enable the lookaside buffer, if it was disabled earlier. */ db->lookaside.bDisable--; |
︙ | ︙ |
Changes to src/func.c.
︙ | ︙ | |||
72 73 74 75 76 77 78 | ** Return the type of the argument. */ static void typeofFunc( sqlite3_context *context, int NotUsed, sqlite3_value **argv ){ | | > < > | > | | > > > > | < < | | 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 | ** Return the type of the argument. */ static void typeofFunc( sqlite3_context *context, int NotUsed, sqlite3_value **argv ){ static const char *azType[] = { "integer", "real", "text", "blob", "null" }; int i = sqlite3_value_type(argv[0]) - 1; UNUSED_PARAMETER(NotUsed); assert( i>=0 && i<ArraySize(azType) ); assert( SQLITE_INTEGER==1 ); assert( SQLITE_FLOAT==2 ); assert( SQLITE_TEXT==3 ); assert( SQLITE_BLOB==4 ); assert( SQLITE_NULL==5 ); /* EVIDENCE-OF: R-01470-60482 The sqlite3_value_type(V) interface returns ** the datatype code for the initial datatype of the sqlite3_value object ** V. The returned value is one of SQLITE_INTEGER, SQLITE_FLOAT, ** SQLITE_TEXT, SQLITE_BLOB, or SQLITE_NULL. */ sqlite3_result_text(context, azType[i], -1, SQLITE_STATIC); } /* ** Implementation of the length() function */ static void lengthFunc( |
︙ | ︙ | |||
196 197 198 199 200 201 202 | UNUSED_PARAMETER(argc); typeHaystack = sqlite3_value_type(argv[0]); typeNeedle = sqlite3_value_type(argv[1]); if( typeHaystack==SQLITE_NULL || typeNeedle==SQLITE_NULL ) return; nHaystack = sqlite3_value_bytes(argv[0]); nNeedle = sqlite3_value_bytes(argv[1]); | > | | | | | | | | | > | | | | | | | | > | 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 | UNUSED_PARAMETER(argc); typeHaystack = sqlite3_value_type(argv[0]); typeNeedle = sqlite3_value_type(argv[1]); if( typeHaystack==SQLITE_NULL || typeNeedle==SQLITE_NULL ) return; nHaystack = sqlite3_value_bytes(argv[0]); nNeedle = sqlite3_value_bytes(argv[1]); if( nNeedle>0 ){ if( typeHaystack==SQLITE_BLOB && typeNeedle==SQLITE_BLOB ){ zHaystack = sqlite3_value_blob(argv[0]); zNeedle = sqlite3_value_blob(argv[1]); isText = 0; }else{ zHaystack = sqlite3_value_text(argv[0]); zNeedle = sqlite3_value_text(argv[1]); isText = 1; } if( zNeedle==0 || (nHaystack && zHaystack==0) ) return; while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){ N++; do{ nHaystack--; zHaystack++; }while( isText && (zHaystack[0]&0xc0)==0x80 ); } if( nNeedle>nHaystack ) N = 0; } sqlite3_result_int(context, N); } /* ** Implementation of the printf() function. */ static void printfFunc( |
︙ | ︙ | |||
592 593 594 595 596 597 598 | ** case. Thus 'a' LIKE 'A' would be true. */ static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 }; /* If SQLITE_CASE_SENSITIVE_LIKE is defined, then the LIKE operator ** is case sensitive causing 'a' LIKE 'A' to be false */ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 }; /* | > > > > > > > | | < > > > > | 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 | ** case. Thus 'a' LIKE 'A' would be true. */ static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 }; /* If SQLITE_CASE_SENSITIVE_LIKE is defined, then the LIKE operator ** is case sensitive causing 'a' LIKE 'A' to be false */ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 }; /* ** Possible error returns from patternMatch() */ #define SQLITE_MATCH 0 #define SQLITE_NOMATCH 1 #define SQLITE_NOWILDCARDMATCH 2 /* ** Compare two UTF-8 strings for equality where the first string is ** a GLOB or LIKE expression. Return values: ** ** SQLITE_MATCH: Match ** SQLITE_NOMATCH: No match ** SQLITE_NOWILDCARDMATCH: No match in spite of having * or % wildcards. ** ** Globbing rules: ** ** '*' Matches any sequence of zero or more characters. ** ** '?' Matches exactly one character. ** |
︙ | ︙ | |||
645 646 647 648 649 650 651 | while( (c = Utf8Read(zPattern))!=0 ){ if( c==matchAll ){ /* Match "*" */ /* Skip over multiple "*" characters in the pattern. If there ** are also "?" characters, skip those as well, but consume a ** single character of the input string for each "?" skipped */ while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){ if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){ | | | | | | > | | > | > > | > | | | | 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 | while( (c = Utf8Read(zPattern))!=0 ){ if( c==matchAll ){ /* Match "*" */ /* Skip over multiple "*" characters in the pattern. If there ** are also "?" characters, skip those as well, but consume a ** single character of the input string for each "?" skipped */ while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){ if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){ return SQLITE_NOWILDCARDMATCH; } } if( c==0 ){ return SQLITE_MATCH; /* "*" at the end of the pattern matches */ }else if( c==matchOther ){ if( pInfo->matchSet==0 ){ c = sqlite3Utf8Read(&zPattern); if( c==0 ) return SQLITE_NOWILDCARDMATCH; }else{ /* "[...]" immediately follows the "*". We have to do a slow ** recursive search in this case, but it is an unusual case. */ assert( matchOther<0x80 ); /* '[' is a single-byte character */ while( *zString ){ int bMatch = patternCompare(&zPattern[-1],zString,pInfo,matchOther); if( bMatch!=SQLITE_NOMATCH ) return bMatch; SQLITE_SKIP_UTF8(zString); } return SQLITE_NOWILDCARDMATCH; } } /* At this point variable c contains the first character of the ** pattern string past the "*". Search in the input string for the ** first matching character and recursively continue the match from ** that point. ** ** For a case-insensitive search, set variable cx to be the same as ** c but in the other case and search the input string for either ** c or cx. */ if( c<=0x80 ){ u32 cx; int bMatch; if( noCase ){ cx = sqlite3Toupper(c); c = sqlite3Tolower(c); }else{ cx = c; } while( (c2 = *(zString++))!=0 ){ if( c2!=c && c2!=cx ) continue; bMatch = patternCompare(zPattern,zString,pInfo,matchOther); if( bMatch!=SQLITE_NOMATCH ) return bMatch; } }else{ int bMatch; while( (c2 = Utf8Read(zString))!=0 ){ if( c2!=c ) continue; bMatch = patternCompare(zPattern,zString,pInfo,matchOther); if( bMatch!=SQLITE_NOMATCH ) return bMatch; } } return SQLITE_NOWILDCARDMATCH; } if( c==matchOther ){ if( pInfo->matchSet==0 ){ c = sqlite3Utf8Read(&zPattern); if( c==0 ) return SQLITE_NOMATCH; zEscaped = zPattern; }else{ u32 prior_c = 0; int seen = 0; int invert = 0; c = sqlite3Utf8Read(&zString); if( c==0 ) return SQLITE_NOMATCH; c2 = sqlite3Utf8Read(&zPattern); if( c2=='^' ){ invert = 1; c2 = sqlite3Utf8Read(&zPattern); } if( c2==']' ){ if( c==']' ) seen = 1; |
︙ | ︙ | |||
729 730 731 732 733 734 735 | seen = 1; } prior_c = c2; } c2 = sqlite3Utf8Read(&zPattern); } if( c2==0 || (seen ^ invert)==0 ){ | | | | | > | | > | | 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 | seen = 1; } prior_c = c2; } c2 = sqlite3Utf8Read(&zPattern); } if( c2==0 || (seen ^ invert)==0 ){ return SQLITE_NOMATCH; } continue; } } c2 = Utf8Read(zString); if( c==c2 ) continue; if( noCase && sqlite3Tolower(c)==sqlite3Tolower(c2) && c<0x80 && c2<0x80 ){ continue; } if( c==matchOne && zPattern!=zEscaped && c2!=0 ) continue; return SQLITE_NOMATCH; } return *zString==0 ? SQLITE_MATCH : SQLITE_NOMATCH; } /* ** The sqlite3_strglob() interface. Return 0 on a match (like strcmp()) and ** non-zero if there is no match. */ int sqlite3_strglob(const char *zGlobPattern, const char *zString){ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '['); } /* ** The sqlite3_strlike() interface. Return 0 on a match and non-zero for ** a miss - like strcmp(). */ int sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){ return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc); } /* ** Count the number of times that the LIKE operator (or GLOB which is ** just a variation of LIKE) gets called. This is used for testing ** only. */ |
︙ | ︙ | |||
837 838 839 840 841 842 843 | }else{ escape = pInfo->matchSet; } if( zA && zB ){ #ifdef SQLITE_TEST sqlite3_like_count++; #endif | | > | 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 | }else{ escape = pInfo->matchSet; } if( zA && zB ){ #ifdef SQLITE_TEST sqlite3_like_count++; #endif sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)==SQLITE_MATCH); } } /* ** Implementation of the NULLIF(x,y) function. The result is the first ** argument if the arguments are different. The result is NULL if the ** arguments are equal to each other. |
︙ | ︙ | |||
1608 1609 1610 1611 1612 1613 1614 | if( argc==2 ){ zSep = (char*)sqlite3_value_text(argv[1]); nSep = sqlite3_value_bytes(argv[1]); }else{ zSep = ","; nSep = 1; } | | | 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 | if( argc==2 ){ zSep = (char*)sqlite3_value_text(argv[1]); nSep = sqlite3_value_bytes(argv[1]); }else{ zSep = ","; nSep = 1; } if( zSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep); } zVal = (char*)sqlite3_value_text(argv[0]); nVal = sqlite3_value_bytes(argv[0]); if( zVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal); } } static void groupConcatFinalize(sqlite3_context *context){ |
︙ | ︙ | |||
1678 1679 1680 1681 1682 1683 1684 | setLikeOptFlag(db, "like", caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE); } /* ** pExpr points to an expression which implements a function. If ** it is appropriate to apply the LIKE optimization to that function | | | | > > > > > > | < < < > | > > > > > > > > > > | 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 | setLikeOptFlag(db, "like", caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE); } /* ** pExpr points to an expression which implements a function. If ** it is appropriate to apply the LIKE optimization to that function ** then set aWc[0] through aWc[2] to the wildcard characters and the ** escape character and then return TRUE. If the function is not a ** LIKE-style function then return FALSE. ** ** The expression "a LIKE b ESCAPE c" is only considered a valid LIKE ** operator if c is a string literal that is exactly one byte in length. ** That one byte is stored in aWc[3]. aWc[3] is set to zero if there is ** no ESCAPE clause. ** ** *pIsNocase is set to true if uppercase and lowercase are equivalent for ** the function (default for LIKE). If the function makes the distinction ** between uppercase and lowercase (as does GLOB) then *pIsNocase is set to ** false. */ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ FuncDef *pDef; int nExpr; if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){ return 0; } assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); nExpr = pExpr->x.pList->nExpr; pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0); if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){ return 0; } if( nExpr<3 ){ aWc[3] = 0; }else{ Expr *pEscape = pExpr->x.pList->a[2].pExpr; char *zEscape; if( pEscape->op!=TK_STRING ) return 0; zEscape = pEscape->u.zToken; if( zEscape[0]==0 || zEscape[1]!=0 ) return 0; aWc[3] = zEscape[0]; } /* The memcpy() statement assumes that the wildcard characters are ** the first three statements in the compareInfo structure. The ** asserts() that follow verify that assumption */ memcpy(aWc, pDef->pUserData, 3); assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll ); |
︙ | ︙ | |||
1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 | #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), FUNCTION(ltrim, 1, 1, 0, trimFunc ), FUNCTION(ltrim, 2, 1, 0, trimFunc ), FUNCTION(rtrim, 1, 2, 0, trimFunc ), FUNCTION(rtrim, 2, 2, 0, trimFunc ), FUNCTION(trim, 1, 3, 0, trimFunc ), FUNCTION(trim, 2, 3, 0, trimFunc ), FUNCTION(min, -1, 0, 1, minmaxFunc ), | > > > | 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 | #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), #ifdef SQLITE_DEBUG FUNCTION2(affinity, 1, 0, 0, noopFunc, SQLITE_FUNC_AFFINITY), #endif FUNCTION(ltrim, 1, 1, 0, trimFunc ), FUNCTION(ltrim, 2, 1, 0, trimFunc ), FUNCTION(rtrim, 1, 2, 0, trimFunc ), FUNCTION(rtrim, 2, 2, 0, trimFunc ), FUNCTION(trim, 1, 3, 0, trimFunc ), FUNCTION(trim, 2, 3, 0, trimFunc ), FUNCTION(min, -1, 0, 1, minmaxFunc ), |
︙ | ︙ |
Changes to src/global.c.
︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 | ** ** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled ** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. ** ** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally ** disabled. The default value may be changed by compiling with the ** SQLITE_USE_URI symbol defined. */ #ifndef SQLITE_USE_URI | > > > > > > | > | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | ** ** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled ** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. ** ** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally ** disabled. The default value may be changed by compiling with the ** SQLITE_USE_URI symbol defined. ** ** URI filenames are enabled by default if SQLITE_HAS_CODEC is ** enabled. */ #ifndef SQLITE_USE_URI # ifdef SQLITE_HAS_CODEC # define SQLITE_USE_URI 1 # else # define SQLITE_USE_URI 0 # endif #endif /* EVIDENCE-OF: R-38720-18127 The default setting is determined by the ** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if ** that compile-time option is omitted. */ #ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN |
︙ | ︙ | |||
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | ** memory. (The statement journal is also always held entirely in memory ** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this ** setting.) */ #ifndef SQLITE_STMTJRNL_SPILL # define SQLITE_STMTJRNL_SPILL (64*1024) #endif /* ** The following singleton contains the global configuration for ** the SQLite library. */ SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ SQLITE_USE_URI, /* bOpenUri */ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ | > > > > > > > > > > > > > > | < < < < | 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 | ** memory. (The statement journal is also always held entirely in memory ** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this ** setting.) */ #ifndef SQLITE_STMTJRNL_SPILL # define SQLITE_STMTJRNL_SPILL (64*1024) #endif /* ** The default lookaside-configuration, the format "SZ,N". SZ is the ** number of bytes in each lookaside slot (should be a multiple of 8) ** and N is the number of slots. The lookaside-configuration can be ** changed as start-time using sqlite3_config(SQLITE_CONFIG_LOOKASIDE) ** or at run-time for an individual database connection using ** sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE); */ #ifndef SQLITE_DEFAULT_LOOKASIDE # define SQLITE_DEFAULT_LOOKASIDE 1200,100 #endif /* ** The following singleton contains the global configuration for ** the SQLite library. */ SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ SQLITE_USE_URI, /* bOpenUri */ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 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 */ (void*)0, /* pHeap */ 0, /* nHeap */ 0, 0, /* mnHeap, mxHeap */ SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */ SQLITE_MAX_MMAP_SIZE, /* mxMmap */ (void*)0, /* pPage */ 0, /* szPage */ SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ 0, /* mxParserStack */ 0, /* sharedCacheEnabled */ SQLITE_SORTER_PMASZ, /* szPma */ /* All the rest should always be initialized to zero */ |
︙ | ︙ | |||
215 216 217 218 219 220 221 | 0, /* xSqllog */ 0, /* pSqllogArg */ #endif #ifdef SQLITE_VDBE_COVERAGE 0, /* xVdbeBranch */ 0, /* pVbeBranchArg */ #endif | | | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | 0, /* xSqllog */ 0, /* pSqllogArg */ #endif #ifdef SQLITE_VDBE_COVERAGE 0, /* xVdbeBranch */ 0, /* pVbeBranchArg */ #endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ #endif 0, /* bLocaltimeFault */ 0x7ffffffe /* iOnceResetThreshold */ }; /* |
︙ | ︙ |
Changes to src/hash.c.
︙ | ︙ | |||
136 137 138 139 140 141 142 | next_elem = elem->next; insertElement(pH, &new_ht[h], elem); } return 1; } /* This function (for internal use only) locates an element in an | | | > > | | | 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 | next_elem = elem->next; insertElement(pH, &new_ht[h], elem); } return 1; } /* This function (for internal use only) locates an element in an ** hash table that matches the given key. If no element is found, ** a pointer to a static null element with HashElem.data==0 is returned. ** If pH is not NULL, then the hash for this key is written to *pH. */ static HashElem *findElementWithHash( const Hash *pH, /* The pH to be searched */ const char *pKey, /* The key we are searching for */ unsigned int *pHash /* Write the hash value here */ ){ HashElem *elem; /* Used to loop thru the element list */ int count; /* Number of elements left to test */ unsigned int h; /* The computed hash */ static HashElem nullElement = { 0, 0, 0, 0 }; if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/ struct _ht *pEntry; h = strHash(pKey) % pH->htsize; pEntry = &pH->ht[h]; elem = pEntry->chain; count = pEntry->count; }else{ h = 0; elem = pH->first; count = pH->count; } if( pHash ) *pHash = h; while( count-- ){ assert( elem!=0 ); if( sqlite3StrICmp(elem->pKey,pKey)==0 ){ return elem; } elem = elem->next; } return &nullElement; } /* Remove a single entry from the hash table given a pointer to that ** element and a hash on the element's key. */ static void removeElementGivenHash( Hash *pH, /* The pH containing "elem" */ |
︙ | ︙ | |||
209 210 211 212 213 214 215 | } /* Attempt to locate an element of the hash table pH with a key ** that matches pKey. Return the data for this element if it is ** found, or NULL if there is no match. */ void *sqlite3HashFind(const Hash *pH, const char *pKey){ | < < < | < | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | } /* Attempt to locate an element of the hash table pH with a key ** that matches pKey. Return the data for this element if it is ** found, or NULL if there is no match. */ void *sqlite3HashFind(const Hash *pH, const char *pKey){ assert( pH!=0 ); assert( pKey!=0 ); return findElementWithHash(pH, pKey, 0)->data; } /* Insert an element into the hash table pH. The key is pKey ** and the data is "data". ** ** If no element exists with a matching key, then a new ** element is created and NULL is returned. |
︙ | ︙ | |||
240 241 242 243 244 245 246 | unsigned int h; /* the hash of the key modulo hash table size */ HashElem *elem; /* Used to loop thru the element list */ HashElem *new_elem; /* New element added to the pH */ assert( pH!=0 ); assert( pKey!=0 ); elem = findElementWithHash(pH,pKey,&h); | | | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | unsigned int h; /* the hash of the key modulo hash table size */ HashElem *elem; /* Used to loop thru the element list */ HashElem *new_elem; /* New element added to the pH */ assert( pH!=0 ); assert( pKey!=0 ); elem = findElementWithHash(pH,pKey,&h); if( elem->data ){ void *old_data = elem->data; if( data==0 ){ removeElementGivenHash(pH,elem,h); }else{ elem->data = data; elem->pKey = pKey; } |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
222 223 224 225 226 227 228 | static int autoIncBegin( Parse *pParse, /* Parsing context */ int iDb, /* Index of the database holding pTab */ Table *pTab /* The table we are writing to */ ){ int memId = 0; /* Register holding maximum rowid */ if( (pTab->tabFlags & TF_Autoincrement)!=0 | | | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | static int autoIncBegin( Parse *pParse, /* Parsing context */ int iDb, /* Index of the database holding pTab */ Table *pTab /* The table we are writing to */ ){ int memId = 0; /* Register holding maximum rowid */ if( (pTab->tabFlags & TF_Autoincrement)!=0 && (pParse->db->mDbFlags & DBFLAG_Vacuum)==0 ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); AutoincInfo *pInfo; pInfo = pToplevel->pAinc; while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } if( pInfo==0 ){ |
︙ | ︙ | |||
481 482 483 484 485 486 487 | Select *pSelect, /* A SELECT statement to use as the data source */ IdList *pColumn, /* Column names corresponding to IDLIST. */ int onError /* How to handle constraint errors */ ){ sqlite3 *db; /* The main database structure */ Table *pTab; /* The table to insert into. aka TABLE */ char *zTab; /* Name of the table into which we are inserting */ | | | 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 | Select *pSelect, /* A SELECT statement to use as the data source */ IdList *pColumn, /* Column names corresponding to IDLIST. */ int onError /* How to handle constraint errors */ ){ sqlite3 *db; /* The main database structure */ Table *pTab; /* The table to insert into. aka TABLE */ char *zTab; /* Name of the table into which we are inserting */ int i, j; /* Loop counters */ Vdbe *v; /* Generate code into this virtual machine */ Index *pIdx; /* For looping over indices of the table */ int nColumn; /* Number of columns in the data */ int nHidden = 0; /* Number of hidden columns if TABLE is virtual */ int iDataCur = 0; /* VDBE cursor that is the main data repository */ int iIdxCur = 0; /* First index cursor */ int ipkColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ |
︙ | ︙ | |||
517 518 519 520 521 522 523 | #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ Trigger *pTrigger; /* List of triggers on pTab, if required */ int tmask; /* Mask of trigger times */ #endif db = pParse->db; | < > | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ Trigger *pTrigger; /* List of triggers on pTab, if required */ int tmask; /* Mask of trigger times */ #endif db = pParse->db; if( pParse->nErr || db->mallocFailed ){ goto insert_cleanup; } dest.iSDParm = 0; /* Suppress a harmless compiler warning */ /* If the Select object is really just a simple VALUES() list with a ** single row (the common case) then keep that one row of values ** and discard the other (unused) parts of the pSelect object */ if( pSelect && (pSelect->selFlags & SF_Values)!=0 && pSelect->pPrior==0 ){ pList = pSelect->pEList; |
︙ | ︙ | |||
788 789 790 791 792 793 794 | int nIdx; nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0, &iDataCur, &iIdxCur); aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+1)); if( aRegIdx==0 ){ goto insert_cleanup; } | | > > | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 | int nIdx; nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0, &iDataCur, &iIdxCur); aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+1)); if( aRegIdx==0 ){ goto insert_cleanup; } for(i=0, pIdx=pTab->pIndex; i<nIdx; pIdx=pIdx->pNext, i++){ assert( pIdx ); aRegIdx[i] = ++pParse->nMem; pParse->nMem += pIdx->nColumn; } } /* This is the top of the main insertion loop */ if( useTempTable ){ /* This block codes the top of loop only. The complete loop is the ** following pseudocode (template 4): |
︙ | ︙ | |||
991 992 993 994 995 996 997 998 999 1000 1001 1002 | sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite3MayAbort(pParse); }else #endif { int isReplace; /* Set to true if constraints may cause a replace */ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0 ); sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, | > > > > > > > > > > > > > | > | 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 | sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite3MayAbort(pParse); }else #endif { int isReplace; /* Set to true if constraints may cause a replace */ int bUseSeek; /* True to use OPFLAG_SEEKRESULT */ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0 ); sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); /* Set the OPFLAG_USESEEKRESULT flag if either (a) there are no REPLACE ** constraints or (b) there are no triggers and this table is not a ** parent table in a foreign key constraint. It is safe to set the ** flag in the second case as if any REPLACE constraint is hit, an ** OP_Delete or OP_IdxDelete instruction will be executed on each ** cursor that is disturbed. And these instructions both clear the ** VdbeCursor.seekResult variable, disabling the OPFLAG_USESEEKRESULT ** functionality. */ bUseSeek = (isReplace==0 || (pTrigger==0 && ((db->flags & SQLITE_ForeignKeys)==0 || sqlite3FkReferences(pTab)==0) )); sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, regIns, aRegIdx, 0, appendFlag, bUseSeek ); } } /* Update the count of rows that are inserted */ if( (db->flags & SQLITE_CountRows)!=0 ){ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); |
︙ | ︙ | |||
1025 1026 1027 1028 1029 1030 1031 | sqlite3VdbeJumpHere(v, addrInsTop); sqlite3VdbeAddOp1(v, OP_Close, srcTab); }else if( pSelect ){ sqlite3VdbeGoto(v, addrCont); sqlite3VdbeJumpHere(v, addrInsTop); } | < < < < < < < < | 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 | sqlite3VdbeJumpHere(v, addrInsTop); sqlite3VdbeAddOp1(v, OP_Close, srcTab); }else if( pSelect ){ sqlite3VdbeGoto(v, addrCont); sqlite3VdbeJumpHere(v, addrInsTop); } insert_end: /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite3AutoincrementEnd(pParse); |
︙ | ︙ | |||
1239 1240 1241 1242 1243 1244 1245 | int addr1; /* Address of jump instruction */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */ int ipkTop = 0; /* Top of the rowid change constraint check */ int ipkBottom = 0; /* Bottom of the rowid change constraint check */ u8 isUpdate; /* True if this is an UPDATE operation */ u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */ | < | 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 | int addr1; /* Address of jump instruction */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */ int ipkTop = 0; /* Top of the rowid change constraint check */ int ipkBottom = 0; /* Bottom of the rowid change constraint check */ u8 isUpdate; /* True if this is an UPDATE operation */ u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */ isUpdate = regOldData!=0; db = pParse->db; v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ nCol = pTab->nCol; |
︙ | ︙ | |||
1294 1295 1296 1297 1298 1299 1300 | case OE_Abort: sqlite3MayAbort(pParse); /* Fall through */ case OE_Rollback: case OE_Fail: { char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, pTab->aCol[i].zName); | | | > | 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 | case OE_Abort: sqlite3MayAbort(pParse); /* Fall through */ case OE_Rollback: case OE_Fail: { char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, pTab->aCol[i].zName); sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError, regNewData+1+i); sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); sqlite3VdbeChangeP5(v, P5_ConstraintNotNull); VdbeCoverage(v); break; } case OE_Ignore: { sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest); VdbeCoverage(v); |
︙ | ︙ | |||
1321 1322 1323 1324 1325 1326 1327 | } /* Test all CHECK constraints */ #ifndef SQLITE_OMIT_CHECK if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ ExprList *pCheck = pTab->pCheck; | | > | | 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 | } /* Test all CHECK constraints */ #ifndef SQLITE_OMIT_CHECK if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ ExprList *pCheck = pTab->pCheck; pParse->iSelfTab = -(regNewData+1); onError = overrideError!=OE_Default ? overrideError : OE_Abort; for(i=0; i<pCheck->nExpr; i++){ int allOk; Expr *pExpr = pCheck->a[i].pExpr; if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue; allOk = sqlite3VdbeMakeLabel(v); sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL); if( onError==OE_Ignore ){ sqlite3VdbeGoto(v, ignoreDest); }else{ char *zName = pCheck->a[i].zName; if( zName==0 ) zName = pTab->zName; if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK, onError, zName, P4_TRANSIENT, P5_ConstraintCheck); } sqlite3VdbeResolveLabel(v, allOk); } pParse->iSelfTab = 0; } #endif /* !defined(SQLITE_OMIT_CHECK) */ /* If rowid is changing, make sure the new rowid does not previously ** exist in the table. */ if( pkChng && pPk==0 ){ int addrRowidOk = sqlite3VdbeMakeLabel(v); /* Figure out what action to take in case of a rowid collision */ onError = pTab->keyConf; if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } if( isUpdate ){ /* pkChng!=0 does not mean that the rowid has changed, only that ** it might have changed. Skip the conflict logic below if the rowid ** is unchanged. */ sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData); sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v); } |
︙ | ︙ | |||
1437 1438 1439 1440 1441 1442 1443 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( HasRowid(pTab) ){ /* This OP_Delete opcode fires the pre-update-hook only. It does ** not modify the b-tree. It is more efficient to let the coming ** OP_Insert replace the existing entry than it is to delete the ** existing entry and then insert a new one. */ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP); | | | 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( HasRowid(pTab) ){ /* This OP_Delete opcode fires the pre-update-hook only. It does ** not modify the b-tree. It is more efficient to let the coming ** OP_Insert replace the existing entry than it is to delete the ** existing entry and then insert a new one. */ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP); sqlite3VdbeAppendP4(v, pTab, P4_TABLE); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ if( pTab->pIndex ){ sqlite3MultiWrite(pParse); sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,-1); } } |
︙ | ︙ | |||
1485 1486 1487 1488 1489 1490 1491 | } iThisCur = iIdxCur+ix; addrUniqueOk = sqlite3VdbeMakeLabel(v); /* Skip partial indices for which the WHERE clause is not true */ if( pIdx->pPartIdxWhere ){ sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]); | | | | | | < < > | > < | > > > > > > > > > > > > > > > > > > > | 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 | } iThisCur = iIdxCur+ix; addrUniqueOk = sqlite3VdbeMakeLabel(v); /* Skip partial indices for which the WHERE clause is not true */ if( pIdx->pPartIdxWhere ){ sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]); pParse->iSelfTab = -(regNewData+1); sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, addrUniqueOk, SQLITE_JUMPIFNULL); pParse->iSelfTab = 0; } /* Create a record for this index entry as it should appear after ** the insert or update. Store that record in the aRegIdx[ix] register */ regIdx = aRegIdx[ix]+1; for(i=0; i<pIdx->nColumn; i++){ int iField = pIdx->aiColumn[i]; int x; if( iField==XN_EXPR ){ pParse->iSelfTab = -(regNewData+1); sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i); pParse->iSelfTab = 0; VdbeComment((v, "%s column %d", pIdx->zName, i)); }else{ if( iField==XN_ROWID || iField==pTab->iPKey ){ x = regNewData; }else{ x = iField + regNewData + 1; } sqlite3VdbeAddOp2(v, iField<0 ? OP_IntCopy : OP_SCopy, x, regIdx+i); VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName)); } } sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); VdbeComment((v, "for %s", pIdx->zName)); #ifdef SQLITE_ENABLE_NULL_TRIM if( pIdx->idxType==2 ) sqlite3SetMakeRecordP5(v, pIdx->pTable); #endif /* In an UPDATE operation, if this index is the PRIMARY KEY index ** of a WITHOUT ROWID table and there has been no change the ** primary key, then no collision is possible. The collision detection ** logic below can all be skipped. */ if( isUpdate && pPk==pIdx && pkChng==0 ){ sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; } /* Find out what action to take in case there is a uniqueness conflict */ onError = pIdx->onError; if( onError==OE_None ){ sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; /* pIdx is not a UNIQUE index */ } if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } /* Collision detection may be omitted if all of the following are true: ** (1) The conflict resolution algorithm is REPLACE ** (2) The table is a WITHOUT ROWID table ** (3) There are no secondary indexes on the table ** (4) No delete triggers need to be fired if there is a conflict ** (5) No FK constraint counters need to be updated if a conflict occurs. */ if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ && pPk==pIdx /* Condition 2 */ && onError==OE_Replace /* Condition 1 */ && ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */ 0==sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0)) && ( 0==(db->flags&SQLITE_ForeignKeys) || /* Condition 5 */ (0==pTab->pFKey && 0==sqlite3FkReferences(pTab))) ){ sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; } /* Check to see if the new index entry will be unique */ sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, regIdx, pIdx->nKeyCol); VdbeCoverage(v); /* Generate code to handle collisions */ regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); if( isUpdate || onError==OE_Replace ){ |
︙ | ︙ | |||
1624 1625 1626 1627 1628 1629 1630 | assert( onError==OE_Replace ); sqlite3MultiWrite(pParse); if( db->flags&SQLITE_RecTriggers ){ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, regR, nPkField, 0, OE_Replace, | | < > > > > > > > > > > > > > > > > > > > > > > | > > > > > < | < > > > > > > > | > > > > > > | > | > | | | 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 | assert( onError==OE_Replace ); sqlite3MultiWrite(pParse); if( db->flags&SQLITE_RecTriggers ){ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, regR, nPkField, 0, OE_Replace, (pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur); seenReplace = 1; break; } } sqlite3VdbeResolveLabel(v, addrUniqueOk); if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField); } if( ipkTop ){ sqlite3VdbeGoto(v, ipkTop+1); sqlite3VdbeJumpHere(v, ipkBottom); } *pbMayReplace = seenReplace; VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace)); } #ifdef SQLITE_ENABLE_NULL_TRIM /* ** Change the P5 operand on the last opcode (which should be an OP_MakeRecord) ** to be the number of columns in table pTab that must not be NULL-trimmed. ** ** Or if no columns of pTab may be NULL-trimmed, leave P5 at zero. */ void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){ u16 i; /* Records with omitted columns are only allowed for schema format ** version 2 and later (SQLite version 3.1.4, 2005-02-20). */ if( pTab->pSchema->file_format<2 ) return; for(i=pTab->nCol-1; i>0; i--){ if( pTab->aCol[i].pDflt!=0 ) break; if( pTab->aCol[i].colFlags & COLFLAG_PRIMKEY ) break; } sqlite3VdbeChangeP5(v, i+1); } #endif /* ** This routine generates code to finish the INSERT or UPDATE operation ** that was started by a prior call to sqlite3GenerateConstraintChecks. ** A consecutive range of registers starting at regNewData contains the ** rowid and the content to be inserted. ** ** The arguments to this routine should be the same as the first six ** arguments to sqlite3GenerateConstraintChecks. */ void sqlite3CompleteInsertion( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int iDataCur, /* Cursor of the canonical data source */ int iIdxCur, /* First index cursor */ int regNewData, /* Range of content */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ int update_flags, /* True for UPDATE, False for INSERT */ int appendBias, /* True if this is likely to be an append */ int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */ ){ Vdbe *v; /* Prepared statements under construction */ Index *pIdx; /* An index being inserted or updated */ u8 pik_flags; /* flag values passed to the btree insert */ int regData; /* Content registers (after the rowid) */ int regRec; /* Register holding assembled record for the table */ int i; /* Loop counter */ u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */ assert( update_flags==0 || update_flags==OPFLAG_ISUPDATE || update_flags==(OPFLAG_ISUPDATE|OPFLAG_SAVEPOSITION) ); v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( aRegIdx[i]==0 ) continue; bAffinityDone = 1; if( pIdx->pPartIdxWhere ){ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2); VdbeCoverage(v); } pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0); if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ assert( pParse->nested==0 ); pik_flags |= OPFLAG_NCHANGE; pik_flags |= (update_flags & OPFLAG_SAVEPOSITION); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( update_flags==0 ){ sqlite3VdbeAddOp4(v, OP_InsertInt, iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE ); sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); } #endif } sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i], aRegIdx[i]+1, pIdx->uniqNotNull ? pIdx->nKeyCol: pIdx->nColumn); sqlite3VdbeChangeP5(v, pik_flags); } if( !HasRowid(pTab) ) return; regData = regNewData + 1; regRec = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec); sqlite3SetMakeRecordP5(v, pTab); if( !bAffinityDone ){ sqlite3TableAffinity(v, pTab, 0); sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol); } if( pParse->nested ){ pik_flags = 0; }else{ pik_flags = OPFLAG_NCHANGE; pik_flags |= (update_flags?update_flags:OPFLAG_LASTROWID); } if( appendBias ){ pik_flags |= OPFLAG_APPEND; } if( useSeekResult ){ pik_flags |= OPFLAG_USESEEKRESULT; } sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData); if( !pParse->nested ){ sqlite3VdbeAppendP4(v, pTab, P4_TABLE); } sqlite3VdbeChangeP5(v, pik_flags); } /* ** Allocate cursors for the pTab table and all its indices and generate ** code to open and initialized those cursors. |
︙ | ︙ | |||
1829 1830 1831 1832 1833 1834 1835 | } for(i=0; i<pSrc->nKeyCol; i++){ if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){ return 0; /* Different columns indexed */ } if( pSrc->aiColumn[i]==XN_EXPR ){ assert( pSrc->aColExpr!=0 && pDest->aColExpr!=0 ); | | | | 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 | } for(i=0; i<pSrc->nKeyCol; i++){ if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){ return 0; /* Different columns indexed */ } if( pSrc->aiColumn[i]==XN_EXPR ){ assert( pSrc->aColExpr!=0 && pDest->aColExpr!=0 ); if( sqlite3ExprCompare(0, pSrc->aColExpr->a[i].pExpr, pDest->aColExpr->a[i].pExpr, -1)!=0 ){ return 0; /* Different expressions in the index */ } } if( pSrc->aSortOrder[i]!=pDest->aSortOrder[i] ){ return 0; /* Different sort orders */ } if( sqlite3_stricmp(pSrc->azColl[i],pDest->azColl[i])!=0 ){ return 0; /* Different collating sequences */ } } if( sqlite3ExprCompare(0, pSrc->pPartIdxWhere, pDest->pPartIdxWhere, -1) ){ return 0; /* Different WHERE clauses */ } /* If no test above fails then the indices must be compatible */ return 1; } |
︙ | ︙ | |||
1910 1911 1912 1913 1914 1915 1916 | ** error if pSelect reads from a CTE named "xxx". */ return 0; } if( sqlite3TriggerList(pParse, pDest) ){ return 0; /* tab1 must not have triggers */ } #ifndef SQLITE_OMIT_VIRTUALTABLE | | | 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 | ** error if pSelect reads from a CTE named "xxx". */ return 0; } if( sqlite3TriggerList(pParse, pDest) ){ return 0; /* tab1 must not have triggers */ } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pDest) ){ return 0; /* tab1 must not be a virtual table */ } #endif if( onError==OE_Default ){ if( pDest->iPKey>=0 ) onError = pDest->keyConf; if( onError==OE_Default ) onError = OE_Abort; } |
︙ | ︙ | |||
1972 1973 1974 1975 1976 1977 1978 | if( pSrc==pDest ){ return 0; /* tab1 and tab2 may not be the same table */ } if( HasRowid(pDest)!=HasRowid(pSrc) ){ return 0; /* source and destination must both be WITHOUT ROWID or not */ } #ifndef SQLITE_OMIT_VIRTUALTABLE | | | | 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 | if( pSrc==pDest ){ return 0; /* tab1 and tab2 may not be the same table */ } if( HasRowid(pDest)!=HasRowid(pSrc) ){ return 0; /* source and destination must both be WITHOUT ROWID or not */ } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pSrc) ){ return 0; /* tab2 must not be a virtual table */ } #endif if( pSrc->pSelect ){ return 0; /* tab2 may not be a view */ } if( pDest->nCol!=pSrc->nCol ){ return 0; /* Number of columns must be the same in tab1 and tab2 */ } if( pDest->iPKey!=pSrc->iPKey ){ return 0; /* Both tables must have the same INTEGER PRIMARY KEY */ } for(i=0; i<pDest->nCol; i++){ Column *pDestCol = &pDest->aCol[i]; Column *pSrcCol = &pSrc->aCol[i]; #ifdef SQLITE_ENABLE_HIDDEN_COLUMNS if( (db->mDbFlags & DBFLAG_Vacuum)==0 && (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN ){ return 0; /* Neither table may have __hidden__ columns */ } #endif if( pDestCol->affinity!=pSrcCol->affinity ){ return 0; /* Affinity must be the same on all columns */ |
︙ | ︙ | |||
2065 2066 2067 2068 2069 2070 2071 | iSrc = pParse->nTab++; iDest = pParse->nTab++; regAutoinc = autoIncBegin(pParse, iDbDest, pDest); regData = sqlite3GetTempReg(pParse); regRowid = sqlite3GetTempReg(pParse); sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); assert( HasRowid(pDest) || destHasUniqueIdx ); | | | | > | > > > > > > > | | 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 | iSrc = pParse->nTab++; iDest = pParse->nTab++; regAutoinc = autoIncBegin(pParse, iDbDest, pDest); regData = sqlite3GetTempReg(pParse); regRowid = sqlite3GetTempReg(pParse); sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); assert( HasRowid(pDest) || destHasUniqueIdx ); if( (db->mDbFlags & DBFLAG_Vacuum)==0 && ( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */ || destHasUniqueIdx /* (2) */ || (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */ )){ /* In some circumstances, we are able to run the xfer optimization ** only if the destination table is initially empty. Unless the ** DBFLAG_Vacuum flag is set, this block generates code to make ** that determination. If DBFLAG_Vacuum is set, then the destination ** table is always empty. ** ** Conditions under which the destination must be empty: ** ** (1) There is no INTEGER PRIMARY KEY but there are indices. ** (If the destination is not initially empty, the rowid fields ** of index entries might need to change.) ** ** (2) The destination has a unique index. (The xfer optimization ** is unable to test uniqueness.) ** ** (3) onError is something other than OE_Abort and OE_Rollback. */ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); VdbeCoverage(v); emptyDestTest = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, addr1); } if( HasRowid(pSrc) ){ u8 insFlags; sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead); emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); if( pDest->iPKey>=0 ){ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); VdbeCoverage(v); sqlite3RowidConstraint(pParse, onError, pDest); sqlite3VdbeJumpHere(v, addr2); autoIncStep(pParse, regAutoinc, regRowid); }else if( pDest->pIndex==0 ){ addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); }else{ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); assert( (pDest->tabFlags & TF_Autoincrement)==0 ); } sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); if( db->mDbFlags & DBFLAG_Vacuum ){ sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID| OPFLAG_APPEND|OPFLAG_USESEEKRESULT; }else{ insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND; } sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid, (char*)pDest, P4_TABLE); sqlite3VdbeChangeP5(v, insFlags); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); }else{ sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName); sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName); } |
︙ | ︙ | |||
2132 2133 2134 2135 2136 2137 2138 | sqlite3VdbeSetP4KeyInfo(pParse, pSrcIdx); VdbeComment((v, "%s", pSrcIdx->zName)); sqlite3VdbeAddOp3(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest); sqlite3VdbeSetP4KeyInfo(pParse, pDestIdx); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR); VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); | | | | < < | | | | 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 | sqlite3VdbeSetP4KeyInfo(pParse, pSrcIdx); VdbeComment((v, "%s", pSrcIdx->zName)); sqlite3VdbeAddOp3(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest); sqlite3VdbeSetP4KeyInfo(pParse, pDestIdx); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR); VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); if( db->mDbFlags & DBFLAG_Vacuum ){ /* This INSERT command is part of a VACUUM operation, which guarantees ** that the destination table is empty. If all indexed columns use ** collation sequence BINARY, then it can also be assumed that the ** index will be populated by inserting keys in strictly sorted ** order. In this case, instead of seeking within the b-tree as part ** of every OP_IdxInsert opcode, an OP_SeekEnd is added before the ** OP_IdxInsert to seek to the point within the b-tree where each key ** should be inserted. This is faster. ** ** If any of the indexed columns use a collation sequence other than ** BINARY, this optimization is disabled. This is because the user ** might change the definition of a collation sequence and then run ** a VACUUM command. In that case keys may not be written in strictly ** sorted order. */ for(i=0; i<pSrcIdx->nColumn; i++){ const char *zColl = pSrcIdx->azColl[i]; if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break; } if( i==pSrcIdx->nColumn ){ idxInsFlags = OPFLAG_USESEEKRESULT; sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); } } if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){ idxInsFlags |= OPFLAG_NCHANGE; } sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData); sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); } if( emptySrcTest ) sqlite3VdbeJumpHere(v, emptySrcTest); sqlite3ReleaseTempReg(pParse, regRowid); |
︙ | ︙ |
Changes to src/legacy.c.
︙ | ︙ | |||
69 70 71 72 73 74 75 | rc = sqlite3_step(pStmt); /* Invoke the callback function if required */ if( xCallback && (SQLITE_ROW==rc || (SQLITE_DONE==rc && !callbackIsInit && db->flags&SQLITE_NullCallback)) ){ if( !callbackIsInit ){ | | > | 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 | rc = sqlite3_step(pStmt); /* Invoke the callback function if required */ if( xCallback && (SQLITE_ROW==rc || (SQLITE_DONE==rc && !callbackIsInit && db->flags&SQLITE_NullCallback)) ){ if( !callbackIsInit ){ azCols = sqlite3DbMallocRaw(db, (2*nCol+1)*sizeof(const char*)); if( azCols==0 ){ goto exec_out; } for(i=0; i<nCol; i++){ azCols[i] = (char *)sqlite3_column_name(pStmt, i); /* sqlite3VdbeSetColName() installs column names as UTF8 ** strings so there is no way for sqlite3_column_name() to fail. */ assert( azCols[i]!=0 ); } callbackIsInit = 1; } if( rc==SQLITE_ROW ){ azVals = &azCols[nCol]; for(i=0; i<nCol; i++){ azVals[i] = (char *)sqlite3_column_text(pStmt, i); if( !azVals[i] && sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){ sqlite3OomFault(db); goto exec_out; } } azVals[i] = 0; } if( xCallback(pArg, nCol, azVals, azCols) ){ /* EVIDENCE-OF: R-38229-40159 If the callback function to ** sqlite3_exec() returns non-zero, then sqlite3_exec() will ** return SQLITE_ABORT. */ rc = SQLITE_ABORT; sqlite3VdbeFinalize((Vdbe *)pStmt); |
︙ | ︙ | |||
122 123 124 125 126 127 128 | exec_out: if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt); sqlite3DbFree(db, azCols); rc = sqlite3ApiExit(db, rc); if( rc!=SQLITE_OK && pzErrMsg ){ | < | | < < | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | exec_out: if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt); sqlite3DbFree(db, azCols); rc = sqlite3ApiExit(db, rc); if( rc!=SQLITE_OK && pzErrMsg ){ *pzErrMsg = sqlite3DbStrDup(0, sqlite3_errmsg(db)); if( *pzErrMsg==0 ){ rc = SQLITE_NOMEM_BKPT; sqlite3Error(db, SQLITE_NOMEM); } }else if( pzErrMsg ){ *pzErrMsg = 0; } assert( (rc&db->errMask)==rc ); sqlite3_mutex_leave(db->mutex); return rc; } |
Changes to src/loadext.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | */ #ifndef SQLITE_CORE #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */ #endif #include "sqlite3ext.h" #include "sqliteInt.h" | < | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | */ #ifndef SQLITE_CORE #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */ #endif #include "sqlite3ext.h" #include "sqliteInt.h" #ifndef SQLITE_OMIT_LOAD_EXTENSION /* ** Some API routines are omitted when various features are ** excluded from a build of SQLite. Substitute a NULL pointer ** for any missing APIs. */ |
︙ | ︙ | |||
48 49 50 51 52 53 54 55 56 57 58 59 60 61 | # define sqlite3_complete16 0 # define sqlite3_create_collation16 0 # define sqlite3_create_function16 0 # define sqlite3_errmsg16 0 # define sqlite3_open16 0 # define sqlite3_prepare16 0 # define sqlite3_prepare16_v2 0 # define sqlite3_result_error16 0 # define sqlite3_result_text16 0 # define sqlite3_result_text16be 0 # define sqlite3_result_text16le 0 # define sqlite3_value_text16 0 # define sqlite3_value_text16be 0 # define sqlite3_value_text16le 0 | > | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | # define sqlite3_complete16 0 # define sqlite3_create_collation16 0 # define sqlite3_create_function16 0 # define sqlite3_errmsg16 0 # define sqlite3_open16 0 # define sqlite3_prepare16 0 # define sqlite3_prepare16_v2 0 # define sqlite3_prepare16_v3 0 # define sqlite3_result_error16 0 # define sqlite3_result_text16 0 # define sqlite3_result_text16be 0 # define sqlite3_result_text16le 0 # define sqlite3_value_text16 0 # define sqlite3_value_text16be 0 # define sqlite3_value_text16le 0 |
︙ | ︙ | |||
418 419 420 421 422 423 424 | sqlite3_status64, sqlite3_strlike, sqlite3_db_cacheflush, /* Version 3.12.0 and later */ sqlite3_system_errno, /* Version 3.14.0 and later */ sqlite3_trace_v2, | | > > > > > > > > | 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | sqlite3_status64, sqlite3_strlike, sqlite3_db_cacheflush, /* Version 3.12.0 and later */ sqlite3_system_errno, /* Version 3.14.0 and later */ sqlite3_trace_v2, sqlite3_expanded_sql, /* Version 3.18.0 and later */ sqlite3_set_last_insert_rowid, /* Version 3.20.0 and later */ sqlite3_prepare_v3, sqlite3_prepare16_v3, sqlite3_bind_pointer, sqlite3_result_pointer, sqlite3_value_pointer }; /* ** Attempt to load an SQLite extension library contained in the file ** zFile. The entry point is zProc. zProc may be 0 in which case a ** default entry point name (sqlite3_extension_init) is used. Use ** of the default name is recommended. |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #endif #ifdef SQLITE_ENABLE_ICU # include "sqliteicu.h" #endif #ifdef SQLITE_ENABLE_JSON1 int sqlite3Json1Init(sqlite3*); #endif #ifdef SQLITE_ENABLE_FTS5 int sqlite3Fts5Init(sqlite3*); #endif #ifndef SQLITE_AMALGAMATION /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant ** contains the text of SQLITE_VERSION macro. | > > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #endif #ifdef SQLITE_ENABLE_ICU # include "sqliteicu.h" #endif #ifdef SQLITE_ENABLE_JSON1 int sqlite3Json1Init(sqlite3*); #endif #ifdef SQLITE_ENABLE_STMTVTAB int sqlite3StmtVtabInit(sqlite3*); #endif #ifdef SQLITE_ENABLE_FTS5 int sqlite3Fts5Init(sqlite3*); #endif #ifndef SQLITE_AMALGAMATION /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant ** contains the text of SQLITE_VERSION macro. |
︙ | ︙ | |||
429 430 431 432 433 434 435 | case SQLITE_CONFIG_MEMSTATUS: { /* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes ** single argument of type int, interpreted as a boolean, which enables ** or disables the collection of memory allocation statistics. */ sqlite3GlobalConfig.bMemstat = va_arg(ap, int); break; } | | < < < < < | < | 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | case SQLITE_CONFIG_MEMSTATUS: { /* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes ** single argument of type int, interpreted as a boolean, which enables ** or disables the collection of memory allocation statistics. */ sqlite3GlobalConfig.bMemstat = va_arg(ap, int); break; } case SQLITE_CONFIG_SMALL_MALLOC: { sqlite3GlobalConfig.bSmallMalloc = va_arg(ap, int); break; } case SQLITE_CONFIG_PAGECACHE: { /* EVIDENCE-OF: R-18761-36601 There are three arguments to ** SQLITE_CONFIG_PAGECACHE: A pointer to 8-byte aligned memory (pMem), ** the size of each page cache line (sz), and the number of cache lines ** (N). */ |
︙ | ︙ | |||
657 658 659 660 661 662 663 | ** space for the lookaside memory is obtained from sqlite3_malloc(). ** If pStart is not NULL then it is sz*cnt bytes of memory to use for ** the lookaside memory. */ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ #ifndef SQLITE_OMIT_LOOKASIDE void *pStart; | | > | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 | ** space for the lookaside memory is obtained from sqlite3_malloc(). ** If pStart is not NULL then it is sz*cnt bytes of memory to use for ** the lookaside memory. */ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ #ifndef SQLITE_OMIT_LOOKASIDE void *pStart; if( sqlite3LookasideUsed(db,0)>0 ){ return SQLITE_BUSY; } /* Free any existing lookaside buffer for this handle before ** allocating a new one so we don't have to have space for ** both at the same time. */ if( db->lookaside.bMalloced ){ |
︙ | ︙ | |||
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 | pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ sqlite3EndBenignMalloc(); if( pStart ) cnt = sqlite3MallocSize(pStart)/sz; }else{ pStart = pBuf; } db->lookaside.pStart = pStart; db->lookaside.pFree = 0; db->lookaside.sz = (u16)sz; if( pStart ){ int i; LookasideSlot *p; assert( sz > (int)sizeof(LookasideSlot*) ); p = (LookasideSlot*)pStart; for(i=cnt-1; i>=0; i--){ | > > | | > | 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 | pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ sqlite3EndBenignMalloc(); if( pStart ) cnt = sqlite3MallocSize(pStart)/sz; }else{ pStart = pBuf; } db->lookaside.pStart = pStart; db->lookaside.pInit = 0; db->lookaside.pFree = 0; db->lookaside.sz = (u16)sz; if( pStart ){ int i; LookasideSlot *p; assert( sz > (int)sizeof(LookasideSlot*) ); db->lookaside.nSlot = cnt; p = (LookasideSlot*)pStart; for(i=cnt-1; i>=0; i--){ p->pNext = db->lookaside.pInit; db->lookaside.pInit = p; p = (LookasideSlot*)&((u8*)p)[sz]; } db->lookaside.pEnd = p; db->lookaside.bDisable = 0; db->lookaside.bMalloced = pBuf==0 ?1:0; }else{ db->lookaside.pStart = db; db->lookaside.pEnd = db; db->lookaside.bDisable = 1; db->lookaside.bMalloced = 0; db->lookaside.nSlot = 0; } #endif /* SQLITE_OMIT_LOOKASIDE */ return SQLITE_OK; } /* ** Return the mutex associated with a database connection. |
︙ | ︙ | |||
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 | */ int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_MAINDBNAME: { db->aDb[0].zDbSName = va_arg(ap,char*); rc = SQLITE_OK; break; } case SQLITE_DBCONFIG_LOOKASIDE: { void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */ int sz = va_arg(ap, int); /* IMP: R-47871-25994 */ int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */ rc = setupLookaside(db, pBuf, sz, cnt); break; } default: { static const struct { int op; /* The opcode */ u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ } aFlagOp[] = { { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, { SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer }, { SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ for(i=0; i<ArraySize(aFlagOp); i++){ if( aFlagOp[i].op==op ){ int onoff = va_arg(ap, int); int *pRes = va_arg(ap, int*); | > > > > | | 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 | */ int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_MAINDBNAME: { /* IMP: R-06824-28531 */ /* IMP: R-36257-52125 */ db->aDb[0].zDbSName = va_arg(ap,char*); rc = SQLITE_OK; break; } case SQLITE_DBCONFIG_LOOKASIDE: { void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */ int sz = va_arg(ap, int); /* IMP: R-47871-25994 */ int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */ rc = setupLookaside(db, pBuf, sz, cnt); break; } default: { static const struct { int op; /* The opcode */ u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ } aFlagOp[] = { { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, { SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer }, { SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension }, { SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose }, { SQLITE_DBCONFIG_ENABLE_QPSG, SQLITE_EnableQPSG }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ for(i=0; i<ArraySize(aFlagOp); i++){ if( aFlagOp[i].op==op ){ int onoff = va_arg(ap, int); int *pRes = va_arg(ap, int*); u32 oldFlags = db->flags; if( onoff>0 ){ db->flags |= aFlagOp[i].mask; }else if( onoff==0 ){ db->flags &= ~aFlagOp[i].mask; } if( oldFlags!=db->flags ){ sqlite3ExpirePreparedStatements(db); |
︙ | ︙ | |||
862 863 864 865 866 867 868 869 870 871 872 873 874 875 | int nKey2, const void *pKey2 ){ int rc, n; n = nKey1<nKey2 ? nKey1 : nKey2; /* EVIDENCE-OF: R-65033-28449 The built-in BINARY collation compares ** strings byte by byte using the memcmp() function from the standard C ** library. */ rc = memcmp(pKey1, pKey2, n); if( rc==0 ){ if( padFlag && allSpaces(((char*)pKey1)+n, nKey1-n) && allSpaces(((char*)pKey2)+n, nKey2-n) ){ /* EVIDENCE-OF: R-31624-24737 RTRIM is like BINARY except that extra | > | 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 | int nKey2, const void *pKey2 ){ int rc, n; n = nKey1<nKey2 ? nKey1 : nKey2; /* EVIDENCE-OF: R-65033-28449 The built-in BINARY collation compares ** strings byte by byte using the memcmp() function from the standard C ** library. */ assert( pKey1 && pKey2 ); rc = memcmp(pKey1, pKey2, n); if( rc==0 ){ if( padFlag && allSpaces(((char*)pKey1)+n, nKey1-n) && allSpaces(((char*)pKey2)+n, nKey2-n) ){ /* EVIDENCE-OF: R-31624-24737 RTRIM is like BINARY except that extra |
︙ | ︙ | |||
915 916 917 918 919 920 921 922 923 924 925 926 927 928 | if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif return db->lastRowid; } /* ** Return the number of changes in the most recent call to sqlite3_exec(). */ int sqlite3_changes(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ | > > > > > > > > > > > > > > > | 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 | if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif return db->lastRowid; } /* ** Set the value returned by the sqlite3_last_insert_rowid() API function. */ void sqlite3_set_last_insert_rowid(sqlite3 *db, sqlite3_int64 iRowid){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return; } #endif sqlite3_mutex_enter(db->mutex); db->lastRowid = iRowid; sqlite3_mutex_leave(db->mutex); } /* ** Return the number of changes in the most recent call to sqlite3_exec(). */ int sqlite3_changes(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ |
︙ | ︙ | |||
1204 1205 1206 1207 1208 1209 1210 | ** the same sqliteMalloc() as the one that allocates the database ** structure? */ sqlite3DbFree(db, db->aDb[1].pSchema); sqlite3_mutex_leave(db->mutex); db->magic = SQLITE_MAGIC_CLOSED; sqlite3_mutex_free(db->mutex); | | | 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 | ** the same sqliteMalloc() as the one that allocates the database ** structure? */ sqlite3DbFree(db, db->aDb[1].pSchema); sqlite3_mutex_leave(db->mutex); db->magic = SQLITE_MAGIC_CLOSED; sqlite3_mutex_free(db->mutex); assert( sqlite3LookasideUsed(db,0)==0 ); if( db->lookaside.bMalloced ){ sqlite3_free(db->lookaside.pStart); } sqlite3_free(db); } /* |
︙ | ︙ | |||
1232 1233 1234 1235 1236 1237 1238 | /* Obtain all b-tree mutexes before making any calls to BtreeRollback(). ** This is important in case the transaction being rolled back has ** modified the database schema. If the b-tree mutexes are not taken ** here, then another shared-cache connection might sneak in between ** the database rollback and schema reset, which can cause false ** corruption reports in some cases. */ sqlite3BtreeEnterAll(db); | | | | 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 | /* Obtain all b-tree mutexes before making any calls to BtreeRollback(). ** This is important in case the transaction being rolled back has ** modified the database schema. If the b-tree mutexes are not taken ** here, then another shared-cache connection might sneak in between ** the database rollback and schema reset, which can cause false ** corruption reports in some cases. */ sqlite3BtreeEnterAll(db); schemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0 && db->init.busy==0; for(i=0; i<db->nDb; i++){ Btree *p = db->aDb[i].pBt; if( p ){ if( sqlite3BtreeIsInTrans(p) ){ inTrans = 1; } sqlite3BtreeRollback(p, tripCode, !schemaChange); } } sqlite3VtabRollback(db); sqlite3EndBenignMalloc(); if( (db->mDbFlags&DBFLAG_SchemaChange)!=0 && db->init.busy==0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetAllSchemasOfConnection(db); } sqlite3BtreeLeaveAll(db); /* Any deferred constraint violations have now been resolved. */ db->nDeferredCons = 0; |
︙ | ︙ | |||
1379 1380 1381 1382 1383 1384 1385 | /* ** Return a static string that describes the kind of error specified in the ** argument. */ const char *sqlite3ErrStr(int rc){ static const char* const aMsg[] = { /* SQLITE_OK */ "not an error", | | | | | > > > > | | | | 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 | /* ** Return a static string that describes the kind of error specified in the ** argument. */ const char *sqlite3ErrStr(int rc){ static const char* const aMsg[] = { /* SQLITE_OK */ "not an error", /* SQLITE_ERROR */ "SQL logic error", /* SQLITE_INTERNAL */ 0, /* SQLITE_PERM */ "access permission denied", /* SQLITE_ABORT */ "query aborted", /* SQLITE_BUSY */ "database is locked", /* SQLITE_LOCKED */ "database table is locked", /* SQLITE_NOMEM */ "out of memory", /* SQLITE_READONLY */ "attempt to write a readonly database", /* SQLITE_INTERRUPT */ "interrupted", /* SQLITE_IOERR */ "disk I/O error", /* SQLITE_CORRUPT */ "database disk image is malformed", /* SQLITE_NOTFOUND */ "unknown operation", /* SQLITE_FULL */ "database or disk is full", /* SQLITE_CANTOPEN */ "unable to open database file", /* SQLITE_PROTOCOL */ "locking protocol", /* SQLITE_EMPTY */ 0, /* SQLITE_SCHEMA */ "database schema has changed", /* SQLITE_TOOBIG */ "string or blob too big", /* SQLITE_CONSTRAINT */ "constraint failed", /* SQLITE_MISMATCH */ "datatype mismatch", /* SQLITE_MISUSE */ "bad parameter or other API misuse", #ifdef SQLITE_DISABLE_LFS /* SQLITE_NOLFS */ "large file support is disabled", #else /* SQLITE_NOLFS */ 0, #endif /* SQLITE_AUTH */ "authorization denied", /* SQLITE_FORMAT */ 0, /* SQLITE_RANGE */ "column index out of range", /* SQLITE_NOTADB */ "file is not a database", }; const char *zErr = "unknown error"; switch( rc ){ case SQLITE_ABORT_ROLLBACK: { zErr = "abort due to ROLLBACK"; break; } |
︙ | ︙ | |||
1563 1564 1565 1566 1567 1568 1569 | } /* ** Cause any pending operation to stop at its earliest opportunity. */ void sqlite3_interrupt(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR | | | 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 | } /* ** Cause any pending operation to stop at its earliest opportunity. */ void sqlite3_interrupt(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){ (void)SQLITE_MISUSE_BKPT; return; } #endif db->u1.isInterrupted = 1; } |
︙ | ︙ | |||
2144 2145 2146 2147 2148 2149 2150 | ** associated with the specific b-tree being checkpointed is taken by ** this function while the checkpoint is running. ** ** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are ** checkpointed. If an error is encountered it is returned immediately - ** no attempt is made to checkpoint any remaining databases. ** | | > | 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 | ** associated with the specific b-tree being checkpointed is taken by ** this function while the checkpoint is running. ** ** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are ** checkpointed. If an error is encountered it is returned immediately - ** no attempt is made to checkpoint any remaining databases. ** ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL, RESTART ** or TRUNCATE. */ int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; /* Return code */ int i; /* Used to iterate through attached dbs */ int bBusy = 0; /* True if SQLITE_BUSY has been encountered */ assert( sqlite3_mutex_held(db->mutex) ); |
︙ | ︙ | |||
2244 2245 2246 2247 2248 2249 2250 | ** error. */ const void *sqlite3_errmsg16(sqlite3 *db){ static const u16 outOfMem[] = { 'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0 }; static const u16 misuse[] = { | | < < | | < | 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 | ** error. */ const void *sqlite3_errmsg16(sqlite3 *db){ static const u16 outOfMem[] = { 'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0 }; static const u16 misuse[] = { 'b', 'a', 'd', ' ', 'p', 'a', 'r', 'a', 'm', 'e', 't', 'e', 'r', ' ', 'o', 'r', ' ', 'o', 't', 'h', 'e', 'r', ' ', 'A', 'P', 'I', ' ', 'm', 'i', 's', 'u', 's', 'e', 0 }; const void *z; if( !db ){ return (void *)outOfMem; } if( !sqlite3SafetyCheckSickOrOk(db) ){ |
︙ | ︙ | |||
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 | && sqlite3Isxdigit(zUri[iIn+1]) ){ int octet = (sqlite3HexToInt(zUri[iIn++]) << 4); octet += sqlite3HexToInt(zUri[iIn++]); assert( octet>=0 && octet<256 ); if( octet==0 ){ /* This branch is taken when "%00" appears within the URI. In this ** case we ignore all text in the remainder of the path, name or ** value currently being parsed. So ignore the current character ** and skip to the next "?", "=" or "&", as appropriate. */ while( (c = zUri[iIn])!=0 && c!='#' && (eState!=0 || c!='?') && (eState!=1 || (c!='=' && c!='&')) && (eState!=2 || c!='&') ){ iIn++; } continue; } c = octet; }else if( eState==1 && (c=='&' || c=='=') ){ if( zFile[iOut-1]==0 ){ /* An empty option name. Ignore this option altogether. */ while( zUri[iIn] && zUri[iIn]!='#' && zUri[iIn-1]!='&' ) iIn++; continue; | > > > > > > > | 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 | && sqlite3Isxdigit(zUri[iIn+1]) ){ int octet = (sqlite3HexToInt(zUri[iIn++]) << 4); octet += sqlite3HexToInt(zUri[iIn++]); assert( octet>=0 && octet<256 ); if( octet==0 ){ #ifndef SQLITE_ENABLE_URI_00_ERROR /* This branch is taken when "%00" appears within the URI. In this ** case we ignore all text in the remainder of the path, name or ** value currently being parsed. So ignore the current character ** and skip to the next "?", "=" or "&", as appropriate. */ while( (c = zUri[iIn])!=0 && c!='#' && (eState!=0 || c!='?') && (eState!=1 || (c!='=' && c!='&')) && (eState!=2 || c!='&') ){ iIn++; } continue; #else /* If ENABLE_URI_00_ERROR is defined, "%00" in a URI is an error. */ *pzErrMsg = sqlite3_mprintf("unexpected %%00 in uri"); rc = SQLITE_ERROR; goto parse_uri_out; #endif } c = octet; }else if( eState==1 && (c=='&' || c=='=') ){ if( zFile[iOut-1]==0 ){ /* An empty option name. Ignore this option altogether. */ while( zUri[iIn] && zUri[iIn]!='#' && zUri[iIn-1]!='&' ) iIn++; continue; |
︙ | ︙ | |||
2727 2728 2729 2730 2731 2732 2733 | zOpt = &zVal[nVal+1]; } }else{ zFile = sqlite3_malloc64(nUri+2); if( !zFile ) return SQLITE_NOMEM_BKPT; | > | > | 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 | zOpt = &zVal[nVal+1]; } }else{ zFile = sqlite3_malloc64(nUri+2); if( !zFile ) return SQLITE_NOMEM_BKPT; if( nUri ){ memcpy(zFile, zUri, nUri); } zFile[nUri] = '\0'; zFile[nUri+1] = '\0'; flags &= ~SQLITE_OPEN_URI; } *ppVfs = sqlite3_vfs_find(zVfs); if( *ppVfs==0 ){ |
︙ | ︙ | |||
2775 2776 2777 2778 2779 2780 2781 | #endif *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ) return rc; #endif | < < < < < < < < < < < < < < < < < < < < | 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 | #endif *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ) return rc; #endif if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_NOMUTEX ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_FULLMUTEX ){ isThreadsafe = 1; }else{ |
︙ | ︙ | |||
2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 | #endif #if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) | SQLITE_CellSizeCk #endif #if defined(SQLITE_ENABLE_FTS3_TOKENIZER) | SQLITE_Fts3Tokenizer #endif ; sqlite3HashInit(&db->aCollSeq); #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3HashInit(&db->aModule); #endif /* Add the default collation sequence BINARY. BINARY works for both UTF-8 | > > > | 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 | #endif #if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) | SQLITE_CellSizeCk #endif #if defined(SQLITE_ENABLE_FTS3_TOKENIZER) | SQLITE_Fts3Tokenizer #endif #if defined(SQLITE_ENABLE_QPSG) | SQLITE_EnableQPSG #endif ; sqlite3HashInit(&db->aCollSeq); #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3HashInit(&db->aModule); #endif /* Add the default collation sequence BINARY. BINARY works for both UTF-8 |
︙ | ︙ | |||
2913 2914 2915 2916 2917 2918 2919 | } /* EVIDENCE-OF: R-08308-17224 The default collating function for all ** strings is BINARY. */ db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0); assert( db->pDfltColl!=0 ); | | > > > > > > > > > > > > > > > > > > > > | > | 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 | } /* EVIDENCE-OF: R-08308-17224 The default collating function for all ** strings is BINARY. */ db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0); assert( db->pDfltColl!=0 ); /* Parse the filename/URI argument ** ** Only allow sensible combinations of bits in the flags argument. ** Throw an error if any non-sense combination is used. If we ** do not block illegal combinations here, it could trigger ** assert() statements in deeper layers. Sensible combinations ** are: ** ** 1: SQLITE_OPEN_READONLY ** 2: SQLITE_OPEN_READWRITE ** 6: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE */ db->openFlags = flags; assert( SQLITE_OPEN_READONLY == 0x01 ); assert( SQLITE_OPEN_READWRITE == 0x02 ); assert( SQLITE_OPEN_CREATE == 0x04 ); testcase( (1<<(flags&7))==0x02 ); /* READONLY */ testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ if( ((1<<(flags&7)) & 0x46)==0 ){ rc = SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */ }else{ rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); } if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg); sqlite3_free(zErrMsg); goto opendb_out; } |
︙ | ︙ | |||
3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 | #endif #ifdef SQLITE_ENABLE_JSON1 if( !db->mallocFailed && rc==SQLITE_OK){ rc = sqlite3Json1Init(db); } #endif /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking ** mode. Doing nothing at all also makes NORMAL the default. */ #ifdef SQLITE_DEFAULT_LOCKING_MODE db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; | > > > > > > | 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 | #endif #ifdef SQLITE_ENABLE_JSON1 if( !db->mallocFailed && rc==SQLITE_OK){ rc = sqlite3Json1Init(db); } #endif #ifdef SQLITE_ENABLE_STMTVTAB if( !db->mallocFailed && rc==SQLITE_OK){ rc = sqlite3StmtVtabInit(db); } #endif /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking ** mode. Doing nothing at all also makes NORMAL the default. */ #ifdef SQLITE_DEFAULT_LOCKING_MODE db->dfltLockMode = SQLITE_DEFAULT_LOCKING_MODE; |
︙ | ︙ | |||
3066 3067 3068 3069 3070 3071 3072 | /* Opening a db handle. Fourth parameter is passed 0. */ void *pArg = sqlite3GlobalConfig.pSqllogArg; sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0); } #endif #if defined(SQLITE_HAS_CODEC) if( rc==SQLITE_OK ){ | > | < | | | | > > | | 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 | /* Opening a db handle. Fourth parameter is passed 0. */ void *pArg = sqlite3GlobalConfig.pSqllogArg; sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0); } #endif #if defined(SQLITE_HAS_CODEC) if( rc==SQLITE_OK ){ const char *zKey; if( (zKey = sqlite3_uri_parameter(zOpen, "hexkey"))!=0 && zKey[0] ){ u8 iByte; int i; char zDecoded[40]; for(i=0, iByte=0; i<sizeof(zDecoded)*2 && sqlite3Isxdigit(zKey[i]); i++){ iByte = (iByte<<4) + sqlite3HexToInt(zKey[i]); if( (i&1)!=0 ) zDecoded[i/2] = iByte; } sqlite3_key_v2(db, 0, zDecoded, i/2); }else if( (zKey = sqlite3_uri_parameter(zOpen, "key"))!=0 ){ sqlite3_key_v2(db, 0, zKey, sqlite3Strlen30(zKey)); } } #endif sqlite3_free(zOpen); return rc & 0xff; } |
︙ | ︙ | |||
3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 | return reportError(SQLITE_MISUSE, lineno, "misuse"); } int sqlite3CantopenError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return reportError(SQLITE_CANTOPEN, lineno, "cannot open file"); } #ifdef SQLITE_DEBUG int sqlite3NomemError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return reportError(SQLITE_NOMEM, lineno, "OOM"); } int sqlite3IoerrnomemError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return reportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error"); | > > > > > > | 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 | return reportError(SQLITE_MISUSE, lineno, "misuse"); } int sqlite3CantopenError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return reportError(SQLITE_CANTOPEN, lineno, "cannot open file"); } #ifdef SQLITE_DEBUG int sqlite3CorruptPgnoError(int lineno, Pgno pgno){ char zMsg[100]; sqlite3_snprintf(sizeof(zMsg), zMsg, "database corruption page %d", pgno); testcase( sqlite3GlobalConfig.xLog!=0 ); return reportError(SQLITE_CORRUPT, lineno, zMsg); } int sqlite3NomemError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return reportError(SQLITE_NOMEM, lineno, "OOM"); } int sqlite3IoerrnomemError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return reportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error"); |
︙ | ︙ | |||
3521 3522 3523 3524 3525 3526 3527 | } /* ** Interface to the testing logic. */ int sqlite3_test_control(int op, ...){ int rc = 0; | | | 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 | } /* ** Interface to the testing logic. */ int sqlite3_test_control(int op, ...){ int rc = 0; #ifdef SQLITE_UNTESTABLE UNUSED_PARAMETER(op); #else va_list ap; va_start(ap, op); switch( op ){ /* |
︙ | ︙ | |||
3747 3748 3749 3750 3751 3752 3753 | const char *zWord = va_arg(ap, const char*); int n = sqlite3Strlen30(zWord); rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0; break; } #endif | < < < < < < < < < < < < < < < < | 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 | const char *zWord = va_arg(ap, const char*); int n = sqlite3Strlen30(zWord); rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0; break; } #endif /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); ** ** If parameter onoff is non-zero, configure the wrappers so that all ** subsequent calls to localtime() and variants fail. If onoff is zero, ** undo this setting. */ case SQLITE_TESTCTRL_LOCALTIME_FAULT: { |
︙ | ︙ | |||
3858 3859 3860 3861 3862 3863 3864 | sqlite3ResetAllSchemasOfConnection(db); } sqlite3_mutex_leave(db->mutex); break; } } va_end(ap); | | | 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 | sqlite3ResetAllSchemasOfConnection(db); } sqlite3_mutex_leave(db->mutex); break; } } va_end(ap); #endif /* SQLITE_UNTESTABLE */ return rc; } /* ** This is a utility routine, useful to VFS implementations, that checks ** to see if a database file was a URI that contained a specific query ** parameter, and if so obtains the value of the query parameter. |
︙ | ︙ | |||
3904 3905 3906 3907 3908 3909 3910 | sqlite3_int64 sqlite3_uri_int64( const char *zFilename, /* Filename as passed to xOpen */ const char *zParam, /* URI parameter sought */ sqlite3_int64 bDflt /* return if parameter is missing */ ){ const char *z = sqlite3_uri_parameter(zFilename, zParam); sqlite3_int64 v; | | | < < < < < < < | | 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 | sqlite3_int64 sqlite3_uri_int64( const char *zFilename, /* Filename as passed to xOpen */ const char *zParam, /* URI parameter sought */ sqlite3_int64 bDflt /* return if parameter is missing */ ){ const char *z = sqlite3_uri_parameter(zFilename, zParam); sqlite3_int64 v; if( z && sqlite3DecOrHexToI64(z, &v)==0 ){ bDflt = v; } return bDflt; } /* ** Return the Btree pointer identified by zDbName. Return NULL if not found. */ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ int iDb = zDbName ? sqlite3FindDbName(db, zDbName) : 0; return iDb<0 ? 0 : db->aDb[iDb].pBt; } /* ** Return the filename of the database associated with a database ** connection. */ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ |
︙ | ︙ | |||
3969 3970 3971 3972 3973 3974 3975 | int sqlite3_snapshot_get( sqlite3 *db, const char *zDb, sqlite3_snapshot **ppSnapshot ){ int rc = SQLITE_ERROR; #ifndef SQLITE_OMIT_WAL | < > | | | | | | | > | 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 | int sqlite3_snapshot_get( sqlite3 *db, const char *zDb, sqlite3_snapshot **ppSnapshot ){ int rc = SQLITE_ERROR; #ifndef SQLITE_OMIT_WAL #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ return SQLITE_MISUSE_BKPT; } #endif sqlite3_mutex_enter(db->mutex); if( db->autoCommit==0 ){ int iDb = sqlite3FindDbName(db, zDb); if( iDb==0 || iDb>1 ){ Btree *pBt = db->aDb[iDb].pBt; if( 0==sqlite3BtreeIsInTrans(pBt) ){ rc = sqlite3BtreeBeginTrans(pBt, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); } } } } sqlite3_mutex_leave(db->mutex); #endif /* SQLITE_OMIT_WAL */ return rc; |
︙ | ︙ | |||
4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 | } } sqlite3_mutex_leave(db->mutex); #endif /* SQLITE_OMIT_WAL */ return rc; } /* ** Free a snapshot handle obtained from sqlite3_snapshot_get(). */ void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){ sqlite3_free(pSnapshot); } #endif /* SQLITE_ENABLE_SNAPSHOT */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } } sqlite3_mutex_leave(db->mutex); #endif /* SQLITE_OMIT_WAL */ return rc; } /* ** Recover as many snapshots as possible from the wal file associated with ** schema zDb of database db. */ int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){ int rc = SQLITE_ERROR; int iDb; #ifndef SQLITE_OMIT_WAL #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ return SQLITE_MISUSE_BKPT; } #endif sqlite3_mutex_enter(db->mutex); iDb = sqlite3FindDbName(db, zDb); if( iDb==0 || iDb>1 ){ Btree *pBt = db->aDb[iDb].pBt; if( 0==sqlite3BtreeIsInReadTrans(pBt) ){ rc = sqlite3BtreeBeginTrans(pBt, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt)); sqlite3BtreeCommit(pBt); } } } sqlite3_mutex_leave(db->mutex); #endif /* SQLITE_OMIT_WAL */ return rc; } /* ** Free a snapshot handle obtained from sqlite3_snapshot_get(). */ void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){ sqlite3_free(pSnapshot); } #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* ** Given the name of a compile-time option, return true if that option ** was used and false if not. ** ** The name can optionally begin with "SQLITE_" but the "SQLITE_" prefix ** is not required for a match. */ int sqlite3_compileoption_used(const char *zOptName){ int i, n; int nOpt; const char **azCompileOpt; #if SQLITE_ENABLE_API_ARMOR if( zOptName==0 ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif azCompileOpt = sqlite3CompileOptions(&nOpt); if( sqlite3StrNICmp(zOptName, "SQLITE_", 7)==0 ) zOptName += 7; n = sqlite3Strlen30(zOptName); /* Since nOpt is normally in single digits, a linear search is ** adequate. No need for a binary search. */ for(i=0; i<nOpt; i++){ if( sqlite3StrNICmp(zOptName, azCompileOpt[i], n)==0 && sqlite3IsIdChar((unsigned char)azCompileOpt[i][n])==0 ){ return 1; } } return 0; } /* ** Return the N-th compile-time option string. If N is out of range, ** return a NULL pointer. */ const char *sqlite3_compileoption_get(int N){ int nOpt; const char **azCompileOpt; azCompileOpt = sqlite3CompileOptions(&nOpt); if( N>=0 && N<nOpt ){ return azCompileOpt[N]; } return 0; } #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ |
Changes to src/malloc.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 | ** is a no-op returning zero if SQLite is not compiled with ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */ UNUSED_PARAMETER(n); return 0; #endif } | < < < < < < < < < < < < < < < < < < | | 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 | ** is a no-op returning zero if SQLite is not compiled with ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */ UNUSED_PARAMETER(n); return 0; #endif } /* ** State information local to the memory allocation subsystem. */ static SQLITE_WSD struct Mem0Global { sqlite3_mutex *mutex; /* Mutex to serialize access */ sqlite3_int64 alarmThreshold; /* The soft heap limit */ /* ** True if heap is nearly "full" where "full" is defined by the ** sqlite3_soft_heap_limit() setting. */ int nearlyFull; } mem0 = { 0, 0, 0 }; #define mem0 GLOBAL(struct Mem0Global, mem0) /* ** Return the memory allocator mutex. sqlite3_status() needs it. */ sqlite3_mutex *sqlite3MallocMutex(void){ |
︙ | ︙ | |||
128 129 130 131 132 133 134 | int sqlite3MallocInit(void){ int rc; if( sqlite3GlobalConfig.m.xMalloc==0 ){ sqlite3MemSetDefault(); } memset(&mem0, 0, sizeof(mem0)); mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); | < < < < < < < < < < < < < < < < < < < < < < | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | int sqlite3MallocInit(void){ int rc; if( sqlite3GlobalConfig.m.xMalloc==0 ){ sqlite3MemSetDefault(); } memset(&mem0, 0, sizeof(mem0)); mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 || sqlite3GlobalConfig.nPage<=0 ){ sqlite3GlobalConfig.pPage = 0; sqlite3GlobalConfig.szPage = 0; } rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); |
︙ | ︙ | |||
213 214 215 216 217 218 219 | sqlite3_mutex_enter(mem0.mutex); } /* ** Do a memory allocation with statistics and alarms. Assume the ** lock is already held. */ | | < > > > > > > > > > > > > > > > > | 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 | 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; assert( sqlite3_mutex_held(mem0.mutex) ); assert( n>0 ); /* In Firefox (circa 2017-02-08), xRoundup() is remapped to an internal ** implementation of malloc_good_size(), which must be called in debug ** mode and specifically when the DMD "Dark Matter Detector" is enabled ** or else a crash results. Hence, do not attempt to optimize out the ** following xRoundup() call. */ nFull = sqlite3GlobalConfig.m.xRoundup(n); #ifdef SQLITE_MAX_MEMORY if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nFull>SQLITE_MAX_MEMORY ){ *pp = 0; return; } #endif sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmThreshold>0 ){ sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.alarmThreshold - nFull ){ mem0.nearlyFull = 1; sqlite3MallocAlarm(nFull); }else{ |
︙ | ︙ | |||
241 242 243 244 245 246 247 | #endif if( p ){ nFull = sqlite3MallocSize(p); sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull); sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); } *pp = p; | < | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | #endif if( p ){ nFull = sqlite3MallocSize(p); sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull); sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); } *pp = p; } /* ** Allocate memory. This routine is like sqlite3_malloc() except that it ** assumes the memory subsystem has already been initialized. */ void *sqlite3Malloc(u64 n){ |
︙ | ︙ | |||
286 287 288 289 290 291 292 | void *sqlite3_malloc64(sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif return sqlite3Malloc(n); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | void *sqlite3_malloc64(sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif return sqlite3Malloc(n); } /* ** TRUE if p is a lookaside memory allocation from db */ #ifndef SQLITE_OMIT_LOOKASIDE static int isLookaside(sqlite3 *db, void *p){ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); } |
︙ | ︙ | |||
407 408 409 410 411 412 413 | int sqlite3MallocSize(void *p){ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return sqlite3GlobalConfig.m.xSize(p); } int sqlite3DbMallocSize(sqlite3 *db, void *p){ assert( p!=0 ); if( db==0 || !isLookaside(db,p) ){ | | | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | int sqlite3MallocSize(void *p){ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return sqlite3GlobalConfig.m.xSize(p); } int sqlite3DbMallocSize(sqlite3 *db, void *p){ assert( p!=0 ); if( db==0 || !isLookaside(db,p) ){ #ifdef SQLITE_DEBUG if( db==0 ){ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); }else{ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); } |
︙ | ︙ | |||
456 457 458 459 460 461 462 | */ static SQLITE_NOINLINE void measureAllocationSize(sqlite3 *db, void *p){ *db->pnBytesFreed += sqlite3DbMallocSize(db,p); } /* ** Free memory that might be associated with a particular database | | > | | | < > > > > | 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 | */ static SQLITE_NOINLINE void measureAllocationSize(sqlite3 *db, void *p){ *db->pnBytesFreed += sqlite3DbMallocSize(db,p); } /* ** Free memory that might be associated with a particular database ** connection. Calling sqlite3DbFree(D,X) for X==0 is a harmless no-op. ** The sqlite3DbFreeNN(D,X) version requires that X be non-NULL. */ void sqlite3DbFreeNN(sqlite3 *db, void *p){ assert( db==0 || sqlite3_mutex_held(db->mutex) ); assert( p!=0 ); if( db ){ if( db->pnBytesFreed ){ measureAllocationSize(db, p); return; } if( isLookaside(db, p) ){ LookasideSlot *pBuf = (LookasideSlot*)p; #ifdef SQLITE_DEBUG /* Trash all content in the buffer being freed */ memset(p, 0xaa, db->lookaside.sz); #endif pBuf->pNext = db->lookaside.pFree; db->lookaside.pFree = pBuf; return; } } assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); sqlite3_free(p); } void sqlite3DbFree(sqlite3 *db, void *p){ assert( db==0 || sqlite3_mutex_held(db->mutex) ); if( p ) sqlite3DbFreeNN(db, p); } /* ** Change the size of an existing memory allocation */ void *sqlite3Realloc(void *pOld, u64 nBytes){ int nOld, nNew, nDiff; |
︙ | ︙ | |||
515 516 517 518 519 520 521 | nNew = sqlite3GlobalConfig.m.xRoundup((int)nBytes); if( nOld==nNew ){ pNew = pOld; }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); nDiff = nNew - nOld; | | | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | nNew = sqlite3GlobalConfig.m.xRoundup((int)nBytes); if( nOld==nNew ){ pNew = pOld; }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); nDiff = nNew - nOld; if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= mem0.alarmThreshold-nDiff ){ sqlite3MallocAlarm(nDiff); } pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); if( pNew==0 && mem0.alarmThreshold>0 ){ sqlite3MallocAlarm((int)nBytes); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
︙ | ︙ | |||
631 632 633 634 635 636 637 | assert( db!=0 ); assert( sqlite3_mutex_held(db->mutex) ); assert( db->pnBytesFreed==0 ); if( db->lookaside.bDisable==0 ){ assert( db->mallocFailed==0 ); if( n>db->lookaside.sz ){ db->lookaside.anStat[1]++; | | < < | > | | | < > > | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 | assert( db!=0 ); assert( sqlite3_mutex_held(db->mutex) ); assert( db->pnBytesFreed==0 ); if( db->lookaside.bDisable==0 ){ assert( db->mallocFailed==0 ); if( n>db->lookaside.sz ){ db->lookaside.anStat[1]++; }else if( (pBuf = db->lookaside.pFree)!=0 ){ db->lookaside.pFree = pBuf->pNext; db->lookaside.anStat[0]++; return (void*)pBuf; }else if( (pBuf = db->lookaside.pInit)!=0 ){ db->lookaside.pInit = pBuf->pNext; db->lookaside.anStat[0]++; return (void*)pBuf; }else{ db->lookaside.anStat[2]++; } }else if( db->mallocFailed ){ return 0; } #else assert( db!=0 ); assert( sqlite3_mutex_held(db->mutex) ); |
︙ | ︙ |
Changes to src/mem1.c.
︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /* ** Use the zone allocator available on apple products unless the ** SQLITE_WITHOUT_ZONEMALLOC symbol is defined. */ #include <sys/sysctl.h> #include <malloc/malloc.h> #include <libkern/OSAtomic.h> static malloc_zone_t* _sqliteZone_; #define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x)) #define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x)); #define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y)) #define SQLITE_MALLOCSIZE(x) \ (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x)) | > > | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | /* ** Use the zone allocator available on apple products unless the ** SQLITE_WITHOUT_ZONEMALLOC symbol is defined. */ #include <sys/sysctl.h> #include <malloc/malloc.h> #ifdef SQLITE_MIGHT_BE_SINGLE_CORE #include <libkern/OSAtomic.h> #endif /* SQLITE_MIGHT_BE_SINGLE_CORE */ static malloc_zone_t* _sqliteZone_; #define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x)) #define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x)); #define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y)) #define SQLITE_MALLOCSIZE(x) \ (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x)) |
︙ | ︙ | |||
121 122 123 124 125 126 127 | ** ** For this low-level routine, we are guaranteed that nByte>0 because ** cases of nByte<=0 will be intercepted and dealt with by higher level ** routines. */ static void *sqlite3MemMalloc(int nByte){ #ifdef SQLITE_MALLOCSIZE | > > | | | 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 | ** ** For this low-level routine, we are guaranteed that nByte>0 because ** cases of nByte<=0 will be intercepted and dealt with by higher level ** routines. */ static void *sqlite3MemMalloc(int nByte){ #ifdef SQLITE_MALLOCSIZE void *p; testcase( ROUND8(nByte)==nByte ); p = SQLITE_MALLOC( nByte ); if( p==0 ){ testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); } return p; #else sqlite3_int64 *p; assert( nByte>0 ); testcase( ROUND8(nByte)!=nByte ); p = SQLITE_MALLOC( nByte+8 ); if( p ){ p[0] = nByte; p++; }else{ testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); |
︙ | ︙ | |||
244 245 246 247 248 249 250 | sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); if( cpuCount>1 ){ /* defer MT decisions to system malloc */ _sqliteZone_ = malloc_default_zone(); }else{ /* only 1 core, use our own zone to contention over global locks, ** e.g. we have our own dedicated locks */ | < | | < < < < < < < | < | | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); if( cpuCount>1 ){ /* defer MT decisions to system malloc */ _sqliteZone_ = malloc_default_zone(); }else{ /* only 1 core, use our own zone to contention over global locks, ** e.g. we have our own dedicated locks */ _sqliteZone_ = malloc_create_zone(4096, 0); malloc_set_zone_name(_sqliteZone_, "Sqlite_Heap"); } #endif /* defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) */ UNUSED_PARAMETER(NotUsed); return SQLITE_OK; } /* ** Deinitialize this module. */ |
︙ | ︙ |
Changes to src/memjournal.c.
︙ | ︙ | |||
92 93 94 95 96 97 98 | ){ MemJournal *p = (MemJournal *)pJfd; u8 *zOut = zBuf; int nRead = iAmt; int iChunkOffset; FileChunk *pChunk; | | > | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | ){ MemJournal *p = (MemJournal *)pJfd; u8 *zOut = zBuf; int nRead = iAmt; int iChunkOffset; FileChunk *pChunk; #if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) if( (iAmt+iOfst)>p->endpoint.iOffset ){ return SQLITE_IOERR_SHORT_READ; } #endif assert( (iAmt+iOfst)<=p->endpoint.iOffset ); assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 ); |
︙ | ︙ | |||
211 212 213 214 215 216 217 | else{ /* An in-memory journal file should only ever be appended to. Random ** access writes are not required. The only exception to this is when ** the in-memory journal is being used by a connection using the ** atomic-write optimization. In this case the first 28 bytes of the ** journal file may be written as part of committing the transaction. */ assert( iOfst==p->endpoint.iOffset || iOfst==0 ); | | > | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | else{ /* An in-memory journal file should only ever be appended to. Random ** access writes are not required. The only exception to this is when ** the in-memory journal is being used by a connection using the ** atomic-write optimization. In this case the first 28 bytes of the ** journal file may be written as part of committing the transaction. */ assert( iOfst==p->endpoint.iOffset || iOfst==0 ); #if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) if( iOfst==0 && p->pFirst ){ assert( p->nChunkSize>iAmt ); memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt); }else #else assert( iOfst>0 || p->pFirst==0 ); #endif |
︙ | ︙ | |||
380 381 382 383 384 385 386 | /* ** Open an in-memory journal file. */ void sqlite3MemJournalOpen(sqlite3_file *pJfd){ sqlite3JournalOpen(0, 0, pJfd, 0, -1); } | | > | | | > | > > > > > > > > > > > > | | 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 | /* ** Open an in-memory journal file. */ void sqlite3MemJournalOpen(sqlite3_file *pJfd){ sqlite3JournalOpen(0, 0, pJfd, 0, -1); } #if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) /* ** If the argument p points to a MemJournal structure that is not an ** in-memory-only journal file (i.e. is one that was opened with a +ve ** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying ** file has not yet been created, create it now. */ int sqlite3JournalCreate(sqlite3_file *pJfd){ int rc = SQLITE_OK; MemJournal *p = (MemJournal*)pJfd; if( p->pMethod==&MemJournalMethods && ( #ifdef SQLITE_ENABLE_ATOMIC_WRITE p->nSpill>0 #else /* While this appears to not be possible without ATOMIC_WRITE, the ** paths are complex, so it seems prudent to leave the test in as ** a NEVER(), in case our analysis is subtly flawed. */ NEVER(p->nSpill>0) #endif #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE || (p->flags & SQLITE_OPEN_MAIN_JOURNAL) #endif )){ rc = memjrnlCreateFile(p); } return rc; } #endif /* ** The file-handle passed as the only argument is open on a journal file. |
︙ | ︙ |
Changes to src/mutex_w32.c.
︙ | ︙ | |||
83 84 85 86 87 88 89 | ** compiled without mutexes (SQLITE_THREADSAFE=0). */ void sqlite3MemoryBarrier(void){ #if defined(SQLITE_MEMORY_BARRIER) SQLITE_MEMORY_BARRIER; #elif defined(__GNUC__) __sync_synchronize(); | | < | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | ** compiled without mutexes (SQLITE_THREADSAFE=0). */ void sqlite3MemoryBarrier(void){ #if defined(SQLITE_MEMORY_BARRIER) SQLITE_MEMORY_BARRIER; #elif defined(__GNUC__) __sync_synchronize(); #elif MSVC_VERSION>=1300 _ReadWriteBarrier(); #elif defined(MemoryBarrier) MemoryBarrier(); #endif } /* |
︙ | ︙ | |||
295 296 297 298 299 300 301 | assert( winMutex_isInit==1 ); EnterCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG assert( p->nRef>0 || p->owner==0 ); p->owner = tid; p->nRef++; if( p->trace ){ | | | | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | assert( winMutex_isInit==1 ); EnterCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG assert( p->nRef>0 || p->owner==0 ); p->owner = tid; p->nRef++; if( p->trace ){ OSTRACE(("ENTER-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", tid, p->id, p, p->trace, p->nRef)); } #endif } static int winMutexTry(sqlite3_mutex *p){ #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) DWORD tid = GetCurrentThreadId(); |
︙ | ︙ | |||
338 339 340 341 342 343 344 | rc = SQLITE_OK; } #else UNUSED_PARAMETER(p); #endif #ifdef SQLITE_DEBUG if( p->trace ){ | | | | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | rc = SQLITE_OK; } #else UNUSED_PARAMETER(p); #endif #ifdef SQLITE_DEBUG if( p->trace ){ OSTRACE(("TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n", tid, p->id, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc))); } #endif return rc; } /* ** The sqlite3_mutex_leave() routine exits a mutex that was |
︙ | ︙ | |||
367 368 369 370 371 372 373 | if( p->nRef==0 ) p->owner = 0; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #endif assert( winMutex_isInit==1 ); LeaveCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG if( p->trace ){ | | | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | if( p->nRef==0 ) p->owner = 0; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #endif assert( winMutex_isInit==1 ); LeaveCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG if( p->trace ){ OSTRACE(("LEAVE-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", tid, p->id, p, p->trace, p->nRef)); } #endif } sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ static const sqlite3_mutex_methods sMutex = { winMutexInit, |
︙ | ︙ |
Changes to src/os.c.
︙ | ︙ | |||
94 95 96 97 98 99 100 | return id->pMethods->xWrite(id, pBuf, amt, offset); } int sqlite3OsTruncate(sqlite3_file *id, i64 size){ return id->pMethods->xTruncate(id, size); } int sqlite3OsSync(sqlite3_file *id, int flags){ DO_OS_MALLOC_TEST(id); | | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | return id->pMethods->xWrite(id, pBuf, amt, offset); } int sqlite3OsTruncate(sqlite3_file *id, i64 size){ return id->pMethods->xTruncate(id, size); } int sqlite3OsSync(sqlite3_file *id, int flags){ DO_OS_MALLOC_TEST(id); return flags ? id->pMethods->xSync(id, flags) : SQLITE_OK; } int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){ DO_OS_MALLOC_TEST(id); return id->pMethods->xFileSize(id, pSize); } int sqlite3OsLock(sqlite3_file *id, int lockType){ DO_OS_MALLOC_TEST(id); |
︙ | ︙ |
Changes to src/os_unix.c.
︙ | ︙ | |||
86 87 88 89 90 91 92 93 94 95 96 97 98 99 | /* ** standard include files. */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <sys/time.h> #include <errno.h> #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 # include <sys/mman.h> #endif | > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | /* ** standard include files. */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> #include <time.h> #include <sys/time.h> #include <errno.h> #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 # include <sys/mman.h> #endif |
︙ | ︙ | |||
205 206 207 208 209 210 211 | sqlite3_vfs *pVfs; /* The VFS that created this unixFile */ unixInodeInfo *pInode; /* Info about locks on this inode */ int h; /* The file descriptor */ unsigned char eFileLock; /* The type of lock held on this fd */ unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ | | < < | 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 | sqlite3_vfs *pVfs; /* The VFS that created this unixFile */ unixInodeInfo *pInode; /* Info about locks on this inode */ int h; /* The file descriptor */ unsigned char eFileLock; /* The type of lock held on this fd */ unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pPreallocatedUnused; /* Pre-allocated UnixUnusedFd */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ #if SQLITE_MAX_MMAP_SIZE>0 int nFetchOut; /* Number of outstanding xFetch refs */ sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ void *pMapRegion; /* Memory mapped region */ #endif int sectorSize; /* Device sector size */ int deviceCharacteristics; /* Precomputed device characteristics */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif #if OS_VXWORKS |
︙ | ︙ | |||
323 324 325 326 327 328 329 330 331 332 333 334 335 336 | /* ** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek() ** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined. */ #ifdef __ANDROID__ # define lseek lseek64 #endif /* ** Different Unix systems declare open() in different ways. Same use ** open(const char*,int,mode_t). Others use open(const char*,int,...). ** The difference is important when using a pointer to the function. ** ** The safest way to deal with the problem is to always use this wrapper | > > > > > > > > > > > > > > | 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 | /* ** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek() ** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined. */ #ifdef __ANDROID__ # define lseek lseek64 #endif #ifdef __linux__ /* ** Linux-specific IOCTL magic numbers used for controlling F2FS */ #define F2FS_IOCTL_MAGIC 0xf5 #define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1) #define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2) #define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3) #define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) #define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, u32) #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 #endif /* __linux__ */ /* ** Different Unix systems declare open() in different ways. Same use ** open(const char*,int,mode_t). Others use open(const char*,int,...). ** The difference is important when using a pointer to the function. ** ** The safest way to deal with the problem is to always use this wrapper |
︙ | ︙ | |||
495 496 497 498 499 500 501 502 503 504 505 506 507 508 | #if defined(HAVE_LSTAT) { "lstat", (sqlite3_syscall_ptr)lstat, 0 }, #else { "lstat", (sqlite3_syscall_ptr)0, 0 }, #endif #define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent) }; /* End of the overrideable system calls */ /* ** On some systems, calls to fchown() will trigger a message in a security ** log if they come from non-root processes. So avoid calling fchown() if | > > > | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 | #if defined(HAVE_LSTAT) { "lstat", (sqlite3_syscall_ptr)lstat, 0 }, #else { "lstat", (sqlite3_syscall_ptr)0, 0 }, #endif #define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent) { "ioctl", (sqlite3_syscall_ptr)ioctl, 0 }, #define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent) }; /* End of the overrideable system calls */ /* ** On some systems, calls to fchown() will trigger a message in a security ** log if they come from non-root processes. So avoid calling fchown() if |
︙ | ︙ | |||
1057 1058 1059 1060 1061 1062 1063 | ** to locate a particular unixInodeInfo object. */ struct unixFileId { dev_t dev; /* Device number */ #if OS_VXWORKS struct vxworksFileId *pId; /* Unique file ID for vxworks. */ #else | > > > > > > > | | 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 | ** to locate a particular unixInodeInfo object. */ struct unixFileId { dev_t dev; /* Device number */ #if OS_VXWORKS struct vxworksFileId *pId; /* Unique file ID for vxworks. */ #else /* We are told that some versions of Android contain a bug that ** sizes ino_t at only 32-bits instead of 64-bits. (See ** https://android-review.googlesource.com/#/c/115351/3/dist/sqlite3.c) ** To work around this, always allocate 64-bits for the inode number. ** On small machines that only have 32-bit inodes, this wastes 4 bytes, ** but that should not be a big deal. */ /* WAS: ino_t ino; */ u64 ino; /* Inode number */ #endif }; /* ** An instance of the following structure is allocated for each open ** inode. Or, on LinuxThreads, there is one of these structures for ** each inode opened by each thread. |
︙ | ︙ | |||
1093 1094 1095 1096 1097 1098 1099 | char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ #endif }; /* ** A lists of all unixInodeInfo objects. */ | | > | 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 | char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ #endif }; /* ** A lists of all unixInodeInfo objects. */ static unixInodeInfo *inodeList = 0; /* All unixInodeInfo objects */ static unsigned int nUnusedFd = 0; /* Total unused file descriptors */ /* ** ** This function - unixLogErrorAtLine(), is only ever called via the macro ** unixLogError(). ** ** It is invoked after an error occurs in an OS function and errno has been |
︙ | ︙ | |||
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 | unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *p; UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; robust_close(pFile, p->fd, __LINE__); sqlite3_free(p); } pInode->pUnused = 0; } /* ** Release a unixInodeInfo structure previously allocated by findInodeInfo(). ** | > | 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 | unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *p; UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; robust_close(pFile, p->fd, __LINE__); sqlite3_free(p); nUnusedFd--; } pInode->pUnused = 0; } /* ** Release a unixInodeInfo structure previously allocated by findInodeInfo(). ** |
︙ | ︙ | |||
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 | if( pInode->pNext ){ assert( pInode->pNext->pPrev==pInode ); pInode->pNext->pPrev = pInode->pPrev; } sqlite3_free(pInode); } } } /* ** Given a file descriptor, locate the unixInodeInfo object that ** describes that file descriptor. Create a new one if necessary. The ** return value might be uninitialized if an error occurs. ** | > | 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 | if( pInode->pNext ){ assert( pInode->pNext->pPrev==pInode ); pInode->pNext->pPrev = pInode->pPrev; } sqlite3_free(pInode); } } assert( inodeList!=0 || nUnusedFd==0 ); } /* ** Given a file descriptor, locate the unixInodeInfo object that ** describes that file descriptor. Create a new one if necessary. The ** return value might be uninitialized if an error occurs. ** |
︙ | ︙ | |||
1302 1303 1304 1305 1306 1307 1308 | #endif memset(&fileId, 0, sizeof(fileId)); fileId.dev = statbuf.st_dev; #if OS_VXWORKS fileId.pId = pFile->pId; #else | | > | 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 | #endif memset(&fileId, 0, sizeof(fileId)); fileId.dev = statbuf.st_dev; #if OS_VXWORKS fileId.pId = pFile->pId; #else fileId.ino = (u64)statbuf.st_ino; #endif assert( inodeList!=0 || nUnusedFd==0 ); pInode = inodeList; while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){ pInode = pInode->pNext; } if( pInode==0 ){ pInode = sqlite3_malloc64( sizeof(*pInode) ); if( pInode==0 ){ |
︙ | ︙ | |||
1336 1337 1338 1339 1340 1341 1342 | */ static int fileHasMoved(unixFile *pFile){ #if OS_VXWORKS return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId; #else struct stat buf; return pFile->pInode!=0 && | | > | 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 | */ static int fileHasMoved(unixFile *pFile){ #if OS_VXWORKS return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId; #else struct stat buf; return pFile->pInode!=0 && (osStat(pFile->zPath, &buf)!=0 || (u64)buf.st_ino!=pFile->pInode->fileId.ino); #endif } /* ** Check a unixFile that is a database. Verify the following: ** |
︙ | ︙ | |||
1722 1723 1724 1725 1726 1727 1728 | /* ** Add the file descriptor used by file handle pFile to the corresponding ** pUnused list. */ static void setPendingFd(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; | | | > | 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 | /* ** Add the file descriptor used by file handle pFile to the corresponding ** pUnused list. */ static void setPendingFd(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *p = pFile->pPreallocatedUnused; p->pNext = pInode->pUnused; pInode->pUnused = p; pFile->h = -1; pFile->pPreallocatedUnused = 0; nUnusedFd++; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below |
︙ | ︙ | |||
1951 1952 1953 1954 1955 1956 1957 | osUnlink(pFile->zPath); sqlite3_free(*(char**)&pFile->zPath); pFile->zPath = 0; } #endif OSTRACE(("CLOSE %-3d\n", pFile->h)); OpenCounter(-1); | | | 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 | osUnlink(pFile->zPath); sqlite3_free(*(char**)&pFile->zPath); pFile->zPath = 0; } #endif OSTRACE(("CLOSE %-3d\n", pFile->h)); OpenCounter(-1); sqlite3_free(pFile->pPreallocatedUnused); memset(pFile, 0, sizeof(unixFile)); return SQLITE_OK; } /* ** Close a file. */ |
︙ | ︙ | |||
2288 2289 2290 2291 2292 2293 2294 | rc = lrc; } } } OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS | | | 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 | rc = lrc; } } } OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS if( (rc & 0xff) == SQLITE_IOERR ){ rc = SQLITE_OK; reserved=1; } #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ *pResOut = reserved; return rc; } |
︙ | ︙ | |||
2355 2356 2357 2358 2359 2360 2361 | } else { /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; } OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock), rc==SQLITE_OK ? "ok" : "failed")); #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS | | | 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 | } else { /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; } OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock), rc==SQLITE_OK ? "ok" : "failed")); #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS if( (rc & 0xff) == SQLITE_IOERR ){ rc = SQLITE_BUSY; } #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ return rc; } |
︙ | ︙ | |||
2892 2893 2894 2895 2896 2897 2898 | failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 1); if( failed && (failed2 = afpSetLock(context->dbPath, pFile, SHARED_FIRST + pInode->sharedByte, 1, 1)) ){ /* Can't reestablish the shared lock. Sqlite can't deal, this is ** a critical I/O error */ | | | 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 | failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 1); if( failed && (failed2 = afpSetLock(context->dbPath, pFile, SHARED_FIRST + pInode->sharedByte, 1, 1)) ){ /* Can't reestablish the shared lock. Sqlite can't deal, this is ** a critical I/O error */ rc = ((failed & 0xff) == SQLITE_IOERR) ? failed2 : SQLITE_IOERR_LOCK; goto afp_end_lock; } }else{ rc = failed; } } |
︙ | ︙ | |||
3172 3173 3174 3175 3176 3177 3178 | assert( id ); assert( offset>=0 ); assert( amt>0 ); /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ #if 0 | | | 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 | assert( id ); assert( offset>=0 ); assert( amt>0 ); /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ #if 0 assert( pFile->pPreallocatedUnused==0 || offset>=PENDING_BYTE+512 || offset+amt<=PENDING_BYTE ); #endif #if SQLITE_MAX_MMAP_SIZE>0 /* Deal with as much of this read request as possible by transfering |
︙ | ︙ | |||
3285 3286 3287 3288 3289 3290 3291 | int wrote = 0; assert( id ); assert( amt>0 ); /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ #if 0 | | | 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 | int wrote = 0; assert( id ); assert( amt>0 ); /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ #if 0 assert( pFile->pPreallocatedUnused==0 || offset>=PENDING_BYTE+512 || offset+amt<=PENDING_BYTE ); #endif #ifdef SQLITE_DEBUG /* If we are doing a normal write to a database file (as opposed to |
︙ | ︙ | |||
3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 | /* ** Information and control of an open file handle. */ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ unixFile *pFile = (unixFile*)id; switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = pFile->eFileLock; return SQLITE_OK; } case SQLITE_FCNTL_LAST_ERRNO: { *(int*)pArg = pFile->lastErrno; return SQLITE_OK; | > > > > > > > > > > > > > > > | 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 | /* ** Information and control of an open file handle. */ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ unixFile *pFile = (unixFile*)id; switch( op ){ #if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: { int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE); return rc ? SQLITE_IOERR_BEGIN_ATOMIC : SQLITE_OK; } case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: { int rc = osIoctl(pFile->h, F2FS_IOC_COMMIT_ATOMIC_WRITE); return rc ? SQLITE_IOERR_COMMIT_ATOMIC : SQLITE_OK; } case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: { int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE); return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK; } #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = pFile->eFileLock; return SQLITE_OK; } case SQLITE_FCNTL_LAST_ERRNO: { *(int*)pArg = pFile->lastErrno; return SQLITE_OK; |
︙ | ︙ | |||
3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 | #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; int rc = SQLITE_OK; if( newLimit>sqlite3GlobalConfig.mxMmap ){ newLimit = sqlite3GlobalConfig.mxMmap; } *(i64*)pArg = pFile->mmapSizeMax; if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ pFile->mmapSizeMax = newLimit; if( pFile->mmapSize>0 ){ unixUnmapfile(pFile); rc = unixMapfile(pFile, -1); } | > > > > > > > > | 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 | #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; int rc = SQLITE_OK; if( newLimit>sqlite3GlobalConfig.mxMmap ){ newLimit = sqlite3GlobalConfig.mxMmap; } /* The value of newLimit may be eventually cast to (size_t) and passed ** to mmap(). Restrict its value to 2GB if (size_t) is not at least a ** 64-bit type. */ if( newLimit>0 && sizeof(size_t)<8 ){ newLimit = (newLimit & 0x7FFFFFFF); } *(i64*)pArg = pFile->mmapSizeMax; if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ pFile->mmapSizeMax = newLimit; if( pFile->mmapSize>0 ){ unixUnmapfile(pFile); rc = unixMapfile(pFile, -1); } |
︙ | ︙ | |||
3848 3849 3850 3851 3852 3853 3854 | } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ } return SQLITE_NOTFOUND; } /* | | | > | | < < | | > > | > > > | > > > | | > | > > > | | | < < > > > < | | 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 | } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ } return SQLITE_NOTFOUND; } /* ** If pFd->sectorSize is non-zero when this function is called, it is a ** no-op. Otherwise, the values of pFd->sectorSize and ** pFd->deviceCharacteristics are set according to the file-system ** characteristics. ** ** There are two versions of this function. One for QNX and one for all ** other systems. */ #ifndef __QNXNTO__ static void setDeviceCharacteristics(unixFile *pFd){ assert( pFd->deviceCharacteristics==0 || pFd->sectorSize!=0 ); if( pFd->sectorSize==0 ){ #if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) int res; u32 f = 0; /* Check for support for F2FS atomic batch writes. */ res = osIoctl(pFd->h, F2FS_IOC_GET_FEATURES, &f); if( res==0 && (f & F2FS_FEATURE_ATOMIC_WRITE) ){ pFd->deviceCharacteristics = SQLITE_IOCAP_BATCH_ATOMIC; } #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ /* Set the POWERSAFE_OVERWRITE flag if requested. */ if( pFd->ctrlFlags & UNIXFILE_PSOW ){ pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; } pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; } } #else #include <sys/dcmd_blk.h> #include <sys/statvfs.h> static void setDeviceCharacteristics(unixFile *pFile){ if( pFile->sectorSize == 0 ){ struct statvfs fsInfo; /* Set defaults for non-supported filesystems */ pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; pFile->deviceCharacteristics = 0; if( fstatvfs(pFile->h, &fsInfo) == -1 ) { |
︙ | ︙ | |||
3940 3941 3942 3943 3944 3945 3946 | } /* Last chance verification. If the sector size isn't a multiple of 512 ** then it isn't valid.*/ if( pFile->sectorSize % 512 != 0 ){ pFile->deviceCharacteristics = 0; pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; } | < | > > > > > > > > > > > > > > > > | < < < | < < < < | | 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 | } /* Last chance verification. If the sector size isn't a multiple of 512 ** then it isn't valid.*/ if( pFile->sectorSize % 512 != 0 ){ pFile->deviceCharacteristics = 0; pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; } } #endif /* ** Return the sector size in bytes of the underlying block device for ** the specified file. This is almost always 512 bytes, but may be ** larger for some devices. ** ** SQLite code assumes this function cannot fail. It also assumes that ** if two files are created in the same file-system directory (i.e. ** a database and its journal file) that the sector size will be the ** same for both. */ static int unixSectorSize(sqlite3_file *id){ unixFile *pFd = (unixFile*)id; setDeviceCharacteristics(pFd); return pFd->sectorSize; } /* ** Return the device characteristics for the file. ** ** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default. ** However, that choice is controversial since technically the underlying ** file system does not always provide powersafe overwrites. (In other ** words, after a power-loss event, parts of the file that were never ** written might end up being altered.) However, non-PSOW behavior is very, ** very rare. And asserting PSOW makes a large reduction in the amount ** of required I/O for journaling, since a lot of padding is eliminated. ** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control ** available to turn it off and URI query parameter available to turn it off. */ static int unixDeviceCharacteristics(sqlite3_file *id){ unixFile *pFd = (unixFile*)id; setDeviceCharacteristics(pFd); return pFd->deviceCharacteristics; } #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 /* ** Return the system page size. ** |
︙ | ︙ | |||
5225 5226 5227 5228 5229 5230 5231 | ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; int rc = SQLITE_OK; assert( pNew->pInode==NULL ); | < < < < < < < < < < < | 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 | ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; int rc = SQLITE_OK; assert( pNew->pInode==NULL ); /* No locking occurs in temporary files */ assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 ); OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->pVfs = pVfs; pNew->zPath = zFilename; |
︙ | ︙ | |||
5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 | /* Do not search for an unused file descriptor on vxworks. Not because ** vxworks would not benefit from the change (it might, we're not sure), ** but because no way to test it is currently available. It is better ** not to risk breaking vxworks support for the sake of such an obscure ** feature. */ #if !OS_VXWORKS struct stat sStat; /* Results of stat() call */ /* A stat() call may fail for various reasons. If this happens, it is ** almost certain that an open() call on the same path will also fail. ** For this reason, if an error occurs in the stat() call here, it is ** ignored and -1 is returned. The caller will try to open a new file ** descriptor on the same path, fail, and return an error to SQLite. ** ** Even if a subsequent open() call does succeed, the consequences of ** not searching for a reusable file descriptor are not dire. */ | > > | < | > < > | 5553 5554 5555 5556 5557 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 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 | /* Do not search for an unused file descriptor on vxworks. Not because ** vxworks would not benefit from the change (it might, we're not sure), ** but because no way to test it is currently available. It is better ** not to risk breaking vxworks support for the sake of such an obscure ** feature. */ #if !OS_VXWORKS struct stat sStat; /* Results of stat() call */ unixEnterMutex(); /* A stat() call may fail for various reasons. If this happens, it is ** almost certain that an open() call on the same path will also fail. ** For this reason, if an error occurs in the stat() call here, it is ** ignored and -1 is returned. The caller will try to open a new file ** descriptor on the same path, fail, and return an error to SQLite. ** ** Even if a subsequent open() call does succeed, the consequences of ** not searching for a reusable file descriptor are not dire. */ if( nUnusedFd>0 && 0==osStat(zPath, &sStat) ){ unixInodeInfo *pInode; pInode = inodeList; while( pInode && (pInode->fileId.dev!=sStat.st_dev || pInode->fileId.ino!=(u64)sStat.st_ino) ){ pInode = pInode->pNext; } if( pInode ){ UnixUnusedFd **pp; for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); pUnused = *pp; if( pUnused ){ nUnusedFd--; *pp = pUnused->pNext; } } } unixLeaveMutex(); #endif /* if !OS_VXWORKS */ return pUnused; } /* ** Find the mode, uid and gid of file zFile. */ |
︙ | ︙ | |||
5596 5597 5598 5599 5600 5601 5602 | ** "<path to db>-walNN" ** ** where NN is a decimal number. The NN naming schemes are ** used by the test_multiplex.c module. */ nDb = sqlite3Strlen30(zPath) - 1; while( zPath[nDb]!='-' ){ | < | | < < < | < > < | 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 | ** "<path to db>-walNN" ** ** where NN is a decimal number. The NN naming schemes are ** used by the test_multiplex.c module. */ nDb = sqlite3Strlen30(zPath) - 1; while( zPath[nDb]!='-' ){ /* In normal operation, the journal file name will always contain ** a '-' character. However in 8+3 filename mode, or if a corrupt ** rollback journal specifies a master journal with a goofy name, then ** the '-' might be missing. */ if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK; nDb--; } memcpy(zDb, zPath, nDb); zDb[nDb] = '\0'; rc = getFileMode(zDb, pMode, pUid, pGid); }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ |
︙ | ︙ | |||
5741 5742 5743 5744 5745 5746 5747 | fd = pUnused->fd; }else{ pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM_BKPT; } } | | | 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 | fd = pUnused->fd; }else{ pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM_BKPT; } } p->pPreallocatedUnused = pUnused; /* Database filenames are double-zero terminated if they are not ** URIs with parameters. Hence, they can always be passed into ** sqlite3_uri_parameter(). */ assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 ); }else if( !zName ){ |
︙ | ︙ | |||
5778 5779 5780 5781 5782 5783 5784 | if( fd<0 ){ mode_t openMode; /* Permissions to create file with */ uid_t uid; /* Userid for the file */ gid_t gid; /* Groupid for the file */ rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid); if( rc!=SQLITE_OK ){ | | | 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 | if( fd<0 ){ mode_t openMode; /* Permissions to create file with */ uid_t uid; /* Userid for the file */ gid_t gid; /* Groupid for the file */ rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid); if( rc!=SQLITE_OK ){ assert( !p->pPreallocatedUnused ); assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); return rc; } fd = robust_open(zName, openFlags, openMode); OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); assert( !isExclusive || (openFlags & O_CREAT)!=0 ); if( fd<0 && errno!=EISDIR && isReadWrite ){ |
︙ | ︙ | |||
5812 5813 5814 5815 5816 5817 5818 | } } assert( fd>=0 ); if( pOutFlags ){ *pOutFlags = flags; } | | | | | 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 | } } assert( fd>=0 ); if( pOutFlags ){ *pOutFlags = flags; } if( p->pPreallocatedUnused ){ p->pPreallocatedUnused->fd = fd; p->pPreallocatedUnused->flags = flags; } if( isDelete ){ #if OS_VXWORKS zPath = zName; #elif defined(SQLITE_UNLINK_AFTER_CLOSE) zPath = sqlite3_mprintf("%s", zName); |
︙ | ︙ | |||
5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 | } } goto open_finished; } } #endif rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); open_finished: if( rc!=SQLITE_OK ){ | > > > | | 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 | } } goto open_finished; } } #endif assert( zPath==0 || zPath[0]=='/' || eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); open_finished: if( rc!=SQLITE_OK ){ sqlite3_free(p->pPreallocatedUnused); } return rc; } /* ** Delete the file at zPath. If the dirSync argument is true, fsync() |
︙ | ︙ | |||
6636 6637 6638 6639 6640 6641 6642 | memset(pNew, 0, sizeof(unixFile)); pNew->openFlags = openFlags; memset(&dummyVfs, 0, sizeof(dummyVfs)); dummyVfs.pAppData = (void*)&autolockIoFinder; dummyVfs.zName = "dummy"; pUnused->fd = fd; pUnused->flags = openFlags; | | | 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 | memset(pNew, 0, sizeof(unixFile)); pNew->openFlags = openFlags; memset(&dummyVfs, 0, sizeof(dummyVfs)); dummyVfs.pAppData = (void*)&autolockIoFinder; dummyVfs.zName = "dummy"; pUnused->fd = fd; pUnused->flags = openFlags; pNew->pPreallocatedUnused = pUnused; rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0); if( rc==SQLITE_OK ){ *ppFile = pNew; return SQLITE_OK; } end_create_proxy: |
︙ | ︙ | |||
7586 7587 7588 7589 7590 7591 7592 | UNIXVFS("unix-proxy", proxyIoFinder ), #endif }; unsigned int i; /* Loop counter */ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ | | | 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 | UNIXVFS("unix-proxy", proxyIoFinder ), #endif }; unsigned int i; /* Loop counter */ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ assert( ArraySize(aSyscall)==29 ); /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], i==0); } return SQLITE_OK; } |
︙ | ︙ |
Changes to src/os_win.c.
︙ | ︙ | |||
348 349 350 351 352 353 354 | * winMemShutdown function is called (e.g. by the sqlite3_shutdown * function), all data that was allocated using the isolated heap will * be freed immediately and any attempt to access any of that freed * data will almost certainly result in an immediate access violation. ****************************************************************************** */ #ifndef SQLITE_WIN32_HEAP_CREATE | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > | | > | | | 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 | * winMemShutdown function is called (e.g. by the sqlite3_shutdown * function), all data that was allocated using the isolated heap will * be freed immediately and any attempt to access any of that freed * data will almost certainly result in an immediate access violation. ****************************************************************************** */ #ifndef SQLITE_WIN32_HEAP_CREATE # define SQLITE_WIN32_HEAP_CREATE (TRUE) #endif /* * This is the maximum possible initial size of the Win32-specific heap, in * bytes. */ #ifndef SQLITE_WIN32_HEAP_MAX_INIT_SIZE # define SQLITE_WIN32_HEAP_MAX_INIT_SIZE (4294967295U) #endif /* * This is the extra space for the initial size of the Win32-specific heap, * in bytes. This value may be zero. */ #ifndef SQLITE_WIN32_HEAP_INIT_EXTRA # define SQLITE_WIN32_HEAP_INIT_EXTRA (4194304) #endif /* * Calculate the maximum legal cache size, in pages, based on the maximum * possible initial heap size and the default page size, setting aside the * needed extra space. */ #ifndef SQLITE_WIN32_MAX_CACHE_SIZE # define SQLITE_WIN32_MAX_CACHE_SIZE (((SQLITE_WIN32_HEAP_MAX_INIT_SIZE) - \ (SQLITE_WIN32_HEAP_INIT_EXTRA)) / \ (SQLITE_DEFAULT_PAGE_SIZE)) #endif /* * This is cache size used in the calculation of the initial size of the * Win32-specific heap. It cannot be negative. */ #ifndef SQLITE_WIN32_CACHE_SIZE # if SQLITE_DEFAULT_CACHE_SIZE>=0 # define SQLITE_WIN32_CACHE_SIZE (SQLITE_DEFAULT_CACHE_SIZE) # else # define SQLITE_WIN32_CACHE_SIZE (-(SQLITE_DEFAULT_CACHE_SIZE)) # endif #endif /* * Make sure that the calculated cache size, in pages, cannot cause the * initial size of the Win32-specific heap to exceed the maximum amount * of memory that can be specified in the call to HeapCreate. */ #if SQLITE_WIN32_CACHE_SIZE>SQLITE_WIN32_MAX_CACHE_SIZE # undef SQLITE_WIN32_CACHE_SIZE # define SQLITE_WIN32_CACHE_SIZE (2000) #endif /* * The initial size of the Win32-specific heap. This value may be zero. */ #ifndef SQLITE_WIN32_HEAP_INIT_SIZE # define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_WIN32_CACHE_SIZE) * \ (SQLITE_DEFAULT_PAGE_SIZE) + \ (SQLITE_WIN32_HEAP_INIT_EXTRA)) #endif /* * The maximum size of the Win32-specific heap. This value may be zero. */ #ifndef SQLITE_WIN32_HEAP_MAX_SIZE # define SQLITE_WIN32_HEAP_MAX_SIZE (0) #endif /* * The extra flags to use in calls to the Win32 heap APIs. This value may be * zero for the default behavior. */ #ifndef SQLITE_WIN32_HEAP_FLAGS # define SQLITE_WIN32_HEAP_FLAGS (0) #endif /* ** The winMemData structure stores information required by the Win32-specific ** sqlite3_mem_methods implementation. */ |
︙ | ︙ | |||
3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 | #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; int rc = SQLITE_OK; if( newLimit>sqlite3GlobalConfig.mxMmap ){ newLimit = sqlite3GlobalConfig.mxMmap; } *(i64*)pArg = pFile->mmapSizeMax; if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ pFile->mmapSizeMax = newLimit; if( pFile->mmapSize>0 ){ winUnmapfile(pFile); rc = winMapfile(pFile, -1); } | > > > > > > > > | 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 | #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; int rc = SQLITE_OK; if( newLimit>sqlite3GlobalConfig.mxMmap ){ newLimit = sqlite3GlobalConfig.mxMmap; } /* The value of newLimit may be eventually cast to (SIZE_T) and passed ** to MapViewOfFile(). Restrict its value to 2GB if (SIZE_T) is not at ** least a 64-bit type. */ if( newLimit>0 && sizeof(SIZE_T)<8 ){ newLimit = (newLimit & 0x7FFFFFFF); } *(i64*)pArg = pFile->mmapSizeMax; if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ pFile->mmapSizeMax = newLimit; if( pFile->mmapSize>0 ){ winUnmapfile(pFile); rc = winMapfile(pFile, -1); } |
︙ | ︙ | |||
4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 | }else{ attr = osGetFileAttributesA((char*)zConverted); #endif } return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY); } /* ** Open a file. */ static int winOpen( sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */ const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ | > > > > > > > > | 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 | }else{ attr = osGetFileAttributesA((char*)zConverted); #endif } return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY); } /* forward reference */ static int winAccess( sqlite3_vfs *pVfs, /* Not used on win32 */ const char *zFilename, /* Name of file to check */ int flags, /* Type of test to make on this file */ int *pResOut /* OUT: Result */ ); /* ** Open a file. */ static int winOpen( sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */ const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ |
︙ | ︙ | |||
5005 5006 5007 5008 5009 5010 5011 | extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); extendedParameters.dwFileAttributes = dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK; extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK; extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; extendedParameters.lpSecurityAttributes = NULL; extendedParameters.hTemplateFile = NULL; | > | | | | | < | > > > > | > > | | | | | > | < < > > > > | > > | | | | | > | < < > > > > | > < < > > | 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 | extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); extendedParameters.dwFileAttributes = dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK; extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK; extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; extendedParameters.lpSecurityAttributes = NULL; extendedParameters.hTemplateFile = NULL; do{ h = osCreateFile2((LPCWSTR)zConverted, dwDesiredAccess, dwShareMode, dwCreationDisposition, &extendedParameters); if( h!=INVALID_HANDLE_VALUE ) break; if( isReadWrite ){ int isRO = 0; int rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); if( rc2==SQLITE_OK && isRO ) break; } }while( winRetryIoerr(&cnt, &lastErrno) ); #else do{ h = osCreateFileW((LPCWSTR)zConverted, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); if( h!=INVALID_HANDLE_VALUE ) break; if( isReadWrite ){ int isRO = 0; int rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); if( rc2==SQLITE_OK && isRO ) break; } }while( winRetryIoerr(&cnt, &lastErrno) ); #endif } #ifdef SQLITE_WIN32_HAS_ANSI else{ do{ h = osCreateFileA((LPCSTR)zConverted, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); if( h!=INVALID_HANDLE_VALUE ) break; if( isReadWrite ){ int isRO = 0; int rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); if( rc2==SQLITE_OK && isRO ) break; } }while( winRetryIoerr(&cnt, &lastErrno) ); } #endif winLogIoerr(cnt, __LINE__); OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); if( h==INVALID_HANDLE_VALUE ){ sqlite3_free(zConverted); sqlite3_free(zTmpname); if( isReadWrite && !isExclusive ){ return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY) & ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags); }else{ pFile->lastErrno = lastErrno; winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name); return SQLITE_CANTOPEN_BKPT; } } if( pOutFlags ){ if( isReadWrite ){ *pOutFlags = SQLITE_OPEN_READWRITE; |
︙ | ︙ | |||
5646 5647 5648 5649 5650 5651 5652 | UNUSED_PARAMETER(pVfs); memset(zBuf, 0, nBuf); return nBuf; #else EntropyGatherer e; UNUSED_PARAMETER(pVfs); memset(zBuf, 0, nBuf); | < < < | 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 | UNUSED_PARAMETER(pVfs); memset(zBuf, 0, nBuf); return nBuf; #else EntropyGatherer e; UNUSED_PARAMETER(pVfs); memset(zBuf, 0, nBuf); e.a = (unsigned char*)zBuf; e.na = nBuf; e.nXor = 0; e.i = 0; { SYSTEMTIME x; osGetSystemTime(&x); |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
124 125 126 127 128 129 130 | ** The following two macros are used within the PAGERTRACE() macros above ** to print out file-descriptors. ** ** PAGERID() takes a pointer to a Pager struct as its argument. The ** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file ** struct as its argument. */ | | | | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | ** The following two macros are used within the PAGERTRACE() macros above ** to print out file-descriptors. ** ** PAGERID() takes a pointer to a Pager struct as its argument. The ** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file ** struct as its argument. */ #define PAGERID(p) (SQLITE_PTR_TO_INT(p->fd)) #define FILEHANDLEID(fd) (SQLITE_PTR_TO_INT(fd)) /* ** The Pager.eState variable stores the current 'state' of a pager. A ** pager may be in any one of the seven states shown in the following ** state diagram. ** ** OPEN <------+------+ |
︙ | ︙ | |||
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 | ** ** errCode ** ** The Pager.errCode variable is only ever used in PAGER_ERROR state. It ** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode ** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX ** sub-codes. */ struct Pager { sqlite3_vfs *pVfs; /* OS functions to use for IO */ u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ u8 useJournal; /* Use a rollback journal on this file */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 extraSync; /* sync directory after journal delete */ | > > > > > > > > > > > > | | < | 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 | ** ** errCode ** ** The Pager.errCode variable is only ever used in PAGER_ERROR state. It ** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode ** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX ** sub-codes. ** ** syncFlags, walSyncFlags ** ** syncFlags is either SQLITE_SYNC_NORMAL (0x02) or SQLITE_SYNC_FULL (0x03). ** syncFlags is used for rollback mode. walSyncFlags is used for WAL mode ** and contains the flags used to sync the checkpoint operations in the ** lower two bits, and sync flags used for transaction commits in the WAL ** file in bits 0x04 and 0x08. In other words, to get the correct sync flags ** for checkpoint operations, use (walSyncFlags&0x03) and to get the correct ** sync flags for transaction commit, use ((walSyncFlags>>2)&0x03). Note ** that with synchronous=NORMAL in WAL mode, transaction commit is not synced ** meaning that the 0x04 and 0x08 bits are both zero. */ struct Pager { sqlite3_vfs *pVfs; /* OS functions to use for IO */ u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ u8 useJournal; /* Use a rollback journal on this file */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 extraSync; /* sync directory after journal delete */ u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */ u8 walSyncFlags; /* See description above */ u8 tempFile; /* zFilename is a temporary or immutable file */ u8 noLock; /* Do not lock (except in WAL mode) */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ /************************************************************************** ** The following block contains those class members that change during |
︙ | ︙ | |||
689 690 691 692 693 694 695 696 697 698 699 700 701 702 | int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ int aStat[3]; /* Total cache hits, misses and writes */ #ifdef SQLITE_TEST int nRead; /* Database pages read */ #endif void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ #ifdef SQLITE_HAS_CODEC void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ void (*xCodecFree)(void*); /* Destructor for the codec */ void *pCodec; /* First argument to xCodec... methods */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ | > | 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ int aStat[3]; /* Total cache hits, misses and writes */ #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 */ #ifdef SQLITE_HAS_CODEC void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ void (*xCodecFree)(void*); /* Destructor for the codec */ void *pCodec; /* First argument to xCodec... methods */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ |
︙ | ︙ | |||
809 810 811 812 813 814 815 | ** instead of ** ** if( pPager->jfd->pMethods ){ ... */ #define isOpen(pFd) ((pFd)->pMethods!=0) /* | | | | | > > | > > > > > | 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 | ** instead of ** ** if( pPager->jfd->pMethods ){ ... */ #define isOpen(pFd) ((pFd)->pMethods!=0) /* ** Return true if this pager uses a write-ahead log to read page pgno. ** Return false if the pager reads pgno directly from the database. */ #if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_DIRECT_OVERFLOW_READ) int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){ u32 iRead = 0; int rc; if( pPager->pWal==0 ) return 0; rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); return rc || iRead; } #endif #ifndef SQLITE_OMIT_WAL # define pagerUseWal(x) ((x)->pWal!=0) #else # define pagerUseWal(x) 0 # define pagerRollbackWal(x) 0 # define pagerWalFrames(v,w,x,y) 0 # define pagerOpenWalIfPresent(z) SQLITE_OK # define pagerBeginReadTransaction(z) SQLITE_OK #endif |
︙ | ︙ | |||
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 | assert( p->eLock==EXCLUSIVE_LOCK ); assert( pPager->errCode==SQLITE_OK ); assert( !pagerUseWal(pPager) ); assert( p->eLock>=EXCLUSIVE_LOCK ); assert( isOpen(p->jfd) || p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_WAL ); assert( pPager->dbOrigSize<=pPager->dbHintSize ); break; case PAGER_WRITER_FINISHED: assert( p->eLock==EXCLUSIVE_LOCK ); assert( pPager->errCode==SQLITE_OK ); assert( !pagerUseWal(pPager) ); assert( isOpen(p->jfd) || p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_WAL ); break; case PAGER_ERROR: /* There must be at least one outstanding reference to the pager if ** in ERROR state. Otherwise the pager should have already dropped ** back to OPEN state. | > > | 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 | assert( p->eLock==EXCLUSIVE_LOCK ); assert( pPager->errCode==SQLITE_OK ); assert( !pagerUseWal(pPager) ); assert( p->eLock>=EXCLUSIVE_LOCK ); assert( isOpen(p->jfd) || p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_WAL || (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC) ); assert( pPager->dbOrigSize<=pPager->dbHintSize ); break; case PAGER_WRITER_FINISHED: assert( p->eLock==EXCLUSIVE_LOCK ); assert( pPager->errCode==SQLITE_OK ); assert( !pagerUseWal(pPager) ); assert( isOpen(p->jfd) || p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_WAL || (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC) ); break; case PAGER_ERROR: /* There must be at least one outstanding reference to the pager if ** in ERROR state. Otherwise the pager should have already dropped ** back to OPEN state. |
︙ | ︙ | |||
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 | , p->journalOff, p->journalHdr , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize ); return zRet; } #endif /* ** Return true if it is necessary to write page *pPg into the sub-journal. ** A page needs to be written into the sub-journal if there exists one ** or more open savepoints for which: ** ** * The page-number is less than or equal to PagerSavepoint.nOrig, and | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | , p->journalOff, p->journalHdr , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize ); return zRet; } #endif /* Forward references to the various page getters */ static int getPageNormal(Pager*,Pgno,DbPage**,int); static int getPageError(Pager*,Pgno,DbPage**,int); #if SQLITE_MAX_MMAP_SIZE>0 static int getPageMMap(Pager*,Pgno,DbPage**,int); #endif /* ** Set the Pager.xGet method for the appropriate routine used to fetch ** content from the pager. */ static void setGetterMethod(Pager *pPager){ if( pPager->errCode ){ pPager->xGet = getPageError; #if SQLITE_MAX_MMAP_SIZE>0 }else if( USEFETCH(pPager) #ifdef SQLITE_HAS_CODEC && pPager->xCodec==0 #endif ){ pPager->xGet = getPageMMap; #endif /* SQLITE_MAX_MMAP_SIZE>0 */ }else{ pPager->xGet = getPageNormal; } } /* ** Return true if it is necessary to write page *pPg into the sub-journal. ** A page needs to be written into the sub-journal if there exists one ** or more open savepoints for which: ** ** * The page-number is less than or equal to PagerSavepoint.nOrig, and |
︙ | ︙ | |||
1129 1130 1131 1132 1133 1134 1135 | IOTRACE(("LOCK %p %d\n", pPager, eLock)) } } return rc; } /* | | > | | | | > > > | < < < | > > | < < | | > > > > > > > > > > | | > | | < < > | 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 | IOTRACE(("LOCK %p %d\n", pPager, eLock)) } } return rc; } /* ** This function determines whether or not the atomic-write or ** atomic-batch-write optimizations can be used with this pager. The ** atomic-write optimization can be used if: ** ** (a) the value returned by OsDeviceCharacteristics() indicates that ** a database page may be written atomically, and ** (b) the value returned by OsSectorSize() is less than or equal ** to the page size. ** ** If it can be used, then the value returned is the size of the journal ** file when it contains rollback data for exactly one page. ** ** The atomic-batch-write optimization can be used if OsDeviceCharacteristics() ** returns a value with the SQLITE_IOCAP_BATCH_ATOMIC bit set. -1 is ** returned in this case. ** ** If neither optimization can be used, 0 is returned. */ static int jrnlBufferSize(Pager *pPager){ assert( !MEMDB ); #if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) int dc; /* Device characteristics */ assert( isOpen(pPager->fd) ); dc = sqlite3OsDeviceCharacteristics(pPager->fd); #endif #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE if( dc&SQLITE_IOCAP_BATCH_ATOMIC ){ return -1; } #endif #ifdef SQLITE_ENABLE_ATOMIC_WRITE { int nSector = pPager->sectorSize; int szPage = pPager->pageSize; assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){ return 0; } } return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); #endif return 0; } /* ** If SQLITE_CHECK_PAGES is defined then we do some sanity checking ** on the cache using a hash function. This is used for testing ** and debugging only. */ #ifdef SQLITE_CHECK_PAGES |
︙ | ︙ | |||
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 | unsigned char aMagic[8]; /* A buffer to hold the magic header */ zMaster[0] = '\0'; if( SQLITE_OK!=(rc = sqlite3OsFileSize(pJrnl, &szJ)) || szJ<16 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) || len>=nMaster || len==0 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) || memcmp(aMagic, aJournalMagic, 8) || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len)) ){ return rc; | > | 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 | unsigned char aMagic[8]; /* A buffer to hold the magic header */ zMaster[0] = '\0'; if( SQLITE_OK!=(rc = sqlite3OsFileSize(pJrnl, &szJ)) || szJ<16 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) || len>=nMaster || len>szJ-16 || len==0 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) || memcmp(aMagic, aJournalMagic, 8) || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len)) ){ return rc; |
︙ | ︙ | |||
1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 | pPager->changeCountDone = 0; pPager->eState = PAGER_OPEN; }else{ pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER); } if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0); pPager->errCode = SQLITE_OK; } pPager->journalOff = 0; pPager->journalHdr = 0; pPager->setMaster = 0; } | > | 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 | pPager->changeCountDone = 0; pPager->eState = PAGER_OPEN; }else{ pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER); } if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0); pPager->errCode = SQLITE_OK; setGetterMethod(pPager); } pPager->journalOff = 0; pPager->journalHdr = 0; pPager->setMaster = 0; } |
︙ | ︙ | |||
1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 | pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK || (pPager->errCode & 0xff)==SQLITE_IOERR ); if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ pPager->errCode = rc; pPager->eState = PAGER_ERROR; } return rc; } static int pager_truncate(Pager *pPager, Pgno nPage); /* | > | 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 | pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK || (pPager->errCode & 0xff)==SQLITE_IOERR ); if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ pPager->errCode = rc; pPager->eState = PAGER_ERROR; setGetterMethod(pPager); } return rc; } static int pager_truncate(Pager *pPager, Pgno nPage); /* |
︙ | ︙ | |||
1971 1972 1973 1974 1975 1976 1977 | assert( assert_pager_state(pPager) ); assert( pPager->eState!=PAGER_ERROR ); if( pPager->eState<PAGER_WRITER_LOCKED && pPager->eLock<RESERVED_LOCK ){ return SQLITE_OK; } releaseAllSavepoints(pPager); | | > > | 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 | assert( assert_pager_state(pPager) ); assert( pPager->eState!=PAGER_ERROR ); if( pPager->eState<PAGER_WRITER_LOCKED && pPager->eLock<RESERVED_LOCK ){ return SQLITE_OK; } releaseAllSavepoints(pPager); assert( isOpen(pPager->jfd) || pPager->pInJournal==0 || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_BATCH_ATOMIC) ); if( isOpen(pPager->jfd) ){ assert( !pagerUseWal(pPager) ); /* Finalize the journal file. */ if( sqlite3JournalIsInMemory(pPager->jfd) ){ /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */ sqlite3OsClose(pPager->jfd); |
︙ | ︙ | |||
2033 2034 2035 2036 2037 2038 2039 | } #endif sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; pPager->nRec = 0; if( rc==SQLITE_OK ){ | | | 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 | } #endif sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; pPager->nRec = 0; if( rc==SQLITE_OK ){ if( MEMDB || pagerFlushOnCommit(pPager, bCommit) ){ sqlite3PcacheCleanAll(pPager->pPCache); }else{ sqlite3PcacheClearWritable(pPager->pPCache); } sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); } |
︙ | ︙ | |||
2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 | int rc; PgHdr *pPg; /* An existing page in the cache */ Pgno pgno; /* The page number of a page in journal */ u32 cksum; /* Checksum used for sanity checking */ char *aData; /* Temporary storage for the page */ sqlite3_file *jfd; /* The file descriptor for the journal file */ int isSynced; /* True if journal page is synced */ assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ aData = pPager->pTmpSpace; | > > > > > | 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 | int rc; PgHdr *pPg; /* An existing page in the cache */ Pgno pgno; /* The page number of a page in journal */ u32 cksum; /* Checksum used for sanity checking */ char *aData; /* Temporary storage for the page */ sqlite3_file *jfd; /* The file descriptor for the journal file */ int isSynced; /* True if journal page is synced */ #ifdef SQLITE_HAS_CODEC /* The jrnlEnc flag is true if Journal pages should be passed through ** the codec. It is false for pure in-memory journals. */ const int jrnlEnc = (isMainJrnl || pPager->subjInMemory==0); #endif assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ aData = pPager->pTmpSpace; |
︙ | ︙ | |||
2340 2341 2342 2343 2344 2345 2346 | if( isOpen(pPager->fd) && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) && isSynced ){ i64 ofst = (pgno-1)*(i64)pPager->pageSize; testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); assert( !pagerUseWal(pPager) ); | > > > > > > > > > > | > > > > > > > | | | > > > | 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 | if( isOpen(pPager->fd) && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) && isSynced ){ i64 ofst = (pgno-1)*(i64)pPager->pageSize; testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); assert( !pagerUseWal(pPager) ); /* Write the data read from the journal back into the database file. ** This is usually safe even for an encrypted database - as the data ** was encrypted before it was written to the journal file. The exception ** is if the data was just read from an in-memory sub-journal. In that ** case it must be encrypted here before it is copied into the database ** file. */ #ifdef SQLITE_HAS_CODEC if( !jrnlEnc ){ CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); }else #endif rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); if( pgno>pPager->dbFileSize ){ pPager->dbFileSize = pgno; } if( pPager->pBackup ){ #ifdef SQLITE_HAS_CODEC if( jrnlEnc ){ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT,aData); }else #endif sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); } }else if( !isMainJrnl && pPg==0 ){ /* If this is a rollback of a savepoint and data was not written to ** the database and the page is not in-memory, there is a potential ** problem. When the page is next fetched by the b-tree layer, it ** will be read from the database file, which may or may not be ** current. |
︙ | ︙ | |||
2399 2400 2401 2402 2403 2404 2405 | /* If this was page 1, then restore the value of Pager.dbFileVers. ** Do this before any decoding. */ if( pgno==1 ){ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); } /* Decode the page just read from disk */ | > | > | 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 | /* If this was page 1, then restore the value of Pager.dbFileVers. ** Do this before any decoding. */ if( pgno==1 ){ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); } /* Decode the page just read from disk */ #if SQLITE_HAS_CODEC if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); } #endif sqlite3PcacheRelease(pPg); } return rc; } /* ** Parameter zMaster is the name of a master journal file. A single journal |
︙ | ︙ | |||
2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 | u32 u; /* Unsigned loop counter */ Pgno mxPg = 0; /* Size of the original file in pages */ int rc; /* Result code of a subroutine */ int res = 1; /* Value returned by sqlite3OsAccess() */ char *zMaster = 0; /* Name of master journal file if any */ int needPagerReset; /* True to reset page prior to first page rollback */ int nPlayback = 0; /* Total number of pages restored from journal */ /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ assert( isOpen(pPager->jfd) ); rc = sqlite3OsFileSize(pPager->jfd, &szJ); if( rc!=SQLITE_OK ){ | > | 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 | u32 u; /* Unsigned loop counter */ Pgno mxPg = 0; /* Size of the original file in pages */ int rc; /* Result code of a subroutine */ int res = 1; /* Value returned by sqlite3OsAccess() */ char *zMaster = 0; /* Name of master journal file if any */ int needPagerReset; /* True to reset page prior to first page rollback */ int nPlayback = 0; /* Total number of pages restored from journal */ u32 savedPageSize = pPager->pageSize; /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ assert( isOpen(pPager->jfd) ); rc = sqlite3OsFileSize(pPager->jfd, &szJ); if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | } } } /*NOTREACHED*/ assert( 0 ); end_playback: /* Following a rollback, the database file should be back in its original ** state prior to the start of the transaction, so invoke the ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the ** assertion that the transaction counter was modified. */ #ifdef SQLITE_DEBUG if( pPager->fd->pMethods ){ | > > > | 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 | } } } /*NOTREACHED*/ assert( 0 ); end_playback: if( rc==SQLITE_OK ){ rc = sqlite3PagerSetPagesize(pPager, &savedPageSize, -1); } /* Following a rollback, the database file should be back in its original ** state prior to the start of the transaction, so invoke the ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the ** assertion that the transaction counter was modified. */ #ifdef SQLITE_DEBUG if( pPager->fd->pMethods ){ |
︙ | ︙ | |||
2899 2900 2901 2902 2903 2904 2905 | */ setSectorSize(pPager); return rc; } /* | | > | < < > > > | > < | | < < | | | | | | | 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 | */ setSectorSize(pPager); return rc; } /* ** Read the content for page pPg out of the database file (or out of ** the WAL if that is where the most recent copy if found) into ** pPg->pData. A shared lock or greater must be held on the database ** file before this function is called. ** ** If page 1 is read, then the value of Pager.dbFileVers[] is set to ** the value read from the database file. ** ** If an IO error occurs, then the IO error is returned to the caller. ** Otherwise, SQLITE_OK is returned. */ static int readDbPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ int rc = SQLITE_OK; /* Return code */ u32 iFrame = 0; /* Frame of WAL containing pgno */ assert( pPager->eState>=PAGER_READER && !MEMDB ); assert( isOpen(pPager->fd) ); if( pagerUseWal(pPager) ){ rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); if( rc ) return rc; } if( iFrame ){ rc = sqlite3WalReadFrame(pPager->pWal, iFrame,pPager->pageSize,pPg->pData); }else{ i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize; rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset); if( rc==SQLITE_IOERR_SHORT_READ ){ rc = SQLITE_OK; } } if( pPg->pgno==1 ){ if( rc ){ /* If the read is unsuccessful, set the dbFileVers[] to something ** that will never be a valid file version. dbFileVers[] is a copy ** of bytes 24..39 of the database. Bytes 28..31 should always be ** zero or the size of the database in page. Bytes 32..35 and 35..39 ** should be page numbers which are never 0xffffffff. So filling ** pPager->dbFileVers[] with all 0xff bytes should suffice. ** ** For an encrypted database, the situation is more complex: bytes ** 24..39 of the database are white noise. But the probability of ** white noise equaling 16 bytes of 0xff is vanishingly small so ** we should still be ok. */ memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers)); }else{ u8 *dbFileVers = &((u8*)pPg->pData)[24]; memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); } } CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT); PAGER_INCR(sqlite3_pager_readdb_count); PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pPg->pgno)); PAGERTRACE(("FETCH %d page %d hash(%08x)\n", PAGERID(pPager), pPg->pgno, pager_pagehash(pPg))); return rc; } /* ** Update the value of the change-counter at offsets 24 and 92 in ** the header and the sqlite version number at offset 96. |
︙ | ︙ | |||
3009 3010 3011 3012 3013 3014 3015 | assert( pagerUseWal(pPager) ); pPg = sqlite3PagerLookup(pPager, iPg); if( pPg ){ if( sqlite3PcachePageRefcount(pPg)==1 ){ sqlite3PcacheDrop(pPg); }else{ | < < < | < | 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 | assert( pagerUseWal(pPager) ); pPg = sqlite3PagerLookup(pPager, iPg); if( pPg ){ if( sqlite3PcachePageRefcount(pPg)==1 ){ sqlite3PcacheDrop(pPg); }else{ rc = readDbPage(pPg); if( rc==SQLITE_OK ){ pPager->xReiniter(pPg); } sqlite3PagerUnrefNotNull(pPg); } } |
︙ | ︙ | |||
3185 3186 3187 3188 3189 3190 3191 | assert( pPager->eState==PAGER_OPEN ); assert( pPager->eLock>=SHARED_LOCK ); assert( isOpen(pPager->fd) ); assert( pPager->tempFile==0 ); nPage = sqlite3WalDbsize(pPager->pWal); /* If the number of pages in the database is not available from the | | | 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 | assert( pPager->eState==PAGER_OPEN ); assert( pPager->eLock>=SHARED_LOCK ); assert( isOpen(pPager->fd) ); assert( pPager->tempFile==0 ); nPage = sqlite3WalDbsize(pPager->pWal); /* If the number of pages in the database is not available from the ** WAL sub-system, determine the page count based on the size of ** the database file. If the size of the database file is not an ** integer multiple of the page-size, round up the result. */ if( nPage==0 && ALWAYS(isOpen(pPager->fd)) ){ i64 n = 0; /* Size of db file in bytes */ int rc = sqlite3OsFileSize(pPager->fd, &n); if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
3236 3237 3238 3239 3240 3241 3242 | static int pagerOpenWalIfPresent(Pager *pPager){ int rc = SQLITE_OK; assert( pPager->eState==PAGER_OPEN ); assert( pPager->eLock>=SHARED_LOCK ); if( !pPager->tempFile ){ int isWal; /* True if WAL file exists */ | > > > > > | | | | | < < | < < < < < < | | > | 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 | static int pagerOpenWalIfPresent(Pager *pPager){ int rc = SQLITE_OK; assert( pPager->eState==PAGER_OPEN ); assert( pPager->eLock>=SHARED_LOCK ); if( !pPager->tempFile ){ int isWal; /* True if WAL file exists */ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal ); if( rc==SQLITE_OK ){ if( isWal ){ Pgno nPage; /* Size of the database file */ rc = pagerPagecount(pPager, &nPage); if( rc ) return rc; if( nPage==0 ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0); }else{ testcase( sqlite3PcachePagecount(pPager->pPCache)==0 ); rc = sqlite3PagerOpenWal(pPager, 0); } }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ pPager->journalMode = PAGER_JOURNALMODE_DELETE; } } } return rc; } |
︙ | ︙ | |||
3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 | static void pagerFixMaplimit(Pager *pPager){ #if SQLITE_MAX_MMAP_SIZE>0 sqlite3_file *fd = pPager->fd; if( isOpen(fd) && fd->pMethods->iVersion>=3 ){ sqlite3_int64 sz; sz = pPager->szMmap; pPager->bUseFetch = (sz>0); sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz); } #endif } /* ** Change the maximum size of any memory mapping made of the database file. | > | 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 | static void pagerFixMaplimit(Pager *pPager){ #if SQLITE_MAX_MMAP_SIZE>0 sqlite3_file *fd = pPager->fd; if( isOpen(fd) && fd->pMethods->iVersion>=3 ){ sqlite3_int64 sz; sz = pPager->szMmap; pPager->bUseFetch = (sz>0); setGetterMethod(pPager); sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz); } #endif } /* ** Change the maximum size of any memory mapping made of the database file. |
︙ | ︙ | |||
3520 3521 3522 3523 3524 3525 3526 | }else{ pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0; pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0; pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0; } if( pPager->noSync ){ pPager->syncFlags = 0; | < < < < < < | | > > > | 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 | }else{ pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0; pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0; pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0; } if( pPager->noSync ){ pPager->syncFlags = 0; }else if( pgFlags & PAGER_FULLFSYNC ){ pPager->syncFlags = SQLITE_SYNC_FULL; }else{ pPager->syncFlags = SQLITE_SYNC_NORMAL; } pPager->walSyncFlags = (pPager->syncFlags<<2); if( pPager->fullSync ){ pPager->walSyncFlags |= pPager->syncFlags; } if( (pgFlags & PAGER_CKPT_FULLFSYNC) && !pPager->noSync ){ pPager->walSyncFlags |= (SQLITE_SYNC_FULL<<2); } if( pgFlags & PAGER_CACHESPILL ){ pPager->doNotSpill &= ~SPILLFLAG_OFF; }else{ pPager->doNotSpill |= SPILLFLAG_OFF; } } |
︙ | ︙ | |||
3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 | } if( rc==SQLITE_OK ){ rc = sqlite3OsFileSize(pPager->jfd, &pPager->journalHdr); } return rc; } /* ** Obtain a reference to a memory mapped page object for page number pgno. ** The new object will use the pointer pData, obtained from xFetch(). ** If successful, set *ppPage to point to the new page reference ** and return SQLITE_OK. Otherwise, return an SQLite error code and set ** *ppPage to zero. ** | > | 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 | } if( rc==SQLITE_OK ){ rc = sqlite3OsFileSize(pPager->jfd, &pPager->journalHdr); } return rc; } #if SQLITE_MAX_MMAP_SIZE>0 /* ** Obtain a reference to a memory mapped page object for page number pgno. ** The new object will use the pointer pData, obtained from xFetch(). ** If successful, set *ppPage to point to the new page reference ** and return SQLITE_OK. Otherwise, return an SQLite error code and set ** *ppPage to zero. ** |
︙ | ︙ | |||
3950 3951 3952 3953 3954 3955 3956 | ){ PgHdr *p; /* Memory mapped page to return */ if( pPager->pMmapFreelist ){ *ppPage = p = pPager->pMmapFreelist; pPager->pMmapFreelist = p->pDirty; p->pDirty = 0; | > | | 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 | ){ PgHdr *p; /* Memory mapped page to return */ if( pPager->pMmapFreelist ){ *ppPage = p = pPager->pMmapFreelist; pPager->pMmapFreelist = p->pDirty; p->pDirty = 0; assert( pPager->nExtra>=8 ); memset(p->pExtra, 0, 8); }else{ *ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra); if( p==0 ){ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData); return SQLITE_NOMEM_BKPT; } p->pExtra = (void *)&p[1]; |
︙ | ︙ | |||
3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 | p->pgno = pgno; p->pData = pData; pPager->nMmapOut++; return SQLITE_OK; } /* ** Release a reference to page pPg. pPg must have been returned by an ** earlier call to pagerAcquireMapPage(). */ static void pagerReleaseMapPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; | > | 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 | p->pgno = pgno; p->pData = pData; pPager->nMmapOut++; return SQLITE_OK; } #endif /* ** Release a reference to page pPg. pPg must have been returned by an ** earlier call to pagerAcquireMapPage(). */ static void pagerReleaseMapPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; |
︙ | ︙ | |||
4028 4029 4030 4031 4032 4033 4034 | assert( assert_pager_state(pPager) ); disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pagerFreeMapHdrs(pPager); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL | > | > > | 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 | assert( assert_pager_state(pPager) ); disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pagerFreeMapHdrs(pPager); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL assert( db || pPager->pWal==0 ); sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize, (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp) ); pPager->pWal = 0; #endif pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* If it is open, sync the journal file before calling UnlockAndRollback. |
︙ | ︙ | |||
4404 4405 4406 4407 4408 4409 4410 | /* If the sub-journal was opened successfully (or was already open), ** write the journal record into the file. */ if( rc==SQLITE_OK ){ void *pData = pPg->pData; i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); char *pData2; | | > > | > > > | 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 | /* If the sub-journal was opened successfully (or was already open), ** write the journal record into the file. */ if( rc==SQLITE_OK ){ void *pData = pPg->pData; i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); char *pData2; #if SQLITE_HAS_CODEC if( !pPager->subjInMemory ){ CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); }else #endif pData2 = pData; PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); rc = write32bits(pPager->sjfd, offset, pPg->pgno); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); } } } |
︙ | ︙ | |||
4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 | if( pagerUseWal(pPager) ){ /* Write a single frame for this page to the log. */ rc = subjournalPageIfRequired(pPg); if( rc==SQLITE_OK ){ rc = pagerWalFrames(pPager, pPg, 0, 0); } }else{ /* Sync the journal file if required. */ if( pPg->flags&PGHDR_NEED_SYNC || pPager->eState==PAGER_WRITER_CACHEMOD ){ rc = syncJournal(pPager, 1); } | > > > > > > > | 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 | if( pagerUseWal(pPager) ){ /* Write a single frame for this page to the log. */ rc = subjournalPageIfRequired(pPg); if( rc==SQLITE_OK ){ rc = pagerWalFrames(pPager, pPg, 0, 0); } }else{ #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE if( pPager->tempFile==0 ){ rc = sqlite3JournalCreate(pPager->jfd); if( rc!=SQLITE_OK ) return pager_error(pPager, rc); } #endif /* Sync the journal file if required. */ if( pPg->flags&PGHDR_NEED_SYNC || pPager->eState==PAGER_WRITER_CACHEMOD ){ rc = syncJournal(pPager, 1); } |
︙ | ︙ | |||
4547 4548 4549 4550 4551 4552 4553 | ** and used as the file to be cached. Temporary files are be deleted ** automatically when they are closed. If zFilename is ":memory:" then ** all information is held in cache. It is never written to disk. ** This can be used to implement an in-memory database. ** ** The nExtra parameter specifies the number of bytes of space allocated ** along with each page reference. This space is available to the user | | > > | 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 | ** and used as the file to be cached. Temporary files are be deleted ** automatically when they are closed. If zFilename is ":memory:" then ** all information is held in cache. It is never written to disk. ** This can be used to implement an in-memory database. ** ** The nExtra parameter specifies the number of bytes of space allocated ** along with each page reference. This space is available to the user ** via the sqlite3PagerGetExtra() API. When a new page is allocated, the ** first 8 bytes of this space are zeroed but the remainder is uninitialized. ** (The extra space is used by btree as the MemPage object.) ** ** The flags argument is used to specify properties that affect the ** operation of the pager. It should be passed some bitwise combination ** of the PAGER_* flags. ** ** The vfsFlags parameter is a bitmask to pass to the flags parameter ** of the xOpen() method of the supplied VFS when opening files. |
︙ | ︙ | |||
4777 4778 4779 4780 4781 4782 4783 | assert( pPager->memDb==0 ); rc = sqlite3PagerSetPagesize(pPager, &szPageDflt, -1); testcase( rc!=SQLITE_OK ); } /* Initialize the PCache object. */ if( rc==SQLITE_OK ){ | < > | 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 | assert( pPager->memDb==0 ); rc = sqlite3PagerSetPagesize(pPager, &szPageDflt, -1); testcase( rc!=SQLITE_OK ); } /* Initialize the PCache object. */ if( rc==SQLITE_OK ){ nExtra = ROUND8(nExtra); assert( nExtra>=8 && nExtra<1000 ); rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); } /* If an error occurred above, free the Pager structure and close the file. */ if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
4820 4821 4822 4823 4824 4825 4826 | assert( useJournal || pPager->tempFile ); pPager->noSync = pPager->tempFile; if( pPager->noSync ){ assert( pPager->fullSync==0 ); assert( pPager->extraSync==0 ); assert( pPager->syncFlags==0 ); assert( pPager->walSyncFlags==0 ); | < | < > | 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 | assert( useJournal || pPager->tempFile ); pPager->noSync = pPager->tempFile; if( pPager->noSync ){ assert( pPager->fullSync==0 ); assert( pPager->extraSync==0 ); assert( pPager->syncFlags==0 ); assert( pPager->walSyncFlags==0 ); }else{ pPager->fullSync = 1; pPager->extraSync = 0; pPager->syncFlags = SQLITE_SYNC_NORMAL; pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2); } /* pPager->pFirst = 0; */ /* pPager->pFirstSynced = 0; */ /* pPager->pLast = 0; */ pPager->nExtra = (u16)nExtra; pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; assert( isOpen(pPager->fd) || tempFile ); setSectorSize(pPager); if( !useJournal ){ pPager->journalMode = PAGER_JOURNALMODE_OFF; }else if( memDb ){ pPager->journalMode = PAGER_JOURNALMODE_MEMORY; } /* pPager->xBusyHandler = 0; */ /* pPager->pBusyHandlerArg = 0; */ pPager->xReiniter = xReinit; setGetterMethod(pPager); /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */ *ppPager = pPager; return SQLITE_OK; } |
︙ | ︙ | |||
5180 5181 5182 5183 5184 5185 5186 | ** other bytes change randomly with each file change when ** a codec is in use. ** ** There is a vanishingly small chance that a change will not be ** detected. The chance of an undetected change is so small that ** it can be neglected. */ | < < < < < | | | > < | 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 | ** other bytes change randomly with each file change when ** a codec is in use. ** ** There is a vanishingly small chance that a change will not be ** detected. The chance of an undetected change is so small that ** it can be neglected. */ char dbFileVers[sizeof(pPager->dbFileVers)]; IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); if( rc!=SQLITE_OK ){ if( rc!=SQLITE_IOERR_SHORT_READ ){ goto failed; } memset(dbFileVers, 0, sizeof(dbFileVers)); } if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); /* Unmap the database file. It is possible that external processes |
︙ | ︙ | |||
5250 5251 5252 5253 5254 5255 5256 | ** transaction and unlock the pager. ** ** Except, in locking_mode=EXCLUSIVE when there is nothing to in ** the rollback journal, the unlock is not performed and there is ** nothing to rollback, so this routine is a no-op. */ static void pagerUnlockIfUnused(Pager *pPager){ | | > | | > > > > > > > | | | | | | | | < | | < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < | | < < < < < < < < < < < < < < < < < > > > > | | > > > | | < < | | > > > | 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 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 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 | ** transaction and unlock the pager. ** ** Except, in locking_mode=EXCLUSIVE when there is nothing to in ** the rollback journal, the unlock is not performed and there is ** nothing to rollback, so this routine is a no-op. */ static void pagerUnlockIfUnused(Pager *pPager){ if( sqlite3PcacheRefCount(pPager->pPCache)==0 ){ assert( pPager->nMmapOut==0 ); /* because page1 is never memory mapped */ pagerUnlockAndRollback(pPager); } } /* ** The page getter methods each try to acquire a reference to a ** page with page number pgno. If the requested reference is ** successfully obtained, it is copied to *ppPage and SQLITE_OK returned. ** ** There are different implementations of the getter method depending ** on the current state of the pager. ** ** getPageNormal() -- The normal getter ** getPageError() -- Used if the pager is in an error state ** getPageMmap() -- Used if memory-mapped I/O is enabled ** ** If the requested page is already in the cache, it is returned. ** Otherwise, a new page object is allocated and populated with data ** read from the database file. In some cases, the pcache module may ** choose not to allocate a new page object and may reuse an existing ** object with no outstanding references. ** ** The extra data appended to a page is always initialized to zeros the ** first time a page is loaded into memory. If the page requested is ** already in the cache when this function is called, then the extra ** data is left as it was when the page object was last used. ** ** If the database image is smaller than the requested page or if ** the flags parameter contains the PAGER_GET_NOCONTENT bit and the ** requested page is not already stored in the cache, then no ** actual disk read occurs. In this case the memory image of the ** page is initialized to all zeros. ** ** If PAGER_GET_NOCONTENT is true, it means that we do not care about ** the contents of the page. This occurs in two scenarios: ** ** a) When reading a free-list leaf page from the database, and ** ** b) When a savepoint is being rolled back and we need to load ** a new page into the cache to be filled with the data read ** from the savepoint journal. ** ** If PAGER_GET_NOCONTENT is true, then the data returned is zeroed instead ** of being read from the database. Additionally, the bits corresponding ** to pgno in Pager.pInJournal (bitvec of pages already written to the ** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open ** savepoints are set. This means if the page is made writable at any ** point in the future, using a call to sqlite3PagerWrite(), its contents ** will not be journaled. This saves IO. ** ** The acquisition might fail for several reasons. In all cases, ** an appropriate error code is returned and *ppPage is set to NULL. ** ** See also sqlite3PagerLookup(). Both this routine and Lookup() attempt ** to find a page in the in-memory cache first. If the page is not already ** in memory, this routine goes to disk to read it in whereas Lookup() ** just returns 0. This routine acquires a read-lock the first time it ** has to go to disk, and could also playback an old journal if necessary. ** Since Lookup() never goes to disk, it never has to deal with locks ** or journal files. */ static int getPageNormal( Pager *pPager, /* The pager open on the database file */ Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ int rc = SQLITE_OK; PgHdr *pPg; u8 noContent; /* True if PAGER_GET_NOCONTENT is set */ sqlite3_pcache_page *pBase; assert( pPager->errCode==SQLITE_OK ); assert( pPager->eState>=PAGER_READER ); assert( assert_pager_state(pPager) ); assert( pPager->hasHeldSharedLock==1 ); if( pgno==0 ) return SQLITE_CORRUPT_BKPT; pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3); if( pBase==0 ){ pPg = 0; rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase); if( rc!=SQLITE_OK ) goto pager_acquire_err; if( pBase==0 ){ rc = SQLITE_NOMEM_BKPT; goto pager_acquire_err; } } pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase); assert( pPg==(*ppPage) ); assert( pPg->pgno==pgno ); assert( pPg->pPager==pPager || pPg->pPager==0 ); noContent = (flags & PAGER_GET_NOCONTENT)!=0; if( pPg->pPager && !noContent ){ /* In this case the pcache already contains an initialized copy of ** the page. Return without further ado. */ assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); pPager->aStat[PAGER_STAT_HIT]++; return SQLITE_OK; }else{ /* The pager cache has created a new page. Its content needs to ** be initialized. But first some error checks: ** ** (1) The maximum page number is 2^31 ** (2) Never try to fetch the locking page */ if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ rc = SQLITE_CORRUPT_BKPT; goto pager_acquire_err; } pPg->pPager = pPager; assert( !isOpen(pPager->fd) || !MEMDB ); if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){ if( pgno>pPager->mxPgno ){ rc = SQLITE_FULL; goto pager_acquire_err; } |
︙ | ︙ | |||
5450 5451 5452 5453 5454 5455 5456 | TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno); testcase( rc==SQLITE_NOMEM ); sqlite3EndBenignMalloc(); } memset(pPg->pData, 0, pPager->pageSize); IOTRACE(("ZERO %p %d\n", pPager, pgno)); }else{ | < < < < | < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 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 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 | TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno); testcase( rc==SQLITE_NOMEM ); sqlite3EndBenignMalloc(); } memset(pPg->pData, 0, pPager->pageSize); IOTRACE(("ZERO %p %d\n", pPager, pgno)); }else{ assert( pPg->pPager==pPager ); pPager->aStat[PAGER_STAT_MISS]++; rc = readDbPage(pPg); if( rc!=SQLITE_OK ){ goto pager_acquire_err; } } pager_set_pagehash(pPg); } return SQLITE_OK; pager_acquire_err: assert( rc!=SQLITE_OK ); if( pPg ){ sqlite3PcacheDrop(pPg); } pagerUnlockIfUnused(pPager); *ppPage = 0; return rc; } #if SQLITE_MAX_MMAP_SIZE>0 /* The page getter for when memory-mapped I/O is enabled */ static int getPageMMap( Pager *pPager, /* The pager open on the database file */ Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ int rc = SQLITE_OK; PgHdr *pPg = 0; u32 iFrame = 0; /* Frame to read from WAL file */ /* It is acceptable to use a read-only (mmap) page for any page except ** page 1 if there is no write-transaction open or the ACQUIRE_READONLY ** flag was specified by the caller. And so long as the db is not a ** temporary or in-memory database. */ const int bMmapOk = (pgno>1 && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY)) ); assert( USEFETCH(pPager) ); #ifdef SQLITE_HAS_CODEC assert( pPager->xCodec==0 ); #endif /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here ** allows the compiler optimizer to reuse the results of the "pgno>1" ** test in the previous statement, and avoid testing pgno==0 in the ** common case where pgno is large. */ if( pgno<=1 && pgno==0 ){ return SQLITE_CORRUPT_BKPT; } assert( pPager->eState>=PAGER_READER ); assert( assert_pager_state(pPager) ); assert( pPager->hasHeldSharedLock==1 ); assert( pPager->errCode==SQLITE_OK ); if( bMmapOk && pagerUseWal(pPager) ){ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); if( rc!=SQLITE_OK ){ *ppPage = 0; return rc; } } if( bMmapOk && iFrame==0 ){ void *pData = 0; rc = sqlite3OsFetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData ); if( rc==SQLITE_OK && pData ){ if( pPager->eState>PAGER_READER || pPager->tempFile ){ pPg = sqlite3PagerLookup(pPager, pgno); } if( pPg==0 ){ rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg); }else{ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData); } if( pPg ){ assert( rc==SQLITE_OK ); *ppPage = pPg; return SQLITE_OK; } } if( rc!=SQLITE_OK ){ *ppPage = 0; return rc; } } return getPageNormal(pPager, pgno, ppPage, flags); } #endif /* SQLITE_MAX_MMAP_SIZE>0 */ /* The page getter method for when the pager is an error state */ static int getPageError( Pager *pPager, /* The pager open on the database file */ Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ UNUSED_PARAMETER(pgno); UNUSED_PARAMETER(flags); assert( pPager->errCode!=SQLITE_OK ); *ppPage = 0; return pPager->errCode; } /* Dispatch all page fetch requests to the appropriate getter method. */ int sqlite3PagerGet( Pager *pPager, /* The pager open on the database file */ Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ return pPager->xGet(pPager, pgno, ppPage, flags); } /* ** Acquire a page if it is already in the in-memory cache. Do ** not read the page from disk. Return a pointer to the page, ** or 0 if the page is not in cache. ** |
︙ | ︙ | |||
5502 5503 5504 5505 5506 5507 5508 | if( pPage==0 ) return 0; return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); } /* ** Release a page reference. ** | | < | > > | > > > | < > > | > > > > > > > > > | 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 | if( pPage==0 ) return 0; return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); } /* ** Release a page reference. ** ** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be ** used if we know that the page being released is not the last page. ** The btree layer always holds page1 open until the end, so these first ** to routines can be used to release any page other than BtShared.pPage1. ** ** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine ** checks the total number of outstanding pages and if the number of ** pages reaches zero it drops the database lock. */ void sqlite3PagerUnrefNotNull(DbPage *pPg){ TESTONLY( Pager *pPager = pPg->pPager; ) assert( pPg!=0 ); if( pPg->flags & PGHDR_MMAP ){ assert( pPg->pgno!=1 ); /* Page1 is never memory mapped */ pagerReleaseMapPage(pPg); }else{ sqlite3PcacheRelease(pPg); } /* Do not use this routine to release the last reference to page1 */ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); } void sqlite3PagerUnref(DbPage *pPg){ if( pPg ) sqlite3PagerUnrefNotNull(pPg); } void sqlite3PagerUnrefPageOne(DbPage *pPg){ Pager *pPager; assert( pPg!=0 ); assert( pPg->pgno==1 ); assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ pPager = pPg->pPager; sqlite3PcacheRelease(pPg); pagerUnlockIfUnused(pPager); } /* ** This function is called at the start of every write transaction. ** There must already be a RESERVED or EXCLUSIVE lock on the database ** file when this routine is called. ** |
︙ | ︙ | |||
5950 5951 5952 5953 5954 5955 5956 | ** as appropriate. Otherwise, SQLITE_OK. */ int sqlite3PagerWrite(PgHdr *pPg){ Pager *pPager = pPg->pPager; assert( (pPg->flags & PGHDR_MMAP)==0 ); assert( pPager->eState>=PAGER_WRITER_LOCKED ); assert( assert_pager_state(pPager) ); | < < | > > | 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 | ** as appropriate. Otherwise, SQLITE_OK. */ int sqlite3PagerWrite(PgHdr *pPg){ Pager *pPager = pPg->pPager; assert( (pPg->flags & PGHDR_MMAP)==0 ); assert( pPager->eState>=PAGER_WRITER_LOCKED ); assert( assert_pager_state(pPager) ); if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){ if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg); return SQLITE_OK; }else if( pPager->errCode ){ return pPager->errCode; }else if( pPager->sectorSize > (u32)pPager->pageSize ){ assert( pPager->tempFile==0 ); return pagerWriteLargeSector(pPg); }else{ return pager_write(pPg); } } |
︙ | ︙ | |||
6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 | rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1); } sqlite3PagerUnref(pPageOne); if( rc==SQLITE_OK ){ sqlite3PcacheCleanAll(pPager->pPCache); } }else{ /* The following block updates the change-counter. Exactly how it ** does this depends on whether or not the atomic-update optimization ** was enabled at compile time, and if this transaction meets the ** runtime criteria to use the operation: ** ** * The file-system supports the atomic-write property for ** blocks of size page-size, and | > > > > > > > > > > > > > > > | 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 | rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1); } sqlite3PagerUnref(pPageOne); if( rc==SQLITE_OK ){ sqlite3PcacheCleanAll(pPager->pPCache); } }else{ /* The bBatch boolean is true if the batch-atomic-write commit method ** should be used. No rollback journal is created if batch-atomic-write ** is enabled. */ sqlite3_file *fd = pPager->fd; #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE const int bBatch = zMaster==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */ && (sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC) && !pPager->noSync && sqlite3JournalIsInMemory(pPager->jfd); #else # define bBatch 0 #endif #ifdef SQLITE_ENABLE_ATOMIC_WRITE /* The following block updates the change-counter. Exactly how it ** does this depends on whether or not the atomic-update optimization ** was enabled at compile time, and if this transaction meets the ** runtime criteria to use the operation: ** ** * The file-system supports the atomic-write property for ** blocks of size page-size, and |
︙ | ︙ | |||
6255 6256 6257 6258 6259 6260 6261 | ** mode. ** ** Otherwise, if the optimization is both enabled and applicable, ** then call pager_incr_changecounter() to update the change-counter ** in 'direct' mode. In this case the journal file will never be ** created for this transaction. */ | | | | | | | | | | | | | | | | | | | | | | | | | > | > > > > > > | | 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 | ** mode. ** ** Otherwise, if the optimization is both enabled and applicable, ** then call pager_incr_changecounter() to update the change-counter ** in 'direct' mode. In this case the journal file will never be ** created for this transaction. */ if( bBatch==0 ){ PgHdr *pPg; assert( isOpen(pPager->jfd) || pPager->journalMode==PAGER_JOURNALMODE_OFF || pPager->journalMode==PAGER_JOURNALMODE_WAL ); if( !zMaster && isOpen(pPager->jfd) && pPager->journalOff==jrnlBufferSize(pPager) && pPager->dbSize>=pPager->dbOrigSize && (!(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty) ){ /* Update the db file change counter via the direct-write method. The ** following call will modify the in-memory representation of page 1 ** to include the updated change counter and then write page 1 ** directly to the database file. Because of the atomic-write ** property of the host file-system, this is safe. */ rc = pager_incr_changecounter(pPager, 1); }else{ rc = sqlite3JournalCreate(pPager->jfd); if( rc==SQLITE_OK ){ rc = pager_incr_changecounter(pPager, 0); } } } #else #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE if( zMaster ){ rc = sqlite3JournalCreate(pPager->jfd); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; } #endif rc = pager_incr_changecounter(pPager, 0); #endif if( rc!=SQLITE_OK ) goto commit_phase_one_exit; /* Write the master journal name into the journal file. If a master ** journal file name has already been written to the journal file, ** or if zMaster is NULL (no master journal), then this call is a no-op. */ rc = writeMasterJournal(pPager, zMaster); |
︙ | ︙ | |||
6304 6305 6306 6307 6308 6309 6310 | ** journal requires a sync here. However, in locking_mode=exclusive ** on a system under memory pressure it is just possible that this is ** not the case. In this case it is likely enough that the redundant ** xSync() call will be changed to a no-op by the OS anyhow. */ rc = syncJournal(pPager, 0); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; | | > > > > > > > > > > > > > > > > | 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 | ** journal requires a sync here. However, in locking_mode=exclusive ** on a system under memory pressure it is just possible that this is ** not the case. In this case it is likely enough that the redundant ** xSync() call will be changed to a no-op by the OS anyhow. */ rc = syncJournal(pPager, 0); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; if( bBatch ){ /* The pager is now in DBMOD state. But regardless of what happens ** next, attempting to play the journal back into the database would ** be unsafe. Close it now to make sure that does not happen. */ sqlite3OsClose(pPager->jfd); rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; } rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache)); if( bBatch ){ if( rc==SQLITE_OK ){ rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0); }else{ sqlite3OsFileControl(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0); } } if( rc!=SQLITE_OK ){ assert( rc!=SQLITE_IOERR_BLOCKED ); goto commit_phase_one_exit; } sqlite3PcacheCleanAll(pPager->pPCache); /* If the file on disk is smaller than the database image, use |
︙ | ︙ | |||
6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 | if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ /* This can happen using journal_mode=off. Move the pager to the error ** state to indicate that the contents of the cache may not be trusted. ** Any active readers will get SQLITE_ABORT. */ pPager->errCode = SQLITE_ABORT; pPager->eState = PAGER_ERROR; return rc; } }else{ rc = pager_playback(pPager, 0); } assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); | > | 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 | if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ /* This can happen using journal_mode=off. Move the pager to the error ** state to indicate that the contents of the cache may not be trusted. ** Any active readers will get SQLITE_ABORT. */ pPager->errCode = SQLITE_ABORT; pPager->eState = PAGER_ERROR; setGetterMethod(pPager); return rc; } }else{ rc = pager_playback(pPager, 0); } assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); |
︙ | ︙ | |||
6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 | ** can be rolled back at the ZipVFS level. */ else if( pPager->journalMode==PAGER_JOURNALMODE_OFF && pPager->eState>=PAGER_WRITER_CACHEMOD ){ pPager->errCode = SQLITE_ABORT; pPager->eState = PAGER_ERROR; } #endif } return rc; } | > | 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 | ** can be rolled back at the ZipVFS level. */ else if( pPager->journalMode==PAGER_JOURNALMODE_OFF && pPager->eState>=PAGER_WRITER_CACHEMOD ){ pPager->errCode = SQLITE_ABORT; pPager->eState = PAGER_ERROR; setGetterMethod(pPager); } #endif } return rc; } |
︙ | ︙ | |||
6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 | void *pCodec ){ if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); pPager->xCodec = pPager->memDb ? 0 : xCodec; pPager->xCodecSizeChng = xCodecSizeChng; pPager->xCodecFree = xCodecFree; pPager->pCodec = pCodec; pagerReportSize(pPager); } void *sqlite3PagerGetCodec(Pager *pPager){ return pPager->pCodec; } /* | > | 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 | void *pCodec ){ if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); pPager->xCodec = pPager->memDb ? 0 : xCodec; pPager->xCodecSizeChng = xCodecSizeChng; pPager->xCodecFree = xCodecFree; pPager->pCodec = pCodec; setGetterMethod(pPager); pagerReportSize(pPager); } void *sqlite3PagerGetCodec(Pager *pPager){ return pPager->pCodec; } /* |
︙ | ︙ | |||
7203 7204 7205 7206 7207 7208 7209 | int *pnCkpt /* OUT: Final number of checkpointed frames */ ){ int rc = SQLITE_OK; if( pPager->pWal ){ rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode, (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), pPager->pBusyHandlerArg, | | | 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 | int *pnCkpt /* OUT: Final number of checkpointed frames */ ){ int rc = SQLITE_OK; if( pPager->pWal ){ rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode, (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), pPager->pBusyHandlerArg, pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, pnLog, pnCkpt ); } return rc; } int sqlite3PagerWalCallback(Pager *pPager){ |
︙ | ︙ | |||
7360 7361 7362 7363 7364 7365 7366 | /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerExclusiveLock(pPager); if( rc==SQLITE_OK ){ | | | 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 | /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerExclusiveLock(pPager); if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize, (u8*)pPager->pTmpSpace); pPager->pWal = 0; pagerFixMaplimit(pPager); if( rc && !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); } } return rc; |
︙ | ︙ | |||
7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 | if( pPager->pWal ){ sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); }else{ rc = SQLITE_ERROR; } return rc; } #endif /* SQLITE_ENABLE_SNAPSHOT */ #endif /* !SQLITE_OMIT_WAL */ #ifdef SQLITE_ENABLE_ZIPVFS /* ** A read-lock must be held on the pager when this function is called. If ** the pager is in WAL mode and the WAL file currently contains one or more | > > > > > > > > > > > > > > | 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 | if( pPager->pWal ){ sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); }else{ rc = SQLITE_ERROR; } return rc; } /* ** If this is a WAL database, call sqlite3WalSnapshotRecover(). If this ** is not a WAL database, return an error. */ int sqlite3PagerSnapshotRecover(Pager *pPager){ int rc; if( pPager->pWal ){ rc = sqlite3WalSnapshotRecover(pPager->pWal); }else{ rc = SQLITE_ERROR; } return rc; } #endif /* SQLITE_ENABLE_SNAPSHOT */ #endif /* !SQLITE_OMIT_WAL */ #ifdef SQLITE_ENABLE_ZIPVFS /* ** A read-lock must be held on the pager when this function is called. If ** the pager is in WAL mode and the WAL file currently contains one or more |
︙ | ︙ |
Changes to src/pager.h.
︙ | ︙ | |||
147 148 149 150 151 152 153 154 155 156 157 158 159 160 | /* Functions used to obtain and release page references. */ int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); void sqlite3PagerRef(DbPage*); void sqlite3PagerUnref(DbPage*); void sqlite3PagerUnrefNotNull(DbPage*); /* Operations on page references. */ int sqlite3PagerWrite(DbPage*); void sqlite3PagerDontWrite(DbPage*); int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); int sqlite3PagerPageRefcount(DbPage*); void *sqlite3PagerGetData(DbPage *); | > | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | /* Functions used to obtain and release page references. */ int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); void sqlite3PagerRef(DbPage*); void sqlite3PagerUnref(DbPage*); void sqlite3PagerUnrefNotNull(DbPage*); void sqlite3PagerUnrefPageOne(DbPage*); /* Operations on page references. */ int sqlite3PagerWrite(DbPage*); void sqlite3PagerDontWrite(DbPage*); int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); int sqlite3PagerPageRefcount(DbPage*); void *sqlite3PagerGetData(DbPage *); |
︙ | ︙ | |||
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | #ifndef SQLITE_OMIT_WAL int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*); int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); # ifdef SQLITE_ENABLE_SNAPSHOT int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot); int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot); # endif #endif #ifdef SQLITE_ENABLE_ZIPVFS int sqlite3PagerWalFramesize(Pager *pPager); #endif /* Functions used to query pager state and configuration. */ | > > > > > > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | #ifndef SQLITE_OMIT_WAL int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*); int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); # ifdef SQLITE_DIRECT_OVERFLOW_READ int sqlite3PagerUseWal(Pager *pPager, Pgno); # endif # ifdef SQLITE_ENABLE_SNAPSHOT int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot); int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot); int sqlite3PagerSnapshotRecover(Pager *pPager); # endif #else # define sqlite3PagerUseWal(x,y) 0 #endif #ifdef SQLITE_ENABLE_ZIPVFS int sqlite3PagerWalFramesize(Pager *pPager); #endif /* Functions used to query pager state and configuration. */ |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
61 62 63 64 65 66 67 68 69 70 71 72 73 74 | /* ** Indicate that sqlite3ParserFree() will never be called with a null ** pointer. */ #define YYPARSEFREENEVERNULL 1 /* ** Alternative datatype for the argument to the malloc() routine passed ** into sqlite3ParserAlloc(). The default is size_t. */ #define YYMALLOCARGTYPE u64 /* | > > > > > > > > > > > > > | 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 | /* ** Indicate that sqlite3ParserFree() will never be called with a null ** pointer. */ #define YYPARSEFREENEVERNULL 1 /* ** In the amalgamation, the parse.c file generated by lemon and the ** tokenize.c file are concatenated. In that case, sqlite3RunParser() ** has access to the the size of the yyParser object and so the parser ** engine can be allocated from stack. In that case, only the ** sqlite3ParserInit() and sqlite3ParserFinalize() routines are invoked ** and the sqlite3ParserAlloc() and sqlite3ParserFree() routines can be ** omitted. */ #ifdef SQLITE_AMALGAMATION # define sqlite3Parser_ENGINEALWAYSONSTACK 1 #endif /* ** Alternative datatype for the argument to the malloc() routine passed ** into sqlite3ParserAlloc(). The default is size_t. */ #define YYMALLOCARGTYPE u64 /* |
︙ | ︙ | |||
123 124 125 126 127 128 129 | trans_opt ::= TRANSACTION. trans_opt ::= TRANSACTION nm. %type transtype {int} transtype(A) ::= . {A = TK_DEFERRED;} transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/} | | | < | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | trans_opt ::= TRANSACTION. trans_opt ::= TRANSACTION nm. %type transtype {int} transtype(A) ::= . {A = TK_DEFERRED;} transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/} cmd ::= COMMIT|END(X) trans_opt. {sqlite3EndTransaction(pParse,@X);} cmd ::= ROLLBACK(X) trans_opt. {sqlite3EndTransaction(pParse,@X);} savepoint_opt ::= SAVEPOINT. savepoint_opt ::= . cmd ::= SAVEPOINT nm(X). { sqlite3Savepoint(pParse, SAVEPOINT_BEGIN, &X); } cmd ::= RELEASE savepoint_opt nm(X). { |
︙ | ︙ | |||
175 176 177 178 179 180 181 182 183 184 185 186 187 188 | A = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z); } } columnlist ::= columnlist COMMA columnname carglist. columnlist ::= columnname carglist. columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);} // Define operator precedence early so that this is the first occurrence // of the operator tokens in the grammer. Keeping the operators together // causes them to be assigned integer values that are close together, // which keeps parser tables smaller. // // The token values assigned to these symbols is determined by the order | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | A = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z); } } columnlist ::= columnlist COMMA columnname carglist. columnlist ::= columnname carglist. columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);} // Declare some tokens early in order to influence their values, to // improve performance and reduce the executable size. The goal here is // to get the "jump" operations in ISNULL through ESCAPE to have numeric // values that are early enough so that all jump operations are clustered // at the beginning, but also so that the comparison tokens NE through GE // are as large as possible so that they are near to FUNCTION, which is a // token synthesized by addopcodes.tcl. // %token ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST. %token CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL. %token OR AND NOT IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ. %token GT LE LT GE ESCAPE. // The following directive causes tokens ABORT, AFTER, ASC, etc. to // fallback to ID if they will not parse as their original value. // This obviates the need for the "id" nonterminal. // %fallback ID ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION %endif SQLITE_OMIT_COMPOUND_SELECT REINDEX RENAME CTIME_KW IF . %wildcard ANY. // Define operator precedence early so that this is the first occurrence // of the operator tokens in the grammer. Keeping the operators together // causes them to be assigned integer values that are close together, // which keeps parser tables smaller. // // The token values assigned to these symbols is determined by the order |
︙ | ︙ | |||
209 210 211 212 213 214 215 | // %token_class id ID|INDEXED. // A "number" can be either an integer or a floating point value %token_class number INTEGER|FLOAT. | < < < < < < < < < < < < < < < < < | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | // %token_class id ID|INDEXED. // A "number" can be either an integer or a floating point value %token_class number INTEGER|FLOAT. // And "ids" is an identifer-or-string. // %token_class ids ID|STRING. // The name of a column or table can be any of the following: // |
︙ | ︙ | |||
268 269 270 271 272 273 274 | carglist ::= . ccons ::= CONSTRAINT nm(X). {pParse->constraintName = X;} ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT MINUS(A) term(X). { ExprSpan v; | | | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | carglist ::= . ccons ::= CONSTRAINT nm(X). {pParse->constraintName = X;} ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT MINUS(A) term(X). { ExprSpan v; v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, X.pExpr, 0); v.zStart = A.z; v.zEnd = X.zEnd; sqlite3AddDefaultValue(pParse,&v); } ccons ::= DEFAULT id(X). { ExprSpan v; spanExpr(&v, pParse, TK_STRING, X); |
︙ | ︙ | |||
543 544 545 546 547 548 549 | sqlite3ExprListSetSpan(pParse,A,&X); } selcollist(A) ::= sclp(A) STAR. { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); A = sqlite3ExprListAppend(pParse, A, p); } selcollist(A) ::= sclp(A) nm(X) DOT STAR. { | | | | | 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 | sqlite3ExprListSetSpan(pParse,A,&X); } selcollist(A) ::= sclp(A) STAR. { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); A = sqlite3ExprListAppend(pParse, A, p); } selcollist(A) ::= sclp(A) nm(X) DOT STAR. { Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1); Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); A = sqlite3ExprListAppend(pParse,A, pDot); } // An option "AS <id>" phrase that can follow one of the expressions that // define the result set, or one of the tables in the FROM clause. // %type as {Token} |
︙ | ︙ | |||
864 865 866 867 868 869 870 | pOut->zEnd = &t.z[t.n]; } } expr(A) ::= term(A). expr(A) ::= LP(B) expr(X) RP(E). {spanSet(&A,&B,&E); /*A-overwrites-B*/ A.pExpr = X.pExpr;} | < | | | | | < | | > | 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 | pOut->zEnd = &t.z[t.n]; } } expr(A) ::= term(A). expr(A) ::= LP(B) expr(X) RP(E). {spanSet(&A,&B,&E); /*A-overwrites-B*/ A.pExpr = X.pExpr;} expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= nm(X) DOT nm(Y). { Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1); Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1); spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1); Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1); Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &Z, 1); Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3); spanSet(&A,&X,&Z); /*A-overwrites-X*/ A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } term(A) ::= NULL|FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X); /*A-overwrites-X*/} term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X); /*A-overwrites-X*/} term(A) ::= INTEGER(X). { A.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &X, 1); A.zStart = X.z; A.zEnd = X.z + X.n; } expr(A) ::= VARIABLE(X). { if( !(X.z[0]=='#' && sqlite3Isdigit(X.z[1])) ){ u32 n = X.n; spanExpr(&A, pParse, TK_VARIABLE, X); sqlite3ExprAssignVarNumber(pParse, A.pExpr, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers ** in the virtual machine. #N is the N-th register. */ Token t = X; /*A-overwrites-X*/ assert( t.n>=2 ); spanSet(&A, &t, &t); if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); A.pExpr = 0; }else{ A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); if( A.pExpr ) sqlite3GetInt32(&t.z[1], &A.pExpr->iTable); } } } expr(A) ::= expr(A) COLLATE ids(C). { A.pExpr = sqlite3ExprAddCollateToken(pParse, A.pExpr, &C, 1); A.zEnd = &C.z[C.n]; } %ifndef SQLITE_OMIT_CAST expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlite3ExprAlloc(pParse->db, TK_CAST, &T, 1); sqlite3ExprAttachSubtrees(pParse->db, A.pExpr, E.pExpr, 0); } %endif SQLITE_OMIT_CAST expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). { if( Y && Y->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X); } A.pExpr = sqlite3ExprFunction(pParse, Y, &X); |
︙ | ︙ | |||
949 950 951 952 953 954 955 | */ static void spanBinaryExpr( Parse *pParse, /* The parsing context. Errors accumulate here */ int op, /* The binary operation */ ExprSpan *pLeft, /* The left operand, and output */ ExprSpan *pRight /* The right operand */ ){ | | | | | 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 | */ static void spanBinaryExpr( Parse *pParse, /* The parsing context. Errors accumulate here */ int op, /* The binary operation */ ExprSpan *pLeft, /* The left operand, and output */ ExprSpan *pRight /* The right operand */ ){ pLeft->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr); pLeft->zEnd = pRight->zEnd; } /* If doNot is true, then add a TK_NOT Expr-node wrapper around the ** outside of *ppExpr. */ static void exprNot(Parse *pParse, int doNot, ExprSpan *pSpan){ if( doNot ){ pSpan->pExpr = sqlite3PExpr(pParse, TK_NOT, pSpan->pExpr, 0); } } } expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). { ExprList *pList = sqlite3ExprListAppend(pParse, X, Y.pExpr); A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); if( A.pExpr ){ A.pExpr->x.pList = pList; spanSet(&A, &L, &R); }else{ sqlite3ExprListDelete(pParse->db, pList); } } |
︙ | ︙ | |||
987 988 989 990 991 992 993 | {spanBinaryExpr(pParse,@OP,&A,&Y);} expr(A) ::= expr(A) PLUS|MINUS(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} expr(A) ::= expr(A) STAR|SLASH|REM(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} expr(A) ::= expr(A) CONCAT(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} %type likeop {Token} | | | 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 | {spanBinaryExpr(pParse,@OP,&A,&Y);} expr(A) ::= expr(A) PLUS|MINUS(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} expr(A) ::= expr(A) STAR|SLASH|REM(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} expr(A) ::= expr(A) CONCAT(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} %type likeop {Token} likeop(A) ::= LIKE_KW|MATCH(A). likeop(A) ::= NOT LIKE_KW|MATCH(X). {A=X; A.n|=0x80000000; /*A-overwrite-X*/} expr(A) ::= expr(A) likeop(OP) expr(Y). [LIKE_KW] { ExprList *pList; int bNot = OP.n & 0x80000000; OP.n &= 0x7fffffff; pList = sqlite3ExprListAppend(pParse,0, Y.pExpr); pList = sqlite3ExprListAppend(pParse,pList, A.pExpr); |
︙ | ︙ | |||
1022 1023 1024 1025 1026 1027 1028 | */ static void spanUnaryPostfix( Parse *pParse, /* Parsing context to record errors */ int op, /* The operator */ ExprSpan *pOperand, /* The operand, and output */ Token *pPostOp /* The operand token for setting the span */ ){ | | | 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 | */ static void spanUnaryPostfix( Parse *pParse, /* Parsing context to record errors */ int op, /* The operator */ ExprSpan *pOperand, /* The operand, and output */ Token *pPostOp /* The operand token for setting the span */ ){ pOperand->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0); pOperand->zEnd = &pPostOp->z[pPostOp->n]; } } expr(A) ::= expr(A) ISNULL|NOTNULL(E). {spanUnaryPostfix(pParse,@E,&A,&E);} expr(A) ::= expr(A) NOT NULL(E). {spanUnaryPostfix(pParse,TK_NOTNULL,&A,&E);} |
︙ | ︙ | |||
1069 1070 1071 1072 1073 1074 1075 | ExprSpan *pOut, /* Write the new expression node here */ Parse *pParse, /* Parsing context to record errors */ int op, /* The operator */ ExprSpan *pOperand, /* The operand */ Token *pPreOp /* The operand token for setting the span */ ){ pOut->zStart = pPreOp->z; | | | 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 | ExprSpan *pOut, /* Write the new expression node here */ Parse *pParse, /* Parsing context to record errors */ int op, /* The operator */ ExprSpan *pOperand, /* The operand */ Token *pPreOp /* The operand token for setting the span */ ){ pOut->zStart = pPreOp->z; pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0); pOut->zEnd = pOperand->zEnd; } } expr(A) ::= NOT(B) expr(X). |
︙ | ︙ | |||
1091 1092 1093 1094 1095 1096 1097 | %type between_op {int} between_op(A) ::= BETWEEN. {A = 0;} between_op(A) ::= NOT BETWEEN. {A = 1;} expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] { ExprList *pList = sqlite3ExprListAppend(pParse,0, X.pExpr); pList = sqlite3ExprListAppend(pParse,pList, Y.pExpr); | | | 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 | %type between_op {int} between_op(A) ::= BETWEEN. {A = 0;} between_op(A) ::= NOT BETWEEN. {A = 1;} expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] { ExprList *pList = sqlite3ExprListAppend(pParse,0, X.pExpr); pList = sqlite3ExprListAppend(pParse,pList, Y.pExpr); A.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, A.pExpr, 0); if( A.pExpr ){ A.pExpr->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } exprNot(pParse, N, &A); A.zEnd = Y.zEnd; |
︙ | ︙ | |||
1115 1116 1117 1118 1119 1120 1121 | ** expr1 IN () ** expr1 NOT IN () ** ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ sqlite3ExprDelete(pParse->db, A.pExpr); | | | 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 | ** expr1 IN () ** expr1 NOT IN () ** ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ sqlite3ExprDelete(pParse->db, A.pExpr); A.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[N],1); }else if( Y->nExpr==1 ){ /* Expressions of the form: ** ** expr1 IN (?1) ** expr1 NOT IN (?2) ** ** with exactly one value on the RHS can be simplified to something |
︙ | ︙ | |||
1142 1143 1144 1145 1146 1147 1148 | sqlite3ExprListDelete(pParse->db, Y); /* pRHS cannot be NULL because a malloc error would have been detected ** before now and control would have never reached this point */ if( ALWAYS(pRHS) ){ pRHS->flags &= ~EP_Collate; pRHS->flags |= EP_Generic; } | | | | | | | | | 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 | sqlite3ExprListDelete(pParse->db, Y); /* pRHS cannot be NULL because a malloc error would have been detected ** before now and control would have never reached this point */ if( ALWAYS(pRHS) ){ pRHS->flags &= ~EP_Collate; pRHS->flags |= EP_Generic; } A.pExpr = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, A.pExpr, pRHS); }else{ A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0); if( A.pExpr ){ A.pExpr->x.pList = Y; sqlite3ExprSetHeightAndFlags(pParse, A.pExpr); }else{ sqlite3ExprListDelete(pParse->db, Y); } exprNot(pParse, N, &A); } A.zEnd = &E.z[E.n]; } expr(A) ::= LP(B) select(X) RP(E). { spanSet(&A,&B,&E); /*A-overwrites-B*/ A.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0); sqlite3PExprAddSelect(pParse, A.pExpr, X); } expr(A) ::= expr(A) in_op(N) LP select(Y) RP(E). [IN] { A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0); sqlite3PExprAddSelect(pParse, A.pExpr, Y); exprNot(pParse, N, &A); A.zEnd = &E.z[E.n]; } expr(A) ::= expr(A) in_op(N) nm(Y) dbnm(Z) paren_exprlist(E). [IN] { SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); if( E ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, E); A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0); sqlite3PExprAddSelect(pParse, A.pExpr, pSelect); exprNot(pParse, N, &A); A.zEnd = Z.z ? &Z.z[Z.n] : &Y.z[Y.n]; } expr(A) ::= EXISTS(B) LP select(Y) RP(E). { Expr *p; spanSet(&A,&B,&E); /*A-overwrites-B*/ p = A.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); sqlite3PExprAddSelect(pParse, p, Y); } %endif SQLITE_OMIT_SUBQUERY /* CASE expressions */ expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). { spanSet(&A,&C,&E); /*A-overwrites-C*/ A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0); if( A.pExpr ){ A.pExpr->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y; sqlite3ExprSetHeightAndFlags(pParse, A.pExpr); }else{ sqlite3ExprListDelete(pParse->db, Y); sqlite3ExprDelete(pParse->db, Z); } |
︙ | ︙ | |||
1370 1371 1372 1373 1374 1375 1376 | trigger_time(C) trigger_event(D) ON fullname(E) foreach_clause when_clause(G). { sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, E, G, T, NOERR); A = (Z.n==0?B:Z); /*A-overwrites-T*/ } %type trigger_time {int} | | < | 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 | trigger_time(C) trigger_event(D) ON fullname(E) foreach_clause when_clause(G). { sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, E, G, T, NOERR); A = (Z.n==0?B:Z); /*A-overwrites-T*/ } %type trigger_time {int} trigger_time(A) ::= BEFORE|AFTER(X). { A = @X; /*A-overwrites-X*/ } trigger_time(A) ::= INSTEAD OF. { A = TK_INSTEAD;} trigger_time(A) ::= . { A = TK_BEFORE; } %type trigger_event {struct TrigEvent} %destructor trigger_event {sqlite3IdListDelete(pParse->db, $$.b);} trigger_event(A) ::= DELETE|INSERT(X). {A.a = @X; /*A-overwrites-X*/ A.b = 0;} trigger_event(A) ::= UPDATE(X). {A.a = @X; /*A-overwrites-X*/ A.b = 0;} |
︙ | ︙ | |||
1454 1455 1456 1457 1458 1459 1460 | // SELECT trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); /*A-overwrites-X*/} // The special RAISE expression that may occur in trigger programs expr(A) ::= RAISE(X) LP IGNORE RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ | | | | 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 | // SELECT trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); /*A-overwrites-X*/} // The special RAISE expression that may occur in trigger programs expr(A) ::= RAISE(X) LP IGNORE RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0); if( A.pExpr ){ A.pExpr->affinity = OE_Ignore; } } expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlite3ExprAlloc(pParse->db, TK_RAISE, &Z, 1); if( A.pExpr ) { A.pExpr->affinity = (char)T; } } %endif !SQLITE_OMIT_TRIGGER %type raisetype {int} |
︙ | ︙ |
Changes to src/pcache.c.
︙ | ︙ | |||
100 101 102 103 104 105 106 | ** Return false if any invariant is violated. ** ** This routine is for use inside of assert() statements only. For ** example: ** ** assert( sqlite3PcachePageSanity(pPg) ); */ | | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | ** Return false if any invariant is violated. ** ** This routine is for use inside of assert() statements only. For ** example: ** ** assert( sqlite3PcachePageSanity(pPg) ); */ #ifdef SQLITE_DEBUG int sqlite3PcachePageSanity(PgHdr *pPg){ PCache *pCache; assert( pPg!=0 ); assert( pPg->pgno>0 || pPg->pPager==0 ); /* Page number is 1 or more */ pCache = pPg->pCache; assert( pCache!=0 ); /* Every page has an associated PCache */ if( pPg->flags & PGHDR_CLEAN ){ assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */ assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */ assert( pCache->pDirtyTail!=pPg ); } |
︙ | ︙ | |||
187 188 189 190 191 192 193 | p->pDirty = pPage->pDirtyNext; assert( p->bPurgeable || p->eCreate==2 ); if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/ assert( p->bPurgeable==0 || p->eCreate==1 ); p->eCreate = 2; } } | < < | < | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | p->pDirty = pPage->pDirtyNext; assert( p->bPurgeable || p->eCreate==2 ); if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/ assert( p->bPurgeable==0 || p->eCreate==1 ); p->eCreate = 2; } } } if( addRemove & PCACHE_DIRTYLIST_ADD ){ pPage->pDirtyPrev = 0; pPage->pDirtyNext = p->pDirty; if( pPage->pDirtyNext ){ assert( pPage->pDirtyNext->pDirtyPrev==0 ); pPage->pDirtyNext->pDirtyPrev = pPage; }else{ p->pDirtyTail = pPage; if( p->bPurgeable ){ |
︙ | ︙ | |||
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 | int sqlite3PcacheSize(void){ return sizeof(PCache); } /* ** Create a new PCache object. Storage space to hold the object ** has already been allocated and is passed in as the p pointer. ** The caller discovers how much space needs to be allocated by ** calling sqlite3PcacheSize(). */ int sqlite3PcacheOpen( int szPage, /* Size of every page */ int szExtra, /* Extra space associated with each page */ int bPurgeable, /* True if pages are on backing store */ int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ void *pStress, /* Argument to xStress */ PCache *p /* Preallocated space for the PCache */ ){ memset(p, 0, sizeof(PCache)); p->szPage = 1; p->szExtra = szExtra; p->bPurgeable = bPurgeable; p->eCreate = 2; p->xStress = xStress; p->pStress = pStress; p->szCache = 100; p->szSpill = 1; pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable)); | > > > > > > > | 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 | int sqlite3PcacheSize(void){ return sizeof(PCache); } /* ** Create a new PCache object. Storage space to hold the object ** has already been allocated and is passed in as the p pointer. ** The caller discovers how much space needs to be allocated by ** calling sqlite3PcacheSize(). ** ** szExtra is some extra space allocated for each page. The first ** 8 bytes of the extra space will be zeroed as the page is allocated, ** but remaining content will be uninitialized. Though it is opaque ** to this module, the extra space really ends up being the MemPage ** structure in the pager. */ int sqlite3PcacheOpen( int szPage, /* Size of every page */ int szExtra, /* Extra space associated with each page */ int bPurgeable, /* True if pages are on backing store */ int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ void *pStress, /* Argument to xStress */ PCache *p /* Preallocated space for the PCache */ ){ memset(p, 0, sizeof(PCache)); p->szPage = 1; p->szExtra = szExtra; assert( szExtra>=8 ); /* First 8 bytes will be zeroed */ p->bPurgeable = bPurgeable; p->eCreate = 2; p->xStress = xStress; p->pStress = pStress; p->szCache = 100; p->szSpill = 1; pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable)); |
︙ | ︙ | |||
361 362 363 364 365 366 367 | ){ int eCreate; sqlite3_pcache_page *pRes; assert( pCache!=0 ); assert( pCache->pCache!=0 ); assert( createFlag==3 || createFlag==0 ); | < | 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | ){ int eCreate; sqlite3_pcache_page *pRes; assert( pCache!=0 ); assert( pCache->pCache!=0 ); assert( createFlag==3 || createFlag==0 ); assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) ); /* eCreate defines what to do if the page does not exist. ** 0 Do not allocate a new page. (createFlag==0) ** 1 Allocate a new page if doing so is inexpensive. ** (createFlag==1 AND bPurgeable AND pDirty) ** 2 Allocate a new page even it doing so is difficult. |
︙ | ︙ | |||
461 462 463 464 465 466 467 | assert( pPage!=0 ); pPgHdr = (PgHdr*)pPage->pExtra; assert( pPgHdr->pPage==0 ); memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty)); pPgHdr->pPage = pPage; pPgHdr->pData = pPage->pBuf; pPgHdr->pExtra = (void *)&pPgHdr[1]; | | | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | assert( pPage!=0 ); pPgHdr = (PgHdr*)pPage->pExtra; assert( pPgHdr->pPage==0 ); memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty)); pPgHdr->pPage = pPage; pPgHdr->pData = pPage->pBuf; pPgHdr->pExtra = (void *)&pPgHdr[1]; memset(pPgHdr->pExtra, 0, 8); pPgHdr->pCache = pCache; pPgHdr->pgno = pgno; pPgHdr->flags = PGHDR_CLEAN; return sqlite3PcacheFetchFinish(pCache,pgno,pPage); } /* |
︙ | ︙ | |||
503 504 505 506 507 508 509 | */ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){ assert( p->nRef>0 ); p->pCache->nRefSum--; if( (--p->nRef)==0 ){ if( p->flags&PGHDR_CLEAN ){ pcacheUnpin(p); | | < < < < | 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | */ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){ assert( p->nRef>0 ); p->pCache->nRefSum--; if( (--p->nRef)==0 ){ if( p->flags&PGHDR_CLEAN ){ pcacheUnpin(p); }else{ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); } } } /* ** Increase the reference count of a supplied page by 1. |
︙ | ︙ |
Changes to src/pcache.h.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** Every page in the cache is controlled by an instance of the following ** structure. */ struct PgHdr { sqlite3_pcache_page *pPage; /* Pcache object page handle */ void *pData; /* Page data */ void *pExtra; /* Extra content */ PgHdr *pDirty; /* Transient list of dirty sorted by pgno */ Pager *pPager; /* The pager this page is part of */ Pgno pgno; /* Page number for this page */ #ifdef SQLITE_CHECK_PAGES u32 pageHash; /* Hash of page content */ #endif u16 flags; /* PGHDR flags defined below */ /********************************************************************** | > | | > < < > > | 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 | ** Every page in the cache is controlled by an instance of the following ** structure. */ struct PgHdr { sqlite3_pcache_page *pPage; /* Pcache object page handle */ void *pData; /* Page data */ void *pExtra; /* Extra content */ PCache *pCache; /* PRIVATE: Cache that owns this page */ PgHdr *pDirty; /* Transient list of dirty sorted by pgno */ Pager *pPager; /* The pager this page is part of */ Pgno pgno; /* Page number for this page */ #ifdef SQLITE_CHECK_PAGES u32 pageHash; /* Hash of page content */ #endif u16 flags; /* PGHDR flags defined below */ /********************************************************************** ** Elements above, except pCache, are public. All that follow are ** private to pcache.c and should not be accessed by other modules. ** pCache is grouped with the public elements for efficiency. */ i16 nRef; /* Number of users of this page */ PgHdr *pDirtyNext; /* Next element in list of dirty pages */ PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ /* NB: pDirtyNext and pDirtyPrev are undefined if the ** PgHdr object is not dirty */ }; /* Bit values for PgHdr.flags */ #define PGHDR_CLEAN 0x001 /* Page not on the PCache.pDirty list */ #define PGHDR_DIRTY 0x002 /* Page is on the PCache.pDirty list */ #define PGHDR_WRITEABLE 0x004 /* Journaled and ready to modify */ #define PGHDR_NEED_SYNC 0x008 /* Fsync the rollback journal before |
︙ | ︙ |
Changes to src/pcache1.c.
︙ | ︙ | |||
92 93 94 95 96 97 98 | ** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of ** PgHdr1.pCache->szPage bytes is allocated directly before this structure ** in memory. */ struct PgHdr1 { sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ unsigned int iKey; /* Key value (page number) */ | < > > > > > > | 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 | ** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of ** PgHdr1.pCache->szPage bytes is allocated directly before this structure ** in memory. */ struct PgHdr1 { sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ unsigned int iKey; /* Key value (page number) */ u8 isBulkLocal; /* This page from bulk local storage */ u8 isAnchor; /* This is the PGroup.lru element */ PgHdr1 *pNext; /* Next in hash table chain */ PCache1 *pCache; /* Cache that currently owns this page */ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ }; /* ** A page is pinned if it is no on the LRU list */ #define PAGE_IS_PINNED(p) ((p)->pLruNext==0) #define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0) /* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set ** of one or more PCaches that are able to recycle each other's unpinned ** pages when they are under memory pressure. A PGroup is an instance of ** the following object. ** ** This page cache implementation works in one of two modes: ** |
︙ | ︙ | |||
128 129 130 131 132 133 134 | ** SQLITE_MUTEX_STATIC_LRU. */ struct PGroup { sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ unsigned int nMinPage; /* Sum of nMin for purgeable caches */ unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ | | | > > | 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 | ** SQLITE_MUTEX_STATIC_LRU. */ struct PGroup { sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ unsigned int nMinPage; /* Sum of nMin for purgeable caches */ unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ unsigned int nPurgeable; /* Number of purgeable pages allocated */ PgHdr1 lru; /* The beginning and end of the LRU list */ }; /* Each page cache is an instance of the following object. Every ** open database file (including each in-memory database and each ** temporary or transient database) has a single page cache which ** is an instance of this object. ** ** Pointers to structures of this type are cast and returned as ** opaque sqlite3_pcache* handles. */ struct PCache1 { /* Cache configuration parameters. Page size (szPage) and the purgeable ** flag (bPurgeable) and the pnPurgeable pointer are all set when the ** cache is created and are never changed thereafter. nMax may be ** modified at any time by a call to the pcache1Cachesize() method. ** The PGroup mutex must be held when accessing nMax. */ PGroup *pGroup; /* PGroup this cache belongs to */ unsigned int *pnPurgeable; /* Pointer to pGroup->nPurgeable */ int szPage; /* Size of database content section */ int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */ int szAlloc; /* Total size of one pcache line */ int bPurgeable; /* True if cache is purgeable */ unsigned int nMin; /* Minimum number of pages reserved */ unsigned int nMax; /* Configured "cache_size" value */ unsigned int n90pct; /* nMax*9/10 */ |
︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 | ** This routine is called from sqlite3_initialize() and so it is guaranteed ** to be serialized already. There is no need for further mutexing. */ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ if( pcache1.isInit ){ PgFreeslot *p; if( pBuf==0 ) sz = n = 0; sz = ROUNDDOWN8(sz); pcache1.szSlot = sz; pcache1.nSlot = pcache1.nFreeSlot = n; pcache1.nReserve = n>90 ? 10 : (n/10 + 1); pcache1.pStart = pBuf; pcache1.pFree = 0; pcache1.bUnderPressure = 0; | > | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | ** This routine is called from sqlite3_initialize() and so it is guaranteed ** to be serialized already. There is no need for further mutexing. */ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ if( pcache1.isInit ){ PgFreeslot *p; if( pBuf==0 ) sz = n = 0; if( n==0 ) sz = 0; sz = ROUNDDOWN8(sz); pcache1.szSlot = sz; pcache1.nSlot = pcache1.nFreeSlot = n; pcache1.nReserve = n>90 ? 10 : (n/10 + 1); pcache1.pStart = pBuf; pcache1.pFree = 0; pcache1.bUnderPressure = 0; |
︙ | ︙ | |||
281 282 283 284 285 286 287 | if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){ szBulk = pCache->szAlloc*(i64)pCache->nMax; } zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); sqlite3EndBenignMalloc(); if( zBulk ){ int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; | < < > | | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){ szBulk = pCache->szAlloc*(i64)pCache->nMax; } zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); sqlite3EndBenignMalloc(); if( zBulk ){ int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; do{ PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; pX->page.pBuf = zBulk; pX->page.pExtra = &pX[1]; pX->isBulkLocal = 1; pX->isAnchor = 0; pX->pNext = pCache->pFree; pCache->pFree = pX; zBulk += pCache->szAlloc; }while( --nBulk ); } return pCache->pFree!=0; } /* ** Malloc function used within this file to allocate space from the buffer ** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no |
︙ | ︙ | |||
434 435 436 437 438 439 440 | #endif if( pPg==0 ) return 0; p->page.pBuf = pPg; p->page.pExtra = &p[1]; p->isBulkLocal = 0; p->isAnchor = 0; } | | < < | < < | 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 | #endif if( pPg==0 ) return 0; p->page.pBuf = pPg; p->page.pExtra = &p[1]; p->isBulkLocal = 0; p->isAnchor = 0; } (*pCache->pnPurgeable)++; return p; } /* ** Free a page object allocated by pcache1AllocPage(). */ static void pcache1FreePage(PgHdr1 *p){ PCache1 *pCache; assert( p!=0 ); pCache = p->pCache; assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); if( p->isBulkLocal ){ p->pNext = pCache->pFree; pCache->pFree = p; }else{ pcache1Free(p->page.pBuf); #ifdef SQLITE_PCACHE_SEPARATE_HEADER sqlite3_free(p); #endif } (*pCache->pnPurgeable)--; } /* ** Malloc function used by SQLite to obtain space from the buffer configured ** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer ** exists, this function falls back to sqlite3Malloc(). */ |
︙ | ︙ | |||
554 555 556 557 558 559 560 | ** This function is used internally to remove the page pPage from the ** PGroup LRU list, if is part of it. If pPage is not part of the PGroup ** LRU list, then this function is a no-op. ** ** The PGroup mutex must be held when this function is called. */ static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){ | < < | < | < | | | 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 | ** This function is used internally to remove the page pPage from the ** PGroup LRU list, if is part of it. If pPage is not part of the PGroup ** LRU list, then this function is a no-op. ** ** The PGroup mutex must be held when this function is called. */ static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){ assert( pPage!=0 ); assert( PAGE_IS_UNPINNED(pPage) ); assert( pPage->pLruNext ); assert( pPage->pLruPrev ); assert( sqlite3_mutex_held(pPage->pCache->pGroup->mutex) ); pPage->pLruPrev->pLruNext = pPage->pLruNext; pPage->pLruNext->pLruPrev = pPage->pLruPrev; pPage->pLruNext = 0; pPage->pLruPrev = 0; assert( pPage->isAnchor==0 ); assert( pPage->pCache->pGroup->lru.isAnchor==1 ); pPage->pCache->nRecyclable--; return pPage; } /* ** Remove the page supplied as an argument from the hash table ** (PCache1.apHash structure) that it is currently stored in. |
︙ | ︙ | |||
603 604 605 606 607 608 609 | ** If there are currently more than nMaxPage pages allocated, try ** to recycle pages to reduce the number allocated to nMaxPage. */ static void pcache1EnforceMaxPage(PCache1 *pCache){ PGroup *pGroup = pCache->pGroup; PgHdr1 *p; assert( sqlite3_mutex_held(pGroup->mutex) ); | | | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 | ** If there are currently more than nMaxPage pages allocated, try ** to recycle pages to reduce the number allocated to nMaxPage. */ static void pcache1EnforceMaxPage(PCache1 *pCache){ PGroup *pGroup = pCache->pGroup; PgHdr1 *p; assert( sqlite3_mutex_held(pGroup->mutex) ); while( pGroup->nPurgeable>pGroup->nMaxPage && (p=pGroup->lru.pLruPrev)->isAnchor==0 ){ assert( p->pCache->pGroup==pGroup ); assert( PAGE_IS_UNPINNED(p) ); pcache1PinPage(p); pcache1RemoveFromHash(p, 1); } if( pCache->nPage==0 && pCache->pBulk ){ sqlite3_free(pCache->pBulk); pCache->pBulk = pCache->pFree = 0; } |
︙ | ︙ | |||
656 657 658 659 660 661 662 | PgHdr1 *pPage; assert( h<pCache->nHash ); pp = &pCache->apHash[h]; while( (pPage = *pp)!=0 ){ if( pPage->iKey>=iLimit ){ pCache->nPage--; *pp = pPage->pNext; | | | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 | PgHdr1 *pPage; assert( h<pCache->nHash ); pp = &pCache->apHash[h]; while( (pPage = *pp)!=0 ){ if( pPage->iKey>=iLimit ){ pCache->nPage--; *pp = pPage->pNext; if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage); pcache1FreePage(pPage); }else{ pp = &pPage->pNext; TESTONLY( if( nPage>=0 ) nPage++; ) } } if( h==iStop ) break; |
︙ | ︙ | |||
774 775 776 777 778 779 780 781 782 783 784 785 786 787 | pCache->bPurgeable = (bPurgeable ? 1 : 0); pcache1EnterMutex(pGroup); pcache1ResizeHash(pCache); if( bPurgeable ){ pCache->nMin = 10; pGroup->nMinPage += pCache->nMin; pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; } pcache1LeaveMutex(pGroup); if( pCache->nHash==0 ){ pcache1Destroy((sqlite3_pcache*)pCache); pCache = 0; } } | > > > > | 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 | pCache->bPurgeable = (bPurgeable ? 1 : 0); pcache1EnterMutex(pGroup); pcache1ResizeHash(pCache); if( bPurgeable ){ pCache->nMin = 10; pGroup->nMinPage += pCache->nMin; pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; pCache->pnPurgeable = &pGroup->nPurgeable; }else{ static unsigned int dummyCurrentPage; pCache->pnPurgeable = &dummyCurrentPage; } pcache1LeaveMutex(pGroup); if( pCache->nHash==0 ){ pcache1Destroy((sqlite3_pcache*)pCache); pCache = 0; } } |
︙ | ︙ | |||
875 876 877 878 879 880 881 | /* Step 4. Try to recycle a page. */ if( pCache->bPurgeable && !pGroup->lru.pLruPrev->isAnchor && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache)) ){ PCache1 *pOther; pPage = pGroup->lru.pLruPrev; | | | < | 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 | /* Step 4. Try to recycle a page. */ if( pCache->bPurgeable && !pGroup->lru.pLruPrev->isAnchor && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache)) ){ PCache1 *pOther; pPage = pGroup->lru.pLruPrev; assert( PAGE_IS_UNPINNED(pPage) ); pcache1RemoveFromHash(pPage, 0); pcache1PinPage(pPage); pOther = pPage->pCache; if( pOther->szAlloc != pCache->szAlloc ){ pcache1FreePage(pPage); pPage = 0; }else{ pGroup->nPurgeable -= (pOther->bPurgeable - pCache->bPurgeable); } } /* Step 5. If a usable page buffer has still not been found, ** attempt to allocate a new one. */ if( !pPage ){ pPage = pcache1AllocPage(pCache, createFlag==1); } if( pPage ){ unsigned int h = iKey % pCache->nHash; pCache->nPage++; pPage->iKey = iKey; pPage->pNext = pCache->apHash[h]; pPage->pCache = pCache; pPage->pLruPrev = 0; pPage->pLruNext = 0; *(void **)pPage->page.pExtra = 0; pCache->apHash[h] = pPage; if( iKey>pCache->iMaxKey ){ pCache->iMaxKey = iKey; } } return pPage; |
︙ | ︙ | |||
988 989 990 991 992 993 994 | while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; } /* Step 2: If the page was found in the hash table, then return it. ** If the page was not in the hash table and createFlag is 0, abort. ** Otherwise (page not in hash and createFlag!=0) continue with ** subsequent steps to try to create the page. */ if( pPage ){ | | | 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 | while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; } /* Step 2: If the page was found in the hash table, then return it. ** If the page was not in the hash table and createFlag is 0, abort. ** Otherwise (page not in hash and createFlag!=0) continue with ** subsequent steps to try to create the page. */ if( pPage ){ if( PAGE_IS_UNPINNED(pPage) ){ return pcache1PinPage(pPage); }else{ return pPage; } }else if( createFlag ){ /* Steps 3, 4, and 5 implemented by this subroutine */ return pcache1FetchStage2(pCache, iKey, createFlag); |
︙ | ︙ | |||
1063 1064 1065 1066 1067 1068 1069 | assert( pPage->pCache==pCache ); pcache1EnterMutex(pGroup); /* It is an error to call this function if the page is already ** part of the PGroup LRU list. */ assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); | | | < | 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 | assert( pPage->pCache==pCache ); pcache1EnterMutex(pGroup); /* It is an error to call this function if the page is already ** part of the PGroup LRU list. */ assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); assert( PAGE_IS_PINNED(pPage) ); if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){ pcache1RemoveFromHash(pPage, 1); }else{ /* Add the page to the PGroup LRU list. */ PgHdr1 **ppFirst = &pGroup->lru.pLruNext; pPage->pLruPrev = &pGroup->lru; (pPage->pLruNext = *ppFirst)->pLruPrev = pPage; *ppFirst = pPage; pCache->nRecyclable++; } pcache1LeaveMutex(pCache->pGroup); } /* ** Implementation of the sqlite3_pcache.xRekey method. |
︙ | ︙ | |||
1207 1208 1209 1210 1211 1212 1213 | ** been released, the function returns. The return value is the total number ** of bytes of memory released. */ int sqlite3PcacheReleaseMemory(int nReq){ int nFree = 0; assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); assert( sqlite3_mutex_notheld(pcache1.mutex) ); | | | | 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 | ** been released, the function returns. The return value is the total number ** of bytes of memory released. */ int sqlite3PcacheReleaseMemory(int nReq){ int nFree = 0; assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); assert( sqlite3_mutex_notheld(pcache1.mutex) ); if( sqlite3GlobalConfig.pPage==0 ){ PgHdr1 *p; pcache1EnterMutex(&pcache1.grp); while( (nReq<0 || nFree<nReq) && (p=pcache1.grp.lru.pLruPrev)!=0 && p->isAnchor==0 ){ nFree += pcache1MemSize(p->page.pBuf); #ifdef SQLITE_PCACHE_SEPARATE_HEADER nFree += sqlite3MemSize(p); #endif assert( PAGE_IS_UNPINNED(p) ); pcache1PinPage(p); pcache1RemoveFromHash(p, 1); } pcache1LeaveMutex(&pcache1.grp); } return nFree; } |
︙ | ︙ | |||
1242 1243 1244 1245 1246 1247 1248 | int *pnMax, /* OUT: Global maximum cache size */ int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */ int *pnRecyclable /* OUT: Total number of pages available for recycling */ ){ PgHdr1 *p; int nRecyclable = 0; for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){ | | | | 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 | int *pnMax, /* OUT: Global maximum cache size */ int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */ int *pnRecyclable /* OUT: Total number of pages available for recycling */ ){ PgHdr1 *p; int nRecyclable = 0; for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){ assert( PAGE_IS_UNPINNED(p) ); nRecyclable++; } *pnCurrent = pcache1.grp.nPurgeable; *pnMax = (int)pcache1.grp.nMaxPage; *pnMin = (int)pcache1.grp.nMinPage; *pnRecyclable = nRecyclable; } #endif |
Changes to src/pragma.c.
︙ | ︙ | |||
159 160 161 162 163 164 165 | } db->temp_store = (u8)ts; return SQLITE_OK; } #endif /* SQLITE_PAGER_PRAGMAS */ /* | | | | < | < > | > > > > | | | | < < | < < < | 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 | } db->temp_store = (u8)ts; return SQLITE_OK; } #endif /* SQLITE_PAGER_PRAGMAS */ /* ** Set result column names for a pragma. */ static void setPragmaResultColumnNames( Vdbe *v, /* The query under construction */ const PragmaName *pPragma /* The pragma */ ){ u8 n = pPragma->nPragCName; sqlite3VdbeSetNumCols(v, n==0 ? 1 : n); if( n==0 ){ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, pPragma->zName, SQLITE_STATIC); }else{ int i, j; for(i=0, j=pPragma->iPragCName; i<n; i++, j++){ sqlite3VdbeSetColName(v, i, COLNAME_NAME, pragCName[j], SQLITE_STATIC); } } } /* ** Generate code to return a single integer value. */ static void returnSingleInt(Vdbe *v, i64 value){ sqlite3VdbeAddOp4Dup8(v, OP_Int64, 0, 1, 0, (const u8*)&value, P4_INT64); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } /* ** Generate code to return a single text value. */ static void returnSingleText( Vdbe *v, /* Prepared statement under construction */ const char *zValue /* Value to be returned */ ){ if( zValue ){ sqlite3VdbeLoadString(v, 1, (const char*)zValue); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } } /* ** Set the safety_level and pager flags for pager iDb. Or if iDb<0 |
︙ | ︙ | |||
272 273 274 275 276 277 278 279 280 281 282 283 284 285 | assert( PAGER_JOURNALMODE_MEMORY==4 ); assert( PAGER_JOURNALMODE_WAL==5 ); assert( eMode>=0 && eMode<=ArraySize(azModeName) ); if( eMode==ArraySize(azModeName) ) return 0; return azModeName[eMode]; } /* ** Process a pragma statement. ** ** Pragmas are of this form: ** ** PRAGMA [schema.]id [= value-list] | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | assert( PAGER_JOURNALMODE_MEMORY==4 ); assert( PAGER_JOURNALMODE_WAL==5 ); assert( eMode>=0 && eMode<=ArraySize(azModeName) ); if( eMode==ArraySize(azModeName) ) return 0; return azModeName[eMode]; } /* ** Locate a pragma in the aPragmaName[] array. */ static const PragmaName *pragmaLocate(const char *zName){ int upr, lwr, mid = 0, rc; lwr = 0; upr = ArraySize(aPragmaName)-1; while( lwr<=upr ){ mid = (lwr+upr)/2; rc = sqlite3_stricmp(zName, aPragmaName[mid].zName); if( rc==0 ) break; if( rc<0 ){ upr = mid - 1; }else{ lwr = mid + 1; } } return lwr>upr ? 0 : &aPragmaName[mid]; } /* ** Helper subroutine for PRAGMA integrity_check: ** ** Generate code to output a single-column result row with a value of the ** string held in register 3. Decrement the result count in register 1 ** and halt if the maximum number of result rows have been issued. */ static int integrityCheckResultRow(Vdbe *v){ int addr; sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1); addr = sqlite3VdbeAddOp3(v, OP_IfPos, 1, sqlite3VdbeCurrentAddr(v)+2, 1); VdbeCoverage(v); sqlite3VdbeAddOp0(v, OP_Halt); return addr; } /* ** Process a pragma statement. ** ** Pragmas are of this form: ** ** PRAGMA [schema.]id [= value-list] |
︙ | ︙ | |||
296 297 298 299 300 301 302 | ){ char *zLeft = 0; /* Nul-terminated UTF-8 string <id> */ char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */ const char *zDb = 0; /* The database name */ Token *pId; /* Pointer to <id> token */ char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */ int iDb; /* Database index for <database> */ | < | | | 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 | ){ char *zLeft = 0; /* Nul-terminated UTF-8 string <id> */ char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */ const char *zDb = 0; /* The database name */ Token *pId; /* Pointer to <id> token */ char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */ int iDb; /* Database index for <database> */ int rc; /* return value form SQLITE_FCNTL_PRAGMA */ sqlite3 *db = pParse->db; /* The database connection */ Db *pDb; /* The specific database being pragmaed */ Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ const PragmaName *pPragma; /* The pragma */ if( v==0 ) goto pragma_out; sqlite3VdbeRunOnlyOnce(v); pParse->nMem = 2; /* Interpret the [schema.] part of the pragma statement. iDb is the ** index of the database this pragma is being applied to in db.aDb[]. */ iDb = sqlite3TwoPartName(pParse, pId1, pId2, &pId); if( iDb<0 ) goto pragma_out; pDb = &db->aDb[iDb]; /* If the temp database has been explicitly named as part of the ** pragma, make sure it is open. */ if( iDb==1 && sqlite3OpenTempDatabase(pParse) ){ goto pragma_out; } zLeft = sqlite3NameFromToken(db, pId); if( !zLeft ) goto pragma_out; if( pValues ) zRight = pValues->a[0].zName; assert( pId2 ); |
︙ | ︙ | |||
352 353 354 355 356 357 358 | aFcntl[0] = 0; aFcntl[1] = zLeft; aFcntl[2] = zRight; aFcntl[3] = 0; db->busyHandler.nBusy = 0; rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl); if( rc==SQLITE_OK ){ | > > | < | < < < < < < < < < < | < | > > > > > > > | 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 | aFcntl[0] = 0; aFcntl[1] = zLeft; aFcntl[2] = zRight; aFcntl[3] = 0; db->busyHandler.nBusy = 0; rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl); if( rc==SQLITE_OK ){ sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, aFcntl[0], SQLITE_TRANSIENT); returnSingleText(v, aFcntl[0]); sqlite3_free(aFcntl[0]); goto pragma_out; } if( rc!=SQLITE_NOTFOUND ){ if( aFcntl[0] ){ sqlite3ErrorMsg(pParse, "%s", aFcntl[0]); sqlite3_free(aFcntl[0]); } pParse->nErr++; pParse->rc = rc; goto pragma_out; } /* Locate the pragma in the lookup table */ pPragma = pragmaLocate(zLeft); if( pPragma==0 ) goto pragma_out; /* Make sure the database schema is loaded if the pragma requires that */ if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; } /* Register the result column names for pragmas that return results */ if( (pPragma->mPragFlg & PragFlg_NoColumns)==0 && ((pPragma->mPragFlg & PragFlg_NoColumns1)==0 || zRight==0) ){ setPragmaResultColumnNames(v, pPragma); } /* Jump to the appropriate pragma handler */ switch( pPragma->ePragTyp ){ #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) /* ** PRAGMA [schema.]default_cache_size |
︙ | ︙ | |||
423 424 425 426 427 428 429 | { OP_Integer, 0, 1, 0}, /* 6 */ { OP_Noop, 0, 0, 0}, { OP_ResultRow, 1, 1, 0}, }; VdbeOp *aOp; sqlite3VdbeUsesBtree(v, iDb); if( !zRight ){ | < | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | { OP_Integer, 0, 1, 0}, /* 6 */ { OP_Noop, 0, 0, 0}, { OP_ResultRow, 1, 1, 0}, }; VdbeOp *aOp; sqlite3VdbeUsesBtree(v, iDb); if( !zRight ){ pParse->nMem += 2; sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(getCacheSize)); aOp = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize, iLn); if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break; aOp[0].p1 = iDb; aOp[1].p1 = iDb; aOp[6].p1 = SQLITE_DEFAULT_CACHE_SIZE; |
︙ | ︙ | |||
458 459 460 461 462 463 464 | ** the database has not yet been created. */ case PragTyp_PAGE_SIZE: { Btree *pBt = pDb->pBt; assert( pBt!=0 ); if( !zRight ){ int size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0; | | | | > > > | > | | 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 | ** the database has not yet been created. */ case PragTyp_PAGE_SIZE: { Btree *pBt = pDb->pBt; assert( pBt!=0 ); if( !zRight ){ int size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0; returnSingleInt(v, size); }else{ /* Malloc may fail when setting the page-size, as there is an internal ** buffer that the pager module resizes using sqlite3_realloc(). */ db->nextPagesize = sqlite3Atoi(zRight); if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){ sqlite3OomFault(db); } } break; } /* ** PRAGMA [schema.]secure_delete ** PRAGMA [schema.]secure_delete=ON/OFF/FAST ** ** The first form reports the current setting for the ** secure_delete flag. The second form changes the secure_delete ** flag setting and reports the new value. */ case PragTyp_SECURE_DELETE: { Btree *pBt = pDb->pBt; int b = -1; assert( pBt!=0 ); if( zRight ){ if( sqlite3_stricmp(zRight, "fast")==0 ){ b = 2; }else{ b = sqlite3GetBoolean(zRight, 0); } } if( pId2->n==0 && b>=0 ){ int ii; for(ii=0; ii<db->nDb; ii++){ sqlite3BtreeSecureDelete(db->aDb[ii].pBt, b); } } b = sqlite3BtreeSecureDelete(pBt, b); returnSingleInt(v, b); break; } /* ** PRAGMA [schema.]max_page_count ** PRAGMA [schema.]max_page_count=N ** |
︙ | ︙ | |||
525 526 527 528 529 530 531 | if( sqlite3Tolower(zLeft[0])=='p' ){ sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); }else{ sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, sqlite3AbsInt32(sqlite3Atoi(zRight))); } sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); | < < | 558 559 560 561 562 563 564 565 566 567 568 569 570 571 | if( sqlite3Tolower(zLeft[0])=='p' ){ sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); }else{ sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, sqlite3AbsInt32(sqlite3Atoi(zRight))); } sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); break; } /* ** PRAGMA [schema.]locking_mode ** PRAGMA [schema.]locking_mode = (normal|exclusive) */ |
︙ | ︙ | |||
572 573 574 575 576 577 578 | } assert( eMode==PAGER_LOCKINGMODE_NORMAL || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){ zRet = "exclusive"; } | | < | 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 | } assert( eMode==PAGER_LOCKINGMODE_NORMAL || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){ zRet = "exclusive"; } returnSingleText(v, zRet); break; } /* ** PRAGMA [schema.]journal_mode ** PRAGMA [schema.]journal_mode = ** (delete|persist|off|truncate|memory|wal|off) */ case PragTyp_JOURNAL_MODE: { int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */ int ii; /* Loop counter */ if( zRight==0 ){ /* If there is no "=MODE" part of the pragma, do a query for the ** current mode */ eMode = PAGER_JOURNALMODE_QUERY; }else{ const char *zMode; int n = sqlite3Strlen30(zRight); |
︙ | ︙ | |||
631 632 633 634 635 636 637 | Pager *pPager = sqlite3BtreePager(pDb->pBt); i64 iLimit = -2; if( zRight ){ sqlite3DecOrHexToI64(zRight, &iLimit); if( iLimit<-1 ) iLimit = -1; } iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); | | | | 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 | Pager *pPager = sqlite3BtreePager(pDb->pBt); i64 iLimit = -2; if( zRight ){ sqlite3DecOrHexToI64(zRight, &iLimit); if( iLimit<-1 ) iLimit = -1; } iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); returnSingleInt(v, iLimit); break; } #endif /* SQLITE_OMIT_PAGER_PRAGMAS */ /* ** PRAGMA [schema.]auto_vacuum ** PRAGMA [schema.]auto_vacuum=N ** ** Get or set the value of the database 'auto-vacuum' parameter. ** The value is one of: 0 NONE 1 FULL 2 INCREMENTAL */ #ifndef SQLITE_OMIT_AUTOVACUUM case PragTyp_AUTO_VACUUM: { Btree *pBt = pDb->pBt; assert( pBt!=0 ); if( !zRight ){ returnSingleInt(v, sqlite3BtreeGetAutoVacuum(pBt)); }else{ int eAuto = getAutoVacuum(zRight); assert( eAuto>=0 && eAuto<=2 ); db->nextAutovac = (u8)eAuto; /* Call SetAutoVacuum() to set initialize the internal auto and ** incr-vacuum flags. This is required in case this connection ** creates the database file. It is important that it is created |
︙ | ︙ | |||
728 729 730 731 732 733 734 | ** number of pages in the cache. If N is negative, then the ** number of pages is adjusted so that the cache uses -N kibibytes ** of memory. */ case PragTyp_CACHE_SIZE: { assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( !zRight ){ | | | 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 | ** number of pages in the cache. If N is negative, then the ** number of pages is adjusted so that the cache uses -N kibibytes ** of memory. */ case PragTyp_CACHE_SIZE: { assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( !zRight ){ returnSingleInt(v, pDb->pSchema->cache_size); }else{ int size = sqlite3Atoi(zRight); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } break; } |
︙ | ︙ | |||
762 763 764 765 766 767 768 | ** ** The cache_spill=BOOLEAN setting applies to all attached schemas, ** not just the schema specified. */ case PragTyp_CACHE_SPILL: { assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( !zRight ){ | | | 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 | ** ** The cache_spill=BOOLEAN setting applies to all attached schemas, ** not just the schema specified. */ case PragTyp_CACHE_SPILL: { assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( !zRight ){ returnSingleInt(v, (db->flags & SQLITE_CacheSpill)==0 ? 0 : sqlite3BtreeSetSpillSize(pDb->pBt,0)); }else{ int size = 1; if( sqlite3GetInt32(zRight, &size) ){ sqlite3BtreeSetSpillSize(pDb->pBt, size); } |
︙ | ︙ | |||
816 817 818 819 820 821 822 | sz = -1; rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_MMAP_SIZE, &sz); #else sz = 0; rc = SQLITE_OK; #endif if( rc==SQLITE_OK ){ | | | | | 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 | sz = -1; rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_MMAP_SIZE, &sz); #else sz = 0; rc = SQLITE_OK; #endif if( rc==SQLITE_OK ){ returnSingleInt(v, sz); }else if( rc!=SQLITE_NOTFOUND ){ pParse->nErr++; pParse->rc = rc; } break; } /* ** PRAGMA temp_store ** PRAGMA temp_store = "default"|"memory"|"file" ** ** Return or set the local value of the temp_store flag. Changing ** the local value does not make changes to the disk file and the default ** value will be restored the next time the database is opened. ** ** Note that it is possible for the library compile-time options to ** override this setting */ case PragTyp_TEMP_STORE: { if( !zRight ){ returnSingleInt(v, db->temp_store); }else{ changeTempStorage(pParse, zRight); } break; } /* ** PRAGMA temp_store_directory ** PRAGMA temp_store_directory = ""|"directory_name" ** ** Return or set the local value of the temp_store_directory flag. Changing ** the value sets a specific directory to be used for temporary files. ** Setting to a null string reverts to the default temporary directory search. ** If temporary directory is changed, then invalidateTempStorage. ** */ case PragTyp_TEMP_STORE_DIRECTORY: { if( !zRight ){ returnSingleText(v, sqlite3_temp_directory); }else{ #ifndef SQLITE_OMIT_WSD if( zRight[0] ){ int res; rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); if( rc!=SQLITE_OK || res==0 ){ sqlite3ErrorMsg(pParse, "not a writable directory"); |
︙ | ︙ | |||
900 901 902 903 904 905 906 | ** a relative path will probably be based on the current directory for the ** process. Database file specified with an absolute path are not impacted ** by this setting, regardless of its value. ** */ case PragTyp_DATA_STORE_DIRECTORY: { if( !zRight ){ | | | 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 | ** a relative path will probably be based on the current directory for the ** process. Database file specified with an absolute path are not impacted ** by this setting, regardless of its value. ** */ case PragTyp_DATA_STORE_DIRECTORY: { if( !zRight ){ returnSingleText(v, sqlite3_data_directory); }else{ #ifndef SQLITE_OMIT_WSD if( zRight[0] ){ int res; rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); if( rc!=SQLITE_OK || res==0 ){ sqlite3ErrorMsg(pParse, "not a writable directory"); |
︙ | ︙ | |||
939 940 941 942 943 944 945 | case PragTyp_LOCK_PROXY_FILE: { if( !zRight ){ Pager *pPager = sqlite3BtreePager(pDb->pBt); char *proxy_file_path = NULL; sqlite3_file *pFile = sqlite3PagerFile(pPager); sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE, &proxy_file_path); | | | 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 | case PragTyp_LOCK_PROXY_FILE: { if( !zRight ){ Pager *pPager = sqlite3BtreePager(pDb->pBt); char *proxy_file_path = NULL; sqlite3_file *pFile = sqlite3PagerFile(pPager); sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE, &proxy_file_path); returnSingleText(v, proxy_file_path); }else{ Pager *pPager = sqlite3BtreePager(pDb->pBt); sqlite3_file *pFile = sqlite3PagerFile(pPager); int res; if( zRight[0] ){ res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE, zRight); |
︙ | ︙ | |||
971 972 973 974 975 976 977 | ** Return or set the local value of the synchronous flag. Changing ** the local value does not make changes to the disk file and the ** default value will be restored the next time the database is ** opened. */ case PragTyp_SYNCHRONOUS: { if( !zRight ){ | | | > | | 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 | ** Return or set the local value of the synchronous flag. Changing ** the local value does not make changes to the disk file and the ** default value will be restored the next time the database is ** opened. */ case PragTyp_SYNCHRONOUS: { if( !zRight ){ returnSingleInt(v, pDb->safety_level-1); }else{ if( !db->autoCommit ){ sqlite3ErrorMsg(pParse, "Safety level may not be changed inside a transaction"); }else if( iDb!=1 ){ int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK; if( iLevel==0 ) iLevel = 1; pDb->safety_level = iLevel; pDb->bSyncSet = 1; setAllPagerFlags(db); } } break; } #endif /* SQLITE_OMIT_PAGER_PRAGMAS */ #ifndef SQLITE_OMIT_FLAG_PRAGMAS case PragTyp_FLAG: { if( zRight==0 ){ setPragmaResultColumnNames(v, pPragma); returnSingleInt(v, (db->flags & pPragma->iArg)!=0 ); }else{ int mask = pPragma->iArg; /* Mask of bits to set or clear. */ if( db->autoCommit==0 ){ /* Foreign key support may not be enabled or disabled while not ** in auto-commit mode. */ mask &= ~(SQLITE_ForeignKeys); } |
︙ | ︙ | |||
1041 1042 1043 1044 1045 1046 1047 | ** notnull: True if 'NOT NULL' is part of column declaration ** dflt_value: The default value for the column, if any. */ case PragTyp_TABLE_INFO: if( zRight ){ Table *pTab; pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb); if( pTab ){ | < < < < | 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 | ** notnull: True if 'NOT NULL' is part of column declaration ** dflt_value: The default value for the column, if any. */ case PragTyp_TABLE_INFO: if( zRight ){ Table *pTab; pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb); if( pTab ){ int i, k; int nHidden = 0; Column *pCol; Index *pPk = sqlite3PrimaryKeyIndex(pTab); pParse->nMem = 6; sqlite3CodeVerifySchema(pParse, iDb); sqlite3ViewGetColumnNames(pParse, pTab); for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ if( IsHiddenColumn(pCol) ){ nHidden++; continue; } if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ |
︙ | ︙ | |||
1072 1073 1074 1075 1076 1077 1078 | sqlite3VdbeMultiLoad(v, 1, "issisi", i-nHidden, pCol->zName, sqlite3ColumnType(pCol,""), pCol->notNull ? 1 : 0, pCol->pDflt ? pCol->pDflt->u.zToken : 0, k); | < > < < | < | | | | | > | > > > > > < < < | < | | < < < < < < < < < < < > > > | > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < | 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 | sqlite3VdbeMultiLoad(v, 1, "issisi", i-nHidden, pCol->zName, sqlite3ColumnType(pCol,""), pCol->notNull ? 1 : 0, pCol->pDflt ? pCol->pDflt->u.zToken : 0, k); } } } break; #ifdef SQLITE_DEBUG case PragTyp_STATS: { Index *pIdx; HashElem *i; pParse->nMem = 5; sqlite3CodeVerifySchema(pParse, iDb); for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); sqlite3VdbeMultiLoad(v, 1, "ssiii", pTab->zName, 0, pTab->szTabRow, pTab->nRowLogEst, pTab->tabFlags); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ sqlite3VdbeMultiLoad(v, 2, "siiiX", pIdx->zName, pIdx->szIdxRow, pIdx->aiRowLogEst[0], pIdx->hasStat1); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 5); } } } break; #endif case PragTyp_INDEX_INFO: if( zRight ){ Index *pIdx; Table *pTab; pIdx = sqlite3FindIndex(db, zRight, zDb); if( pIdx==0 ){ pTab = sqlite3FindTable(db, zRight, zDb); if( pTab && !HasRowid(pTab) ) pIdx = sqlite3PrimaryKeyIndex(pTab); } if( pIdx ){ int i; int mx; if( pPragma->iArg ){ /* PRAGMA index_xinfo (newer version with more rows and columns) */ mx = pIdx->nColumn; pParse->nMem = 6; }else{ /* PRAGMA index_info (legacy version) */ mx = pIdx->nKeyCol; pParse->nMem = 3; } pTab = pIdx->pTable; sqlite3CodeVerifySchema(pParse, iDb); assert( pParse->nMem<=pPragma->nPragCName ); for(i=0; i<mx; i++){ i16 cnum = pIdx->aiColumn[i]; sqlite3VdbeMultiLoad(v, 1, "iisX", i, cnum, cnum<0 ? 0 : pTab->aCol[cnum].zName); if( pPragma->iArg ){ sqlite3VdbeMultiLoad(v, 4, "isiX", pIdx->aSortOrder[i], pIdx->azColl[i], i<pIdx->nKeyCol); } sqlite3VdbeAddOp2(v, OP_ResultRow, 1, pParse->nMem); } } } break; case PragTyp_INDEX_LIST: if( zRight ){ Index *pIdx; Table *pTab; int i; pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ pParse->nMem = 5; sqlite3CodeVerifySchema(pParse, iDb); for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){ const char *azOrigin[] = { "c", "u", "pk" }; sqlite3VdbeMultiLoad(v, 1, "isisi", i, pIdx->zName, IsUniqueIndex(pIdx), azOrigin[pIdx->idxType], pIdx->pPartIdxWhere!=0); } } } break; case PragTyp_DATABASE_LIST: { int i; pParse->nMem = 3; for(i=0; i<db->nDb; i++){ if( db->aDb[i].pBt==0 ) continue; assert( db->aDb[i].zDbSName!=0 ); sqlite3VdbeMultiLoad(v, 1, "iss", i, db->aDb[i].zDbSName, sqlite3BtreeGetFilename(db->aDb[i].pBt)); } } break; case PragTyp_COLLATION_LIST: { int i = 0; HashElem *p; pParse->nMem = 2; for(p=sqliteHashFirst(&db->aCollSeq); p; p=sqliteHashNext(p)){ CollSeq *pColl = (CollSeq *)sqliteHashData(p); sqlite3VdbeMultiLoad(v, 1, "is", i++, pColl->zName); } } break; #ifdef SQLITE_INTROSPECTION_PRAGMAS case PragTyp_FUNCTION_LIST: { int i; HashElem *j; FuncDef *p; pParse->nMem = 2; for(i=0; i<SQLITE_FUNC_HASH_SZ; i++){ for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash ){ sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 1); } } for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){ p = (FuncDef*)sqliteHashData(j); sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 0); } } break; #ifndef SQLITE_OMIT_VIRTUALTABLE case PragTyp_MODULE_LIST: { HashElem *j; pParse->nMem = 1; for(j=sqliteHashFirst(&db->aModule); j; j=sqliteHashNext(j)){ Module *pMod = (Module*)sqliteHashData(j); sqlite3VdbeMultiLoad(v, 1, "s", pMod->zName); } } break; #endif /* SQLITE_OMIT_VIRTUALTABLE */ case PragTyp_PRAGMA_LIST: { int i; for(i=0; i<ArraySize(aPragmaName); i++){ sqlite3VdbeMultiLoad(v, 1, "s", aPragmaName[i].zName); } } break; #endif /* SQLITE_INTROSPECTION_PRAGMAS */ #endif /* SQLITE_OMIT_SCHEMA_PRAGMAS */ #ifndef SQLITE_OMIT_FOREIGN_KEY case PragTyp_FOREIGN_KEY_LIST: if( zRight ){ FKey *pFK; Table *pTab; pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ pFK = pTab->pFKey; if( pFK ){ int i = 0; pParse->nMem = 8; sqlite3CodeVerifySchema(pParse, iDb); while(pFK){ int j; for(j=0; j<pFK->nCol; j++){ sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j, pFK->zTo, pTab->aCol[pFK->aCol[j].iFrom].zName, pFK->aCol[j].zCol, actionName(pFK->aAction[1]), /* ON UPDATE */ actionName(pFK->aAction[0]), /* ON DELETE */ "NONE"); } ++i; pFK = pFK->pNextFrom; } } } } |
︙ | ︙ | |||
1260 1261 1262 1263 1264 1265 1266 | int x; /* result variable */ int regResult; /* 3 registers to hold a result row */ int regKey; /* Register to hold key for checking the FK */ int regRow; /* Registers to hold a row from pTab */ int addrTop; /* Top of a loop checking foreign keys */ int addrOk; /* Jump here if the key is OK */ int *aiCols; /* child to parent column mapping */ | < < < | 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 | int x; /* result variable */ int regResult; /* 3 registers to hold a result row */ int regKey; /* Register to hold key for checking the FK */ int regRow; /* Registers to hold a row from pTab */ int addrTop; /* Top of a loop checking foreign keys */ int addrOk; /* Jump here if the key is OK */ int *aiCols; /* child to parent column mapping */ regResult = pParse->nMem+1; pParse->nMem += 4; regKey = ++pParse->nMem; regRow = ++pParse->nMem; sqlite3CodeVerifySchema(pParse, iDb); k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); while( k ){ if( zRight ){ pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); k = 0; }else{ |
︙ | ︙ | |||
1314 1315 1316 1317 1318 1319 1320 | pIdx = 0; aiCols = 0; if( pParent ){ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); assert( x==0 ); } addrOk = sqlite3VdbeMakeLabel(v); | < < < < < < < < < | > | < < > | | > | < | | > > > | | | | | > > > > > | | > > | > > > | | 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 | pIdx = 0; aiCols = 0; if( pParent ){ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); assert( x==0 ); } addrOk = sqlite3VdbeMakeLabel(v); /* Generate code to read the child key values into registers ** regRow..regRow+n. If any of the child key values are NULL, this ** row cannot cause an FK violation. Jump directly to addrOk in ** this case. */ for(j=0; j<pFK->nCol; j++){ int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom; sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j); sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); VdbeCoverage(v); } /* Generate code to query the parent index for a matching parent ** key. If a match is found, jump to addrOk. */ if( pIdx ){ sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey, sqlite3IndexAffinityStr(db,pIdx), pFK->nCol); sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); VdbeCoverage(v); }else if( pParent ){ int jmp = sqlite3VdbeCurrentAddr(v)+2; sqlite3VdbeAddOp3(v, OP_SeekRowid, i, jmp, regRow); VdbeCoverage(v); sqlite3VdbeGoto(v, addrOk); assert( pFK->nCol==1 ); } /* Generate code to report an FK violation to the caller. */ if( HasRowid(pTab) ){ sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, regResult+1); } sqlite3VdbeMultiLoad(v, regResult+2, "siX", pFK->zTo, i-1); sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4); sqlite3VdbeResolveLabel(v, addrOk); sqlite3DbFree(db, aiCols); } sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addrTop); } |
︙ | ︙ | |||
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 | case PragTyp_CASE_SENSITIVE_LIKE: { if( zRight ){ sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight, 0)); } } break; #ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX # define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100 #endif #ifndef SQLITE_OMIT_INTEGRITY_CHECK | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | 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 | case PragTyp_CASE_SENSITIVE_LIKE: { if( zRight ){ sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight, 0)); } } break; /* ** PRAGMA est_row_cnt(<table-or-index>,<fraction>); ** ** Seek in <table-or-index> through the first <fraction> of rows and ** estimate the total number of rows based on the path back up to the ** root. */ case PragTyp_EST_COUNT: { Index *pIdx; Table *pTab = 0; Pgno iRoot = 0; const char *zName = 0; int regResult; double r; if( (pIdx = sqlite3FindIndex(db, zRight, zDb))!=0 ){ iRoot = pIdx->tnum; zName = pIdx->zName; }else if( (pTab = sqlite3FindTable(db, zRight, zDb))!=0 ){ zName = pTab->zName; if( HasRowid(pTab) ){ iRoot = pTab->tnum; }else{ pIdx = sqlite3PrimaryKeyIndex(pTab); iRoot = pIdx->tnum; } }else{ break; } sqlite3TableLock(pParse, iDb, iRoot, 0, zName); regResult = ++pParse->nMem; if( pValues->nId>=2 ){ const char *z = pValues->a[1].zName; sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); }else{ r = 0.5; } if( r<0.0 ) r = 0.0; if( r>1.0 ) r = 1.0; sqlite3CodeVerifySchema(pParse, iDb); pParse->nTab++; sqlite3VdbeAddOp4Int(v, OP_OpenRead, 0, iRoot, iDb, 1); if( pIdx ) sqlite3VdbeSetP4KeyInfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_EstRowCnt, 0, regResult, (int)(r*1000000000)); sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 1); } break; /* ** PRAGMA btree_sample(<table-or-index>,<fraction>,<limit>); ** ** Seek in <table-or-index> through the first <fraction> of rows and ** then begin returning rows, one by one. A max of <limit> rows will ** be returned. */ case PragTyp_BTREE_SAMPLE: { Index *pIdx; Table *pTab = 0; Pgno iRoot = 0; Pgno iLock = 0; int nCol = 0; const char *zName = 0; int iLimit = 10; int i; int regResult; int regLimit; int addrTop; int addrJmp; int addrSkip; double r; if( (pIdx = sqlite3FindIndex(db, zRight, zDb))!=0 ){ iRoot = pIdx->tnum; iLock = pIdx->pTable->tnum; zName = pIdx->zName; nCol = pIdx->nColumn; }else if( (pTab = sqlite3FindTable(db, zRight, zDb))!=0 ){ zName = pTab->zName; if( HasRowid(pTab) ){ iLock = iRoot = pTab->tnum; nCol = pTab->nCol; }else{ pIdx = sqlite3PrimaryKeyIndex(pTab); iLock = iRoot = pIdx->tnum; nCol = pIdx->nColumn; } }else{ break; } sqlite3VdbeSetNumCols(v, nCol); for(i=0; i<nCol; i++){ char zCol[30]; sqlite3_snprintf(sizeof(zCol),zCol,"c%06d",i); sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT); } if( pValues->nId>=2 ){ const char *z = pValues->a[1].zName; sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); }else{ r = 0.5; } if( r<0.0 ) r = 0.0; if( r>1.0 ) r = 1.0; if( pValues->nId>=3 ){ iLimit = sqlite3Atoi(pValues->a[2].zName); } pParse->nTab++; sqlite3TableLock(pParse, iDb, iLock, 0, zName); sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeAddOp4Int(v, OP_OpenRead, 0, iRoot, iDb, nCol); if( pIdx ) sqlite3VdbeSetP4KeyInfo(pParse, pIdx); regLimit = ++pParse->nMem; regResult = pParse->nMem+1; pParse->nMem += nCol; sqlite3VdbeAddOp2(v, OP_Integer, iLimit, regLimit); addrSkip = sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_EstRowCnt, 0, regResult, (int)(r*1000000000)); addrTop = sqlite3VdbeCurrentAddr(v); for(i=0; i<nCol; i++){ sqlite3VdbeAddOp3(v, OP_Column, 0, i, regResult+i); } sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nCol); addrJmp = sqlite3VdbeAddOp1(v, OP_DecrJumpZero, regLimit); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addrJmp); sqlite3VdbeJumpHere(v, addrSkip); } break; #ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX # define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100 #endif #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* PRAGMA integrity_check ** PRAGMA integrity_check(N) ** PRAGMA quick_check ** PRAGMA quick_check(N) ** ** Verify the integrity of the database. ** ** The "quick_check" is reduced version of ** integrity_check designed to detect most database corruption ** without the overhead of cross-checking indexes. Quick_check ** is linear time wherease integrity_check is O(NlogN). */ case PragTyp_INTEGRITY_CHECK: { int i, j, addr, mxErr; int isQuick = (sqlite3Tolower(zLeft[0])=='q'); /* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check", |
︙ | ︙ | |||
1406 1407 1408 1409 1410 1411 1412 | ** of all attached databases. */ assert( iDb>=0 ); assert( iDb==0 || pId2->z ); if( pId2->z==0 ) iDb = -1; /* Initialize the VDBE program */ pParse->nMem = 6; | < | | | | | | < < < < < | | > | | | > < | | | | < < < < > > > | < > | > > > > > > | > > > > > > > > > > > > > | > | | | > > > > > > | | | | | | | | | | | | < | | | | | | | < < | | | | | | | | | | | | | | | | | | | | < | | | | | | > > | | | < < < | | | < | | > | > | > > > | > > > | 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 | ** of all attached databases. */ assert( iDb>=0 ); assert( iDb==0 || pId2->z ); if( pId2->z==0 ) iDb = -1; /* Initialize the VDBE program */ pParse->nMem = 6; /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ sqlite3GetInt32(zRight, &mxErr); if( mxErr<=0 ){ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; } } sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */ /* Do an integrity check on each database file */ for(i=0; i<db->nDb; i++){ HashElem *x; /* For looping over tables in the schema */ Hash *pTbls; /* Set of all tables in the schema */ int *aRoot; /* Array of root page numbers of all btrees */ int cnt = 0; /* Number of entries in aRoot[] */ int mxIdx = 0; /* Maximum number of indexes for any table */ if( OMIT_TEMPDB && i==1 ) continue; if( iDb>=0 && i!=iDb ) continue; sqlite3CodeVerifySchema(pParse, i); /* Do an integrity check of the B-Tree ** ** Begin by finding the root pages numbers ** for all tables and indices in the database. */ assert( sqlite3SchemaMutexHeld(db, i, 0) ); pTbls = &db->aDb[i].pSchema->tblHash; 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( HasRowid(pTab) ) cnt++; for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } if( nIdx>mxIdx ) mxIdx = nIdx; } aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1)); if( aRoot==0 ) break; for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx; 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 */ pParse->nMem = MAX( pParse->nMem, 8+mxIdx ); sqlite3ClearTempRegCache(pParse); /* Do the b-tree integrity checks */ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (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); /* 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; int loopTop; int iDataCur, iIdxCur; int r1 = -1; if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); sqlite3ExprCacheClear(pParse); sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, 1, 0, &iDataCur, &iIdxCur); /* reg[7] counts the number of entries in the table. ** reg[8+i] counts the number of entries in the i-th index */ sqlite3VdbeAddOp2(v, OP_Integer, 0, 7); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */ } assert( pParse->nMem>=8+j ); assert( sqlite3NoTempsInRange(pParse,1,7+j) ); sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v); loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1); /* Verify that all NOT NULL columns really are NOT NULL */ for(j=0; j<pTab->nCol; j++){ char *zErr; int jmp2; if( j==pTab->iPKey ) continue; if( pTab->aCol[j].notNull==0 ) continue; sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v); zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, pTab->aCol[j].zName); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, jmp2); } /* Verify CHECK constraints */ if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ ExprList *pCheck = sqlite3ExprListDup(db, pTab->pCheck, 0); if( db->mallocFailed==0 ){ int addrCkFault = sqlite3VdbeMakeLabel(v); int addrCkOk = sqlite3VdbeMakeLabel(v); char *zErr; int k; pParse->iSelfTab = iDataCur + 1; sqlite3ExprCachePush(pParse); for(k=pCheck->nExpr-1; k>0; k--){ sqlite3ExprIfFalse(pParse, pCheck->a[k].pExpr, addrCkFault, 0); } sqlite3ExprIfTrue(pParse, pCheck->a[0].pExpr, addrCkOk, SQLITE_JUMPIFNULL); sqlite3VdbeResolveLabel(v, addrCkFault); pParse->iSelfTab = 0; zErr = sqlite3MPrintf(db, "CHECK constraint failed in %s", pTab->zName); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); integrityCheckResultRow(v); sqlite3VdbeResolveLabel(v, addrCkOk); sqlite3ExprCachePop(pParse); } sqlite3ExprListDelete(db, pCheck); } if( !isQuick ){ /* Omit the remaining tests for quick_check */ /* Sanity check on record header decoding */ sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nCol-1, 3); sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); /* Validate index entries for the current row */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int jmp2, jmp3, jmp4, jmp5; int ckUniq = sqlite3VdbeMakeLabel(v); if( pPk==pIdx ) continue; r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3, pPrior, r1); pPrior = pIdx; sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1);/* increment entry count */ /* Verify that an index entry exists for the current table row */ jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1, pIdx->nColumn); VdbeCoverage(v); sqlite3VdbeLoadString(v, 3, "row "); sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); sqlite3VdbeLoadString(v, 4, " missing from index "); sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName); sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); jmp4 = integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, jmp2); /* For UNIQUE indexes, verify that only one entry exists with the ** current key. The entry is unique if (1) any column is NULL ** or (2) the next entry has a different key */ if( IsUniqueIndex(pIdx) ){ int uniqOk = sqlite3VdbeMakeLabel(v); int jmp6; int kk; for(kk=0; kk<pIdx->nKeyCol; kk++){ int iCol = pIdx->aiColumn[kk]; assert( iCol!=XN_ROWID && iCol<pTab->nCol ); if( iCol>=0 && pTab->aCol[iCol].notNull ) continue; sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk); VdbeCoverage(v); } jmp6 = sqlite3VdbeAddOp1(v, OP_Next, iIdxCur+j); VdbeCoverage(v); sqlite3VdbeGoto(v, uniqOk); sqlite3VdbeJumpHere(v, jmp6); sqlite3VdbeAddOp4Int(v, OP_IdxGT, iIdxCur+j, uniqOk, r1, pIdx->nKeyCol); VdbeCoverage(v); sqlite3VdbeLoadString(v, 3, "non-unique entry in index "); sqlite3VdbeGoto(v, jmp5); sqlite3VdbeResolveLabel(v, uniqOk); } sqlite3VdbeJumpHere(v, jmp4); sqlite3ResolvePartIdxLabel(pParse, jmp3); } } sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, loopTop-1); #ifndef SQLITE_OMIT_BTREECOUNT if( !isQuick ){ sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ if( pPk==pIdx ) continue; sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v); sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); sqlite3VdbeLoadString(v, 4, pIdx->zName); sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, addr); } } #endif /* SQLITE_OMIT_BTREECOUNT */ } } { 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 */ { OP_ResultRow, 3, 1, 0}, /* 3 */ { OP_Halt, 0, 0, 0}, /* 4 */ { OP_String8, 0, 3, 0}, /* 5 */ { OP_Goto, 0, 3, 0}, /* 6 */ }; VdbeOp *aOp; aOp = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn); if( aOp ){ aOp[0].p2 = 1-mxErr; aOp[2].p4type = P4_STATIC; aOp[2].p4.z = "ok"; aOp[5].p4type = P4_STATIC; aOp[5].p4.z = (char*)sqlite3ErrStr(SQLITE_CORRUPT); } sqlite3VdbeChangeP3(v, 0, sqlite3VdbeCurrentAddr(v)-2); } } break; #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ #ifndef SQLITE_OMIT_UTF16 /* |
︙ | ︙ | |||
1658 1659 1660 1661 1662 1663 1664 | }; const struct EncName *pEnc; if( !zRight ){ /* "PRAGMA encoding" */ if( sqlite3ReadSchema(pParse) ) goto pragma_out; assert( encnames[SQLITE_UTF8].enc==SQLITE_UTF8 ); assert( encnames[SQLITE_UTF16LE].enc==SQLITE_UTF16LE ); assert( encnames[SQLITE_UTF16BE].enc==SQLITE_UTF16BE ); | | | 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 | }; const struct EncName *pEnc; if( !zRight ){ /* "PRAGMA encoding" */ if( sqlite3ReadSchema(pParse) ) goto pragma_out; assert( encnames[SQLITE_UTF8].enc==SQLITE_UTF8 ); assert( encnames[SQLITE_UTF16LE].enc==SQLITE_UTF16LE ); assert( encnames[SQLITE_UTF16BE].enc==SQLITE_UTF16BE ); returnSingleText(v, encnames[ENC(pParse->db)].zName); }else{ /* "PRAGMA encoding = XXX" */ /* Only change the value of sqlite.enc if the database handle is not ** initialized. If the main database exists, the new sqlite.enc value ** will be overwritten when the schema is next loaded. If it does not ** already exists, it will be created to use the new encoding value. */ if( |
︙ | ︙ | |||
1721 1722 1723 1724 1725 1726 1727 | ** ** The user-version is not used internally by SQLite. It may be used by ** applications for any purpose. */ case PragTyp_HEADER_VALUE: { int iCookie = pPragma->iArg; /* Which cookie to read or write */ sqlite3VdbeUsesBtree(v, iDb); | | | 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 | ** ** The user-version is not used internally by SQLite. It may be used by ** applications for any purpose. */ case PragTyp_HEADER_VALUE: { int iCookie = pPragma->iArg; /* Which cookie to read or write */ sqlite3VdbeUsesBtree(v, iDb); if( zRight && (pPragma->mPragFlg & PragFlg_ReadOnly)==0 ){ /* Write the specified cookie value */ static const VdbeOpList setCookie[] = { { OP_Transaction, 0, 1, 0}, /* 0 */ { OP_SetCookie, 0, 0, 0}, /* 1 */ }; VdbeOp *aOp; sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(setCookie)); |
︙ | ︙ | |||
1749 1750 1751 1752 1753 1754 1755 | VdbeOp *aOp; sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(readCookie)); aOp = sqlite3VdbeAddOpList(v, ArraySize(readCookie),readCookie,0); if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break; aOp[0].p1 = iDb; aOp[1].p1 = iDb; aOp[1].p3 = iCookie; | < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | < < < | 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 | VdbeOp *aOp; sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(readCookie)); aOp = sqlite3VdbeAddOpList(v, ArraySize(readCookie),readCookie,0); if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break; aOp[0].p1 = iDb; aOp[1].p1 = iDb; aOp[1].p3 = iCookie; sqlite3VdbeReusable(v); } } break; #endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* ** PRAGMA compile_options ** ** Return the names of all compile-time options used in this build, ** one option per row. */ case PragTyp_COMPILE_OPTIONS: { int i = 0; const char *zOpt; pParse->nMem = 1; while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){ sqlite3VdbeLoadString(v, 1, zOpt); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } sqlite3VdbeReusable(v); } break; #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ #ifndef SQLITE_OMIT_WAL /* ** PRAGMA [schema.]wal_checkpoint = passive|full|restart|truncate ** ** Checkpoint the database. */ case PragTyp_WAL_CHECKPOINT: { int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); int eMode = SQLITE_CHECKPOINT_PASSIVE; if( zRight ){ if( sqlite3StrICmp(zRight, "full")==0 ){ eMode = SQLITE_CHECKPOINT_FULL; }else if( sqlite3StrICmp(zRight, "restart")==0 ){ eMode = SQLITE_CHECKPOINT_RESTART; }else if( sqlite3StrICmp(zRight, "truncate")==0 ){ eMode = SQLITE_CHECKPOINT_TRUNCATE; } } pParse->nMem = 3; sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); } break; /* ** PRAGMA wal_autocheckpoint ** PRAGMA wal_autocheckpoint = N ** ** Configure a database connection to automatically checkpoint a database ** after accumulating N frames in the log. Or query for the current value ** of N. */ case PragTyp_WAL_AUTOCHECKPOINT: { if( zRight ){ sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight)); } returnSingleInt(v, db->xWalCallback==sqlite3WalDefaultHook ? SQLITE_PTR_TO_INT(db->pWalArg) : 0); } break; #endif /* ** PRAGMA shrink_memory ** ** IMPLEMENTATION-OF: R-23445-46109 This pragma causes the database ** connection on which it is invoked to free up as much memory as it ** can, by calling sqlite3_db_release_memory(). */ case PragTyp_SHRINK_MEMORY: { sqlite3_db_release_memory(db); break; } /* ** PRAGMA optimize ** PRAGMA optimize(MASK) ** PRAGMA schema.optimize ** PRAGMA schema.optimize(MASK) ** ** Attempt to optimize the database. All schemas are optimized in the first ** two forms, and only the specified schema is optimized in the latter two. ** ** 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: ** ** 0x0001 Debugging mode. Do not actually perform any optimizations ** but instead return one line of text for each optimization ** that would have been done. Off by default. ** ** 0x0002 Run ANALYZE on tables that might benefit. On by default. ** See below for additional information. ** ** 0x0004 (Not yet implemented) Record usage and performance ** information from the current session in the ** database file so that it will be available to "optimize" ** pragmas run by future database connections. ** ** 0x0008 (Not yet implemented) Create indexes that might have ** been helpful to recent queries ** ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all ** of the optimizations listed above except Debug Mode, including new ** optimizations that have not yet been invented. If new optimizations are ** ever added that should be off by default, those off-by-default ** optimizations will have bitmasks of 0x10000 or larger. ** ** DETERMINATION OF WHEN TO RUN ANALYZE ** ** In the current implementation, a table is analyzed if only if all of ** the following are true: ** ** (1) MASK bit 0x02 is set. ** ** (2) The query planner used sqlite_stat1-style statistics for one or ** more indexes of the table at some point during the lifetime of ** the current connection. ** ** (3) One or more indexes of the table are currently unanalyzed OR ** the number of rows in the table has increased by 25 times or more ** since the last time ANALYZE was run. ** ** The rules for when tables are analyzed are likely to change in ** future releases. */ 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 is needd */ char *zSubSql; /* SQL statement for the OP_SqlExec opcode */ u32 opMask; /* Mask of operations to perform */ if( zRight ){ opMask = (u32)sqlite3Atoi(zRight); if( (opMask & 0x02)==0 ) break; }else{ opMask = 0xfffe; } 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); /* If table pTab has not been used in a way that would benefit from ** having analysis statistics during the current session, then skip it. ** This also has the effect of skipping virtual tables and views */ if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue; /* Reanalyze if the table is 25 times larger than the last analysis */ szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 ); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( !pIdx->hasStat1 ){ szThreshold = 0; /* Always analyze if any index lacks statistics */ break; } } if( szThreshold ){ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur, sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold); VdbeCoverage(v); } zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"", db->aDb[iDb].zDbSName, pTab->zName); if( opMask & 0x01 ){ int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC); sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1); }else{ sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC); } } } sqlite3VdbeAddOp0(v, OP_Expire); break; } /* ** PRAGMA busy_timeout ** PRAGMA busy_timeout = N ** ** Call sqlite3_busy_timeout(db, N). Return the current timeout value ** if one is set. If no busy handler or a different busy handler is set ** then 0 is returned. Setting the busy_timeout to 0 or negative ** disables the timeout. */ /*case PragTyp_BUSY_TIMEOUT*/ default: { assert( pPragma->ePragTyp==PragTyp_BUSY_TIMEOUT ); if( zRight ){ sqlite3_busy_timeout(db, sqlite3Atoi(zRight)); } returnSingleInt(v, db->busyTimeout); break; } /* ** PRAGMA soft_heap_limit ** PRAGMA soft_heap_limit = N ** ** IMPLEMENTATION-OF: R-26343-45930 This pragma invokes the ** sqlite3_soft_heap_limit64() interface with the argument N, if N is ** specified and is a non-negative integer. ** IMPLEMENTATION-OF: R-64451-07163 The soft_heap_limit pragma always ** returns the same integer that would be returned by the ** sqlite3_soft_heap_limit64(-1) C-language function. */ case PragTyp_SOFT_HEAP_LIMIT: { sqlite3_int64 N; if( zRight && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK ){ sqlite3_soft_heap_limit64(N); } returnSingleInt(v, sqlite3_soft_heap_limit64(-1)); break; } /* ** PRAGMA threads ** PRAGMA threads = N ** ** Configure the maximum number of worker threads. Return the new ** maximum, which might be less than requested. */ case PragTyp_THREADS: { sqlite3_int64 N; if( zRight && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK && N>=0 ){ sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff)); } returnSingleInt(v, sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1)); break; } #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) /* ** Report the current state of file logs for all databases */ case PragTyp_LOCK_STATUS: { static const char *const azLockName[] = { "unlocked", "shared", "reserved", "pending", "exclusive" }; int i; pParse->nMem = 2; for(i=0; i<db->nDb; i++){ Btree *pBt; const char *zState = "unknown"; int j; if( db->aDb[i].zDbSName==0 ) continue; pBt = db->aDb[i].pBt; if( pBt==0 || sqlite3BtreePager(pBt)==0 ){ zState = "closed"; }else if( sqlite3_file_control(db, i ? db->aDb[i].zDbSName : 0, SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){ zState = azLockName[j]; } sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zDbSName, zState); } break; } #endif #ifdef SQLITE_HAS_CODEC case PragTyp_KEY: { |
︙ | ︙ | |||
1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 | } #endif } break; #endif } /* End of the PRAGMA switch */ pragma_out: sqlite3DbFree(db, zLeft); sqlite3IdListDelete(db, pValues); } #endif /* SQLITE_OMIT_PRAGMA */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } #endif } break; #endif } /* End of the PRAGMA switch */ /* The following block is a no-op unless SQLITE_DEBUG is defined. Its only ** purpose is to execute assert() statements to verify that if the ** PragFlg_NoColumns1 flag is set and the caller specified an argument ** to the PRAGMA, the implementation has not added any OP_ResultRow ** instructions to the VM. */ if( (pPragma->mPragFlg & PragFlg_NoColumns1) && zRight ){ sqlite3VdbeVerifyNoResultRow(v); } pragma_out: sqlite3DbFree(db, zLeft); sqlite3IdListDelete(db, pValues); } #ifndef SQLITE_OMIT_VIRTUALTABLE /***************************************************************************** ** Implementation of an eponymous virtual table that runs a pragma. ** */ typedef struct PragmaVtab PragmaVtab; typedef struct PragmaVtabCursor PragmaVtabCursor; struct PragmaVtab { sqlite3_vtab base; /* Base class. Must be first */ sqlite3 *db; /* The database connection to which it belongs */ const PragmaName *pName; /* Name of the pragma */ u8 nHidden; /* Number of hidden columns */ u8 iHidden; /* Index of the first hidden column */ }; struct PragmaVtabCursor { sqlite3_vtab_cursor base; /* Base class. Must be first */ sqlite3_stmt *pPragma; /* The pragma statement to run */ sqlite_int64 iRowid; /* Current rowid */ char *azArg[2]; /* Value of the argument and schema */ }; /* ** Pragma virtual table module xConnect method. */ static int pragmaVtabConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ const PragmaName *pPragma = (const PragmaName*)pAux; PragmaVtab *pTab = 0; int rc; int i, j; char cSep = '('; StrAccum acc; char zBuf[200]; UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argv); sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); sqlite3StrAccumAppendAll(&acc, "CREATE TABLE x"); for(i=0, j=pPragma->iPragCName; i<pPragma->nPragCName; i++, j++){ sqlite3XPrintf(&acc, "%c\"%s\"", cSep, pragCName[j]); cSep = ','; } if( i==0 ){ sqlite3XPrintf(&acc, "(\"%s\"", pPragma->zName); cSep = ','; i++; } j = 0; if( pPragma->mPragFlg & PragFlg_Result1 ){ sqlite3StrAccumAppendAll(&acc, ",arg HIDDEN"); j++; } if( pPragma->mPragFlg & (PragFlg_SchemaOpt|PragFlg_SchemaReq) ){ sqlite3StrAccumAppendAll(&acc, ",schema HIDDEN"); j++; } sqlite3StrAccumAppend(&acc, ")", 1); sqlite3StrAccumFinish(&acc); assert( strlen(zBuf) < sizeof(zBuf)-1 ); rc = sqlite3_declare_vtab(db, zBuf); if( rc==SQLITE_OK ){ pTab = (PragmaVtab*)sqlite3_malloc(sizeof(PragmaVtab)); if( pTab==0 ){ rc = SQLITE_NOMEM; }else{ memset(pTab, 0, sizeof(PragmaVtab)); pTab->pName = pPragma; pTab->db = db; pTab->iHidden = i; pTab->nHidden = j; } }else{ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } *ppVtab = (sqlite3_vtab*)pTab; return rc; } /* ** Pragma virtual table module xDisconnect method. */ static int pragmaVtabDisconnect(sqlite3_vtab *pVtab){ PragmaVtab *pTab = (PragmaVtab*)pVtab; sqlite3_free(pTab); return SQLITE_OK; } /* Figure out the best index to use to search a pragma virtual table. ** ** There are not really any index choices. But we want to encourage the ** query planner to give == constraints on as many hidden parameters as ** possible, and especially on the first hidden parameter. So return a ** high cost if hidden parameters are unconstrained. */ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ PragmaVtab *pTab = (PragmaVtab*)tab; const struct sqlite3_index_constraint *pConstraint; int i, j; int seen[2]; pIdxInfo->estimatedCost = (double)1; if( pTab->nHidden==0 ){ return SQLITE_OK; } pConstraint = pIdxInfo->aConstraint; seen[0] = 0; seen[1] = 0; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( pConstraint->iColumn < pTab->iHidden ) continue; j = pConstraint->iColumn - pTab->iHidden; assert( j < 2 ); seen[j] = i+1; } if( seen[0]==0 ){ pIdxInfo->estimatedCost = (double)2147483647; pIdxInfo->estimatedRows = 2147483647; return SQLITE_OK; } j = seen[0]-1; pIdxInfo->aConstraintUsage[j].argvIndex = 1; pIdxInfo->aConstraintUsage[j].omit = 1; if( seen[1]==0 ) return SQLITE_OK; pIdxInfo->estimatedCost = (double)20; pIdxInfo->estimatedRows = 20; j = seen[1]-1; pIdxInfo->aConstraintUsage[j].argvIndex = 2; pIdxInfo->aConstraintUsage[j].omit = 1; return SQLITE_OK; } /* Create a new cursor for the pragma virtual table */ static int pragmaVtabOpen(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){ PragmaVtabCursor *pCsr; pCsr = (PragmaVtabCursor*)sqlite3_malloc(sizeof(*pCsr)); if( pCsr==0 ) return SQLITE_NOMEM; memset(pCsr, 0, sizeof(PragmaVtabCursor)); pCsr->base.pVtab = pVtab; *ppCursor = &pCsr->base; return SQLITE_OK; } /* Clear all content from pragma virtual table cursor. */ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ int i; sqlite3_finalize(pCsr->pPragma); pCsr->pPragma = 0; for(i=0; i<ArraySize(pCsr->azArg); i++){ sqlite3_free(pCsr->azArg[i]); pCsr->azArg[i] = 0; } } /* Close a pragma virtual table cursor */ static int pragmaVtabClose(sqlite3_vtab_cursor *cur){ PragmaVtabCursor *pCsr = (PragmaVtabCursor*)cur; pragmaVtabCursorClear(pCsr); sqlite3_free(pCsr); return SQLITE_OK; } /* Advance the pragma virtual table cursor to the next row */ static int pragmaVtabNext(sqlite3_vtab_cursor *pVtabCursor){ PragmaVtabCursor *pCsr = (PragmaVtabCursor*)pVtabCursor; int rc = SQLITE_OK; /* Increment the xRowid value */ pCsr->iRowid++; assert( pCsr->pPragma ); if( SQLITE_ROW!=sqlite3_step(pCsr->pPragma) ){ rc = sqlite3_finalize(pCsr->pPragma); pCsr->pPragma = 0; pragmaVtabCursorClear(pCsr); } return rc; } /* ** Pragma virtual table module xFilter method. */ static int pragmaVtabFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ PragmaVtabCursor *pCsr = (PragmaVtabCursor*)pVtabCursor; PragmaVtab *pTab = (PragmaVtab*)(pVtabCursor->pVtab); int rc; int i, j; StrAccum acc; char *zSql; UNUSED_PARAMETER(idxNum); UNUSED_PARAMETER(idxStr); pragmaVtabCursorClear(pCsr); j = (pTab->pName->mPragFlg & PragFlg_Result1)!=0 ? 0 : 1; for(i=0; i<argc; i++, j++){ const char *zText = (const char*)sqlite3_value_text(argv[i]); assert( j<ArraySize(pCsr->azArg) ); assert( pCsr->azArg[j]==0 ); if( zText ){ pCsr->azArg[j] = sqlite3_mprintf("%s", zText); if( pCsr->azArg[j]==0 ){ return SQLITE_NOMEM; } } } sqlite3StrAccumInit(&acc, 0, 0, 0, pTab->db->aLimit[SQLITE_LIMIT_SQL_LENGTH]); sqlite3StrAccumAppendAll(&acc, "PRAGMA "); if( pCsr->azArg[1] ){ sqlite3XPrintf(&acc, "%Q.", pCsr->azArg[1]); } sqlite3StrAccumAppendAll(&acc, pTab->pName->zName); if( pCsr->azArg[0] ){ sqlite3XPrintf(&acc, "=%Q", pCsr->azArg[0]); } zSql = sqlite3StrAccumFinish(&acc); if( zSql==0 ) return SQLITE_NOMEM; rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pPragma, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK ){ pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db)); return rc; } return pragmaVtabNext(pVtabCursor); } /* ** Pragma virtual table module xEof method. */ static int pragmaVtabEof(sqlite3_vtab_cursor *pVtabCursor){ PragmaVtabCursor *pCsr = (PragmaVtabCursor*)pVtabCursor; return (pCsr->pPragma==0); } /* The xColumn method simply returns the corresponding column from ** the PRAGMA. */ static int pragmaVtabColumn( sqlite3_vtab_cursor *pVtabCursor, sqlite3_context *ctx, int i ){ PragmaVtabCursor *pCsr = (PragmaVtabCursor*)pVtabCursor; PragmaVtab *pTab = (PragmaVtab*)(pVtabCursor->pVtab); if( i<pTab->iHidden ){ sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pPragma, i)); }else{ sqlite3_result_text(ctx, pCsr->azArg[i-pTab->iHidden],-1,SQLITE_TRANSIENT); } return SQLITE_OK; } /* ** Pragma virtual table module xRowid method. */ static int pragmaVtabRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *p){ PragmaVtabCursor *pCsr = (PragmaVtabCursor*)pVtabCursor; *p = pCsr->iRowid; return SQLITE_OK; } /* The pragma virtual table object */ static const sqlite3_module pragmaVtabModule = { 0, /* iVersion */ 0, /* xCreate - create a table */ pragmaVtabConnect, /* xConnect - connect to an existing table */ pragmaVtabBestIndex, /* xBestIndex - Determine search strategy */ pragmaVtabDisconnect, /* xDisconnect - Disconnect from a table */ 0, /* xDestroy - Drop a table */ pragmaVtabOpen, /* xOpen - open a cursor */ pragmaVtabClose, /* xClose - close a cursor */ pragmaVtabFilter, /* xFilter - configure scan constraints */ pragmaVtabNext, /* xNext - advance a cursor */ pragmaVtabEof, /* xEof */ pragmaVtabColumn, /* xColumn - read data */ pragmaVtabRowid, /* xRowid - read data */ 0, /* xUpdate - write data */ 0, /* xBegin - begin transaction */ 0, /* xSync - sync transaction */ 0, /* xCommit - commit transaction */ 0, /* xRollback - rollback transaction */ 0, /* xFindFunction - function overloading */ 0, /* xRename - rename the table */ 0, /* xSavepoint */ 0, /* xRelease */ 0 /* xRollbackTo */ }; /* ** Check to see if zTabName is really the name of a pragma. If it is, ** then register an eponymous virtual table for that pragma and return ** a pointer to the Module object for the new virtual table. */ Module *sqlite3PragmaVtabRegister(sqlite3 *db, const char *zName){ const PragmaName *pName; assert( sqlite3_strnicmp(zName, "pragma_", 7)==0 ); pName = pragmaLocate(zName+7); if( pName==0 ) return 0; if( (pName->mPragFlg & (PragFlg_Result0|PragFlg_Result1))==0 ) return 0; assert( sqlite3HashFind(&db->aModule, zName)==0 ); return sqlite3VtabCreateModule(db, zName, &pragmaVtabModule, (void*)pName, 0); } #endif /* SQLITE_OMIT_VIRTUALTABLE */ #endif /* SQLITE_OMIT_PRAGMA */ |
Changes to src/pragma.h.
1 2 3 4 5 6 7 8 | /* DO NOT EDIT! ** This file is automatically generated by the script at ** ../tool/mkpragmatab.tcl. To update the set of pragmas, edit ** that script and rerun it. */ #define PragTyp_HEADER_VALUE 0 #define PragTyp_AUTO_VACUUM 1 #define PragTyp_FLAG 2 | > > > | | | | | | | | | | > | | > | | | | | | | | | | > > | > | | | < | | | | | | | | | | | | | > | > > > > | > > > > | > > > > | > > > > > | < | < < > | | | > > | < < > > | | | | < | | | | | | | | | > > > > | > | | | < | | | | > | | | > | > > > | > | | > > > > > > > > > > | | | > | > | | | > | < < < < > | | | | > | | > | | | > | < > | > > | < | < < | | | > | < | | | | > | | | | | > | < | | | > | | > > > > | | | | > | | < | | | > | > > > > > > | | | > | | | | | > | > > > > > > > | | > > > > > > > | | > | > | | | | > | > < | | | > | < | | | | > | > > > > > | | | | > | > > | | | > | | > | | | > | > > > | | | > | > > > > | | | | > | < | > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | | | | > | | | | > | | | | > | | | | > | | < < < < < < | | > | | | | | > | | | | | < < < < | < | | | | | > | > > | | | > | > > | | | > | | | | > | | | | > | > | > | | | > | < > > | | | > | < | | | > > > > | > > | | | | | > | > > | | | > | | | | | > | | | > > > > > > > | | > | > > > > > | | | | > | | | | | > | | | | | > | > | | | > | > > > > > > > > > > > > > | | | > | | | | > | | | | | > | | | | < < < < | < | > > > > > > > | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | 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 | /* DO NOT EDIT! ** This file is automatically generated by the script at ** ../tool/mkpragmatab.tcl. To update the set of pragmas, edit ** that script and rerun it. */ /* The various pragma types */ #define PragTyp_HEADER_VALUE 0 #define PragTyp_AUTO_VACUUM 1 #define PragTyp_FLAG 2 #define PragTyp_BTREE_SAMPLE 3 #define PragTyp_BUSY_TIMEOUT 4 #define PragTyp_CACHE_SIZE 5 #define PragTyp_CACHE_SPILL 6 #define PragTyp_CASE_SENSITIVE_LIKE 7 #define PragTyp_COLLATION_LIST 8 #define PragTyp_COMPILE_OPTIONS 9 #define PragTyp_DATA_STORE_DIRECTORY 10 #define PragTyp_DATABASE_LIST 11 #define PragTyp_DEFAULT_CACHE_SIZE 12 #define PragTyp_ENCODING 13 #define PragTyp_EST_COUNT 14 #define PragTyp_FOREIGN_KEY_CHECK 15 #define PragTyp_FOREIGN_KEY_LIST 16 #define PragTyp_FUNCTION_LIST 17 #define PragTyp_INCREMENTAL_VACUUM 18 #define PragTyp_INDEX_INFO 19 #define PragTyp_INDEX_LIST 20 #define PragTyp_INTEGRITY_CHECK 21 #define PragTyp_JOURNAL_MODE 22 #define PragTyp_JOURNAL_SIZE_LIMIT 23 #define PragTyp_LOCK_PROXY_FILE 24 #define PragTyp_LOCKING_MODE 25 #define PragTyp_PAGE_COUNT 26 #define PragTyp_MMAP_SIZE 27 #define PragTyp_MODULE_LIST 28 #define PragTyp_OPTIMIZE 29 #define PragTyp_PAGE_SIZE 30 #define PragTyp_PRAGMA_LIST 31 #define PragTyp_SECURE_DELETE 32 #define PragTyp_SHRINK_MEMORY 33 #define PragTyp_SOFT_HEAP_LIMIT 34 #define PragTyp_SYNCHRONOUS 35 #define PragTyp_TABLE_INFO 36 #define PragTyp_TEMP_STORE 37 #define PragTyp_TEMP_STORE_DIRECTORY 38 #define PragTyp_THREADS 39 #define PragTyp_WAL_AUTOCHECKPOINT 40 #define PragTyp_WAL_CHECKPOINT 41 #define PragTyp_ACTIVATE_EXTENSIONS 42 #define PragTyp_HEXKEY 43 #define PragTyp_KEY 44 #define PragTyp_REKEY 45 #define PragTyp_LOCK_STATUS 46 #define PragTyp_PARSER_TRACE 47 #define PragTyp_STATS 48 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */ #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */ #define PragFlg_ReadOnly 0x08 /* Read-only HEADER_VALUE */ #define PragFlg_Result0 0x10 /* Acts as query when no argument */ #define PragFlg_Result1 0x20 /* Acts as query when has one argument */ #define PragFlg_SchemaOpt 0x40 /* Schema restricts name search if present */ #define PragFlg_SchemaReq 0x80 /* Schema required - "main" is default */ /* Names of columns for pragmas that return multi-column result ** or that return single-column results where the name of the ** result column is different from the name of the pragma */ static const char *const pragCName[] = { /* 0 */ "cache_size", /* Used by: default_cache_size */ /* 1 */ "cid", /* Used by: table_info */ /* 2 */ "name", /* 3 */ "type", /* 4 */ "notnull", /* 5 */ "dflt_value", /* 6 */ "pk", /* 7 */ "tbl", /* Used by: stats */ /* 8 */ "idx", /* 9 */ "wdth", /* 10 */ "hght", /* 11 */ "flgs", /* 12 */ "seqno", /* Used by: index_info */ /* 13 */ "cid", /* 14 */ "name", /* 15 */ "seqno", /* Used by: index_xinfo */ /* 16 */ "cid", /* 17 */ "name", /* 18 */ "desc", /* 19 */ "coll", /* 20 */ "key", /* 21 */ "seq", /* Used by: index_list */ /* 22 */ "name", /* 23 */ "unique", /* 24 */ "origin", /* 25 */ "partial", /* 26 */ "seq", /* Used by: database_list */ /* 27 */ "name", /* 28 */ "file", /* 29 */ "name", /* Used by: function_list */ /* 30 */ "builtin", /* 31 */ "name", /* Used by: module_list pragma_list */ /* 32 */ "seq", /* Used by: collation_list */ /* 33 */ "name", /* 34 */ "id", /* Used by: foreign_key_list */ /* 35 */ "seq", /* 36 */ "table", /* 37 */ "from", /* 38 */ "to", /* 39 */ "on_update", /* 40 */ "on_delete", /* 41 */ "match", /* 42 */ "table", /* Used by: foreign_key_check */ /* 43 */ "rowid", /* 44 */ "parent", /* 45 */ "fkid", /* 46 */ "busy", /* Used by: wal_checkpoint */ /* 47 */ "log", /* 48 */ "checkpointed", /* 49 */ "timeout", /* Used by: busy_timeout */ /* 50 */ "database", /* Used by: lock_status */ /* 51 */ "status", }; /* Definitions of all built-in pragmas */ typedef struct PragmaName { const char *const zName; /* Name of pragma */ u8 ePragTyp; /* PragTyp_XXX value */ u8 mPragFlg; /* Zero or more PragFlg_XXX values */ u8 iPragCName; /* Start of column names in pragCName[] */ u8 nPragCName; /* Num of col names. 0 means use pragma name */ u32 iArg; /* Extra argument */ } PragmaName; static const PragmaName aPragmaName[] = { #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) {/* zName: */ "activate_extensions", /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) {/* zName: */ "application_id", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ BTREE_APPLICATION_ID }, #endif #if !defined(SQLITE_OMIT_AUTOVACUUM) {/* zName: */ "auto_vacuum", /* ePragTyp: */ PragTyp_AUTO_VACUUM, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_AUTOMATIC_INDEX) {/* zName: */ "automatic_index", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_AutoIndex }, #endif #endif {/* zName: */ "btree_sample", /* ePragTyp: */ PragTyp_BTREE_SAMPLE, /* ePragFlg: */ PragFlg_NeedSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "busy_timeout", /* ePragTyp: */ PragTyp_BUSY_TIMEOUT, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 49, 1, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "cache_size", /* ePragTyp: */ PragTyp_CACHE_SIZE, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "cache_spill", /* ePragTyp: */ PragTyp_CACHE_SPILL, /* ePragFlg: */ PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif {/* zName: */ "case_sensitive_like", /* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE, /* ePragFlg: */ PragFlg_NoColumns, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "cell_size_check", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_CellSizeCk }, #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "checkpoint_fullfsync", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_CkptFullFSync }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "collation_list", /* ePragTyp: */ PragTyp_COLLATION_LIST, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 32, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) {/* zName: */ "compile_options", /* ePragTyp: */ PragTyp_COMPILE_OPTIONS, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "count_changes", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_CountRows }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN {/* zName: */ "data_store_directory", /* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY, /* ePragFlg: */ PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) {/* zName: */ "data_version", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlg: */ PragFlg_ReadOnly|PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ BTREE_DATA_VERSION }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "database_list", /* ePragTyp: */ PragTyp_DATABASE_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, /* ColNames: */ 26, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) {/* zName: */ "default_cache_size", /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, /* ColNames: */ 0, 1, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) {/* zName: */ "defer_foreign_keys", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_DeferFKs }, #endif #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "empty_result_callbacks", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_NullCallback }, #endif #if !defined(SQLITE_OMIT_UTF16) {/* zName: */ "encoding", /* ePragTyp: */ PragTyp_ENCODING, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif {/* zName: */ "est_count", /* ePragTyp: */ PragTyp_EST_COUNT, /* ePragFlg: */ PragFlg_NeedSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) {/* zName: */ "foreign_key_check", /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, /* ColNames: */ 42, 4, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FOREIGN_KEY) {/* zName: */ "foreign_key_list", /* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 34, 8, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) {/* zName: */ "foreign_keys", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_ForeignKeys }, #endif #endif #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) {/* zName: */ "freelist_count", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlg: */ PragFlg_ReadOnly|PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ BTREE_FREE_PAGE_COUNT }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "full_column_names", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_FullColNames }, {/* zName: */ "fullfsync", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_FullFSync }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) #if defined(SQLITE_INTROSPECTION_PRAGMAS) {/* zName: */ "function_list", /* ePragTyp: */ PragTyp_FUNCTION_LIST, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 29, 2, /* iArg: */ 0 }, #endif #endif #if defined(SQLITE_HAS_CODEC) {/* zName: */ "hexkey", /* ePragTyp: */ PragTyp_HEXKEY, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "hexrekey", /* ePragTyp: */ PragTyp_HEXKEY, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_CHECK) {/* zName: */ "ignore_check_constraints", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_IgnoreChecks }, #endif #endif #if !defined(SQLITE_OMIT_AUTOVACUUM) {/* zName: */ "incremental_vacuum", /* ePragTyp: */ PragTyp_INCREMENTAL_VACUUM, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_NoColumns, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "index_info", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 12, 3, /* iArg: */ 0 }, {/* zName: */ "index_list", /* ePragTyp: */ PragTyp_INDEX_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 21, 5, /* iArg: */ 0 }, {/* zName: */ "index_xinfo", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 15, 6, /* iArg: */ 1 }, #endif #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) {/* zName: */ "integrity_check", /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "journal_mode", /* ePragTyp: */ PragTyp_JOURNAL_MODE, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "journal_size_limit", /* ePragTyp: */ PragTyp_JOURNAL_SIZE_LIMIT, /* ePragFlg: */ PragFlg_Result0|PragFlg_SchemaReq, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if defined(SQLITE_HAS_CODEC) {/* zName: */ "key", /* ePragTyp: */ PragTyp_KEY, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "legacy_file_format", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_LegacyFileFmt }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE {/* zName: */ "lock_proxy_file", /* ePragTyp: */ PragTyp_LOCK_PROXY_FILE, /* ePragFlg: */ PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) {/* zName: */ "lock_status", /* ePragTyp: */ PragTyp_LOCK_STATUS, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 50, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "locking_mode", /* ePragTyp: */ PragTyp_LOCKING_MODE, /* ePragFlg: */ PragFlg_Result0|PragFlg_SchemaReq, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "max_page_count", /* ePragTyp: */ PragTyp_PAGE_COUNT, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "mmap_size", /* ePragTyp: */ PragTyp_MMAP_SIZE, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) #if !defined(SQLITE_OMIT_VIRTUALTABLE) #if defined(SQLITE_INTROSPECTION_PRAGMAS) {/* zName: */ "module_list", /* ePragTyp: */ PragTyp_MODULE_LIST, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 31, 1, /* iArg: */ 0 }, #endif #endif #endif {/* zName: */ "optimize", /* ePragTyp: */ PragTyp_OPTIMIZE, /* ePragFlg: */ PragFlg_Result1|PragFlg_NeedSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "page_count", /* ePragTyp: */ PragTyp_PAGE_COUNT, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "page_size", /* ePragTyp: */ PragTyp_PAGE_SIZE, /* ePragFlg: */ PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_PARSER_TRACE) {/* zName: */ "parser_trace", /* ePragTyp: */ PragTyp_PARSER_TRACE, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if defined(SQLITE_INTROSPECTION_PRAGMAS) {/* zName: */ "pragma_list", /* ePragTyp: */ PragTyp_PRAGMA_LIST, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 31, 1, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "query_only", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_QueryOnly }, #endif #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) {/* zName: */ "quick_check", /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "read_uncommitted", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_ReadUncommit }, {/* zName: */ "recursive_triggers", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_RecTriggers }, #endif #if defined(SQLITE_HAS_CODEC) {/* zName: */ "rekey", /* ePragTyp: */ PragTyp_REKEY, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "reverse_unordered_selects", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_ReverseOrder }, #endif #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) {/* zName: */ "schema_version", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ BTREE_SCHEMA_VERSION }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "secure_delete", /* ePragTyp: */ PragTyp_SECURE_DELETE, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "short_column_names", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_ShortColNames }, #endif {/* zName: */ "shrink_memory", /* ePragTyp: */ PragTyp_SHRINK_MEMORY, /* ePragFlg: */ PragFlg_NoColumns, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "soft_heap_limit", /* ePragTyp: */ PragTyp_SOFT_HEAP_LIMIT, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if defined(SQLITE_DEBUG) {/* zName: */ "sql_trace", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_SqlTrace }, #endif #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) {/* zName: */ "stats", /* ePragTyp: */ PragTyp_STATS, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, /* ColNames: */ 7, 5, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "synchronous", /* ePragTyp: */ PragTyp_SYNCHRONOUS, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "table_info", /* ePragTyp: */ PragTyp_TABLE_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 1, 6, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "temp_store", /* ePragTyp: */ PragTyp_TEMP_STORE, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "temp_store_directory", /* ePragTyp: */ PragTyp_TEMP_STORE_DIRECTORY, /* ePragFlg: */ PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif {/* zName: */ "threads", /* ePragTyp: */ PragTyp_THREADS, /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) {/* zName: */ "user_version", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ BTREE_USER_VERSION }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if defined(SQLITE_DEBUG) {/* zName: */ "vdbe_addoptrace", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_VdbeAddopTrace }, {/* zName: */ "vdbe_debug", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_SqlTrace|SQLITE_VdbeListing|SQLITE_VdbeTrace }, {/* zName: */ "vdbe_eqp", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_VdbeEQP }, {/* zName: */ "vdbe_listing", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_VdbeListing }, {/* zName: */ "vdbe_trace", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_VdbeTrace }, #endif #endif #if !defined(SQLITE_OMIT_WAL) {/* zName: */ "wal_autocheckpoint", /* ePragTyp: */ PragTyp_WAL_AUTOCHECKPOINT, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "wal_checkpoint", /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, /* ePragFlg: */ PragFlg_NeedSchema, /* ColNames: */ 46, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "writable_schema", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_WriteSchema }, #endif }; /* Number of pragmas: 62 on by default, 79 total. */ |
Changes to src/prepare.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 | */ static void corruptSchema( InitData *pData, /* Initialization context */ const char *zObj, /* Object being parsed at the point of error */ const char *zExtra /* Error information */ ){ sqlite3 *db = pData->db; | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | */ static void corruptSchema( InitData *pData, /* Initialization context */ const char *zObj, /* Object being parsed at the point of error */ const char *zExtra /* Error information */ ){ sqlite3 *db = pData->db; if( !db->mallocFailed && (db->flags & SQLITE_WriteSchema)==0 ){ char *z; if( zObj==0 ) zObj = "?"; z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj); if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra); sqlite3DbFree(db, *pData->pzErrMsg); *pData->pzErrMsg = z; } |
︙ | ︙ | |||
81 82 83 84 85 86 87 | db->init.iDb = iDb; db->init.newTnum = sqlite3Atoi(argv[1]); db->init.orphanTrigger = 0; TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0); rc = db->errCode; assert( (rc&0xFF)==(rcp&0xFF) ); db->init.iDb = saved_iDb; | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | db->init.iDb = iDb; db->init.newTnum = sqlite3Atoi(argv[1]); db->init.orphanTrigger = 0; TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0); rc = db->errCode; assert( (rc&0xFF)==(rcp&0xFF) ); db->init.iDb = saved_iDb; assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); if( SQLITE_OK!=rc ){ if( db->init.orphanTrigger ){ assert( iDb==1 ); }else{ pData->rc = rc; if( rc==SQLITE_NOMEM ){ sqlite3OomFault(db); |
︙ | ︙ | |||
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | const char *zMasterName; int openedTransaction = 0; assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pSchema ); assert( sqlite3_mutex_held(db->mutex) ); assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); /* Construct the in-memory representation schema tables (sqlite_master or ** sqlite_temp_master) by invoking the parser directly. The appropriate ** table name will be inserted automatically by the parser so we can just ** use the abbreviation "x" here. The parser will also automatically tag ** the schema table as read-only. */ azArg[0] = zMasterName = SCHEMA_TABLE(iDb); azArg[1] = "1"; azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text," | > > | | | < | > | 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 | const char *zMasterName; int openedTransaction = 0; assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pSchema ); assert( sqlite3_mutex_held(db->mutex) ); assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); db->init.busy = 1; /* Construct the in-memory representation schema tables (sqlite_master or ** sqlite_temp_master) by invoking the parser directly. The appropriate ** table name will be inserted automatically by the parser so we can just ** use the abbreviation "x" here. The parser will also automatically tag ** the schema table as read-only. */ azArg[0] = zMasterName = SCHEMA_TABLE(iDb); azArg[1] = "1"; azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text," "rootpage int,sql text)"; azArg[3] = 0; initData.db = db; initData.iDb = iDb; initData.rc = SQLITE_OK; initData.pzErrMsg = pzErrMsg; sqlite3InitCallback(&initData, 3, (char **)azArg, 0); if( initData.rc ){ rc = initData.rc; goto error_out; } /* Create a cursor to hold the database open */ pDb = &db->aDb[iDb]; if( pDb->pBt==0 ){ assert( iDb==1 ); DbSetProperty(db, 1, DB_SchemaLoaded); rc = SQLITE_OK; goto error_out; } /* If there is not already a read-only (or read-write) transaction opened ** on the b-tree database, open one now. If a transaction is opened, it ** will be closed before this function returns. */ sqlite3BtreeEnter(pDb->pBt); if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ |
︙ | ︙ | |||
308 309 310 311 312 313 314 | } #endif } if( db->mallocFailed ){ rc = SQLITE_NOMEM_BKPT; sqlite3ResetAllSchemasOfConnection(db); } | | | | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | } #endif } if( db->mallocFailed ){ rc = SQLITE_NOMEM_BKPT; sqlite3ResetAllSchemasOfConnection(db); } if( rc==SQLITE_OK || (db->flags&SQLITE_WriteSchema)){ /* Black magic: If the SQLITE_WriteSchema flag is set, then consider ** the schema loaded, even if errors occurred. In this situation the ** current sqlite3_prepare() operation will fail, but the following one ** will attempt to compile the supplied statement against whatever subset ** of the schema was loaded before the error occurred. The primary ** purpose of this is to allow access to the sqlite_master table ** even when its contents have been corrupted. */ |
︙ | ︙ | |||
332 333 334 335 336 337 338 | initone_error_out: if( openedTransaction ){ sqlite3BtreeCommit(pDb->pBt); } sqlite3BtreeLeave(pDb->pBt); error_out: | > | | | > > > | < < | > | | | < | < | < < < < < | | | | < < < < | < | | 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 | initone_error_out: if( openedTransaction ){ sqlite3BtreeCommit(pDb->pBt); } sqlite3BtreeLeave(pDb->pBt); error_out: if( rc ){ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ sqlite3OomFault(db); } sqlite3ResetOneSchema(db, iDb); } db->init.busy = 0; return rc; } /* ** Initialize all database files - the main database file, the file ** used to store temporary tables, and any additional database files ** created using ATTACH statements. Return a success code. If an ** error occurs, write an error message into *pzErrMsg. ** ** After a database is initialized, the DB_SchemaLoaded bit is set ** bit is set in the flags field of the Db structure. If the database ** file was of zero-length, then the DB_Empty flag is also set. */ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ int i, rc; int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange); assert( sqlite3_mutex_held(db->mutex) ); assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) ); assert( db->init.busy==0 ); ENC(db) = SCHEMA_ENC(db); assert( db->nDb>0 ); /* Do the main schema first */ if( !DbHasProperty(db, 0, DB_SchemaLoaded) ){ rc = sqlite3InitOne(db, 0, pzErrMsg); if( rc ) return rc; } /* All other schemas after the main schema. The "temp" schema must be last */ for(i=db->nDb-1; i>0; i--){ if( !DbHasProperty(db, i, DB_SchemaLoaded) ){ rc = sqlite3InitOne(db, i, pzErrMsg); if( rc ) return rc; } } if( commit_internal ){ sqlite3CommitInternalChanges(db); } return SQLITE_OK; } /* ** This routine is a no-op if the database schema is already initialized. ** Otherwise, the schema is loaded. An error code is returned. */ int sqlite3ReadSchema(Parse *pParse){ |
︙ | ︙ | |||
490 491 492 493 494 495 496 | return i; } /* ** Free all memory allocations in the pParse object */ void sqlite3ParserReset(Parse *pParse){ | < | | | | | | | | < | > > > > > > > > | 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 | return i; } /* ** Free all memory allocations in the pParse object */ void sqlite3ParserReset(Parse *pParse){ sqlite3 *db = pParse->db; sqlite3DbFree(db, pParse->aLabel); sqlite3ExprListDelete(db, pParse->pConstExpr); if( db ){ assert( db->lookaside.bDisable >= pParse->disableLookaside ); db->lookaside.bDisable -= pParse->disableLookaside; } pParse->disableLookaside = 0; } /* ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. */ static int sqlite3Prepare( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ u32 prepFlags, /* Zero or more SQLITE_PREPARE_* flags */ Vdbe *pReprepare, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ char *zErrMsg = 0; /* Error message */ int rc = SQLITE_OK; /* Result code */ int i; /* Loop counter */ Parse sParse; /* Parsing context */ memset(&sParse, 0, PARSE_HDR_SZ); memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ); sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ assert( sqlite3_mutex_held(db->mutex) ); /* For a long-term use prepared statement avoid the use of ** lookaside memory. */ if( prepFlags & SQLITE_PREPARE_PERSISTENT ){ sParse.disableLookaside++; db->lookaside.bDisable++; } /* Check to verify that it is possible to get a read lock on all ** database schemas. The inability to get a read lock indicates that ** some other database connection is holding a write-lock, which in ** turn means that the other connection has made uncommitted changes ** to the schema. ** |
︙ | ︙ | |||
557 558 559 560 561 562 563 | Btree *pBt = db->aDb[i].pBt; if( pBt ){ assert( sqlite3BtreeHoldsMutex(pBt) ); rc = sqlite3BtreeSchemaLocked(pBt); if( rc ){ const char *zDb = db->aDb[i].zDbSName; sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb); | | | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | Btree *pBt = db->aDb[i].pBt; if( pBt ){ assert( sqlite3BtreeHoldsMutex(pBt) ); rc = sqlite3BtreeSchemaLocked(pBt); if( rc ){ const char *zDb = db->aDb[i].zDbSName; sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb); testcase( db->flags & SQLITE_ReadUncommit ); goto end_prepare; } } } sqlite3VtabUnlockList(db); |
︙ | ︙ | |||
625 626 627 628 629 630 631 | sqlite3VdbeSetColName(sParse.pVdbe, i-iFirst, COLNAME_NAME, azColName[i], SQLITE_STATIC); } } #endif if( db->init.busy==0 ){ | < | | 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 | sqlite3VdbeSetColName(sParse.pVdbe, i-iFirst, COLNAME_NAME, azColName[i], SQLITE_STATIC); } } #endif if( db->init.busy==0 ){ sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags); } if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){ sqlite3VdbeFinalize(sParse.pVdbe); assert(!(*ppStmt)); }else{ *ppStmt = (sqlite3_stmt*)sParse.pVdbe; } |
︙ | ︙ | |||
660 661 662 663 664 665 666 | assert( (rc&db->errMask)==rc ); return rc; } static int sqlite3LockAndPrepare( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ | | | > | > > | | 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 | assert( (rc&db->errMask)==rc ); return rc; } static int sqlite3LockAndPrepare( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ u32 prepFlags, /* Zero or more SQLITE_PREPARE_* flags */ Vdbe *pOld, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ int rc; #ifdef SQLITE_ENABLE_API_ARMOR if( ppStmt==0 ) return SQLITE_MISUSE_BKPT; #endif *ppStmt = 0; if( !sqlite3SafetyCheckOk(db)||zSql==0 ){ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); if( rc==SQLITE_SCHEMA ){ sqlite3ResetOneSchema(db, -1); sqlite3_finalize(*ppStmt); rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); } sqlite3BtreeLeaveAll(db); sqlite3_mutex_leave(db->mutex); assert( rc==SQLITE_OK || *ppStmt==0 ); return rc; } /* ** Rerun the compilation of a statement after a schema change. ** ** If the statement is successfully recompiled, return SQLITE_OK. Otherwise, ** if the statement cannot be recompiled because another connection has ** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error ** occurs, return SQLITE_SCHEMA. */ int sqlite3Reprepare(Vdbe *p){ int rc; sqlite3_stmt *pNew; const char *zSql; sqlite3 *db; u8 prepFlags; assert( sqlite3_mutex_held(sqlite3VdbeDb(p)->mutex) ); zSql = sqlite3_sql((sqlite3_stmt *)p); assert( zSql!=0 ); /* Reprepare only called for prepare_v2() statements */ db = sqlite3VdbeDb(p); assert( sqlite3_mutex_held(db->mutex) ); prepFlags = sqlite3VdbePrepareFlags(p); rc = sqlite3LockAndPrepare(db, zSql, -1, prepFlags, p, &pNew, 0); if( rc ){ if( rc==SQLITE_NOMEM ){ sqlite3OomFault(db); } assert( pNew==0 ); return rc; }else{ |
︙ | ︙ | |||
752 753 754 755 756 757 758 | sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ int rc; | > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > | | 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 | sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ int rc; /* EVIDENCE-OF: R-37923-12173 The sqlite3_prepare_v2() interface works ** exactly the same as sqlite3_prepare_v3() with a zero prepFlags ** parameter. ** ** Proof in that the 5th parameter to sqlite3LockAndPrepare is 0 */ rc = sqlite3LockAndPrepare(db,zSql,nBytes,SQLITE_PREPARE_SAVESQL,0, ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); return rc; } int sqlite3_prepare_v3( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_* flags */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ int rc; /* EVIDENCE-OF: R-56861-42673 sqlite3_prepare_v3() differs from ** sqlite3_prepare_v2() only in having the extra prepFlags parameter, ** which is a bit array consisting of zero or more of the ** SQLITE_PREPARE_* flags. ** ** Proof by comparison to the implementation of sqlite3_prepare_v2() ** directly above. */ rc = sqlite3LockAndPrepare(db,zSql,nBytes, SQLITE_PREPARE_SAVESQL|(prepFlags&SQLITE_PREPARE_MASK), 0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); return rc; } #ifndef SQLITE_OMIT_UTF16 /* ** Compile the UTF-16 encoded SQL statement zSql into a statement handle. */ static int sqlite3Prepare16( sqlite3 *db, /* Database handle. */ const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ u32 prepFlags, /* Zero or more SQLITE_PREPARE_* flags */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const void **pzTail /* OUT: End of parsed string */ ){ /* This function currently works by first transforming the UTF-16 ** encoded string to UTF-8, then invoking sqlite3_prepare(). The ** tricky bit is figuring out the pointer to return in *pzTail. */ |
︙ | ︙ | |||
794 795 796 797 798 799 800 | const char *z = (const char*)zSql; for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){} nBytes = sz; } sqlite3_mutex_enter(db->mutex); zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); if( zSql8 ){ | | | 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 | const char *z = (const char*)zSql; for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){} nBytes = sz; } sqlite3_mutex_enter(db->mutex); zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); if( zSql8 ){ rc = sqlite3LockAndPrepare(db, zSql8, -1, prepFlags, 0, ppStmt, &zTail8); } if( zTail8 && pzTail ){ /* If sqlite3_prepare returns a tail pointer, we calculate the ** equivalent pointer into the UTF-16 string by counting the unicode ** characters between zSql8 and zTail8, and then returning a pointer ** the same number of characters into the UTF-16 string. |
︙ | ︙ | |||
840 841 842 843 844 845 846 | sqlite3 *db, /* Database handle. */ const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const void **pzTail /* OUT: End of parsed string */ ){ int rc; | > > > > > > > > > > > > > | > > | 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 | sqlite3 *db, /* Database handle. */ const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const void **pzTail /* OUT: End of parsed string */ ){ int rc; rc = sqlite3Prepare16(db,zSql,nBytes,SQLITE_PREPARE_SAVESQL,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } int sqlite3_prepare16_v3( sqlite3 *db, /* Database handle. */ const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_* flags */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const void **pzTail /* OUT: End of parsed string */ ){ int rc; rc = sqlite3Prepare16(db,zSql,nBytes, SQLITE_PREPARE_SAVESQL|(prepFlags&SQLITE_PREPARE_MASK), ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } #endif /* SQLITE_OMIT_UTF16 */ |
Changes to src/printf.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | */ #include "sqliteInt.h" /* ** Conversion types fall into various categories as defined by the ** following enumeration. */ | | > | | 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 | */ #include "sqliteInt.h" /* ** Conversion types fall into various categories as defined by the ** following enumeration. */ #define etRADIX 0 /* non-decimal integer types. %x %o */ #define etFLOAT 1 /* Floating point. %f */ #define etEXP 2 /* Exponentional notation. %e and %E */ #define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */ #define etSIZE 4 /* Return number of characters processed so far. %n */ #define etSTRING 5 /* Strings. %s */ #define etDYNSTRING 6 /* Dynamically allocated strings. %z */ #define etPERCENT 7 /* Percent symbol. %% */ #define etCHARX 8 /* Characters. %c */ /* The rest are extensions, not normally found in printf() */ #define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */ #define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', NULL pointers replaced by SQL NULL. %Q */ #define etTOKEN 11 /* a pointer to a Token structure */ #define etSRCLIST 12 /* a pointer to a SrcList */ #define etPOINTER 13 /* The %p conversion */ #define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ #define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ #define etDECIMAL 16 /* %d or %u, but not %x, %o */ #define etINVALID 17 /* Any unrecognized conversion type */ /* ** An "etByte" is an 8-bit unsigned value. */ typedef unsigned char etByte; |
︙ | ︙ | |||
54 55 56 57 58 59 60 | etByte charset; /* Offset into aDigits[] of the digits string */ etByte prefix; /* Offset into aPrefix[] of the prefix string */ } et_info; /* ** Allowed values for et_info.flags */ | | < | | | | | < | | | | 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 | etByte charset; /* Offset into aDigits[] of the digits string */ etByte prefix; /* Offset into aPrefix[] of the prefix string */ } et_info; /* ** Allowed values for et_info.flags */ #define FLAG_SIGNED 1 /* True if the value to convert is signed */ #define FLAG_STRING 4 /* Allow infinite precision */ /* ** The following table is searched linearly, so it is good to put the ** most frequently used conversion types first. */ static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; static const char aPrefix[] = "-x0\000X0"; static const et_info fmtinfo[] = { { 'd', 10, 1, etDECIMAL, 0, 0 }, { 's', 0, 4, etSTRING, 0, 0 }, { 'g', 0, 1, etGENERIC, 30, 0 }, { 'z', 0, 4, etDYNSTRING, 0, 0 }, { 'q', 0, 4, etSQLESCAPE, 0, 0 }, { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, { 'c', 0, 0, etCHARX, 0, 0 }, { 'o', 8, 0, etRADIX, 0, 2 }, { 'u', 10, 0, etDECIMAL, 0, 0 }, { 'x', 16, 0, etRADIX, 16, 1 }, { 'X', 16, 0, etRADIX, 0, 4 }, #ifndef SQLITE_OMIT_FLOATING_POINT { 'f', 0, 1, etFLOAT, 0, 0 }, { 'e', 0, 1, etEXP, 30, 0 }, { 'E', 0, 1, etEXP, 14, 0 }, { 'G', 0, 1, etGENERIC, 14, 0 }, #endif { 'i', 10, 1, etDECIMAL, 0, 0 }, { 'n', 0, 0, etSIZE, 0, 0 }, { '%', 0, 0, etPERCENT, 0, 0 }, { 'p', 16, 0, etPOINTER, 0, 1 }, /* All the rest are undocumented and are for internal use only */ { 'T', 0, 0, etTOKEN, 0, 0 }, { 'S', 0, 0, etSRCLIST, 0, 0 }, { 'r', 10, 1, etORDINAL, 0, 0 }, }; /* ** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point ** conversions will work. */ #ifndef SQLITE_OMIT_FLOATING_POINT |
︙ | ︙ | |||
177 178 179 180 181 182 183 | int c; /* Next character in the format string */ char *bufpt; /* Pointer to the conversion buffer */ int precision; /* Precision of the current field */ int length; /* Length of the field */ int idx; /* A general purpose loop counter */ int width; /* Width of the current field */ etByte flag_leftjustify; /* True if "-" flag is present */ | | < | < > < < | | < < > | | | | > | 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 | int c; /* Next character in the format string */ char *bufpt; /* Pointer to the conversion buffer */ int precision; /* Precision of the current field */ int length; /* Length of the field */ int idx; /* A general purpose loop counter */ int width; /* Width of the current field */ etByte flag_leftjustify; /* True if "-" flag is present */ etByte flag_prefix; /* '+' or ' ' or 0 for prefix */ etByte flag_alternateform; /* True if "#" flag is present */ etByte flag_altform2; /* True if "!" flag is present */ etByte flag_zeropad; /* True if field width constant starts with zero */ etByte flag_long; /* 1 for the "l" flag, 2 for "ll", 0 by default */ etByte done; /* Loop termination flag */ etByte cThousand; /* Thousands separator for %d and %u */ etByte xtype = etINVALID; /* Conversion paradigm */ u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ LONGDOUBLE_TYPE realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ char *zOut; /* Rendering buffer */ int nOut; /* Size of the rendering buffer */ char *zExtra = 0; /* Malloced memory used by some conversion */ #ifndef SQLITE_OMIT_FLOATING_POINT int exp, e2; /* exponent of real numbers */ int nsd; /* Number of significant digits returned */ double rounder; /* Used for rounding floating point values */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ #endif PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ bufpt = 0; if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){ pArgList = va_arg(ap, PrintfArguments*); bArgList = 1; }else{ bArgList = 0; } for(; (c=(*fmt))!=0; ++fmt){ if( c!='%' ){ bufpt = (char *)fmt; #if HAVE_STRCHRNUL fmt = strchrnul(fmt, '%'); #else do{ fmt++; }while( *fmt && *fmt != '%' ); #endif sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt)); if( *fmt==0 ) break; } if( (c=(*++fmt))==0 ){ sqlite3StrAccumAppend(pAccum, "%", 1); break; } /* Find out what flags are present */ flag_leftjustify = flag_prefix = cThousand = flag_alternateform = flag_altform2 = flag_zeropad = 0; done = 0; do{ switch( c ){ case '-': flag_leftjustify = 1; break; case '+': flag_prefix = '+'; break; case ' ': flag_prefix = ' '; break; case '#': flag_alternateform = 1; break; case '!': flag_altform2 = 1; break; case '0': flag_zeropad = 1; break; case ',': cThousand = ','; break; default: done = 1; break; } }while( !done && (c=(*++fmt))!=0 ); /* Get the field width */ if( c=='*' ){ if( bArgList ){ width = (int)getIntArg(pArgList); |
︙ | ︙ | |||
310 311 312 313 314 315 316 | /* Get the conversion type modifier */ if( c=='l' ){ flag_long = 1; c = *++fmt; if( c=='l' ){ | | < < | < | < < < | | < < < < | | | > > > | > | | | > < < | | > | | | > | | | > | > > > > > > > > > > > > > > > | 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 | /* Get the conversion type modifier */ if( c=='l' ){ flag_long = 1; c = *++fmt; if( c=='l' ){ flag_long = 2; c = *++fmt; } }else{ flag_long = 0; } /* Fetch the info entry for the field */ infop = &fmtinfo[0]; xtype = etINVALID; for(idx=0; idx<ArraySize(fmtinfo); idx++){ if( c==fmtinfo[idx].fmttype ){ infop = &fmtinfo[idx]; xtype = infop->type; break; } } /* ** At this point, variables are initialized as follows: ** ** flag_alternateform TRUE if a '#' is present. ** flag_altform2 TRUE if a '!' is present. ** flag_prefix '+' or ' ' or zero ** flag_leftjustify TRUE if a '-' is present or if the ** field width was negative. ** flag_zeropad TRUE if the width began with 0. ** flag_long 1 for "l", 2 for "ll" ** width The specified field width. This is ** always non-negative. Zero is the default. ** precision The specified precision. The default ** is -1. ** xtype The class of the conversion. ** infop Pointer to the appropriate info struct. */ switch( xtype ){ case etPOINTER: flag_long = sizeof(char*)==sizeof(i64) ? 2 : sizeof(char*)==sizeof(long int) ? 1 : 0; /* Fall through into the next case */ case etORDINAL: case etRADIX: cThousand = 0; /* Fall through into the next case */ case etDECIMAL: if( infop->flags & FLAG_SIGNED ){ i64 v; if( bArgList ){ v = getIntArg(pArgList); }else if( flag_long ){ if( flag_long==2 ){ v = va_arg(ap,i64) ; }else{ v = va_arg(ap,long int); } }else{ v = va_arg(ap,int); } if( v<0 ){ if( v==SMALLEST_INT64 ){ longvalue = ((u64)1)<<63; }else{ longvalue = -v; } prefix = '-'; }else{ longvalue = v; prefix = flag_prefix; } }else{ if( bArgList ){ longvalue = (u64)getIntArg(pArgList); }else if( flag_long ){ if( flag_long==2 ){ longvalue = va_arg(ap,u64); }else{ longvalue = va_arg(ap,unsigned long int); } }else{ longvalue = va_arg(ap,unsigned int); } prefix = 0; } if( longvalue==0 ) flag_alternateform = 0; if( flag_zeropad && precision<width-(prefix!=0) ){ precision = width-(prefix!=0); } if( precision<etBUFSIZE-10-etBUFSIZE/3 ){ nOut = etBUFSIZE; zOut = buf; }else{ u64 n = (u64)precision + 10 + precision/3; zOut = zExtra = sqlite3Malloc( n ); if( zOut==0 ){ setStrAccumError(pAccum, STRACCUM_NOMEM); return; } nOut = (int)n; } bufpt = &zOut[nOut-1]; if( xtype==etORDINAL ){ static const char zOrd[] = "thstndrd"; int x = (int)(longvalue % 10); if( x>=4 || (longvalue/10)%10==1 ){ x = 0; } *(--bufpt) = zOrd[x*2+1]; *(--bufpt) = zOrd[x*2]; } { const char *cset = &aDigits[infop->charset]; u8 base = infop->base; do{ /* Convert to ascii */ *(--bufpt) = cset[longvalue%base]; longvalue = longvalue/base; }while( longvalue>0 ); } length = (int)(&zOut[nOut-1]-bufpt); while( precision>length ){ *(--bufpt) = '0'; /* Zero pad */ length++; } if( cThousand ){ int nn = (length - 1)/3; /* Number of "," to insert */ int ix = (length - 1)%3 + 1; bufpt -= nn; for(idx=0; nn>0; idx++){ bufpt[idx] = bufpt[idx+nn]; ix--; if( ix==0 ){ bufpt[++idx] = cThousand; nn--; ix = 3; } } } if( prefix ) *(--bufpt) = prefix; /* Add sign */ if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ const char *pre; char x; pre = &aPrefix[infop->prefix]; for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; |
︙ | ︙ | |||
459 460 461 462 463 464 465 | length = 0; #else if( precision<0 ) precision = 6; /* Set default precision */ if( realvalue<0.0 ){ realvalue = -realvalue; prefix = '-'; }else{ | < < | | 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 | length = 0; #else if( precision<0 ) precision = 6; /* Set default precision */ if( realvalue<0.0 ){ realvalue = -realvalue; prefix = '-'; }else{ prefix = flag_prefix; } if( xtype==etGENERIC && precision>0 ) precision--; testcase( precision>0xfff ); for(idx=precision&0xfff, rounder=0.5; idx>0; idx--, rounder*=0.1){} if( xtype==etFLOAT ) realvalue += rounder; /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ exp = 0; |
︙ | ︙ | |||
647 648 649 650 651 652 653 | bufpt = ""; }else if( xtype==etDYNSTRING ){ zExtra = bufpt; } if( precision>=0 ){ for(length=0; length<precision && bufpt[length]; length++){} }else{ | | | 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 | bufpt = ""; }else if( xtype==etDYNSTRING ){ zExtra = bufpt; } if( precision>=0 ){ for(length=0; length<precision && bufpt[length]; length++){} }else{ length = 0x7fffffff & (int)strlen(bufpt); } break; case etSQLESCAPE: /* Escape ' characters */ case etSQLESCAPE2: /* Escape ' and enclose in '...' */ case etSQLESCAPE3: { /* Escape " characters */ int i, j, k, n, isnull; int needQuote; |
︙ | ︙ | |||
697 698 699 700 701 702 703 | length = j; /* The precision in %q and %Q means how many input characters to ** consume, not the length of the output... ** if( precision>=0 && precision<length ) length = precision; */ break; } case etTOKEN: { | > > | > > > > | | | | 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 | length = j; /* The precision in %q and %Q means how many input characters to ** consume, not the length of the output... ** if( precision>=0 && precision<length ) length = precision; */ break; } case etTOKEN: { Token *pToken; if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; pToken = va_arg(ap, Token*); assert( bArgList==0 ); if( pToken && pToken->n ){ sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n); } length = width = 0; break; } case etSRCLIST: { SrcList *pSrc; int k; struct SrcList_item *pItem; if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; pSrc = va_arg(ap, SrcList*); k = va_arg(ap, int); pItem = &pSrc->a[k]; assert( bArgList==0 ); assert( k>=0 && k<pSrc->nSrc ); if( pItem->zDatabase ){ sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase); sqlite3StrAccumAppend(pAccum, ".", 1); } sqlite3StrAccumAppendAll(pAccum, pItem->zName); |
︙ | ︙ | |||
730 731 732 733 734 735 736 | }/* End switch over the format type */ /* ** The text of the conversion is pointed to by "bufpt" and is ** "length" characters long. The field width is "width". Do ** the output. */ width -= length; | > | | | > > > | 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 | }/* End switch over the format type */ /* ** The text of the conversion is pointed to by "bufpt" and is ** "length" characters long. The field width is "width". Do ** the output. */ width -= length; if( width>0 ){ if( !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' '); sqlite3StrAccumAppend(pAccum, bufpt, length); if( flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' '); }else{ sqlite3StrAccumAppend(pAccum, bufpt, length); } if( zExtra ){ sqlite3DbFree(pAccum->db, zExtra); zExtra = 0; } }/* End for loop over the format string */ } /* End of function */ |
︙ | ︙ | |||
763 764 765 766 767 768 769 | if( p->mxAlloc==0 ){ N = p->nAlloc - p->nChar - 1; setStrAccumError(p, STRACCUM_TOOBIG); return N; }else{ char *zOld = isMalloced(p) ? p->zText : 0; i64 szNew = p->nChar; | < | 778 779 780 781 782 783 784 785 786 787 788 789 790 791 | if( p->mxAlloc==0 ){ N = p->nAlloc - p->nChar - 1; setStrAccumError(p, STRACCUM_TOOBIG); return N; }else{ char *zOld = isMalloced(p) ? p->zText : 0; i64 szNew = p->nChar; szNew += N + 1; if( szNew+p->nChar<=p->mxAlloc ){ /* Force exponential buffer size growth as long as it does not overflow, ** to avoid having to call this routine too often */ szNew += p->nChar; } if( szNew > p->mxAlloc ){ |
︙ | ︙ | |||
805 806 807 808 809 810 811 | ** Append N copies of character c to the given string buffer. */ void sqlite3AppendChar(StrAccum *p, int N, char c){ testcase( p->nChar + (i64)N > 0x7fffffff ); if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){ return; } | < < | | | < < | | | | | | | | > > > > > > > > < | 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 | ** Append N copies of character c to the given string buffer. */ void sqlite3AppendChar(StrAccum *p, int N, char c){ testcase( p->nChar + (i64)N > 0x7fffffff ); if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){ return; } while( (N--)>0 ) p->zText[p->nChar++] = c; } /* ** The StrAccum "p" is not large enough to accept N new bytes of z[]. ** So enlarge if first, then do the append. ** ** This is a helper routine to sqlite3StrAccumAppend() that does special-case ** work (enlarging the buffer) using tail recursion, so that the ** sqlite3StrAccumAppend() routine can use fast calling semantics. */ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){ N = sqlite3StrAccumEnlarge(p, N); if( N>0 ){ memcpy(&p->zText[p->nChar], z, N); p->nChar += N; } } /* ** Append N bytes of text from z to the StrAccum object. Increase the ** size of the memory allocation for StrAccum if necessary. */ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ assert( z!=0 || N==0 ); assert( p->zText!=0 || p->nChar==0 || p->accError ); assert( N>=0 ); assert( p->accError==0 || p->nAlloc==0 ); if( p->nChar+N >= p->nAlloc ){ enlargeAndAppend(p,z,N); }else if( N ){ assert( p->zText ); p->nChar += N; memcpy(&p->zText[p->nChar-N], z, N); } } /* ** Append the complete text of zero-terminated string z[] to the p string. */ void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){ sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z)); } /* ** Finish off a string by making sure it is zero-terminated. ** Return a pointer to the resulting string. Return a NULL ** pointer if any kind of error was encountered. */ static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){ char *zText; assert( p->mxAlloc>0 && !isMalloced(p) ); zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); if( zText ){ memcpy(zText, p->zText, p->nChar+1); p->printfFlags |= SQLITE_PRINTF_MALLOCED; }else{ setStrAccumError(p, STRACCUM_NOMEM); } p->zText = zText; return zText; } char *sqlite3StrAccumFinish(StrAccum *p){ if( p->zText ){ p->zText[p->nChar] = 0; if( p->mxAlloc>0 && !isMalloced(p) ){ return strAccumFinishRealloc(p); } } return p->zText; } /* ** Reset an StrAccum string. Reclaim all malloced memory. */ void sqlite3StrAccumReset(StrAccum *p){ if( isMalloced(p) ){ sqlite3DbFree(p->db, p->zText); p->printfFlags &= ~SQLITE_PRINTF_MALLOCED; } p->zText = 0; } |
︙ | ︙ | |||
901 902 903 904 905 906 907 | ** is malloced. ** n: Size of zBase in bytes. If total space requirements never exceed ** n then no memory allocations ever occur. ** mx: Maximum number of bytes to accumulate. If mx==0 then no memory ** allocations will ever occur. */ void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){ | | < > | 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 | ** is malloced. ** n: Size of zBase in bytes. If total space requirements never exceed ** n then no memory allocations ever occur. ** mx: Maximum number of bytes to accumulate. If mx==0 then no memory ** allocations will ever occur. */ void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){ p->zText = zBase; p->db = db; p->nAlloc = n; p->mxAlloc = mx; p->nChar = 0; p->accError = 0; p->printfFlags = 0; } /* ** Print into memory obtained from sqliteMalloc(). Use the internal ** %-conversion extensions. |
︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 | (void)SQLITE_MISUSE_BKPT; if( zBuf ) zBuf[0] = 0; return zBuf; } #endif sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); sqlite3VXPrintf(&acc, zFormat, ap); | > | | 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 | (void)SQLITE_MISUSE_BKPT; if( zBuf ) zBuf[0] = 0; return zBuf; } #endif sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); sqlite3VXPrintf(&acc, zFormat, ap); zBuf[acc.nChar] = 0; return zBuf; } char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ char *z; va_list ap; va_start(ap,zFormat); z = sqlite3_vsnprintf(n, zBuf, zFormat, ap); va_end(ap); |
︙ | ︙ |
Changes to src/random.c.
︙ | ︙ | |||
102 103 104 105 106 107 108 | wsdPrng.s[wsdPrng.j] = t; t += wsdPrng.s[wsdPrng.i]; *(zBuf++) = wsdPrng.s[t]; }while( --N ); sqlite3_mutex_leave(mutex); } | | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | wsdPrng.s[wsdPrng.j] = t; t += wsdPrng.s[wsdPrng.i]; *(zBuf++) = wsdPrng.s[t]; }while( --N ); sqlite3_mutex_leave(mutex); } #ifndef SQLITE_UNTESTABLE /* ** For testing purposes, we sometimes want to preserve the state of ** PRNG and restore the PRNG to its saved state at a later time, or ** to reset the PRNG to its initial state. These routines accomplish ** those tasks. ** ** The sqlite3_test_control() interface calls these routines to |
︙ | ︙ | |||
127 128 129 130 131 132 133 | void sqlite3PrngRestoreState(void){ memcpy( &GLOBAL(struct sqlite3PrngType, sqlite3Prng), &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng), sizeof(sqlite3Prng) ); } | | | 127 128 129 130 131 132 133 134 | void sqlite3PrngRestoreState(void){ memcpy( &GLOBAL(struct sqlite3PrngType, sqlite3Prng), &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng), sizeof(sqlite3Prng) ); } #endif /* SQLITE_UNTESTABLE */ |
Changes to src/resolve.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | ************************************************************************* ** ** This file contains routines used for walking the parser tree and ** resolve all identifiers by associating them with a particular ** table and column. */ #include "sqliteInt.h" | < < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ************************************************************************* ** ** This file contains routines used for walking the parser tree and ** resolve all identifiers by associating them with a particular ** table and column. */ #include "sqliteInt.h" /* ** Walk the expression tree pExpr and increase the aggregate function ** depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node. ** This needs to occur when copying a TK_AGG_FUNCTION node from an ** outer query into an inner subquery. ** |
︙ | ︙ | |||
227 228 229 230 231 232 233 | break; } } } } /* Start at the inner-most context and move outward until a match is found */ | | > | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | break; } } } } /* Start at the inner-most context and move outward until a match is found */ assert( pNC && cnt==0 ); do{ ExprList *pEList; SrcList *pSrcList = pNC->pSrcList; if( pSrcList ){ for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); |
︙ | ︙ | |||
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | assert( pExpr->pLeft==0 && pExpr->pRight==0 ); assert( pExpr->x.pList==0 ); assert( pExpr->x.pSelect==0 ); pOrig = pEList->a[j].pExpr; if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){ sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); return WRC_Abort; } resolveAlias(pParse, pEList, j, pExpr, "", nSubquery); cnt = 1; pMatch = 0; assert( zTab==0 && zDb==0 ); goto lookupname_end; } } } /* Advance to the next name context. The loop will exit when either ** we have a match (cnt>0) or when we run out of name contexts. */ | > > > > | | | | | | 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 | assert( pExpr->pLeft==0 && pExpr->pRight==0 ); assert( pExpr->x.pList==0 ); assert( pExpr->x.pSelect==0 ); pOrig = pEList->a[j].pExpr; if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){ sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); return WRC_Abort; } if( sqlite3ExprVectorSize(pOrig)!=1 ){ sqlite3ErrorMsg(pParse, "row value misused"); return WRC_Abort; } resolveAlias(pParse, pEList, j, pExpr, "", nSubquery); cnt = 1; pMatch = 0; assert( zTab==0 && zDb==0 ); goto lookupname_end; } } } /* Advance to the next name context. The loop will exit when either ** we have a match (cnt>0) or when we run out of name contexts. */ if( cnt ) break; pNC = pNC->pNext; nSubquery++; }while( pNC ); /* ** If X and Y are NULL (in other words if only the column name Z is ** supplied) and the value of Z is enclosed in double-quotes, then ** Z is a string literal if it doesn't match any column names. In that ** case, we need to return right away and not make any changes to ** pExpr. |
︙ | ︙ | |||
471 472 473 474 475 476 477 478 479 480 481 482 483 484 | /* Clean up and return */ sqlite3ExprDelete(db, pExpr->pLeft); pExpr->pLeft = 0; sqlite3ExprDelete(db, pExpr->pRight); pExpr->pRight = 0; pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN); lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); if( !ExprHasProperty(pExpr, EP_Alias) ){ sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); } /* Increment the nRef value on all name contexts from TopNC up to | > | 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | /* Clean up and return */ sqlite3ExprDelete(db, pExpr->pLeft); pExpr->pLeft = 0; sqlite3ExprDelete(db, pExpr->pRight); pExpr->pRight = 0; pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN); ExprSetProperty(pExpr, EP_Leaf); lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); if( !ExprHasProperty(pExpr, EP_Alias) ){ sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); } /* Increment the nRef value on all name contexts from TopNC up to |
︙ | ︙ | |||
509 510 511 512 513 514 515 | p->iColumn = -1; }else{ p->iColumn = (ynVar)iCol; testcase( iCol==BMS ); testcase( iCol==BMS-1 ); pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); } | < | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 | p->iColumn = -1; }else{ p->iColumn = (ynVar)iCol; testcase( iCol==BMS ); testcase( iCol==BMS-1 ); pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); } } return p; } /* ** Report an error that an expression is not valid for some set of ** pNC->ncFlags values determined by validMask. |
︙ | ︙ | |||
569 570 571 572 573 574 575 | Parse *pParse; pNC = pWalker->u.pNC; assert( pNC!=0 ); pParse = pNC->pParse; assert( pParse==pWalker->pParse ); | < < | 572 573 574 575 576 577 578 579 580 581 582 583 584 585 | Parse *pParse; pNC = pWalker->u.pNC; assert( pNC!=0 ); pParse = pNC->pParse; assert( pParse==pWalker->pParse ); #ifndef NDEBUG if( pNC->pSrcList && pNC->pSrcList->nAlloc>0 ){ SrcList *pSrcList = pNC->pSrcList; int i; for(i=0; i<pNC->pSrcList->nSrc; i++){ assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursor<pParse->nTab); } |
︙ | ︙ | |||
602 603 604 605 606 607 608 | pExpr->iColumn = -1; pExpr->affinity = SQLITE_AFF_INTEGER; break; } #endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */ | < < < < < | | > > > > > | > > > > | | | | | | | | | | | > | 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 | pExpr->iColumn = -1; pExpr->affinity = SQLITE_AFF_INTEGER; break; } #endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */ /* 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 *zColumn; const char *zTable; const char *zDb; Expr *pRight; if( pExpr->op==TK_ID ){ zDb = 0; zTable = 0; zColumn = pExpr->u.zToken; }else{ notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr); pRight = pExpr->pRight; if( pRight->op==TK_ID ){ zDb = 0; zTable = pExpr->pLeft->u.zToken; zColumn = pRight->u.zToken; }else{ assert( pRight->op==TK_DOT ); zDb = pExpr->pLeft->u.zToken; zTable = pRight->pLeft->u.zToken; zColumn = pRight->pRight->u.zToken; } } return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); } /* Resolve function names */ case TK_FUNCTION: { |
︙ | ︙ | |||
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 | } break; } case TK_VARIABLE: { notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr); break; } case TK_EQ: case TK_NE: case TK_LT: case TK_LE: case TK_GT: case TK_GE: case TK_IS: case TK_ISNOT: { int nLeft, nRight; if( pParse->db->mallocFailed ) break; | > < > > > > > > > | > > | 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 | } break; } case TK_VARIABLE: { notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr); break; } case TK_BETWEEN: case TK_EQ: case TK_NE: case TK_LT: case TK_LE: case TK_GT: case TK_GE: case TK_IS: case TK_ISNOT: { int nLeft, nRight; if( pParse->db->mallocFailed ) break; assert( pExpr->pLeft!=0 ); nLeft = sqlite3ExprVectorSize(pExpr->pLeft); if( pExpr->op==TK_BETWEEN ){ nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[0].pExpr); if( nRight==nLeft ){ nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[1].pExpr); } }else{ assert( pExpr->pRight!=0 ); nRight = sqlite3ExprVectorSize(pExpr->pRight); } if( nLeft!=nRight ){ testcase( pExpr->op==TK_EQ ); testcase( pExpr->op==TK_NE ); testcase( pExpr->op==TK_LT ); testcase( pExpr->op==TK_LE ); testcase( pExpr->op==TK_GT ); testcase( pExpr->op==TK_GE ); testcase( pExpr->op==TK_IS ); testcase( pExpr->op==TK_ISNOT ); testcase( pExpr->op==TK_BETWEEN ); sqlite3ErrorMsg(pParse, "row value misused"); } break; } } return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; } |
︙ | ︙ | |||
889 890 891 892 893 894 895 | if( rc ) return 0; /* Try to match the ORDER BY expression against an expression ** in the result set. Return an 1-based index of the matching ** result-set entry. */ for(i=0; i<pEList->nExpr; i++){ | | | 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 | if( rc ) return 0; /* Try to match the ORDER BY expression against an expression ** in the result set. Return an 1-based index of the matching ** result-set entry. */ for(i=0; i<pEList->nExpr; i++){ if( sqlite3ExprCompare(0, pEList->a[i].pExpr, pE, -1)<2 ){ return i+1; } } /* If no match, return 0. */ return 0; } |
︙ | ︙ | |||
1123 1124 1125 1126 1127 1128 1129 | /* Otherwise, treat the ORDER BY term as an ordinary expression */ pItem->u.x.iOrderByCol = 0; if( sqlite3ResolveExprNames(pNC, pE) ){ return 1; } for(j=0; j<pSelect->pEList->nExpr; j++){ | | | 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 | /* Otherwise, treat the ORDER BY term as an ordinary expression */ pItem->u.x.iOrderByCol = 0; if( sqlite3ResolveExprNames(pNC, pE) ){ return 1; } for(j=0; j<pSelect->pEList->nExpr; j++){ if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ pItem->u.x.iOrderByCol = j+1; } } } return sqlite3ResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType); } |
︙ | ︙ | |||
1409 1410 1411 1412 1413 1414 1415 | int sqlite3ResolveExprNames( NameContext *pNC, /* Namespace to resolve expressions in. */ Expr *pExpr /* The expression to be analyzed. */ ){ u16 savedHasAgg; Walker w; | | < < < < < < < < < < < > > > > > > | < < < | | 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 | int sqlite3ResolveExprNames( NameContext *pNC, /* Namespace to resolve expressions in. */ Expr *pExpr /* The expression to be analyzed. */ ){ u16 savedHasAgg; Walker w; if( pExpr==0 ) return SQLITE_OK; savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg); pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg); w.pParse = pNC->pParse; w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; w.xSelectCallback2 = 0; w.u.pNC = pNC; #if SQLITE_MAX_EXPR_DEPTH>0 w.pParse->nHeight += pExpr->nHeight; if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){ return SQLITE_ERROR; } #endif sqlite3WalkExpr(&w, pExpr); #if SQLITE_MAX_EXPR_DEPTH>0 w.pParse->nHeight -= pExpr->nHeight; #endif if( pNC->ncFlags & NC_HasAgg ){ ExprSetProperty(pExpr, EP_Agg); } pNC->ncFlags |= savedHasAgg; return pNC->nErr>0 || w.pParse->nErr>0; } /* ** Resolve all names for all expression in an expression list. This is ** just like sqlite3ResolveExprNames() except that it works for an expression ** list rather than a single expression. */ |
︙ | ︙ | |||
1480 1481 1482 1483 1484 1485 1486 | Parse *pParse, /* The parser context */ Select *p, /* The SELECT statement being coded. */ NameContext *pOuterNC /* Name context for parent SELECT statement */ ){ Walker w; assert( p!=0 ); | < > | 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 | Parse *pParse, /* The parser context */ Select *p, /* The SELECT statement being coded. */ NameContext *pOuterNC /* Name context for parent SELECT statement */ ){ Walker w; assert( p!=0 ); w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; w.xSelectCallback2 = 0; w.pParse = pParse; w.u.pNC = pOuterNC; sqlite3WalkSelect(&w, p); } /* ** Resolve names in expressions that can only reference a single table: |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
72 73 74 75 76 77 78 | sqlite3ExprDelete(db, p->pWhere); sqlite3ExprListDelete(db, p->pGroupBy); sqlite3ExprDelete(db, p->pHaving); sqlite3ExprListDelete(db, p->pOrderBy); sqlite3ExprDelete(db, p->pLimit); sqlite3ExprDelete(db, p->pOffset); if( p->pWith ) sqlite3WithDelete(db, p->pWith); | | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | sqlite3ExprDelete(db, p->pWhere); sqlite3ExprListDelete(db, p->pGroupBy); sqlite3ExprDelete(db, p->pHaving); sqlite3ExprListDelete(db, p->pOrderBy); sqlite3ExprDelete(db, p->pLimit); sqlite3ExprDelete(db, p->pOffset); if( p->pWith ) sqlite3WithDelete(db, p->pWith); if( bFree ) sqlite3DbFreeNN(db, p); p = pPrior; bFree = 1; } } /* ** Initialize a SelectDest structure. |
︙ | ︙ | |||
108 109 110 111 112 113 114 | ExprList *pOrderBy, /* the ORDER BY clause */ u32 selFlags, /* Flag parameters, such as SF_Distinct */ Expr *pLimit, /* LIMIT value. NULL means not used */ Expr *pOffset /* OFFSET value. NULL means no offset */ ){ Select *pNew; Select standin; | < | | | | | | | | 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 | ExprList *pOrderBy, /* the ORDER BY clause */ u32 selFlags, /* Flag parameters, such as SF_Distinct */ Expr *pLimit, /* LIMIT value. NULL means not used */ Expr *pOffset /* OFFSET value. NULL means no offset */ ){ Select *pNew; Select standin; pNew = sqlite3DbMallocRawNN(pParse->db, sizeof(*pNew) ); if( pNew==0 ){ assert( pParse->db->mallocFailed ); pNew = &standin; } if( pEList==0 ){ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(pParse->db,TK_ASTERISK,0)); } pNew->pEList = pEList; pNew->op = TK_SELECT; pNew->selFlags = selFlags; pNew->iLimit = 0; pNew->iOffset = 0; #if SELECTTRACE_ENABLED pNew->zSelName[0] = 0; #endif pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; pNew->nSelectRow = 0; if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc)); pNew->pSrc = pSrc; pNew->pWhere = pWhere; pNew->pGroupBy = pGroupBy; pNew->pHaving = pHaving; pNew->pOrderBy = pOrderBy; pNew->pPrior = 0; pNew->pNext = 0; pNew->pLimit = pLimit; pNew->pOffset = pOffset; pNew->pWith = 0; assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || pParse->db->mallocFailed!=0 ); if( pParse->db->mallocFailed ) { clearSelect(pParse->db, pNew, pNew!=&standin); pNew = 0; }else{ assert( pNew->pSrc!=0 || pParse->nErr>0 ); } assert( pNew!=&standin ); return pNew; } |
︙ | ︙ | |||
330 331 332 333 334 335 336 | assert( pSrc->nSrc>iRight ); assert( pSrc->a[iLeft].pTab ); assert( pSrc->a[iRight].pTab ); pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft); pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); | | | 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | assert( pSrc->nSrc>iRight ); assert( pSrc->a[iLeft].pTab ); assert( pSrc->a[iRight].pTab ); pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft); pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2); if( pEq && isOuterJoin ){ ExprSetProperty(pEq, EP_FromJoin); assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); ExprSetVVAProperty(pEq, EP_NoReduce); pEq->iRightJoinTable = (i16)pE2->iTable; } *ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq); |
︙ | ︙ | |||
517 518 519 520 521 522 523 | int regBase; /* Regs for sorter record */ int regRecord = ++pParse->nMem; /* Assembled sorter record */ int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */ int op; /* Opcode to add sorter record to sorter */ int iLimit; /* LIMIT counter */ assert( bSeq==0 || bSeq==1 ); | | | | | 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 | int regBase; /* Regs for sorter record */ int regRecord = ++pParse->nMem; /* Assembled sorter record */ int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */ int op; /* Opcode to add sorter record to sorter */ int iLimit; /* LIMIT counter */ assert( bSeq==0 || bSeq==1 ); assert( nData==1 || regData==regOrigData || regOrigData==0 ); if( nPrefixReg ){ assert( nPrefixReg==nExpr+bSeq ); regBase = regData - nExpr - bSeq; }else{ regBase = pParse->nMem + 1; pParse->nMem += nBase; } assert( pSelect->iOffset==0 || pSelect->iLimit!=0 ); iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit; pSort->labelDone = sqlite3VdbeMakeLabel(v); sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData, SQLITE_ECEL_DUP | (regOrigData? SQLITE_ECEL_REF : 0)); if( bSeq ){ sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr); } if( nPrefixReg==0 && nData>0 ){ sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData); } sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord); if( nOBSat>0 ){ int regPrevKey; /* The first nOBSat columns of the previous row */ int addrFirst; /* Address of the OP_IfNot opcode */ int addrJmp; /* Address of the OP_Jump opcode */ |
︙ | ︙ | |||
559 560 561 562 563 564 565 | } VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat); pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex); if( pParse->db->mallocFailed ) return; pOp->p2 = nKey + nData; pKI = pOp->p4.pKeyInfo; | | | | | > | | 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 | } VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat); pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex); if( pParse->db->mallocFailed ) return; pOp->p2 = nKey + nData; pKI = pOp->p4.pKeyInfo; memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */ sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO); testcase( pKI->nAllField > pKI->nKeyField+2 ); pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, pKI->nAllField-pKI->nKeyField-1); addrJmp = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); pSort->labelBkOut = sqlite3VdbeMakeLabel(v); pSort->regReturn = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor); if( iLimit ){ sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, pSort->labelDone); VdbeCoverage(v); } sqlite3VdbeJumpHere(v, addrFirst); sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat); sqlite3VdbeJumpHere(v, addrJmp); } if( pSort->sortFlags & SORTFLAG_UseSorter ){ op = OP_SorterInsert; }else{ op = OP_IdxInsert; } sqlite3VdbeAddOp4Int(v, op, pSort->iECursor, regRecord, regBase+nOBSat, nBase-nOBSat); if( iLimit ){ int addr; int r1 = 0; /* Fill the sorter until it contains LIMIT+OFFSET entries. (The iLimit ** register is initialized with value of LIMIT+OFFSET.) After the sorter ** fills up, delete the least entry in the sorter after each insert. ** Thus we never hold more than the LIMIT+OFFSET rows in memory at once */ addr = sqlite3VdbeAddOp1(v, OP_IfNotZero, iLimit); VdbeCoverage(v); sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor); if( pSort->bOrderedInnerLoop ){ r1 = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_Column, pSort->iECursor, nExpr, r1); VdbeComment((v, "seq")); } sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor); |
︙ | ︙ | |||
651 652 653 654 655 656 657 | Vdbe *v; int r1; v = pParse->pVdbe; r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); | | > | | | < | | < > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > | | 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 | Vdbe *v; int r1; v = pParse->pVdbe; r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, iMem, N); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3ReleaseTempReg(pParse, r1); } /* ** This routine generates the code for the inside of the inner loop ** of a SELECT. ** ** If srcTab is negative, then the p->pEList expressions ** are evaluated in order to get the data for this row. If srcTab is ** zero or more, then data is pulled from srcTab and p->pEList is used only ** to get the number of columns and the collation sequence for each column. */ static void selectInnerLoop( Parse *pParse, /* The parser context */ Select *p, /* The complete select statement being coded */ int srcTab, /* Pull data from this table if non-negative */ SortCtx *pSort, /* If not NULL, info on how to process ORDER BY */ DistinctCtx *pDistinct, /* If not NULL, info on how to process DISTINCT */ SelectDest *pDest, /* How to dispose of the results */ int iContinue, /* Jump here to continue with next row */ int iBreak /* Jump here to break out of the inner loop */ ){ Vdbe *v = pParse->pVdbe; int i; int hasDistinct; /* True if the DISTINCT keyword is present */ int eDest = pDest->eDest; /* How to dispose of results */ int iParm = pDest->iSDParm; /* First argument to disposal method */ int nResultCol; /* Number of result columns */ int nPrefixReg = 0; /* Number of extra registers before regResult */ /* Usually, regResult is the first cell in an array of memory cells ** containing the current result row. In this case regOrig is set to the ** same value. However, if the results are being sent to the sorter, the ** values for any expressions that are also part of the sort-key are omitted ** from this array. In this case regOrig is set to zero. */ int regResult; /* Start of memory holding current results */ int regOrig; /* Start of memory holding full result (or 0) */ assert( v ); assert( p->pEList!=0 ); hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP; if( pSort && pSort->pOrderBy==0 ) pSort = 0; if( pSort==0 && !hasDistinct ){ assert( iContinue!=0 ); codeOffset(v, p->iOffset, iContinue); } /* Pull the requested columns. */ nResultCol = p->pEList->nExpr; if( pDest->iSdst==0 ){ if( pSort ){ nPrefixReg = pSort->pOrderBy->nExpr; if( !(pSort->sortFlags & SORTFLAG_UseSorter) ) nPrefixReg++; pParse->nMem += nPrefixReg; } pDest->iSdst = pParse->nMem+1; pParse->nMem += nResultCol; }else if( pDest->iSdst+nResultCol > pParse->nMem ){ /* This is an error condition that can result, for example, when a SELECT ** on the right-hand side of an INSERT contains more result columns than ** there are columns in the table on the left. The error will be caught ** and reported later. But we need to make sure enough memory is allocated ** to avoid other spurious errors in the meantime. */ pParse->nMem += nResultCol; } pDest->nSdst = nResultCol; regOrig = regResult = pDest->iSdst; if( srcTab>=0 ){ for(i=0; i<nResultCol; i++){ sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i); VdbeComment((v, "%s", p->pEList->a[i].zName)); } }else if( eDest!=SRT_Exists ){ /* If the destination is an EXISTS(...) expression, the actual ** values returned by the SELECT are not required. */ u8 ecelFlags; if( eDest==SRT_Mem || eDest==SRT_Output || eDest==SRT_Coroutine ){ ecelFlags = SQLITE_ECEL_DUP; }else{ ecelFlags = 0; } if( pSort && hasDistinct==0 && eDest!=SRT_EphemTab && eDest!=SRT_Table ){ /* For each expression in p->pEList that is a copy of an expression in ** the ORDER BY clause (pSort->pOrderBy), set the associated ** iOrderByCol value to one more than the index of the ORDER BY ** expression within the sort-key that pushOntoSorter() will generate. ** This allows the p->pEList field to be omitted from the sorted record, ** saving space and CPU cycles. */ ecelFlags |= (SQLITE_ECEL_OMITREF|SQLITE_ECEL_REF); for(i=pSort->nOBSat; i<pSort->pOrderBy->nExpr; i++){ int j; if( (j = pSort->pOrderBy->a[i].u.x.iOrderByCol)>0 ){ p->pEList->a[j-1].u.x.iOrderByCol = i+1-pSort->nOBSat; } } regOrig = 0; assert( eDest==SRT_Set || eDest==SRT_Mem || eDest==SRT_Coroutine || eDest==SRT_Output ); } nResultCol = sqlite3ExprCodeExprList(pParse,p->pEList,regResult,0,ecelFlags); } /* If the DISTINCT keyword was present on the SELECT statement ** and this row has been seen before, then do not make this row ** part of the result. */ if( hasDistinct ){ |
︙ | ︙ | |||
762 763 764 765 766 767 768 | pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct); pOp->opcode = OP_Null; pOp->p1 = 1; pOp->p2 = regPrev; iJump = sqlite3VdbeCurrentAddr(v) + nResultCol; for(i=0; i<nResultCol; i++){ | | | 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 | pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct); pOp->opcode = OP_Null; pOp->p1 = 1; pOp->p2 = regPrev; iJump = sqlite3VdbeCurrentAddr(v) + nResultCol; for(i=0; i<nResultCol; i++){ CollSeq *pColl = sqlite3ExprCollSeq(pParse, p->pEList->a[i].pExpr); if( i<nResultCol-1 ){ sqlite3VdbeAddOp3(v, OP_Ne, regResult+i, iJump, regPrev+i); VdbeCoverage(v); }else{ sqlite3VdbeAddOp3(v, OP_Eq, regResult+i, iContinue, regPrev+i); VdbeCoverage(v); } |
︙ | ︙ | |||
804 805 806 807 808 809 810 | ** table iParm. */ #ifndef SQLITE_OMIT_COMPOUND_SELECT case SRT_Union: { int r1; r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); | | | 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 | ** table iParm. */ #ifndef SQLITE_OMIT_COMPOUND_SELECT case SRT_Union: { int r1; r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol); sqlite3ReleaseTempReg(pParse, r1); break; } /* Construct a record from the query result, but instead of ** saving that record, use it as a key to delete elements from ** the temporary table iParm. |
︙ | ︙ | |||
841 842 843 844 845 846 847 | ** on an ephemeral index. If the current row is already present ** in the index, do not write it to the output. If not, add the ** current row to the index and proceed with writing it to the ** output table as well. */ int addr = sqlite3VdbeCurrentAddr(v) + 4; sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); VdbeCoverage(v); | | | 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 | ** on an ephemeral index. If the current row is already present ** in the index, do not write it to the output. If not, add the ** current row to the index and proceed with writing it to the ** output table as well. */ int addr = sqlite3VdbeCurrentAddr(v) + 4; sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); VdbeCoverage(v); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm+1, r1,regResult,nResultCol); assert( pSort==0 ); } #endif if( pSort ){ pushOntoSorter(pParse, pSort, p, r1+nPrefixReg,regResult,1,nPrefixReg); }else{ int r2 = sqlite3GetTempReg(pParse); |
︙ | ︙ | |||
870 871 872 873 874 875 876 | case SRT_Set: { if( pSort ){ /* At first glance you would think we could optimize out the ** ORDER BY in this case since the order of entries in the set ** does not matter. But there might be a LIMIT clause, in which ** case the order does matter */ pushOntoSorter( | | | < > | > | | 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 | case SRT_Set: { if( pSort ){ /* At first glance you would think we could optimize out the ** ORDER BY in this case since the order of entries in the set ** does not matter. But there might be a LIMIT clause, in which ** case the order does matter */ pushOntoSorter( pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); }else{ int r1 = sqlite3GetTempReg(pParse); assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol, r1, pDest->zAffSdst, nResultCol); sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol); sqlite3ReleaseTempReg(pParse, r1); } break; } /* If any row exist in the result set, record that fact and abort. */ case SRT_Exists: { sqlite3VdbeAddOp2(v, OP_Integer, 1, iParm); /* The LIMIT clause will terminate the loop for us */ break; } /* If this is a scalar select that is part of an expression, then ** store the results in the appropriate memory cell or array of ** memory cells and break out of the scan loop. */ case SRT_Mem: { if( pSort ){ assert( nResultCol<=pDest->nSdst ); pushOntoSorter( pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); }else{ assert( nResultCol==pDest->nSdst ); assert( regResult==iParm ); /* The LIMIT clause will jump out of the loop for us */ } break; } #endif /* #ifndef SQLITE_OMIT_SUBQUERY */ case SRT_Coroutine: /* Send data to a co-routine */ case SRT_Output: { /* Return the results */ testcase( eDest==SRT_Coroutine ); testcase( eDest==SRT_Output ); if( pSort ){ pushOntoSorter(pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); }else if( eDest==SRT_Coroutine ){ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); }else{ sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nResultCol); sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol); } |
︙ | ︙ | |||
963 964 965 966 967 968 969 | for(i=0; i<nKey; i++){ sqlite3VdbeAddOp2(v, OP_SCopy, regResult + pSO->a[i].u.x.iOrderByCol - 1, r2+i); } sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey); sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1); | | | 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 | for(i=0; i<nKey; i++){ sqlite3VdbeAddOp2(v, OP_SCopy, regResult + pSO->a[i].u.x.iOrderByCol - 1, r2+i); } sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey); sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, r2, nKey+2); if( addrTest ) sqlite3VdbeJumpHere(v, addrTest); sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempRange(pParse, r2, nKey+2); break; } #endif /* SQLITE_OMIT_CTE */ |
︙ | ︙ | |||
1000 1001 1002 1003 1004 1005 1006 | } /* ** Allocate a KeyInfo object sufficient for an index of N key columns and ** X extra columns. */ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ | | | | | | 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 | } /* ** Allocate a KeyInfo object sufficient for an index of N key columns and ** X extra columns. */ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*); KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra); if( p ){ p->aSortOrder = (u8*)&p->aColl[N+X]; p->nKeyField = (u16)N; p->nAllField = (u16)(N+X); p->enc = ENC(db); p->db = db; p->nRef = 1; memset(&p[1], 0, nExtra); }else{ sqlite3OomFault(db); } return p; } /* ** Deallocate a KeyInfo object */ void sqlite3KeyInfoUnref(KeyInfo *p){ if( p ){ assert( p->nRef>0 ); p->nRef--; if( p->nRef==0 ) sqlite3DbFreeNN(p->db, p); } } /* ** Make a new pointer to a KeyInfo object */ KeyInfo *sqlite3KeyInfoRef(KeyInfo *p){ |
︙ | ︙ | |||
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 | int addrOnce = 0; int iTab; ExprList *pOrderBy = pSort->pOrderBy; int eDest = pDest->eDest; int iParm = pDest->iSDParm; int regRow; int regRowid; int nKey; int iSortTab; /* Sorter cursor to read from */ int nSortData; /* Trailing values to read from sorter */ int i; int bSeq; /* True if sorter record includes seq. no. */ | > < < | 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 | int addrOnce = 0; int iTab; ExprList *pOrderBy = pSort->pOrderBy; int eDest = pDest->eDest; int iParm = pDest->iSDParm; int regRow; int regRowid; int iCol; int nKey; int iSortTab; /* Sorter cursor to read from */ int nSortData; /* Trailing values to read from sorter */ int i; int bSeq; /* True if sorter record includes seq. no. */ struct ExprList_item *aOutEx = p->pEList->a; assert( addrBreak<0 ); if( pSort->labelBkOut ){ sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); sqlite3VdbeGoto(v, addrBreak); sqlite3VdbeResolveLabel(v, pSort->labelBkOut); } |
︙ | ︙ | |||
1243 1244 1245 1246 1247 1248 1249 | bSeq = 0; }else{ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v); codeOffset(v, p->iOffset, addrContinue); iSortTab = iTab; bSeq = 1; } | | > > > > > > | > | | 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 | bSeq = 0; }else{ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v); codeOffset(v, p->iOffset, addrContinue); iSortTab = iTab; bSeq = 1; } for(i=0, iCol=nKey+bSeq; i<nSortData; i++){ int iRead; if( aOutEx[i].u.x.iOrderByCol ){ iRead = aOutEx[i].u.x.iOrderByCol-1; }else{ iRead = iCol++; } sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i); VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan)); } switch( eDest ){ case SRT_Table: case SRT_EphemTab: { sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); break; } #ifndef SQLITE_OMIT_SUBQUERY case SRT_Set: { assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid, pDest->zAffSdst, nColumn); sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, regRowid, regRow, nColumn); break; } case SRT_Mem: { /* The LIMIT clause will terminate the loop for us */ break; } #endif |
︙ | ︙ | |||
1401 1402 1403 1404 1405 1406 1407 | assert( pTab && pExpr->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. */ | | < < < | 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 | assert( pTab && pExpr->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>=0 && iCol<pS->pEList->nExpr ){ /* 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; sNC.pSrcList = pS->pSrc; sNC.pNext = pNC; sNC.pParse = pNC->pParse; zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol, &estWidth); |
︙ | ︙ | |||
1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 | ){ #ifndef SQLITE_OMIT_DECLTYPE Vdbe *v = pParse->pVdbe; int i; NameContext sNC; sNC.pSrcList = pTabList; sNC.pParse = pParse; for(i=0; i<pEList->nExpr; i++){ Expr *p = pEList->a[i].pExpr; const char *zType; #ifdef SQLITE_ENABLE_COLUMN_METADATA const char *zOrigDb = 0; const char *zOrigTab = 0; const char *zOrigCol = 0; | > | 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 | ){ #ifndef SQLITE_OMIT_DECLTYPE Vdbe *v = pParse->pVdbe; int i; NameContext sNC; sNC.pSrcList = pTabList; sNC.pParse = pParse; sNC.pNext = 0; for(i=0; i<pEList->nExpr; i++){ Expr *p = pEList->a[i].pExpr; const char *zType; #ifdef SQLITE_ENABLE_COLUMN_METADATA const char *zOrigDb = 0; const char *zOrigTab = 0; const char *zOrigCol = 0; |
︙ | ︙ | |||
1515 1516 1517 1518 1519 1520 1521 1522 1523 | #else zType = columnType(&sNC, p, 0, 0, 0, 0); #endif sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT); } #endif /* !defined(SQLITE_OMIT_DECLTYPE) */ } /* | > | > > > > > > > > > > > | > > > > | > > > > > > > > > > < < > | > > > | > > > > > | | < | | > > > > | < < < < < | > < < < | | 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 | #else zType = columnType(&sNC, p, 0, 0, 0, 0); #endif sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT); } #endif /* !defined(SQLITE_OMIT_DECLTYPE) */ } /* ** Compute the column names for a SELECT statement. ** ** The only guarantee that SQLite makes about column names is that if the ** column has an AS clause assigning it a name, that will be the name used. ** That is the only documented guarantee. However, countless applications ** developed over the years have made baseless assumptions about column names ** and will break if those assumptions changes. Hence, use extreme caution ** when modifying this routine to avoid breaking legacy. ** ** See Also: sqlite3ColumnsFromExprList() ** ** The PRAGMA short_column_names and PRAGMA full_column_names settings are ** deprecated. The default setting is short=ON, full=OFF. 99.9% of all ** applications should operate this way. Nevertheless, we need to support the ** other modes for legacy: ** ** short=OFF, full=OFF: Column name is the text of the expression has it ** originally appears in the SELECT statement. In ** other words, the zSpan of the result expression. ** ** short=ON, full=OFF: (This is the default setting). If the result ** refers directly to a table column, then the result ** column name is just the table column name: COLUMN. ** Otherwise use zSpan. ** ** full=ON, short=ANY: If the result refers directly to a table column, ** then the result column name with the table name ** prefix, ex: TABLE.COLUMN. Otherwise use zSpan. */ static void generateColumnNames( Parse *pParse, /* Parser context */ Select *pSelect /* Generate column names for this SELECT statement */ ){ Vdbe *v = pParse->pVdbe; int i; Table *pTab; SrcList *pTabList; ExprList *pEList; sqlite3 *db = pParse->db; int fullName; /* TABLE.COLUMN if no AS clause and is a direct table ref */ int srcName; /* COLUMN or TABLE.COLUMN if no AS clause and is direct */ #ifndef SQLITE_OMIT_EXPLAIN /* If this is an EXPLAIN, skip this step */ if( pParse->explain ){ return; } #endif if( pParse->colNamesSet || db->mallocFailed ) return; /* Column names are determined by the left-most term of a compound select */ while( pSelect->pPrior ) pSelect = pSelect->pPrior; pTabList = pSelect->pSrc; pEList = pSelect->pEList; assert( v!=0 ); assert( pTabList!=0 ); pParse->colNamesSet = 1; fullName = (db->flags & SQLITE_FullColNames)!=0; srcName = (db->flags & SQLITE_ShortColNames)!=0 || fullName; sqlite3VdbeSetNumCols(v, pEList->nExpr); for(i=0; i<pEList->nExpr; i++){ Expr *p = pEList->a[i].pExpr; assert( p!=0 ); assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */ assert( p->op!=TK_COLUMN || p->pTab!=0 ); /* Covering indexes not yet coded */ if( pEList->a[i].zName ){ /* An AS clause always takes first priority */ char *zName = pEList->a[i].zName; sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT); }else if( srcName && p->op==TK_COLUMN ){ char *zCol; int iCol = p->iColumn; pTab = p->pTab; assert( pTab!=0 ); if( iCol<0 ) iCol = pTab->iPKey; assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) ); if( iCol<0 ){ zCol = "rowid"; }else{ zCol = pTab->aCol[iCol].zName; } if( fullName ){ char *zName = 0; zName = sqlite3MPrintf(db, "%s.%s", pTab->zName, zCol); sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_DYNAMIC); }else{ sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT); } }else{ |
︙ | ︙ | |||
1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 | ** All column names will be unique. ** ** Only the column names are computed. Column.zType, Column.zColl, ** and other fields of Column are zeroed. ** ** Return SQLITE_OK on success. If a memory allocation error occurs, ** store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM. */ int sqlite3ColumnsFromExprList( Parse *pParse, /* Parsing context */ ExprList *pEList, /* Expr list from which to derive column names */ i16 *pnCol, /* Write the number of columns here */ Column **paCol /* Write the new column list here */ ){ sqlite3 *db = pParse->db; /* Database connection */ int i, j; /* Loop counters */ u32 cnt; /* Index added to make the name unique */ Column *aCol, *pCol; /* For looping over result columns */ int nCol; /* Number of columns in the result set */ | > > > > > > > > > < < | < > | > | > > > | > | 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 | ** All column names will be unique. ** ** Only the column names are computed. Column.zType, Column.zColl, ** and other fields of Column are zeroed. ** ** Return SQLITE_OK on success. If a memory allocation error occurs, ** store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM. ** ** The only guarantee that SQLite makes about column names is that if the ** column has an AS clause assigning it a name, that will be the name used. ** That is the only documented guarantee. However, countless applications ** developed over the years have made baseless assumptions about column names ** and will break if those assumptions changes. Hence, use extreme caution ** when modifying this routine to avoid breaking legacy. ** ** See Also: generateColumnNames() */ int sqlite3ColumnsFromExprList( Parse *pParse, /* Parsing context */ ExprList *pEList, /* Expr list from which to derive column names */ i16 *pnCol, /* Write the number of columns here */ Column **paCol /* Write the new column list here */ ){ sqlite3 *db = pParse->db; /* Database connection */ int i, j; /* Loop counters */ u32 cnt; /* Index added to make the name unique */ Column *aCol, *pCol; /* For looping over result columns */ int nCol; /* Number of columns in the result set */ char *zName; /* Column name */ int nName; /* Size of name in zName[] */ Hash ht; /* Hash table of column names */ sqlite3HashInit(&ht); if( pEList ){ nCol = pEList->nExpr; aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); testcase( aCol==0 ); }else{ nCol = 0; aCol = 0; } assert( nCol==(i16)nCol ); *pnCol = nCol; *paCol = aCol; for(i=0, pCol=aCol; i<nCol && !db->mallocFailed; i++, pCol++){ /* Get an appropriate name for the column */ if( (zName = pEList->a[i].zName)!=0 ){ /* If the column contains an "AS <name>" phrase, use <name> as the name */ }else{ Expr *pColExpr = sqlite3ExprSkipCollate(pEList->a[i].pExpr); while( pColExpr->op==TK_DOT ){ pColExpr = pColExpr->pRight; assert( pColExpr!=0 ); } if( (pColExpr->op==TK_COLUMN || pColExpr->op==TK_AGG_COLUMN) && pColExpr->pTab!=0 ){ /* For columns use the column name name */ int iCol = pColExpr->iColumn; Table *pTab = pColExpr->pTab; if( iCol<0 ) iCol = pTab->iPKey; zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; }else if( pColExpr->op==TK_ID ){ assert( !ExprHasProperty(pColExpr, EP_IntValue) ); zName = pColExpr->u.zToken; }else{ /* Use the original text of the column expression as its name */ zName = pEList->a[i].zSpan; } } if( zName ){ zName = sqlite3DbStrDup(db, zName); }else{ zName = sqlite3MPrintf(db,"column%d",i+1); } /* Make sure the column name is unique. If the name is not unique, ** append an integer to the name so that it becomes unique. */ cnt = 0; while( zName && sqlite3HashFind(&ht, zName)!=0 ){ nName = sqlite3Strlen30(zName); |
︙ | ︙ | |||
1769 1770 1771 1772 1773 1774 1775 | pTab = sqlite3DbMallocZero(db, sizeof(Table) ); if( pTab==0 ){ return 0; } /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside ** is disabled */ assert( db->lookaside.bDisable ); | | | | | > | < < < < | 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 | pTab = sqlite3DbMallocZero(db, sizeof(Table) ); if( pTab==0 ){ return 0; } /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside ** is disabled */ assert( db->lookaside.bDisable ); pTab->nTabRef = 1; pTab->zName = 0; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect); pTab->iPKey = -1; if( db->mallocFailed ){ sqlite3DeleteTable(db, pTab); return 0; } return pTab; } /* ** Get a VDBE for the given parser context. Create a new one if necessary. ** If an error occurs, return NULL and leave a message in pParse. */ Vdbe *sqlite3GetVdbe(Parse *pParse){ if( pParse->pVdbe ){ return pParse->pVdbe; } if( pParse->pToplevel==0 && OptimizationEnabled(pParse->db,SQLITE_FactorOutConst) ){ pParse->okConstFactor = 1; } return sqlite3VdbeCreate(pParse); } /* ** Compute the iLimit and iOffset fields of the SELECT based on the ** pLimit and pOffset expressions. pLimit and pOffset hold the expressions ** that appear in the original SQL statement after the LIMIT and OFFSET |
︙ | ︙ | |||
2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 | int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */ /* Obtain authorization to do a recursive query */ if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return; /* Process the LIMIT and OFFSET clauses, if they exist */ addrBreak = sqlite3VdbeMakeLabel(v); computeLimitRegisters(pParse, p, addrBreak); pLimit = p->pLimit; pOffset = p->pOffset; regLimit = p->iLimit; regOffset = p->iOffset; p->pLimit = p->pOffset = 0; p->iLimit = p->iOffset = 0; | > | 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 | int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */ /* Obtain authorization to do a recursive query */ if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return; /* Process the LIMIT and OFFSET clauses, if they exist */ addrBreak = sqlite3VdbeMakeLabel(v); p->nSelectRow = 320; /* 4 billion rows */ computeLimitRegisters(pParse, p, addrBreak); pLimit = p->pLimit; pOffset = p->pOffset; regLimit = p->iLimit; regOffset = p->iOffset; p->pLimit = p->pOffset = 0; p->iLimit = p->iOffset = 0; |
︙ | ︙ | |||
2070 2071 2072 2073 2074 2075 2076 | sqlite3VdbeAddOp2(v, OP_RowData, iQueue, regCurrent); } sqlite3VdbeAddOp1(v, OP_Delete, iQueue); /* Output the single row in Current */ addrCont = sqlite3VdbeMakeLabel(v); codeOffset(v, regOffset, addrCont); | | | 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 | sqlite3VdbeAddOp2(v, OP_RowData, iQueue, regCurrent); } sqlite3VdbeAddOp1(v, OP_Delete, iQueue); /* Output the single row in Current */ addrCont = sqlite3VdbeMakeLabel(v); codeOffset(v, regOffset, addrCont); selectInnerLoop(pParse, p, iCurrent, 0, 0, pDest, addrCont, addrBreak); if( regLimit ){ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, regLimit, addrBreak); VdbeCoverage(v); } sqlite3VdbeResolveLabel(v, addrCont); |
︙ | ︙ | |||
2208 2209 2210 2211 2212 2213 2214 | ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. */ assert( p && p->pPrior ); /* Calling function guarantees this much */ assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION ); db = pParse->db; pPrior = p->pPrior; dest = *pDest; | | < < < < < < | | | 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 | ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. */ assert( p && p->pPrior ); /* Calling function guarantees this much */ assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION ); db = pParse->db; pPrior = p->pPrior; dest = *pDest; if( pPrior->pOrderBy || pPrior->pLimit ){ sqlite3ErrorMsg(pParse,"%s clause should come after %s not before", pPrior->pOrderBy!=0 ? "ORDER BY" : "LIMIT", selectOpName(p->op)); rc = 1; goto multi_select_end; } v = sqlite3GetVdbe(pParse); assert( v!=0 ); /* The VDBE already created by calling function */ |
︙ | ︙ | |||
2384 2385 2386 2387 2388 2389 2390 | /* Convert the data in the temporary table into whatever form ** it is that we currently need. */ assert( unionTab==dest.iSDParm || dest.eDest!=priorOp ); if( dest.eDest!=priorOp ){ int iCont, iBreak, iStart; assert( p->pEList ); | < < < < < | | 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 | /* Convert the data in the temporary table into whatever form ** it is that we currently need. */ assert( unionTab==dest.iSDParm || dest.eDest!=priorOp ); if( dest.eDest!=priorOp ){ int iCont, iBreak, iStart; assert( p->pEList ); iBreak = sqlite3VdbeMakeLabel(v); iCont = sqlite3VdbeMakeLabel(v); computeLimitRegisters(pParse, p, iBreak); sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v); iStart = sqlite3VdbeCurrentAddr(v); selectInnerLoop(pParse, p, unionTab, 0, 0, &dest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v); sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0); } break; |
︙ | ︙ | |||
2459 2460 2461 2462 2463 2464 2465 | p->pLimit = pLimit; p->pOffset = pOffset; /* Generate code to take the intersection of the two temporary ** tables. */ assert( p->pEList ); | < < < < < | | | 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 | p->pLimit = pLimit; p->pOffset = pOffset; /* Generate code to take the intersection of the two temporary ** tables. */ assert( p->pEList ); iBreak = sqlite3VdbeMakeLabel(v); iCont = sqlite3VdbeMakeLabel(v); computeLimitRegisters(pParse, p, iBreak); sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v); r1 = sqlite3GetTempReg(pParse); iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1); sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); VdbeCoverage(v); sqlite3ReleaseTempReg(pParse, r1); selectInnerLoop(pParse, p, tab1, 0, 0, &dest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v); sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); break; |
︙ | ︙ | |||
2636 2637 2638 2639 2640 2641 2642 | case SRT_Set: { int r1; testcase( pIn->nSdst>1 ); r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1, pDest->zAffSdst, pIn->nSdst); sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst); | | > | 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 | case SRT_Set: { int r1; testcase( pIn->nSdst>1 ); r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1, pDest->zAffSdst, pIn->nSdst); sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pDest->iSDParm, r1, pIn->iSdst, pIn->nSdst); sqlite3ReleaseTempReg(pParse, r1); break; } /* If this is a scalar select that is part of an expression, then ** store the results in the appropriate memory cell and break out ** of the scan loop. |
︙ | ︙ | |||
2860 2861 2862 2863 2864 2865 2866 | if( pItem->u.x.iOrderByCol==i ) break; } if( j==nOrderBy ){ Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); if( pNew==0 ) return SQLITE_NOMEM_BKPT; pNew->flags |= EP_IntValue; pNew->u.iValue = i; | | | 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 | if( pItem->u.x.iOrderByCol==i ) break; } if( j==nOrderBy ){ Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); if( pNew==0 ) return SQLITE_NOMEM_BKPT; pNew->flags |= EP_IntValue; pNew->u.iValue = i; p->pOrderBy = pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); if( pOrderBy ) pOrderBy->a[nOrderBy++].u.x.iOrderByCol = (u16)i; } } } /* Compute the comparison permutation and keyinfo that is used with ** the permutation used to determine if the next |
︙ | ︙ | |||
3070 3071 3072 3073 3074 3075 3076 | sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v); /* Jump to the this point in order to terminate the query. */ sqlite3VdbeResolveLabel(v, labelEnd); | < < < < < < < < > > > > > > > > > > > > > > > | | | | | < < > > > | > > | > > > > > > > > > > > | > > > > > > > | | | > > > > | | | | | | < < | | | < < | | | | | | | | | 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 | sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v); /* Jump to the this point in order to terminate the query. */ sqlite3VdbeResolveLabel(v, labelEnd); /* Reassembly the compound query so that it will be freed correctly ** by the calling function */ if( p->pPrior ){ sqlite3SelectDelete(db, p->pPrior); } p->pPrior = pPrior; pPrior->pNext = p; /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ explainComposite(pParse, p->op, iSub1, iSub2, 0); return pParse->nErr!=0; } #endif #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* An instance of the SubstContext object describes an substitution edit ** to be performed on a parse tree. ** ** All references to columns in table iTable are to be replaced by corresponding ** expressions in pEList. */ typedef struct SubstContext { Parse *pParse; /* The parsing context */ int iTable; /* Replace references to this table */ int iNewTable; /* New table number */ int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ ExprList *pEList; /* Replacement expressions */ } SubstContext; /* Forward Declarations */ static void substExprList(SubstContext*, ExprList*); static void substSelect(SubstContext*, Select*, int); /* ** Scan through the expression pExpr. Replace every reference to ** a column in table number iTable with a copy of the iColumn-th ** entry in pEList. (But leave references to the ROWID column ** unchanged.) ** ** This routine is part of the flattening procedure. A subquery ** whose result set is defined by pEList appears as entry in the ** FROM clause of a SELECT such that the VDBE cursor assigned to that ** FORM clause entry is iTable. This routine makes the necessary ** changes to pExpr so that it refers directly to the source table ** of the subquery rather the result set of the subquery. */ static Expr *substExpr( SubstContext *pSubst, /* Description of the substitution */ Expr *pExpr /* Expr in which substitution occurs */ ){ if( pExpr==0 ) return 0; if( ExprHasProperty(pExpr, EP_FromJoin) && pExpr->iRightJoinTable==pSubst->iTable ){ pExpr->iRightJoinTable = pSubst->iNewTable; } if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){ if( pExpr->iColumn<0 ){ pExpr->op = TK_NULL; }else{ Expr *pNew; Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; Expr ifNullRow; assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr ); assert( pExpr->pLeft==0 && pExpr->pRight==0 ); if( sqlite3ExprIsVector(pCopy) ){ sqlite3VectorErrorMsg(pSubst->pParse, pCopy); }else{ sqlite3 *db = pSubst->pParse->db; if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ memset(&ifNullRow, 0, sizeof(ifNullRow)); ifNullRow.op = TK_IF_NULL_ROW; ifNullRow.pLeft = pCopy; ifNullRow.iTable = pSubst->iNewTable; pCopy = &ifNullRow; } pNew = sqlite3ExprDup(db, pCopy, 0); if( pNew && pSubst->isLeftJoin ){ ExprSetProperty(pNew, EP_CanBeNull); } if( pNew && ExprHasProperty(pExpr,EP_FromJoin) ){ pNew->iRightJoinTable = pExpr->iRightJoinTable; ExprSetProperty(pNew, EP_FromJoin); } sqlite3ExprDelete(db, pExpr); pExpr = pNew; } } }else{ if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){ pExpr->iTable = pSubst->iNewTable; } pExpr->pLeft = substExpr(pSubst, pExpr->pLeft); pExpr->pRight = substExpr(pSubst, pExpr->pRight); if( ExprHasProperty(pExpr, EP_xIsSelect) ){ substSelect(pSubst, pExpr->x.pSelect, 1); }else{ substExprList(pSubst, pExpr->x.pList); } } return pExpr; } static void substExprList( SubstContext *pSubst, /* Description of the substitution */ ExprList *pList /* List to scan and in which to make substitutes */ ){ int i; if( pList==0 ) return; for(i=0; i<pList->nExpr; i++){ pList->a[i].pExpr = substExpr(pSubst, pList->a[i].pExpr); } } static void substSelect( SubstContext *pSubst, /* Description of the substitution */ Select *p, /* SELECT statement in which to make substitutions */ int doPrior /* Do substitutes on p->pPrior too */ ){ SrcList *pSrc; struct SrcList_item *pItem; int i; if( !p ) return; do{ substExprList(pSubst, p->pEList); substExprList(pSubst, p->pGroupBy); substExprList(pSubst, p->pOrderBy); p->pHaving = substExpr(pSubst, p->pHaving); p->pWhere = substExpr(pSubst, p->pWhere); pSrc = p->pSrc; assert( pSrc!=0 ); for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ substSelect(pSubst, pItem->pSelect, 1); if( pItem->fg.isTabFunc ){ substExprList(pSubst, pItem->u1.pFuncArg); } } }while( doPrior && (p = p->pPrior)!=0 ); } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) |
︙ | ︙ | |||
3217 3218 3219 3220 3221 3222 3223 | ** (1) The subquery and the outer query do not both use aggregates. ** ** (2) The subquery is not an aggregate or (2a) the outer query is not a join ** and (2b) the outer query does not use subqueries other than the one ** FROM-clause subquery that is a candidate for flattening. (2b is ** due to ticket [2f7170d73bf9abf80] from 2015-02-09.) ** | | > > | | | 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 | ** (1) The subquery and the outer query do not both use aggregates. ** ** (2) The subquery is not an aggregate or (2a) the outer query is not a join ** and (2b) the outer query does not use subqueries other than the one ** FROM-clause subquery that is a candidate for flattening. (2b is ** due to ticket [2f7170d73bf9abf80] from 2015-02-09.) ** ** (3) The subquery is not the right operand of a LEFT JOIN ** or (a) the subquery is not itself a join and (b) the FROM clause ** of the subquery does not contain a virtual table and (c) the ** outer query is not an aggregate. ** ** (4) The subquery is not DISTINCT. ** ** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT ** sub-queries that were excluded from this optimization. Restriction ** (4) has since been expanded to exclude all DISTINCT subqueries. ** ** (6) The subquery does not use aggregates or the outer query is not ** DISTINCT. ** ** (7) The subquery has a FROM clause. TODO: For subqueries without ** A FROM clause, consider adding a FROM clause with the special ** table sqlite_once that consists of a single row containing a ** single NULL. ** ** (8) The subquery does not use LIMIT or the outer query is not a join. ** ** (9) The subquery does not use LIMIT or the outer query does not use ** aggregates. |
︙ | ︙ | |||
3334 3335 3336 3337 3338 3339 3340 | ){ const char *zSavedAuthContext = pParse->zAuthContext; Select *pParent; /* Current UNION ALL term of the other query */ Select *pSub; /* The inner query or "subquery" */ Select *pSub1; /* Pointer to the rightmost select in sub-query */ SrcList *pSrc; /* The FROM clause of the outer query */ SrcList *pSubSrc; /* The FROM clause of the subquery */ | < > > | 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 | ){ const char *zSavedAuthContext = pParse->zAuthContext; Select *pParent; /* Current UNION ALL term of the other query */ Select *pSub; /* The inner query or "subquery" */ Select *pSub1; /* Pointer to the rightmost select in sub-query */ SrcList *pSrc; /* The FROM clause of the outer query */ SrcList *pSubSrc; /* The FROM clause of the subquery */ int iParent; /* VDBE cursor number of the pSub result set temp table */ int iNewParent = -1;/* Replacement table for iParent */ int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ int i; /* Loop counter */ Expr *pWhere; /* The WHERE clause */ struct SrcList_item *pSubitem; /* The subquery */ sqlite3 *db = pParse->db; /* Check to see if flattening is permitted. Return 0 if not. */ |
︙ | ︙ | |||
3362 3363 3364 3365 3366 3367 3368 | if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery)) || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0 || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0 ){ return 0; /* Restriction (2b) */ } } | | | 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 | if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery)) || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0 || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0 ){ return 0; /* Restriction (2b) */ } } pSubSrc = pSub->pSrc; assert( pSubSrc ); /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants, ** not arbitrary expressions, we allowed some combining of LIMIT and OFFSET ** because they could be computed at compile-time. But when LIMIT and OFFSET ** became arbitrary expressions, we were forced to add restrictions (13) ** and (14). */ |
︙ | ︙ | |||
3400 3401 3402 3403 3404 3405 3406 | if( pSub->selFlags & (SF_Recursive|SF_MinMaxAgg) ){ return 0; /* Restrictions (22) and (24) */ } if( (p->selFlags & SF_Recursive) && pSub->pPrior ){ return 0; /* Restriction (23) */ } | | | | < < | < | < < < > | < < < < < < < < < > > > | | > > > > > > > > > | 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 | if( pSub->selFlags & (SF_Recursive|SF_MinMaxAgg) ){ return 0; /* Restrictions (22) and (24) */ } if( (p->selFlags & SF_Recursive) && pSub->pPrior ){ return 0; /* Restriction (23) */ } /* ** If the subquery is the right operand of a LEFT JOIN, then the ** subquery may not be a join itself. Example of why this is not allowed: ** ** t1 LEFT OUTER JOIN (t2 JOIN t3) ** ** If we flatten the above, we would get ** ** (t1 LEFT OUTER JOIN t2) JOIN t3 ** ** which is not at all the same thing. ** ** If the subquery is the right operand of a LEFT JOIN, then the outer ** query cannot be an aggregate. This is an artifact of the way aggregates ** are processed - there is no mechanism to determine if the LEFT JOIN ** table should be all-NULL. ** ** See also tickets #306, #350, and #3300. */ if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){ isLeftJoin = 1; if( pSubSrc->nSrc>1 || isAgg || IsVirtual(pSubSrc->a[0].pTab) ){ return 0; /* Restriction (3) */ } } #ifdef SQLITE_EXTRA_IFNULLROW else if( iFrom>0 && !isAgg ){ /* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for ** every reference to any result column from subquery in a join, even though ** they are not necessary. This will stress-test the OP_IfNullRow opcode. */ isLeftJoin = -1; } #endif /* Restriction 17: If the sub-query is a compound SELECT, then it must ** use only the UNION ALL operator. And none of the simple select queries ** that make up the compound SELECT are allowed to be aggregate or distinct ** queries. */ if( pSub->pPrior ){ |
︙ | ︙ | |||
3571 3572 3573 3574 3575 3576 3577 | ** complete, since there may still exist Expr.pTab entries that ** refer to the subquery even after flattening. Ticket #3346. ** ** pSubitem->pTab is always non-NULL by test restrictions and tests above. */ if( ALWAYS(pSubitem->pTab!=0) ){ Table *pTabToDel = pSubitem->pTab; | | | | 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 | ** complete, since there may still exist Expr.pTab entries that ** refer to the subquery even after flattening. Ticket #3346. ** ** 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); pTabToDel->pNextZombie = pToplevel->pZombieTab; pToplevel->pZombieTab = pTabToDel; }else{ pTabToDel->nTabRef--; } pSubitem->pTab = 0; } /* The following loop runs once for each term in a compound-subquery ** flattening (as described above). If we are doing a different kind ** of flattening - a flattening other than a compound-subquery flattening - |
︙ | ︙ | |||
3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 | /* Transfer the FROM clause terms from the subquery into the ** outer query. */ for(i=0; i<nSubSrc; i++){ sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing); assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); pSrc->a[i+iFrom] = pSubSrc->a[i]; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } pSrc->a[iFrom].fg.jointype = jointype; /* Now begin substituting subquery result set expressions for ** references to the iParent in the outer query. ** ** Example: ** ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; ** \ \_____________ subquery __________/ / ** \_____________________ outer query ______________________________/ ** ** We look at every expression in the outer query and every place we see ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". */ | > < < < < < < < < | 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 | /* Transfer the FROM clause terms from the subquery into the ** outer query. */ for(i=0; i<nSubSrc; i++){ sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing); assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); pSrc->a[i+iFrom] = pSubSrc->a[i]; iNewParent = pSubSrc->a[i].iCursor; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } pSrc->a[iFrom].fg.jointype = jointype; /* Now begin substituting subquery result set expressions for ** references to the iParent in the outer query. ** ** Example: ** ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; ** \ \_____________ subquery __________/ / ** \_____________________ outer query ______________________________/ ** ** We look at every expression in the outer query and every place we see ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". */ if( pSub->pOrderBy ){ /* At this point, any non-zero iOrderByCol values indicate that the ** ORDER BY column expression is identical to the iOrderByCol'th ** expression returned by SELECT statement pSub. Since these values ** do not necessarily correspond to columns in SELECT statement pParent, ** zero them before transfering the ORDER BY clause. ** |
︙ | ︙ | |||
3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 | } assert( pParent->pOrderBy==0 ); assert( pSub->pPrior==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); if( subqueryIsAgg ){ assert( pParent->pHaving==0 ); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; pParent->pHaving = sqlite3ExprAnd(db, sqlite3ExprDup(db, pSub->pHaving, 0), pParent->pHaving ); assert( pParent->pGroupBy==0 ); pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0); }else{ pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere); } | > > > > > > > > > > | > | 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 | } assert( pParent->pOrderBy==0 ); assert( pSub->pPrior==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); if( isLeftJoin>0 ){ setJoinExpr(pWhere, iNewParent); } if( subqueryIsAgg ){ assert( pParent->pHaving==0 ); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; pParent->pHaving = sqlite3ExprAnd(db, sqlite3ExprDup(db, pSub->pHaving, 0), pParent->pHaving ); assert( pParent->pGroupBy==0 ); pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0); }else{ pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere); } if( db->mallocFailed==0 ){ SubstContext x; x.pParse = pParse; x.iTable = iParent; x.iNewTable = iNewParent; x.isLeftJoin = isLeftJoin; x.pEList = pSub->pEList; substSelect(&x, pParent, 0); } /* The flattened query is distinct if either the inner or the ** outer query is distinct. */ pParent->selFlags |= pSub->selFlags & SF_Distinct; /* |
︙ | ︙ | |||
3773 3774 3775 3776 3777 3778 3779 | ** (5) The WHERE clause expression originates in the ON or USING clause ** of a LEFT JOIN. ** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. */ static int pushDownWhereTerms( | | | > | > > > > > | | | 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 | ** (5) The WHERE clause expression originates in the ON or USING clause ** of a LEFT JOIN. ** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. */ static int pushDownWhereTerms( Parse *pParse, /* Parse context (for malloc() and error reporting) */ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ Expr *pWhere, /* The WHERE clause of the outer query */ int iCursor /* Cursor number of the subquery */ ){ Expr *pNew; int nChng = 0; Select *pX; /* For looping over compound SELECTs in pSubq */ if( pWhere==0 ) return 0; for(pX=pSubq; pX; pX=pX->pPrior){ if( (pX->selFlags & (SF_Aggregate|SF_Recursive))!=0 ){ testcase( pX->selFlags & SF_Aggregate ); testcase( pX->selFlags & SF_Recursive ); testcase( pX!=pSubq ); return 0; /* restrictions (1) and (2) */ } } if( pSubq->pLimit!=0 ){ return 0; /* restriction (3) */ } while( pWhere->op==TK_AND ){ nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, iCursor); pWhere = pWhere->pLeft; } if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction 5 */ if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){ nChng++; while( pSubq ){ SubstContext x; pNew = sqlite3ExprDup(pParse->db, pWhere, 0); x.pParse = pParse; x.iTable = iCursor; x.iNewTable = iCursor; x.isLeftJoin = 0; x.pEList = pSubq->pEList; pNew = substExpr(&x, pNew); pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew); pSubq = pSubq->pPrior; } } return nChng; } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ |
︙ | ︙ | |||
4095 4096 4097 4098 4099 4100 4101 | return SQLITE_ERROR; } if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR; assert( pFrom->pTab==0 ); pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; | | | 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 | return SQLITE_ERROR; } if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR; assert( pFrom->pTab==0 ); pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nTabRef = 1; pTab->zName = sqlite3DbStrDup(db, pCte->zName); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; assert( pFrom->pSelect ); |
︙ | ︙ | |||
4118 4119 4120 4121 4122 4123 4124 | struct SrcList_item *pItem = &pSrc->a[i]; if( pItem->zDatabase==0 && pItem->zName!=0 && 0==sqlite3StrICmp(pItem->zName, pCte->zName) ){ pItem->pTab = pTab; pItem->fg.isRecursive = 1; | | | | > > > > > > > | > | 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 | struct SrcList_item *pItem = &pSrc->a[i]; if( pItem->zDatabase==0 && pItem->zName!=0 && 0==sqlite3StrICmp(pItem->zName, pCte->zName) ){ pItem->pTab = pTab; pItem->fg.isRecursive = 1; pTab->nTabRef++; pSel->selFlags |= SF_Recursive; } } } /* Only one recursive reference is permitted. */ if( pTab->nTabRef>2 ){ sqlite3ErrorMsg( pParse, "multiple references to recursive table: %s", pCte->zName ); return SQLITE_ERROR; } assert( pTab->nTabRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nTabRef==2 )); pCte->zCteErr = "circular reference: %s"; pSavedWith = pParse->pWith; pParse->pWith = pWith; if( bMayRecursive ){ Select *pPrior = pSel->pPrior; assert( pPrior->pWith==0 ); pPrior->pWith = pSel->pWith; sqlite3WalkSelect(pWalker, pPrior); pPrior->pWith = 0; }else{ sqlite3WalkSelect(pWalker, pSel); } pParse->pWith = pWith; for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); pEList = pLeft->pEList; if( pCte->pCols ){ if( pEList && pEList->nExpr!=pCte->pCols->nExpr ){ sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns", |
︙ | ︙ | |||
4180 4181 4182 4183 4184 4185 4186 | ** ** This function is used as the xSelectCallback2() callback by ** sqlite3SelectExpand() when walking a SELECT tree to resolve table ** names and other FROM clause elements. */ static void selectPopWith(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; | > | | | | > | 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 | ** ** This function is used as the xSelectCallback2() callback by ** sqlite3SelectExpand() when walking a SELECT tree to resolve table ** names and other FROM clause elements. */ static void selectPopWith(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; if( pParse->pWith && p->pPrior==0 ){ With *pWith = findRightmost(p)->pWith; if( pWith!=0 ){ assert( pParse->pWith==pWith ); pParse->pWith = pWith->pOuter; } } } #else #define selectPopWith 0 #endif /* |
︙ | ︙ | |||
4233 4234 4235 4236 4237 4238 4239 | return WRC_Abort; } if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ return WRC_Prune; } pTabList = p->pSrc; pEList = p->pEList; | | | | 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 | return WRC_Abort; } if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ return WRC_Prune; } pTabList = p->pSrc; pEList = p->pEList; if( p->pWith ){ sqlite3WithPush(pParse, p->pWith, 0); } /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. */ sqlite3SrcListAssignCursors(pParse, pTabList); |
︙ | ︙ | |||
4264 4265 4266 4267 4268 4269 4270 | Select *pSel = pFrom->pSelect; /* A sub-query in the FROM clause of a SELECT */ assert( pSel!=0 ); assert( pFrom->pTab==0 ); if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; | | | | | 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 | Select *pSel = pFrom->pSelect; /* A sub-query in the FROM clause of a SELECT */ assert( pSel!=0 ); assert( pFrom->pTab==0 ); if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nTabRef = 1; pTab->zName = sqlite3MPrintf(db, "sqlite_sq_%p", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); pTab->tabFlags |= TF_Ephemeral; #endif }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pTab==0 ); pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); if( pTab==0 ) return WRC_Abort; if( pTab->nTabRef>=0xffff ){ sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535", pTab->zName); pFrom->pTab = 0; return WRC_Abort; } pTab->nTabRef++; if( !IsVirtual(pTab) && cannotBeFunction(pParse, pFrom) ){ return WRC_Abort; } #if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE) if( IsVirtual(pTab) || pTab->pSelect ){ i16 nCol; if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; |
︙ | ︙ | |||
4433 4434 4435 4436 4437 4438 4439 | } pRight = sqlite3Expr(db, TK_ID, zName); zColname = zName; zToFree = 0; if( longNames || pTabList->nSrc>1 ){ Expr *pLeft; pLeft = sqlite3Expr(db, TK_ID, zTabName); | | | | 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 | } pRight = sqlite3Expr(db, TK_ID, zName); zColname = zName; zToFree = 0; if( longNames || pTabList->nSrc>1 ){ Expr *pLeft; pLeft = sqlite3Expr(db, TK_ID, zTabName); pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); if( zSchemaName ){ pLeft = sqlite3Expr(db, TK_ID, zSchemaName); pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr); } if( longNames ){ zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); zToFree = zColname; } }else{ pExpr = pRight; |
︙ | ︙ | |||
4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 | ** subquery in the parser tree. */ int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){ UNUSED_PARAMETER2(NotUsed, NotUsed2); return WRC_Continue; } /* ** This routine "expands" a SELECT statement and all of its subqueries. ** For additional information on what it means to "expand" a SELECT ** statement, see the comment on the selectExpand worker callback above. ** ** Expanding a SELECT statement is the first step in processing a ** SELECT statement. The SELECT statement must be expanded before ** name resolution is performed. ** ** If anything goes wrong, an error message is written into pParse. ** The calling function can detect the problem by looking at pParse->nErr ** and/or pParse->db->mallocFailed. */ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ Walker w; | > > > > > > > > > > > > > > > > > > > < > < | < | 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 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 | ** subquery in the parser tree. */ int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){ UNUSED_PARAMETER2(NotUsed, NotUsed2); return WRC_Continue; } /* ** No-op routine for the parse-tree walker for SELECT statements. ** subquery in the parser tree. */ int sqlite3SelectWalkNoop(Walker *NotUsed, Select *NotUsed2){ UNUSED_PARAMETER2(NotUsed, NotUsed2); return WRC_Continue; } #if SQLITE_DEBUG /* ** Always assert. This xSelectCallback2 implementation proves that the ** xSelectCallback2 is never invoked. */ void sqlite3SelectWalkAssert2(Walker *NotUsed, Select *NotUsed2){ UNUSED_PARAMETER2(NotUsed, NotUsed2); assert( 0 ); } #endif /* ** This routine "expands" a SELECT statement and all of its subqueries. ** For additional information on what it means to "expand" a SELECT ** statement, see the comment on the selectExpand worker callback above. ** ** Expanding a SELECT statement is the first step in processing a ** SELECT statement. The SELECT statement must be expanded before ** name resolution is performed. ** ** If anything goes wrong, an error message is written into pParse. ** The calling function can detect the problem by looking at pParse->nErr ** and/or pParse->db->mallocFailed. */ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ Walker w; w.xExprCallback = sqlite3ExprWalkNoop; w.pParse = pParse; if( pParse->hasCompound ){ w.xSelectCallback = convertCompoundSelectToSubquery; w.xSelectCallback2 = 0; sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; w.xSelectCallback2 = selectPopWith; sqlite3WalkSelect(&w, pSelect); } #ifndef SQLITE_OMIT_SUBQUERY /* ** This is a Walker.xSelectCallback callback for the sqlite3SelectTypeInfo() |
︙ | ︙ | |||
4579 4580 4581 4582 4583 4584 4585 | ** SELECT statement. ** ** Use this routine after name resolution. */ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){ #ifndef SQLITE_OMIT_SUBQUERY Walker w; | | | 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 | ** SELECT statement. ** ** Use this routine after name resolution. */ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){ #ifndef SQLITE_OMIT_SUBQUERY Walker w; w.xSelectCallback = sqlite3SelectWalkNoop; w.xSelectCallback2 = selectAddSubqueryTypeInfo; w.xExprCallback = sqlite3ExprWalkNoop; w.pParse = pParse; sqlite3WalkSelect(&w, pSelect); #endif } |
︙ | ︙ | |||
4673 4674 4675 4676 4677 4678 4679 | static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ Vdbe *v = pParse->pVdbe; int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ ExprList *pList = pF->pExpr->x.pList; assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); | | | | 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 | static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ Vdbe *v = pParse->pVdbe; int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ ExprList *pList = pF->pExpr->x.pList; assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0); sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); } } /* ** Update the accumulator memory cells for an aggregate based on ** the current cursor position. */ |
︙ | ︙ | |||
4725 4726 4727 4728 4729 4730 4731 | } if( !pColl ){ pColl = pParse->db->pDfltColl; } if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ); } | | | | 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 | } if( !pColl ){ pColl = pParse->db->pDfltColl; } if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ); } sqlite3VdbeAddOp3(v, OP_AggStep0, 0, regAgg, pF->iMem); sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg); sqlite3ReleaseTempRange(pParse, regAgg, nArg); if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); sqlite3ExprCacheClear(pParse); } |
︙ | ︙ | |||
4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 | ); } } #else # define explainSimpleCount(a,b,c) #endif /* ** Generate code for the SELECT statement given in the p argument. ** ** The results are returned according to the SelectDest structure. ** See comments in sqliteInt.h for further information. ** ** This routine returns the number of errors. If any errors are | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ); } } #else # define explainSimpleCount(a,b,c) #endif /* ** Context object for havingToWhereExprCb(). */ struct HavingToWhereCtx { Expr **ppWhere; ExprList *pGroupBy; }; /* ** sqlite3WalkExpr() callback used by havingToWhere(). ** ** If the node passed to the callback is a TK_AND node, return ** WRC_Continue to tell sqlite3WalkExpr() to iterate through child nodes. ** ** Otherwise, return WRC_Prune. In this case, also check if the ** sub-expression matches the criteria for being moved to the WHERE ** clause. If so, add it to the WHERE clause and replace the sub-expression ** within the HAVING expression with a constant "1". */ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ if( pExpr->op!=TK_AND ){ struct HavingToWhereCtx *p = pWalker->u.pHavingCtx; if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, p->pGroupBy) ){ sqlite3 *db = pWalker->pParse->db; Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0); if( pNew ){ Expr *pWhere = *(p->ppWhere); SWAP(Expr, *pNew, *pExpr); pNew = sqlite3ExprAnd(db, pWhere, pNew); *(p->ppWhere) = pNew; } } return WRC_Prune; } return WRC_Continue; } /* ** Transfer eligible terms from the HAVING clause of a query, which is ** processed after grouping, to the WHERE clause, which is processed before ** grouping. For example, the query: ** ** SELECT * FROM <tables> WHERE a=? GROUP BY b HAVING b=? AND c=? ** ** can be rewritten as: ** ** SELECT * FROM <tables> WHERE a=? AND b=? GROUP BY b HAVING c=? ** ** A term of the HAVING expression is eligible for transfer if it consists ** entirely of constants and expressions that are also GROUP BY terms that ** use the "BINARY" collation sequence. */ static void havingToWhere( Parse *pParse, ExprList *pGroupBy, Expr *pHaving, Expr **ppWhere ){ struct HavingToWhereCtx sCtx; Walker sWalker; sCtx.ppWhere = ppWhere; sCtx.pGroupBy = pGroupBy; memset(&sWalker, 0, sizeof(sWalker)); sWalker.pParse = pParse; sWalker.xExprCallback = havingToWhereExprCb; sWalker.u.pHavingCtx = &sCtx; sqlite3WalkExpr(&sWalker, pHaving); } /* ** Check to see if the pThis entry of pTabList is a self-join of a prior view. ** If it is, then return the SrcList_item for the prior view. If it is not, ** then return 0. */ static struct SrcList_item *isSelfJoinView( SrcList *pTabList, /* Search for self-joins in this FROM clause */ struct SrcList_item *pThis /* Search for prior reference to this subquery */ ){ struct SrcList_item *pItem; for(pItem = pTabList->a; pItem<pThis; pItem++){ if( pItem->pSelect==0 ) continue; if( pItem->fg.viaCoroutine ) continue; if( pItem->zName==0 ) continue; if( sqlite3_stricmp(pItem->zDatabase, pThis->zDatabase)!=0 ) continue; if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue; if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pItem->pSelect->pWhere, -1) ){ /* The view was modified by some other optimization such as ** pushDownWhereTerms() */ continue; } return pItem; } return 0; } #ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION /* ** Attempt to transform a query of the form ** ** SELECT count(*) FROM (SELECT x FROM t1 UNION ALL SELECT y FROM t2) ** ** Into this: ** ** SELECT (SELECT count(*) FROM t1)+(SELECT count(*) FROM t2) ** ** The transformation only works if all of the following are true: ** ** * The subquery is a UNION ALL of two or more terms ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries ** * The outer query is a simple count(*) ** ** Return TRUE if the optimization is undertaken. */ static int countOfViewOptimization(Parse *pParse, Select *p){ Select *pSub, *pPrior; Expr *pExpr; Expr *pCount; sqlite3 *db; if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate query */ if( p->pEList->nExpr!=1 ) return 0; /* Single result column */ pExpr = p->pEList->a[0].pExpr; if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */ if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Must be count() */ if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */ if( p->pSrc->nSrc!=1 ) return 0; /* One table in the FROM clause */ pSub = p->pSrc->a[0].pSelect; if( pSub==0 ) return 0; /* The FROM is a subquery */ if( pSub->pPrior==0 ) return 0; /* Must be a compound subquery */ do{ if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ if( pSub->pWhere ) return 0; /* No WHERE clause */ if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ pSub = pSub->pPrior; /* Repeat over compound terms */ }while( pSub ); /* If we reach this point, that means it is OK to perform the transformation */ db = pParse->db; pCount = pExpr; pExpr = 0; pSub = p->pSrc->a[0].pSelect; p->pSrc->a[0].pSelect = 0; sqlite3SrcListDelete(db, p->pSrc); p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); while( pSub ){ Expr *pTerm; pPrior = pSub->pPrior; pSub->pPrior = 0; pSub->pNext = 0; pSub->selFlags |= SF_Aggregate; pSub->selFlags &= ~SF_Compound; pSub->nSelectRow = 0; sqlite3ExprListDelete(db, 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{ pExpr = sqlite3PExpr(pParse, TK_PLUS, pTerm, pExpr); } pSub = pPrior; } p->pEList->a[0].pExpr = pExpr; p->selFlags &= ~SF_Aggregate; #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif return 1; } #endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ /* ** Generate code for the SELECT statement given in the p argument. ** ** The results are returned according to the SelectDest structure. ** See comments in sqliteInt.h for further information. ** ** This routine returns the number of errors. If any errors are |
︙ | ︙ | |||
4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 | isAgg = (p->selFlags & SF_Aggregate)!=0; #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p, ("after name resolution:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif /* Try to flatten subqueries in the FROM clause up into the main query */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; Select *pSub = pItem->pSelect; | > > > > > > > > | 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 | isAgg = (p->selFlags & SF_Aggregate)!=0; #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p, ("after name resolution:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif /* Get a pointer the VDBE under construction, allocating a new VDBE if one ** does not already exist */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto select_end; if( pDest->eDest==SRT_Output ){ generateColumnNames(pParse, p); } /* Try to flatten subqueries in the FROM clause up into the main query */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; Select *pSub = pItem->pSelect; |
︙ | ︙ | |||
4905 4906 4907 4908 4909 4910 4911 | if( db->mallocFailed ) goto select_end; if( !IgnorableOrderby(pDest) ){ sSort.pOrderBy = p->pOrderBy; } } #endif | < < < < < > > | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 5214 5215 5216 5217 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 | if( db->mallocFailed ) goto select_end; if( !IgnorableOrderby(pDest) ){ sSort.pOrderBy = p->pOrderBy; } } #endif #ifndef SQLITE_OMIT_COMPOUND_SELECT /* Handle compound SELECT statements using the separate multiSelect() ** procedure. */ if( p->pPrior ){ rc = multiSelect(pParse, p, pDest); explainSetInteger(pParse->iSelectId, iRestoreSelectId); #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p,("end compound-select processing\n")); pParse->nSelectIndent--; #endif return rc; } #endif /* For each term in the FROM clause, do two things: ** (1) Authorized unreferenced tables ** (2) Generate code for all sub-queries */ for(i=0; i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; SelectDest dest; Select *pSub; /* Issue SQLITE_READ authorizations with a fake column name for any tables that ** are referenced but from which no values are extracted. Examples of where these ** kinds of null SQLITE_READ authorizations would occur: ** ** SELECT count(*) FROM t1; -- SQLITE_READ t1."" ** SELECT t1.* FROM t1, t2; -- SQLITE_READ t2."" ** ** The fake column name is an empty string. It is possible for a table to ** have a column named by the empty string, in which case there is no way to ** distinguish between an unreferenced table and an actual reference to the ** "" column. The original design was for the fake column name to be a NULL, ** which would be unambiguous. But legacy authorization callbacks might ** assume the column name is non-NULL and segfault. The use of an empty string ** for the fake column name seems safer. */ if( pItem->colUsed==0 ){ 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 ) continue; /* Sometimes the code for a subquery will be generated more than ** once, if the subquery is part of the WHERE clause in a LEFT JOIN, ** for example. In that case, do not regenerate the code to manifest ** a view or the co-routine to implement a view. The first instance ** is sufficient, though the subroutine to manifest the view does need ** to be invoked again. */ if( pItem->addrFillSub ){ if( pItem->fg.viaCoroutine==0 ){ /* The subroutine that manifests the view might be a one-time routine, ** or it might need to be rerun on each iteration because it ** encodes a correlated subquery. */ testcase( sqlite3VdbeGetOp(v, pItem->addrFillSub)->opcode==OP_Once ); sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); } continue; } /* 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 ** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit ** more conservative than necessary, but much easier than enforcing ** an exact limit. */ pParse->nHeight += sqlite3SelectExprHeight(p); /* Make copies of constant WHERE-clause terms in the outer query down ** inside the subquery. This can help the subquery to run more efficiently. */ if( (pItem->fg.jointype & JT_OUTER)==0 && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor) ){ #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After WHERE-clause push-down:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif |
︙ | ︙ | |||
5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 | ** the content of this subquery. pItem->addrFillSub will point ** to the address of the generated subroutine. pItem->regReturn ** is a register allocated to hold the subroutine return address */ int topAddr; int onceAddr = 0; int retAddr; assert( pItem->addrFillSub==0 ); pItem->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); pItem->addrFillSub = topAddr+1; if( pItem->fg.isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName)); }else{ VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName)); } | > > > > > > > > > | | | > < > > > > > > > > > > > | 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 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 | ** the content of this subquery. pItem->addrFillSub will point ** to the address of the generated subroutine. pItem->regReturn ** is a register allocated to hold the subroutine return address */ int topAddr; int onceAddr = 0; int retAddr; struct SrcList_item *pPrior; assert( pItem->addrFillSub==0 ); pItem->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); pItem->addrFillSub = topAddr+1; if( pItem->fg.isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName)); }else{ VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName)); } pPrior = isSelfJoinView(pTabList, pItem); if( pPrior ){ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); explainSetInteger(pItem->iSelectId, pPrior->iSelectId); assert( pPrior->pSelect!=0 ); pSub->nSelectRow = pPrior->pSelect->nSelectRow; }else{ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); sqlite3Select(pParse, pSub, &dest); } pItem->pTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); VdbeComment((v, "end %s", pItem->pTab->zName)); sqlite3VdbeChangeP1(v, topAddr, retAddr); sqlite3ClearTempRegCache(pParse); } if( db->mallocFailed ) goto select_end; pParse->nHeight -= sqlite3SelectExprHeight(p); #endif } /* Various elements of the SELECT copied into local variables for ** convenience */ pEList = p->pEList; pWhere = p->pWhere; pGroupBy = p->pGroupBy; pHaving = p->pHaving; sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif #ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView) && countOfViewOptimization(pParse, p) ){ if( db->mallocFailed ) goto select_end; pEList = p->pEList; pTabList = p->pSrc; } #endif /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and ** if the select-list is the same as the ORDER BY list, then this query ** can be rewritten as a GROUP BY. In other words, this: ** ** SELECT DISTINCT xyz FROM ... ORDER BY xyz ** |
︙ | ︙ | |||
5122 5123 5124 5125 5126 5127 5128 | if( pDest->eDest==SRT_EphemTab ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); } /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); | > | > | 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 | if( pDest->eDest==SRT_EphemTab ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); } /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); if( (p->selFlags & SF_FixedLimit)==0 ){ p->nSelectRow = 320; /* 4 billion rows */ } computeLimitRegisters(pParse, p, iEnd); if( p->iLimit==0 && sSort.addrSortIndex>=0 ){ sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen); sSort.sortFlags |= SORTFLAG_UseSorter; } /* Open an ephemeral index to use for the distinct set. |
︙ | ︙ | |||
5176 5177 5178 5179 5180 5181 5182 | ** into an OP_Noop. */ if( sSort.addrSortIndex>=0 && sSort.pOrderBy==0 ){ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex); } /* Use the standard inner loop. */ | > | | 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 | ** into an OP_Noop. */ if( sSort.addrSortIndex>=0 && sSort.pOrderBy==0 ){ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex); } /* Use the standard inner loop. */ assert( p->pEList==pEList ); selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, sqlite3WhereContinueLabel(pWInfo), sqlite3WhereBreakLabel(pWInfo)); /* End the database scan loop. */ sqlite3WhereEnd(pWInfo); }else{ |
︙ | ︙ | |||
5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 | sNC.pAggInfo = &sAggInfo; sAggInfo.mnReg = pParse->nMem+1; sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; sAggInfo.pGroupBy = pGroupBy; sqlite3ExprAnalyzeAggList(&sNC, pEList); sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy); if( pHaving ){ sqlite3ExprAnalyzeAggregates(&sNC, pHaving); } sAggInfo.nAccumulator = sAggInfo.nColumn; for(i=0; i<sAggInfo.nFunc; i++){ assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); sNC.ncFlags |= NC_InAggFunc; sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); | > > > > > | 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 | sNC.pAggInfo = &sAggInfo; sAggInfo.mnReg = pParse->nMem+1; sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; sAggInfo.pGroupBy = pGroupBy; sqlite3ExprAnalyzeAggList(&sNC, pEList); sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy); if( pHaving ){ if( pGroupBy ){ assert( pWhere==p->pWhere ); havingToWhere(pParse, pGroupBy, pHaving, &p->pWhere); pWhere = p->pWhere; } sqlite3ExprAnalyzeAggregates(&sNC, pHaving); } sAggInfo.nAccumulator = sAggInfo.nColumn; for(i=0; i<sAggInfo.nFunc; i++){ assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); sNC.ncFlags |= NC_InAggFunc; sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); |
︙ | ︙ | |||
5474 5475 5476 5477 5478 5479 5480 | addrOutputRow = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2); VdbeCoverage(v); VdbeComment((v, "Groupby result generator entry point")); sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); finalizeAggFunctions(pParse, &sAggInfo); sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL); | | | 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 | addrOutputRow = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2); VdbeCoverage(v); VdbeComment((v, "Groupby result generator entry point")); sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); finalizeAggFunctions(pParse, &sAggInfo); sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL); selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addrOutputRow+1, addrSetAbort); sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); VdbeComment((v, "end groupby result generator")); /* Generate a subroutine that will reset the group-by accumulator */ |
︙ | ︙ | |||
5600 5601 5602 5603 5604 5605 5606 | } /* This case runs if the aggregate has no GROUP BY clause. The ** processing is much simpler since there is only a single row ** of output. */ resetAccumulator(pParse, &sAggInfo); | | | | 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 | } /* This case runs if the aggregate has no GROUP BY clause. The ** processing is much simpler since there is only a single row ** of output. */ resetAccumulator(pParse, &sAggInfo); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax, 0,flag,0); if( pWInfo==0 ){ sqlite3ExprListDelete(db, pDel); goto select_end; } updateAccumulator(pParse, &sAggInfo); assert( pMinMax==0 || pMinMax->nExpr==1 ); if( sqlite3WhereIsOrdered(pWInfo)>0 ){ sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo)); VdbeComment((v, "%s() by index", (flag==WHERE_ORDERBY_MIN?"min":"max"))); } sqlite3WhereEnd(pWInfo); finalizeAggFunctions(pParse, &sAggInfo); } sSort.pOrderBy = 0; sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL); selectInnerLoop(pParse, p, -1, 0, 0, pDest, addrEnd, addrEnd); sqlite3ExprListDelete(db, pDel); } sqlite3VdbeResolveLabel(v, addrEnd); } /* endif aggregate query */ |
︙ | ︙ | |||
5653 5654 5655 5656 5657 5658 5659 | /* Control jumps to here if an error is encountered above, or upon ** successful coding of the SELECT. */ select_end: explainSetInteger(pParse->iSelectId, iRestoreSelectId); | < < < < < < | 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 | /* Control jumps to here if an error is encountered above, or upon ** successful coding of the SELECT. */ select_end: explainSetInteger(pParse->iSelectId, iRestoreSelectId); sqlite3DbFree(db, sAggInfo.aCol); sqlite3DbFree(db, sAggInfo.aFunc); #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p,("end processing\n")); pParse->nSelectIndent--; #endif return rc; } |
Changes to src/shell.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /* ** 2001 September 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. ** ************************************************************************* ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. */ #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) /* This needs to come before any includes for MSVC compiler */ #define _CRT_SECURE_NO_WARNINGS #endif /* | > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > | | 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 | /* DO NOT EDIT! ** This file is automatically generated by the script in the canonical ** SQLite source tree at tool/mkshellc.tcl. That script combines source ** code from various constituent source files of SQLite into this single ** "shell.c" file used to implement the SQLite command-line shell. ** ** Most of the code found below comes from the "src/shell.c.in" file in ** the canonical SQLite source tree. That main file contains "INCLUDE" ** lines that specify other files in the canonical source tree that are ** inserted to getnerate this complete program source file. ** ** The code from multiple files is combined into this single "shell.c" ** source file to help make the command-line program easier to compile. ** ** To modify this program, get a copy of the canonical SQLite source tree, ** edit the src/shell.c.in" and/or some of the other files that are included ** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script. */ /* ** 2001 September 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. ** ************************************************************************* ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. */ #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) /* This needs to come before any includes for MSVC compiler */ #define _CRT_SECURE_NO_WARNINGS #endif /* ** Warning pragmas copied from msvc.h in the core. */ #if defined(_MSC_VER) #pragma warning(disable : 4054) #pragma warning(disable : 4055) #pragma warning(disable : 4100) #pragma warning(disable : 4127) #pragma warning(disable : 4130) #pragma warning(disable : 4152) #pragma warning(disable : 4189) #pragma warning(disable : 4206) #pragma warning(disable : 4210) #pragma warning(disable : 4232) #pragma warning(disable : 4244) #pragma warning(disable : 4305) #pragma warning(disable : 4306) #pragma warning(disable : 4702) #pragma warning(disable : 4706) #endif /* defined(_MSC_VER) */ /* ** No support for loadable extensions in VxWorks. */ #if (defined(__RTP__) || defined(_WRS_KERNEL)) && !SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION 1 #endif |
︙ | ︙ | |||
423 424 425 426 427 428 429 430 431 432 433 434 435 436 | z = sqlite3_vmprintf(zFormat, ap); va_end(ap); utf8_printf(iotrace, "%s", z); sqlite3_free(z); } #endif /* ** Determines if a string is a number of not. */ static int isNumber(const char *z, int *realnum){ if( *z=='-' || *z=='+' ) z++; if( !IsDigit(*z) ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | z = sqlite3_vmprintf(zFormat, ap); va_end(ap); utf8_printf(iotrace, "%s", z); sqlite3_free(z); } #endif /* ** Output string zUtf to stream pOut as w characters. If w is negative, ** then right-justify the text. W is the width in UTF-8 characters, not ** in bytes. This is different from the %*.*s specification in printf ** since with %*.*s the width is measured in bytes, not characters. */ static void utf8_width_print(FILE *pOut, int w, const char *zUtf){ int i; int n; int aw = w<0 ? -w : w; char zBuf[1000]; if( aw>(int)sizeof(zBuf)/3 ) aw = (int)sizeof(zBuf)/3; for(i=n=0; zUtf[i]; i++){ if( (zUtf[i]&0xc0)!=0x80 ){ n++; if( n==aw ){ do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); break; } } } if( n>=aw ){ utf8_printf(pOut, "%.*s", i, zUtf); }else if( w<0 ){ utf8_printf(pOut, "%*s%s", aw-n, "", zUtf); }else{ utf8_printf(pOut, "%s%*s", zUtf, aw-n, ""); } } /* ** Determines if a string is a number of not. */ static int isNumber(const char *z, int *realnum){ if( *z=='-' || *z=='+' ) z++; if( !IsDigit(*z) ){ |
︙ | ︙ | |||
450 451 452 453 454 455 456 | if( *z=='+' || *z=='-' ) z++; if( !IsDigit(*z) ) return 0; while( IsDigit(*z) ){ z++; } if( realnum ) *realnum = 1; } return *z==0; } | < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > | 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 | if( *z=='+' || *z=='-' ) z++; if( !IsDigit(*z) ) return 0; while( IsDigit(*z) ){ z++; } if( realnum ) *realnum = 1; } return *z==0; } /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. */ static int strlen30(const char *z){ const char *z2 = z; while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } /* ** Return the length of a string in characters. Multibyte UTF8 characters ** count as a single character. */ static int strlenChar(const char *z){ int n = 0; while( *z ){ if( (0xc0&*(z++))!=0x80 ) n++; } return n; } /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. ** |
︙ | ︙ | |||
573 574 575 576 577 578 579 580 581 582 583 584 585 586 | free(zPrior); zResult = shell_readline(zPrompt); if( zResult && *zResult ) shell_add_history(zResult); #endif } return zResult; } #if defined(SQLITE_ENABLE_SESSION) /* ** State information for a single open session */ typedef struct OpenSession OpenSession; struct OpenSession { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 | free(zPrior); zResult = shell_readline(zPrompt); if( zResult && *zResult ) shell_add_history(zResult); #endif } return zResult; } /* ** A variable length string to which one can append text. */ typedef struct ShellText ShellText; struct ShellText { char *z; int n; int nAlloc; }; /* ** Initialize and destroy a ShellText object */ static void initText(ShellText *p){ memset(p, 0, sizeof(*p)); } static void freeText(ShellText *p){ free(p->z); initText(p); } /* zIn is either a pointer to a NULL-terminated string in memory obtained ** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** added to zIn, and the result returned in memory obtained from malloc(). ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ static void appendText(ShellText *p, char const *zAppend, char quote){ int len; int i; int nAppend = strlen30(zAppend); len = nAppend+p->n+1; if( quote ){ len += 2; for(i=0; i<nAppend; i++){ if( zAppend[i]==quote ) len++; } } if( p->n+len>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + len + 20; p->z = realloc(p->z, p->nAlloc); if( p->z==0 ){ memset(p, 0, sizeof(*p)); return; } } if( quote ){ char *zCsr = p->z+p->n; *zCsr++ = quote; for(i=0; i<nAppend; i++){ *zCsr++ = zAppend[i]; if( zAppend[i]==quote ) *zCsr++ = quote; } *zCsr++ = quote; p->n = (int)(zCsr - p->z); *zCsr = '\0'; }else{ memcpy(p->z+p->n, zAppend, nAppend); p->n += nAppend; p->z[p->n] = '\0'; } } /* ** Attempt to determine if identifier zName needs to be quoted, either ** because it contains non-alphanumeric characters, or because it is an ** SQLite keyword. Be conservative in this estimate: When in doubt assume ** that quoting is required. ** ** Return '"' if quoting is required. Return 0 if no quoting is required. */ static char quoteChar(const char *zName){ /* All SQLite keywords, in alphabetical order */ static const char *azKeywords[] = { "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH", "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA", "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE", "WITH", "WITHOUT", }; int i, lwr, upr, mid, c; if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; for(i=0; zName[i]; i++){ if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; } lwr = 0; upr = sizeof(azKeywords)/sizeof(azKeywords[0]) - 1; while( lwr<=upr ){ mid = (lwr+upr)/2; c = sqlite3_stricmp(azKeywords[mid], zName); if( c==0 ) return '"'; if( c<0 ){ lwr = mid+1; }else{ upr = mid-1; } } return 0; } /* ** SQL function: shell_add_schema(S,X) ** ** Add the schema name X to the CREATE statement in S and return the result. ** Examples: ** ** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); ** ** Also works on ** ** CREATE INDEX ** CREATE UNIQUE INDEX ** CREATE VIEW ** CREATE TRIGGER ** CREATE VIRTUAL TABLE ** ** This UDF is used by the .schema command to insert the schema name of ** attached databases into the middle of the sqlite_master.sql field. */ static void shellAddSchemaName( sqlite3_context *pCtx, int nVal, sqlite3_value **apVal ){ static const char *aPrefix[] = { "TABLE", "INDEX", "UNIQUE INDEX", "VIEW", "TRIGGER", "VIRTUAL TABLE" }; int i = 0; const char *zIn = (const char*)sqlite3_value_text(apVal[0]); const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); assert( nVal==2 ); if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){ for(i=0; i<(int)(sizeof(aPrefix)/sizeof(aPrefix[0])); i++){ int n = strlen30(aPrefix[i]); if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){ char cQuote = quoteChar(zSchema); char *z; if( cQuote ){ z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8); }else{ z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8); } sqlite3_result_text(pCtx, z, -1, sqlite3_free); return; } } } sqlite3_result_value(pCtx, apVal[0]); } /* ** The source code for several run-time loadable extensions is inserted ** below by the ../tool/mkshellc.tcl script. Before processing that included ** code, we need to override some macros to make the included program code ** work here in the middle of this regular program. */ #define SQLITE_EXTENSION_INIT1 #define SQLITE_EXTENSION_INIT2(X) (void)(X) /************************* Begin ../ext/misc/shathree.c ******************/ /* ** 2017-03-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. ** ****************************************************************************** ** ** This SQLite extension implements a functions that compute SHA1 hashes. ** Two SQL functions are implemented: ** ** sha3(X,SIZE) ** sha3_query(Y,SIZE) ** ** The sha3(X) function computes the SHA3 hash of the input X, or NULL if ** X is NULL. ** ** The sha3_query(Y) function evalutes all queries in the SQL statements of Y ** and returns a hash of their results. ** ** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm ** is used. If SIZE is included it must be one of the integers 224, 256, ** 384, or 512, to determine SHA3 hash variant that is computed. */ SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <stdarg.h> typedef sqlite3_uint64 u64; /****************************************************************************** ** The Hash Engine */ /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** ** For best performance, an attempt is made to guess at the byte-order ** using C-preprocessor macros. If that is unsuccessful, or if ** -DSHA3_BYTEORDER=0 is set, then byte-order is determined ** at run-time. */ #ifndef SHA3_BYTEORDER # if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__arm__) # define SHA3_BYTEORDER 1234 # elif defined(sparc) || defined(__ppc__) # define SHA3_BYTEORDER 4321 # else # define SHA3_BYTEORDER 0 # endif #endif /* ** State structure for a SHA3 hash in progress */ typedef struct SHA3Context SHA3Context; struct SHA3Context { union { u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */ unsigned char x[1600]; /* ... or 1600 bytes */ } u; unsigned nRate; /* Bytes of input accepted per Keccak iteration */ unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ }; /* ** A single step of the Keccak mixing function for a 1600-bit state */ static void KeccakF1600Step(SHA3Context *p){ int i; u64 B0, B1, B2, B3, B4; u64 C0, C1, C2, C3, C4; u64 D0, D1, D2, D3, D4; static const u64 RC[] = { 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL }; # define A00 (p->u.s[0]) # define A01 (p->u.s[1]) # define A02 (p->u.s[2]) # define A03 (p->u.s[3]) # define A04 (p->u.s[4]) # define A10 (p->u.s[5]) # define A11 (p->u.s[6]) # define A12 (p->u.s[7]) # define A13 (p->u.s[8]) # define A14 (p->u.s[9]) # define A20 (p->u.s[10]) # define A21 (p->u.s[11]) # define A22 (p->u.s[12]) # define A23 (p->u.s[13]) # define A24 (p->u.s[14]) # define A30 (p->u.s[15]) # define A31 (p->u.s[16]) # define A32 (p->u.s[17]) # define A33 (p->u.s[18]) # define A34 (p->u.s[19]) # define A40 (p->u.s[20]) # define A41 (p->u.s[21]) # define A42 (p->u.s[22]) # define A43 (p->u.s[23]) # define A44 (p->u.s[24]) # define ROL64(a,x) ((a<<x)|(a>>(64-x))) for(i=0; i<24; i+=4){ C0 = A00^A10^A20^A30^A40; C1 = A01^A11^A21^A31^A41; C2 = A02^A12^A22^A32^A42; C3 = A03^A13^A23^A33^A43; C4 = A04^A14^A24^A34^A44; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A11^D1), 44); B2 = ROL64((A22^D2), 43); B3 = ROL64((A33^D3), 21); B4 = ROL64((A44^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i]; A11 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B2 = ROL64((A20^D0), 3); B3 = ROL64((A31^D1), 45); B4 = ROL64((A42^D2), 61); B0 = ROL64((A03^D3), 28); B1 = ROL64((A14^D4), 20); A20 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A40^D0), 18); B0 = ROL64((A01^D1), 1); B1 = ROL64((A12^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A34^D4), 8); A40 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B1 = ROL64((A10^D0), 36); B2 = ROL64((A21^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A43^D3), 56); B0 = ROL64((A04^D4), 27); A10 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B3 = ROL64((A30^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A02^D2), 62); B1 = ROL64((A13^D3), 55); B2 = ROL64((A24^D4), 39); A30 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); C0 = A00^A20^A40^A10^A30; C1 = A11^A31^A01^A21^A41; C2 = A22^A42^A12^A32^A02; C3 = A33^A03^A23^A43^A13; C4 = A44^A14^A34^A04^A24; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A31^D1), 44); B2 = ROL64((A12^D2), 43); B3 = ROL64((A43^D3), 21); B4 = ROL64((A24^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+1]; A31 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B2 = ROL64((A40^D0), 3); B3 = ROL64((A21^D1), 45); B4 = ROL64((A02^D2), 61); B0 = ROL64((A33^D3), 28); B1 = ROL64((A14^D4), 20); A40 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A30^D0), 18); B0 = ROL64((A11^D1), 1); B1 = ROL64((A42^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A04^D4), 8); A30 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B1 = ROL64((A20^D0), 36); B2 = ROL64((A01^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A13^D3), 56); B0 = ROL64((A44^D4), 27); A20 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B3 = ROL64((A10^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A22^D2), 62); B1 = ROL64((A03^D3), 55); B2 = ROL64((A34^D4), 39); A10 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); C0 = A00^A40^A30^A20^A10; C1 = A31^A21^A11^A01^A41; C2 = A12^A02^A42^A32^A22; C3 = A43^A33^A23^A13^A03; C4 = A24^A14^A04^A44^A34; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A21^D1), 44); B2 = ROL64((A42^D2), 43); B3 = ROL64((A13^D3), 21); B4 = ROL64((A34^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+2]; A21 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B2 = ROL64((A30^D0), 3); B3 = ROL64((A01^D1), 45); B4 = ROL64((A22^D2), 61); B0 = ROL64((A43^D3), 28); B1 = ROL64((A14^D4), 20); A30 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A10^D0), 18); B0 = ROL64((A31^D1), 1); B1 = ROL64((A02^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A44^D4), 8); A10 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B1 = ROL64((A40^D0), 36); B2 = ROL64((A11^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A03^D3), 56); B0 = ROL64((A24^D4), 27); A40 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B3 = ROL64((A20^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A12^D2), 62); B1 = ROL64((A33^D3), 55); B2 = ROL64((A04^D4), 39); A20 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); C0 = A00^A30^A10^A40^A20; C1 = A21^A01^A31^A11^A41; C2 = A42^A22^A02^A32^A12; C3 = A13^A43^A23^A03^A33; C4 = A34^A14^A44^A24^A04; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A01^D1), 44); B2 = ROL64((A02^D2), 43); B3 = ROL64((A03^D3), 21); B4 = ROL64((A04^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+3]; A01 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B2 = ROL64((A10^D0), 3); B3 = ROL64((A11^D1), 45); B4 = ROL64((A12^D2), 61); B0 = ROL64((A13^D3), 28); B1 = ROL64((A14^D4), 20); A10 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A20^D0), 18); B0 = ROL64((A21^D1), 1); B1 = ROL64((A22^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A24^D4), 8); A20 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B1 = ROL64((A30^D0), 36); B2 = ROL64((A31^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A33^D3), 56); B0 = ROL64((A34^D4), 27); A30 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B3 = ROL64((A40^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A42^D2), 62); B1 = ROL64((A43^D3), 55); B2 = ROL64((A44^D4), 39); A40 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); } } /* ** Initialize a new hash. iSize determines the size of the hash ** in bits and should be one of 224, 256, 384, or 512. Or iSize ** can be zero to use the default hash size of 256 bits. */ static void SHA3Init(SHA3Context *p, int iSize){ memset(p, 0, sizeof(*p)); if( iSize>=128 && iSize<=512 ){ p->nRate = (1600 - ((iSize + 31)&~31)*2)/8; }else{ p->nRate = (1600 - 2*256)/8; } #if SHA3_BYTEORDER==1234 /* Known to be little-endian at compile-time. No-op */ #elif SHA3_BYTEORDER==4321 p->ixMask = 7; /* Big-endian */ #else { static unsigned int one = 1; if( 1==*(unsigned char*)&one ){ /* Little endian. No byte swapping. */ p->ixMask = 0; }else{ /* Big endian. Byte swap. */ p->ixMask = 7; } } #endif } /* ** Make consecutive calls to the SHA3Update function to add new content ** to the hash */ static void SHA3Update( SHA3Context *p, const unsigned char *aData, unsigned int nData ){ unsigned int i = 0; #if SHA3_BYTEORDER==1234 if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){ for(; i+7<nData; i+=8){ p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i]; p->nLoaded += 8; if( p->nLoaded>=p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } } #endif for(; i<nData; i++){ #if SHA3_BYTEORDER==1234 p->u.x[p->nLoaded] ^= aData[i]; #elif SHA3_BYTEORDER==4321 p->u.x[p->nLoaded^0x07] ^= aData[i]; #else p->u.x[p->nLoaded^p->ixMask] ^= aData[i]; #endif p->nLoaded++; if( p->nLoaded==p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } } /* ** After all content has been added, invoke SHA3Final() to compute ** the final hash. The function returns a pointer to the binary ** hash value. */ static unsigned char *SHA3Final(SHA3Context *p){ unsigned int i; if( p->nLoaded==p->nRate-1 ){ const unsigned char c1 = 0x86; SHA3Update(p, &c1, 1); }else{ const unsigned char c2 = 0x06; const unsigned char c3 = 0x80; SHA3Update(p, &c2, 1); p->nLoaded = p->nRate - 1; SHA3Update(p, &c3, 1); } for(i=0; i<p->nRate; i++){ p->u.x[i+p->nRate] = p->u.x[i^p->ixMask]; } return &p->u.x[p->nRate]; } /* End of the hashing logic *****************************************************************************/ /* ** Implementation of the sha3(X,SIZE) function. ** ** Return a BLOB which is the SIZE-bit SHA3 hash of X. The default ** size is 256. If X is a BLOB, it is hashed as is. ** For all other non-NULL types of input, X is converted into a UTF-8 string ** and the string is hashed without the trailing 0x00 terminator. The hash ** of a NULL value is NULL. */ static void sha3Func( sqlite3_context *context, int argc, sqlite3_value **argv ){ SHA3Context cx; int eType = sqlite3_value_type(argv[0]); int nByte = sqlite3_value_bytes(argv[0]); int iSize; if( argc==1 ){ iSize = 256; }else{ iSize = sqlite3_value_int(argv[1]); if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){ sqlite3_result_error(context, "SHA3 size should be one of: 224 256 " "384 512", -1); return; } } if( eType==SQLITE_NULL ) return; SHA3Init(&cx, iSize); if( eType==SQLITE_BLOB ){ SHA3Update(&cx, sqlite3_value_blob(argv[0]), nByte); }else{ SHA3Update(&cx, sqlite3_value_text(argv[0]), nByte); } sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); } /* Compute a string using sqlite3_vsnprintf() with a maximum length ** of 50 bytes and add it to the hash. */ static void hash_step_vformat( SHA3Context *p, /* Add content to this context */ const char *zFormat, ... ){ va_list ap; int n; char zBuf[50]; va_start(ap, zFormat); sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); va_end(ap); n = (int)strlen(zBuf); SHA3Update(p, (unsigned char*)zBuf, n); } /* ** Implementation of the sha3_query(SQL,SIZE) function. ** ** This function compiles and runs the SQL statement(s) given in the ** argument. The results are hashed using a SIZE-bit SHA3. The default ** size is 256. ** ** The format of the byte stream that is hashed is summarized as follows: ** ** S<n>:<sql> ** R ** N ** I<int> ** F<ieee-float> ** B<size>:<bytes> ** T<size>:<text> ** ** <sql> is the original SQL text for each statement run and <n> is ** the size of that text. The SQL text is UTF-8. A single R character ** occurs before the start of each row. N means a NULL value. ** I mean an 8-byte little-endian integer <int>. F is a floating point ** number with an 8-byte little-endian IEEE floating point value <ieee-float>. ** B means blobs of <size> bytes. T means text rendered as <size> ** bytes of UTF-8. The <n> and <size> values are expressed as an ASCII ** text integers. ** ** For each SQL statement in the X input, there is one S segment. Each ** S segment is followed by zero or more R segments, one for each row in the ** result set. After each R, there are one or more N, I, F, B, or T segments, ** one for each column in the result set. Segments are concatentated directly ** with no delimiters of any kind. */ static void sha3QueryFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3 *db = sqlite3_context_db_handle(context); const char *zSql = (const char*)sqlite3_value_text(argv[0]); sqlite3_stmt *pStmt = 0; int nCol; /* Number of columns in the result set */ int i; /* Loop counter */ int rc; int n; const char *z; SHA3Context cx; int iSize; if( argc==1 ){ iSize = 256; }else{ iSize = sqlite3_value_int(argv[1]); if( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){ sqlite3_result_error(context, "SHA3 size should be one of: 224 256 " "384 512", -1); return; } } if( zSql==0 ) return; SHA3Init(&cx, iSize); while( zSql[0] ){ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); if( rc ){ char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s", zSql, sqlite3_errmsg(db)); sqlite3_finalize(pStmt); sqlite3_result_error(context, zMsg, -1); sqlite3_free(zMsg); return; } if( !sqlite3_stmt_readonly(pStmt) ){ char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt)); sqlite3_finalize(pStmt); sqlite3_result_error(context, zMsg, -1); sqlite3_free(zMsg); return; } nCol = sqlite3_column_count(pStmt); z = sqlite3_sql(pStmt); n = (int)strlen(z); hash_step_vformat(&cx,"S%d:",n); SHA3Update(&cx,(unsigned char*)z,n); /* Compute a hash over the result of the query */ while( SQLITE_ROW==sqlite3_step(pStmt) ){ SHA3Update(&cx,(const unsigned char*)"R",1); for(i=0; i<nCol; i++){ switch( sqlite3_column_type(pStmt,i) ){ case SQLITE_NULL: { SHA3Update(&cx, (const unsigned char*)"N",1); break; } case SQLITE_INTEGER: { sqlite3_uint64 u; int j; unsigned char x[9]; sqlite3_int64 v = sqlite3_column_int64(pStmt,i); memcpy(&u, &v, 8); for(j=8; j>=1; j--){ x[j] = u & 0xff; u >>= 8; } x[0] = 'I'; SHA3Update(&cx, x, 9); break; } case SQLITE_FLOAT: { sqlite3_uint64 u; int j; unsigned char x[9]; double r = sqlite3_column_double(pStmt,i); memcpy(&u, &r, 8); for(j=8; j>=1; j--){ x[j] = u & 0xff; u >>= 8; } x[0] = 'F'; SHA3Update(&cx,x,9); break; } case SQLITE_TEXT: { int n2 = sqlite3_column_bytes(pStmt, i); const unsigned char *z2 = sqlite3_column_text(pStmt, i); hash_step_vformat(&cx,"T%d:",n2); SHA3Update(&cx, z2, n2); break; } case SQLITE_BLOB: { int n2 = sqlite3_column_bytes(pStmt, i); const unsigned char *z2 = sqlite3_column_blob(pStmt, i); hash_step_vformat(&cx,"B%d:",n2); SHA3Update(&cx, z2, n2); break; } } } } sqlite3_finalize(pStmt); } sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); } #ifdef _WIN32 #endif int sqlite3_shathree_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); (void)pzErrMsg; /* Unused parameter */ rc = sqlite3_create_function(db, "sha3", 1, SQLITE_UTF8, 0, sha3Func, 0, 0); if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "sha3", 2, SQLITE_UTF8, 0, sha3Func, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "sha3_query", 1, SQLITE_UTF8, 0, sha3QueryFunc, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "sha3_query", 2, SQLITE_UTF8, 0, sha3QueryFunc, 0, 0); } return rc; } /************************* End ../ext/misc/shathree.c ********************/ /************************* Begin ../ext/misc/fileio.c ******************/ /* ** 2014-06-13 ** ** 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 SQLite extension implements SQL functions readfile() and ** writefile(). */ SQLITE_EXTENSION_INIT1 #include <stdio.h> /* ** Implementation of the "readfile(X)" SQL function. The entire content ** of the file named X is read and returned as a BLOB. NULL is returned ** if the file does not exist or is unreadable. */ static void readfileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zName; FILE *in; long nIn; void *pBuf; (void)(argc); /* Unused parameter */ zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; in = fopen(zName, "rb"); if( in==0 ) return; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); pBuf = sqlite3_malloc( nIn ); if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); }else{ sqlite3_free(pBuf); } fclose(in); } /* ** Implementation of the "writefile(X,Y)" SQL function. The argument Y ** is written into file X. The number of bytes written is returned. Or ** NULL is returned if something goes wrong, such as being unable to open ** file X for writing. */ static void writefileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ FILE *out; const char *z; sqlite3_int64 rc; const char *zFile; (void)(argc); /* Unused parameter */ zFile = (const char*)sqlite3_value_text(argv[0]); if( zFile==0 ) return; out = fopen(zFile, "wb"); if( out==0 ) return; z = (const char*)sqlite3_value_blob(argv[1]); if( z==0 ){ rc = 0; }else{ rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out); } fclose(out); sqlite3_result_int64(context, rc); } #ifdef _WIN32 #endif int sqlite3_fileio_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); (void)pzErrMsg; /* Unused parameter */ rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, readfileFunc, 0, 0); if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, writefileFunc, 0, 0); } return rc; } /************************* End ../ext/misc/fileio.c ********************/ /************************* Begin ../ext/misc/completion.c ******************/ /* ** 2017-07-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 an eponymous virtual table that returns suggested ** completions for a partial SQL input. ** ** Suggested usage: ** ** SELECT DISTINCT candidate COLLATE nocase ** FROM completion($prefix,$wholeline) ** ORDER BY 1; ** ** The two query parameters are optional. $prefix is the text of the ** current word being typed and that is to be completed. $wholeline is ** the complete input line, used for context. ** ** The raw completion() table might return the same candidate multiple ** times, for example if the same column name is used to two or more ** tables. And the candidates are returned in an arbitrary order. Hence, ** the DISTINCT and ORDER BY are recommended. ** ** This virtual table operates at the speed of human typing, and so there ** is no attempt to make it fast. Even a slow implementation will be much ** faster than any human can type. ** */ SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <ctype.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* completion_vtab is a subclass of sqlite3_vtab which will ** serve as the underlying representation of a completion virtual table */ typedef struct completion_vtab completion_vtab; struct completion_vtab { sqlite3_vtab base; /* Base class - must be first */ sqlite3 *db; /* Database connection for this completion vtab */ }; /* completion_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct completion_cursor completion_cursor; struct completion_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3 *db; /* Database connection for this cursor */ int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */ char *zPrefix; /* The prefix for the word we want to complete */ char *zLine; /* The whole that we want to complete */ const char *zCurrentRow; /* Current output row */ sqlite3_stmt *pStmt; /* Current statement */ sqlite3_int64 iRowid; /* The rowid */ int ePhase; /* Current phase */ int j; /* inter-phase counter */ }; /* Values for ePhase: */ #define COMPLETION_FIRST_PHASE 1 #define COMPLETION_KEYWORDS 1 #define COMPLETION_PRAGMAS 2 #define COMPLETION_FUNCTIONS 3 #define COMPLETION_COLLATIONS 4 #define COMPLETION_INDEXES 5 #define COMPLETION_TRIGGERS 6 #define COMPLETION_DATABASES 7 #define COMPLETION_TABLES 8 #define COMPLETION_COLUMNS 9 #define COMPLETION_MODULES 10 #define COMPLETION_EOF 11 /* ** The completionConnect() method is invoked to create a new ** completion_vtab that describes the completion virtual table. ** ** Think of this routine as the constructor for completion_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the completion_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against completion will look like. */ static int completionConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ completion_vtab *pNew; int rc; (void)(pAux); /* Unused parameter */ (void)(argc); /* Unused parameter */ (void)(argv); /* Unused parameter */ (void)(pzErr); /* Unused parameter */ /* Column numbers */ #define COMPLETION_COLUMN_CANDIDATE 0 /* Suggested completion of the input */ #define COMPLETION_COLUMN_PREFIX 1 /* Prefix of the word to be completed */ #define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */ #define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(" " candidate TEXT," " prefix TEXT HIDDEN," " wholeline TEXT HIDDEN," " phase INT HIDDEN" /* Used for debugging only */ ")"); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); pNew->db = db; } return rc; } /* ** This method is the destructor for completion_cursor objects. */ static int completionDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* ** Constructor for a new completion_cursor object. */ static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ completion_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->db = ((completion_vtab*)p)->db; *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Reset the completion_cursor. */ static void completionCursorReset(completion_cursor *pCur){ sqlite3_free(pCur->zPrefix); pCur->zPrefix = 0; pCur->nPrefix = 0; sqlite3_free(pCur->zLine); pCur->zLine = 0; pCur->nLine = 0; sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; pCur->j = 0; } /* ** Destructor for a completion_cursor. */ static int completionClose(sqlite3_vtab_cursor *cur){ completionCursorReset((completion_cursor*)cur); sqlite3_free(cur); return SQLITE_OK; } /* ** All SQL keywords understood by SQLite */ static const char *completionKwrds[] = { "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH", "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA", "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE", "WITH", "WITHOUT", }; #define completionKwCount \ (int)(sizeof(completionKwrds)/sizeof(completionKwrds[0])) /* ** Advance a completion_cursor to its next row of output. ** ** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object ** record the current state of the scan. This routine sets ->zCurrentRow ** to the current row of output and then returns. If no more rows remain, ** then ->ePhase is set to COMPLETION_EOF which will signal the virtual ** table that has reached the end of its scan. ** ** The current implementation just lists potential identifiers and ** keywords and filters them by zPrefix. Future enhancements should ** take zLine into account to try to restrict the set of identifiers and ** keywords based on what would be legal at the current point of input. */ static int completionNext(sqlite3_vtab_cursor *cur){ completion_cursor *pCur = (completion_cursor*)cur; int eNextPhase = 0; /* Next phase to try if current phase reaches end */ int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */ pCur->iRowid++; while( pCur->ePhase!=COMPLETION_EOF ){ switch( pCur->ePhase ){ case COMPLETION_KEYWORDS: { if( pCur->j >= completionKwCount ){ pCur->zCurrentRow = 0; pCur->ePhase = COMPLETION_DATABASES; }else{ pCur->zCurrentRow = completionKwrds[pCur->j++]; } iCol = -1; break; } case COMPLETION_DATABASES: { if( pCur->pStmt==0 ){ sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pCur->pStmt, 0); } iCol = 1; eNextPhase = COMPLETION_TABLES; break; } case COMPLETION_TABLES: { if( pCur->pStmt==0 ){ sqlite3_stmt *pS2; char *zSql = 0; const char *zSep = ""; sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0); while( sqlite3_step(pS2)==SQLITE_ROW ){ const char *zDb = (const char*)sqlite3_column_text(pS2, 1); zSql = sqlite3_mprintf( "%z%s" "SELECT name FROM \"%w\".sqlite_master" " WHERE type='table'", zSql, zSep, zDb ); if( zSql==0 ) return SQLITE_NOMEM; zSep = " UNION "; } sqlite3_finalize(pS2); sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); sqlite3_free(zSql); } iCol = 0; eNextPhase = COMPLETION_COLUMNS; break; } case COMPLETION_COLUMNS: { if( pCur->pStmt==0 ){ sqlite3_stmt *pS2; char *zSql = 0; const char *zSep = ""; sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0); while( sqlite3_step(pS2)==SQLITE_ROW ){ const char *zDb = (const char*)sqlite3_column_text(pS2, 1); zSql = sqlite3_mprintf( "%z%s" "SELECT pti.name FROM \"%w\".sqlite_master AS sm" " JOIN pragma_table_info(sm.name,%Q) AS pti" " WHERE sm.type='table'", zSql, zSep, zDb, zDb ); if( zSql==0 ) return SQLITE_NOMEM; zSep = " UNION "; } sqlite3_finalize(pS2); sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); sqlite3_free(zSql); } iCol = 0; eNextPhase = COMPLETION_EOF; break; } } if( iCol<0 ){ /* This case is when the phase presets zCurrentRow */ if( pCur->zCurrentRow==0 ) continue; }else{ if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){ /* Extract the next row of content */ pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol); }else{ /* When all rows are finished, advance to the next phase */ sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; pCur->ePhase = eNextPhase; continue; } } if( pCur->nPrefix==0 ) break; if( sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 ){ break; } } return SQLITE_OK; } /* ** Return values of columns for the row at which the completion_cursor ** is currently pointing. */ static int completionColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ completion_cursor *pCur = (completion_cursor*)cur; switch( i ){ case COMPLETION_COLUMN_CANDIDATE: { sqlite3_result_text(ctx, pCur->zCurrentRow, -1, SQLITE_TRANSIENT); break; } case COMPLETION_COLUMN_PREFIX: { sqlite3_result_text(ctx, pCur->zPrefix, -1, SQLITE_TRANSIENT); break; } case COMPLETION_COLUMN_WHOLELINE: { sqlite3_result_text(ctx, pCur->zLine, -1, SQLITE_TRANSIENT); break; } case COMPLETION_COLUMN_PHASE: { sqlite3_result_int(ctx, pCur->ePhase); break; } } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int completionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ completion_cursor *pCur = (completion_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int completionEof(sqlite3_vtab_cursor *cur){ completion_cursor *pCur = (completion_cursor*)cur; return pCur->ePhase >= COMPLETION_EOF; } /* ** This method is called to "rewind" the completion_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to completionColumn() or completionRowid() or ** completionEof(). */ static int completionFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ completion_cursor *pCur = (completion_cursor *)pVtabCursor; int iArg = 0; (void)(idxStr); /* Unused parameter */ (void)(argc); /* Unused parameter */ completionCursorReset(pCur); if( idxNum & 1 ){ pCur->nPrefix = sqlite3_value_bytes(argv[iArg]); if( pCur->nPrefix>0 ){ pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg])); if( pCur->zPrefix==0 ) return SQLITE_NOMEM; } iArg++; } if( idxNum & 2 ){ pCur->nLine = sqlite3_value_bytes(argv[iArg]); if( pCur->nLine>0 ){ pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg])); if( pCur->zLine==0 ) return SQLITE_NOMEM; } iArg++; } if( pCur->zLine!=0 && pCur->zPrefix==0 ){ int i = pCur->nLine; while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){ i--; } pCur->nPrefix = pCur->nLine - i; if( pCur->nPrefix>0 ){ pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i); if( pCur->zPrefix==0 ) return SQLITE_NOMEM; } } pCur->iRowid = 0; pCur->ePhase = COMPLETION_FIRST_PHASE; return completionNext(pVtabCursor); } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the completion virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. ** ** There are two hidden parameters that act as arguments to the table-valued ** function: "prefix" and "wholeline". Bit 0 of idxNum is set if "prefix" ** is available and bit 1 is set if "wholeline" is available. */ static int completionBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; /* Loop over constraints */ int idxNum = 0; /* The query plan bitmask */ int prefixIdx = -1; /* Index of the start= constraint, or -1 if none */ int wholelineIdx = -1; /* Index of the stop= constraint, or -1 if none */ int nArg = 0; /* Number of arguments that completeFilter() expects */ const struct sqlite3_index_constraint *pConstraint; (void)(tab); /* Unused parameter */ pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; switch( pConstraint->iColumn ){ case COMPLETION_COLUMN_PREFIX: prefixIdx = i; idxNum |= 1; break; case COMPLETION_COLUMN_WHOLELINE: wholelineIdx = i; idxNum |= 2; break; } } if( prefixIdx>=0 ){ pIdxInfo->aConstraintUsage[prefixIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[prefixIdx].omit = 1; } if( wholelineIdx>=0 ){ pIdxInfo->aConstraintUsage[wholelineIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[wholelineIdx].omit = 1; } pIdxInfo->idxNum = idxNum; pIdxInfo->estimatedCost = (double)5000 - 1000*nArg; pIdxInfo->estimatedRows = 500 - 100*nArg; return SQLITE_OK; } /* ** This following structure defines all the methods for the ** completion virtual table. */ static sqlite3_module completionModule = { 0, /* iVersion */ 0, /* xCreate */ completionConnect, /* xConnect */ completionBestIndex, /* xBestIndex */ completionDisconnect, /* xDisconnect */ 0, /* xDestroy */ completionOpen, /* xOpen - open a cursor */ completionClose, /* xClose - close a cursor */ completionFilter, /* xFilter - configure scan constraints */ completionNext, /* xNext - advance a cursor */ completionEof, /* xEof - check for end of scan */ completionColumn, /* xColumn - read data */ completionRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0 /* xRollbackTo */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ int sqlite3CompletionVtabInit(sqlite3 *db){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3_create_module(db, "completion", &completionModule, 0); #endif return rc; } #ifdef _WIN32 #endif int sqlite3_completion_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); (void)(pzErrMsg); /* Unused parameter */ #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3CompletionVtabInit(db); #endif return rc; } /************************* End ../ext/misc/completion.c ********************/ #if defined(SQLITE_ENABLE_SESSION) /* ** State information for a single open session */ typedef struct OpenSession OpenSession; struct OpenSession { |
︙ | ︙ | |||
606 607 608 609 610 611 612 | /* ** State information about the database connection is contained in an ** instance of the following structure. */ typedef struct ShellState ShellState; struct ShellState { sqlite3 *db; /* The database */ | < < < | 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 | /* ** State information about the database connection is contained in an ** instance of the following structure. */ typedef struct ShellState ShellState; struct ShellState { sqlite3 *db; /* The database */ int autoExplain; /* Automatically turn on .explain mode */ int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ int statsOn; /* True to display memory stats before each finalize */ int scanstatsOn; /* True to display scan stats before each finalize */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ int cMode; /* temporary output mode for the current query */ |
︙ | ︙ | |||
651 652 653 654 655 656 657 | OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */ #endif }; /* ** These are the allowed shellFlgs values */ | < | | > > > > > > > > > > > > > | | | | | > | 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 | OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */ #endif }; /* ** These are the allowed shellFlgs values */ #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ #define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ #define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */ #define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ #define SHFLG_CountChanges 0x00000020 /* .changes setting */ #define SHFLG_Echo 0x00000040 /* .echo or --echo setting */ /* ** Macros for testing and setting shellFlgs */ #define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) #define ShellSetFlag(P,X) ((P)->shellFlgs|=(X)) #define ShellClearFlag(P,X) ((P)->shellFlgs&=(~(X))) /* ** These are the allowed modes. */ #define MODE_Line 0 /* One column per line. Blank line between records */ #define MODE_Column 1 /* One record per line in neat columns */ #define MODE_List 2 /* One record per line with a separator */ #define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ #define MODE_Html 4 /* Generate an XHTML table */ #define MODE_Insert 5 /* Generate SQL "insert" statements */ #define MODE_Quote 6 /* Quote values as for SQL */ #define MODE_Tcl 7 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 8 /* Quote strings, numbers are plain */ #define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */ #define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */ #define MODE_Pretty 11 /* Pretty-print schemas */ static const char *modeDescr[] = { "line", "column", "list", "semi", "html", "insert", "quote", "tcl", "csv", "explain", "ascii", "prettyprint", }; |
︙ | ︙ | |||
722 723 724 725 726 727 728 729 730 731 732 733 734 | static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ int i; char *zBlob = (char *)pBlob; raw_printf(out,"X'"); for(i=0; i<nBlob; i++){ raw_printf(out,"%02x",zBlob[i]&0xff); } raw_printf(out,"'"); } /* ** Output the given string as a quoted string using SQL quoting conventions. */ static void output_quoted_string(FILE *out, const char *z){ int i; | > > > > > > > > > > > > > > > > > > > > > > > < > | > > > > > > | > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | | < < < | | < > > | > > > > > > > | | > > > > > > > > | 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 | static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ int i; char *zBlob = (char *)pBlob; raw_printf(out,"X'"); for(i=0; i<nBlob; i++){ raw_printf(out,"%02x",zBlob[i]&0xff); } raw_printf(out,"'"); } /* ** Find a string that is not found anywhere in z[]. Return a pointer ** to that string. ** ** Try to use zA and zB first. If both of those are already found in z[] ** then make up some string and store it in the buffer zBuf. */ static const char *unused_string( const char *z, /* Result must not appear anywhere in z */ const char *zA, const char *zB, /* Try these first */ char *zBuf /* Space to store a generated string */ ){ unsigned i = 0; if( strstr(z, zA)==0 ) return zA; if( strstr(z, zB)==0 ) return zB; do{ sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++); }while( strstr(z,zBuf)!=0 ); return zBuf; } /* ** Output the given string as a quoted string using SQL quoting conventions. ** ** See also: output_quoted_escaped_string() */ static void output_quoted_string(FILE *out, const char *z){ int i; char c; setBinaryMode(out, 1); for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c==0 ){ utf8_printf(out,"'%s'",z); }else{ raw_printf(out, "'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c=='\'' ) i++; if( i ){ utf8_printf(out, "%.*s", i, z); z += i; } if( c=='\'' ){ raw_printf(out, "'"); continue; } if( c==0 ){ break; } z++; } raw_printf(out, "'"); } setTextMode(out, 1); } /* ** Output the given string as a quoted string using SQL quoting conventions. ** Additionallly , escape the "\n" and "\r" characters so that they do not ** get corrupted by end-of-line translation facilities in some operating ** systems. ** ** This is like output_quoted_string() but with the addition of the \r\n ** escape mechanism. */ static void output_quoted_escaped_string(FILE *out, const char *z){ int i; char c; setBinaryMode(out, 1); for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} if( c==0 ){ utf8_printf(out,"'%s'",z); }else{ const char *zNL = 0; const char *zCR = 0; int nNL = 0; int nCR = 0; char zBuf1[20], zBuf2[20]; for(i=0; z[i]; i++){ if( z[i]=='\n' ) nNL++; if( z[i]=='\r' ) nCR++; } if( nNL ){ raw_printf(out, "replace("); zNL = unused_string(z, "\\n", "\\012", zBuf1); } if( nCR ){ raw_printf(out, "replace("); zCR = unused_string(z, "\\r", "\\015", zBuf2); } raw_printf(out, "'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} if( c=='\'' ) i++; if( i ){ utf8_printf(out, "%.*s", i, z); z += i; } if( c=='\'' ){ raw_printf(out, "'"); continue; } if( c==0 ){ break; } z++; if( c=='\n' ){ raw_printf(out, "%s", zNL); continue; } raw_printf(out, "%s", zCR); } raw_printf(out, "'"); if( nCR ){ raw_printf(out, ",'%s',char(13))", zCR); } if( nNL ){ raw_printf(out, ",'%s',char(10))", zNL); } } setTextMode(out, 1); } /* ** Output the given string as a quoted according to C or TCL quoting rules. */ |
︙ | ︙ | |||
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 | } } raw_printf(p->out, "\n"); return SQLITE_OK; } #endif /* ** This is the callback routine that the shell ** invokes for each row of a query result. */ static int shell_callback( void *pArg, int nArg, /* Number of result columns */ char **azArg, /* Text of each result column */ char **azCol, /* Column names */ int *aiType /* Column types */ ){ int i; ShellState *p = (ShellState*)pArg; switch( p->cMode ){ case MODE_Line: { int w = 5; if( azArg==0 ) break; for(i=0; i<nArg; i++){ int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; | > > > > > > > > > > > > > > > > > > > > | 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 | } } raw_printf(p->out, "\n"); return SQLITE_OK; } #endif /* ** Print a schema statement. Part of MODE_Semi and MODE_Pretty output. ** ** This routine converts some CREATE TABLE statements for shadow tables ** in FTS3/4/5 into CREATE TABLE IF NOT EXISTS statements. */ static void printSchemaLine(FILE *out, const char *z, const char *zTail){ if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ utf8_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); }else{ utf8_printf(out, "%s%s", z, zTail); } } static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){ char c = z[n]; z[n] = 0; printSchemaLine(out, z, zTail); z[n] = c; } /* ** This is the callback routine that the shell ** invokes for each row of a query result. */ static int shell_callback( void *pArg, int nArg, /* Number of result columns */ char **azArg, /* Text of each result column */ char **azCol, /* Column names */ int *aiType /* Column types */ ){ int i; ShellState *p = (ShellState*)pArg; if( azArg==0 ) return 0; switch( p->cMode ){ case MODE_Line: { int w = 5; if( azArg==0 ) break; for(i=0; i<nArg; i++){ int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; |
︙ | ︙ | |||
994 995 996 997 998 999 1000 | int w, n; if( i<ArraySize(p->colWidth) ){ w = colWidth[i]; }else{ w = 0; } if( w==0 ){ | | | < | | < < < < | 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 | int w, n; if( i<ArraySize(p->colWidth) ){ w = colWidth[i]; }else{ w = 0; } if( w==0 ){ w = strlenChar(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; n = strlenChar(azArg && azArg[i] ? azArg[i] : p->nullValue); if( w<n ) w = n; } if( i<ArraySize(p->actualWidth) ){ p->actualWidth[i] = w; } if( showHdr ){ utf8_width_print(p->out, w, azCol[i]); utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " "); } } if( showHdr ){ for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; |
︙ | ︙ | |||
1036 1037 1038 1039 1040 1041 1042 | for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; }else{ w = 10; } | | | < < | | < < < < < | | 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 | for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; }else{ w = 10; } if( p->cMode==MODE_Explain && azArg[i] && strlenChar(azArg[i])>w ){ w = strlenChar(azArg[i]); } if( i==1 && p->aiIndent && p->pStmt ){ if( p->iIndent<p->nIndent ){ utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); } p->iIndent++; } utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue); utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " "); } break; } case MODE_Semi: { /* .schema and .fullschema output */ printSchemaLine(p->out, azArg[0], ";\n"); break; } case MODE_Pretty: { /* .schema and .fullschema with --indent */ char *z; int j; int nParen = 0; char cEnd = 0; |
︙ | ︙ | |||
1102 1103 1104 1105 1106 1107 1108 | }else if( c=='[' ){ cEnd = ']'; }else if( c=='(' ){ nParen++; }else if( c==')' ){ nParen--; if( nLine>0 && nParen==0 && j>0 ){ | | | | | 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 | }else if( c=='[' ){ cEnd = ']'; }else if( c=='(' ){ nParen++; }else if( c==')' ){ nParen--; if( nLine>0 && nParen==0 && j>0 ){ printSchemaLineN(p->out, z, j, "\n"); j = 0; } } z[j++] = c; if( nParen==1 && (c=='(' || c==',' || c=='\n') ){ if( c=='\n' ) j--; printSchemaLineN(p->out, z, j, "\n "); j = 0; nLine++; while( IsSpace(z[i+1]) ){ i++; } } } z[j] = 0; } printSchemaLine(p->out, z, ";\n"); sqlite3_free(z); break; } case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ utf8_printf(p->out,"%s%s",azCol[i], |
︙ | ︙ | |||
1195 1196 1197 1198 1199 1200 1201 | } utf8_printf(p->out, "%s", p->rowSeparator); } setTextMode(p->out, 1); break; } case MODE_Insert: { | < > > | | > > > > | | | | | | > > | | > > > > > < | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | | 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 | } utf8_printf(p->out, "%s", p->rowSeparator); } setTextMode(p->out, 1); break; } case MODE_Insert: { if( azArg==0 ) break; utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); if( p->showHeader ){ raw_printf(p->out,"("); for(i=0; i<nArg; i++){ if( i>0 ) raw_printf(p->out, ","); if( quoteChar(azCol[i]) ){ char *z = sqlite3_mprintf("\"%w\"", azCol[i]); utf8_printf(p->out, "%s", z); sqlite3_free(z); }else{ raw_printf(p->out, "%s", azCol[i]); } } raw_printf(p->out,")"); } p->cnt++; for(i=0; i<nArg; i++){ raw_printf(p->out, i>0 ? "," : " VALUES("); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ utf8_printf(p->out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ if( ShellHasFlag(p, SHFLG_Newlines) ){ output_quoted_string(p->out, azArg[i]); }else{ output_quoted_escaped_string(p->out, azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ utf8_printf(p->out,"%s", azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); raw_printf(p->out, "%s", z); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ utf8_printf(p->out,"%s", azArg[i]); }else if( ShellHasFlag(p, SHFLG_Newlines) ){ output_quoted_string(p->out, azArg[i]); }else{ output_quoted_escaped_string(p->out, azArg[i]); } } raw_printf(p->out,");\n"); break; } case MODE_Quote: { if( azArg==0 ) break; if( p->cnt==0 && p->showHeader ){ for(i=0; i<nArg; i++){ if( i>0 ) raw_printf(p->out, ","); output_quoted_string(p->out, azCol[i]); } raw_printf(p->out,"\n"); } p->cnt++; for(i=0; i<nArg; i++){ if( i>0 ) raw_printf(p->out, ","); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ utf8_printf(p->out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ output_quoted_string(p->out, azArg[i]); }else if( aiType && aiType[i]==SQLITE_INTEGER ){ utf8_printf(p->out,"%s", azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); raw_printf(p->out, "%s", z); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ utf8_printf(p->out,"%s", azArg[i]); }else{ output_quoted_string(p->out, azArg[i]); } } raw_printf(p->out,"\n"); break; } case MODE_Ascii: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator); utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : ""); } |
︙ | ︙ | |||
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 | ** This is the callback routine that the SQLite library ** invokes for each row of a query result. */ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ /* since we don't have type info, call the shell_callback with a NULL value */ return shell_callback(pArg, nArg, azArg, azCol, NULL); } /* ** Set the destination table field of the ShellState structure to ** the name of the table given. Escape any quote characters in the ** table name. */ static void set_table_name(ShellState *p, const char *zName){ int i, n; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < | | < < | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | ** This is the callback routine that the SQLite library ** invokes for each row of a query result. */ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ /* since we don't have type info, call the shell_callback with a NULL value */ return shell_callback(pArg, nArg, azArg, azCol, NULL); } /* ** This is the callback routine from sqlite3_exec() that appends all ** output onto the end of a ShellText object. */ static int captureOutputCallback(void *pArg, int nArg, char **azArg, char **az){ ShellText *p = (ShellText*)pArg; int i; UNUSED_PARAMETER(az); if( azArg==0 ) return 0; if( p->n ) appendText(p, "|", 0); for(i=0; i<nArg; i++){ if( i ) appendText(p, ",", 0); if( azArg[i] ) appendText(p, azArg[i], 0); } return 0; } /* ** Generate an appropriate SELFTEST table in the main database. */ static void createSelftestTable(ShellState *p){ char *zErrMsg = 0; sqlite3_exec(p->db, "SAVEPOINT selftest_init;\n" "CREATE TABLE IF NOT EXISTS selftest(\n" " tno INTEGER PRIMARY KEY,\n" /* Test number */ " op TEXT,\n" /* Operator: memo run */ " cmd TEXT,\n" /* Command text */ " ans TEXT\n" /* Desired answer */ ");" "CREATE TEMP TABLE [_shell$self](op,cmd,ans);\n" "INSERT INTO [_shell$self](rowid,op,cmd)\n" " VALUES(coalesce((SELECT (max(tno)+100)/10 FROM selftest),10),\n" " 'memo','Tests generated by --init');\n" "INSERT INTO [_shell$self]\n" " SELECT 'run',\n" " 'SELECT hex(sha3_query(''SELECT type,name,tbl_name,sql " "FROM sqlite_master ORDER BY 2'',224))',\n" " hex(sha3_query('SELECT type,name,tbl_name,sql " "FROM sqlite_master ORDER BY 2',224));\n" "INSERT INTO [_shell$self]\n" " SELECT 'run'," " 'SELECT hex(sha3_query(''SELECT * FROM \"' ||" " printf('%w',name) || '\" NOT INDEXED'',224))',\n" " hex(sha3_query(printf('SELECT * FROM \"%w\" NOT INDEXED',name),224))\n" " FROM (\n" " SELECT name FROM sqlite_master\n" " WHERE type='table'\n" " AND name<>'selftest'\n" " AND coalesce(rootpage,0)>0\n" " )\n" " ORDER BY name;\n" "INSERT INTO [_shell$self]\n" " VALUES('run','PRAGMA integrity_check','ok');\n" "INSERT INTO selftest(tno,op,cmd,ans)" " SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n" "DROP TABLE [_shell$self];" ,0,0,&zErrMsg); if( zErrMsg ){ utf8_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); sqlite3_free(zErrMsg); } sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); } /* ** Set the destination table field of the ShellState structure to ** the name of the table given. Escape any quote characters in the ** table name. */ static void set_table_name(ShellState *p, const char *zName){ int i, n; char cQuote; char *z; if( p->zDestTable ){ free(p->zDestTable); p->zDestTable = 0; } if( zName==0 ) return; cQuote = quoteChar(zName); n = strlen30(zName); if( cQuote ) n += n+2; z = p->zDestTable = malloc( n+1 ); if( z==0 ){ raw_printf(stderr,"Error: out of memory\n"); exit(1); } n = 0; if( cQuote ) z[n++] = cQuote; for(i=0; zName[i]; i++){ z[n++] = zName[i]; if( zName[i]==cQuote ) z[n++] = cQuote; } if( cQuote ) z[n++] = cQuote; z[n] = 0; } /* ** Execute a query statement that will generate SQL output. Print ** the result columns, comma-separated, on a line and then add a ** semicolon terminator to the end of that line. |
︙ | ︙ | |||
1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 | } } } fclose(in); } #endif /* ** Display memory stats. */ static int display_stats( sqlite3 *db, /* Database to query */ ShellState *pArg, /* Pointer to ShellState */ int bReset /* True to reset the stats */ ){ int iCur; int iHiwtr; if( pArg && pArg->out ){ | > > > > > > > > > > > > > > > > > > > > > > > > > | < | | < < < | < < < < < | < < < | < < < < < < < < < < < < | < < | | < < | | < < | < < < > | < < | 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 | } } } fclose(in); } #endif /* ** Display a single line of status using 64-bit values. */ static void displayStatLine( ShellState *p, /* The shell context */ char *zLabel, /* Label for this one line */ char *zFormat, /* Format for the result */ int iStatusCtrl, /* Which status to display */ int bReset /* True to reset the stats */ ){ sqlite3_int64 iCur = -1; sqlite3_int64 iHiwtr = -1; int i, nPercent; char zLine[200]; sqlite3_status64(iStatusCtrl, &iCur, &iHiwtr, bReset); for(i=0, nPercent=0; zFormat[i]; i++){ if( zFormat[i]=='%' ) nPercent++; } if( nPercent>1 ){ sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iCur, iHiwtr); }else{ sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); } raw_printf(p->out, "%-36s %s\n", zLabel, zLine); } /* ** Display memory stats. */ static int display_stats( sqlite3 *db, /* Database to query */ ShellState *pArg, /* Pointer to ShellState */ int bReset /* True to reset the stats */ ){ int iCur; int iHiwtr; if( pArg && pArg->out ){ displayStatLine(pArg, "Memory Used:", "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); displayStatLine(pArg, "Number of Outstanding Allocations:", "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); if( pArg->shellFlgs & SHFLG_Pagecache ){ displayStatLine(pArg, "Number of Pcache Pages Used:", "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); } displayStatLine(pArg, "Number of Pcache Overflow Bytes:", "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); displayStatLine(pArg, "Largest Allocation:", "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); displayStatLine(pArg, "Largest Pcache Allocation:", "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); #ifdef YYTRACKMAXSTACKDEPTH displayStatLine(pArg, "Deepest Parser Stack:", "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); #endif } if( pArg && pArg->out && db ){ if( pArg->shellFlgs & SHFLG_Lookaside ){ iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, |
︙ | ︙ | |||
1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 | if( !pStmt ){ /* this happens for a comment or white-space */ zSql = zLeftover; while( IsSpace(zSql[0]) ) zSql++; continue; } zStmtSql = sqlite3_sql(pStmt); while( IsSpace(zStmtSql[0]) ) zStmtSql++; /* save off the prepared statment handle and reset row count */ if( pArg ){ pArg->pStmt = pStmt; pArg->cnt = 0; } /* echo the sql statement if echo on */ | > | | 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 | if( !pStmt ){ /* this happens for a comment or white-space */ zSql = zLeftover; while( IsSpace(zSql[0]) ) zSql++; continue; } zStmtSql = sqlite3_sql(pStmt); if( zStmtSql==0 ) zStmtSql = ""; while( IsSpace(zStmtSql[0]) ) zStmtSql++; /* save off the prepared statment handle and reset row count */ if( pArg ){ pArg->pStmt = pStmt; pArg->cnt = 0; } /* echo the sql statement if echo on */ if( pArg && ShellHasFlag(pArg, SHFLG_Echo) ){ utf8_printf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); } /* Show the EXPLAIN QUERY PLAN if .eqp is on */ if( pArg && pArg->autoEQP && sqlite3_strlike("EXPLAIN%",zStmtSql,0)!=0 ){ sqlite3_stmt *pExplain; char *zEQP; |
︙ | ︙ | |||
1989 1990 1991 1992 1993 1994 1995 | } } } /* end while */ return rc; } | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | | | < | | | | | < < < > < | | > | < > | > > > > | | < > > > > | | > | < < > | | < | | | > > > > | < < | | | | | > | | | | > | > | > > > | > | 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 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 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 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 | } } } /* end while */ return rc; } /* ** Release memory previously allocated by tableColumnList(). */ static void freeColumnList(char **azCol){ int i; for(i=1; azCol[i]; i++){ sqlite3_free(azCol[i]); } /* azCol[0] is a static string */ sqlite3_free(azCol); } /* ** Return a list of pointers to strings which are the names of all ** columns in table zTab. The memory to hold the names is dynamically ** allocated and must be released by the caller using a subsequent call ** to freeColumnList(). ** ** The azCol[0] entry is usually NULL. However, if zTab contains a rowid ** value that needs to be preserved, then azCol[0] is filled in with the ** name of the rowid column. ** ** The first regular column in the table is azCol[1]. The list is terminated ** by an entry with azCol[i]==0. */ static char **tableColumnList(ShellState *p, const char *zTab){ char **azCol = 0; sqlite3_stmt *pStmt; char *zSql; int nCol = 0; int nAlloc = 0; int nPK = 0; /* Number of PRIMARY KEY columns seen */ int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */ int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid); int rc; zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ) return 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nCol>=nAlloc-2 ){ nAlloc = nAlloc*2 + nCol + 10; azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0])); if( azCol==0 ){ raw_printf(stderr, "Error: out of memory\n"); exit(1); } } azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); if( sqlite3_column_int(pStmt, 5) ){ nPK++; if( nPK==1 && sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2), "INTEGER")==0 ){ isIPK = 1; }else{ isIPK = 0; } } } sqlite3_finalize(pStmt); azCol[0] = 0; azCol[nCol+1] = 0; /* The decision of whether or not a rowid really needs to be preserved ** is tricky. We never need to preserve a rowid for a WITHOUT ROWID table ** or a table with an INTEGER PRIMARY KEY. We are unable to preserve ** rowids on tables where the rowid is inaccessible because there are other ** columns in the table named "rowid", "_rowid_", and "oid". */ if( preserveRowid && isIPK ){ /* If a single PRIMARY KEY column with type INTEGER was seen, then it ** might be an alise for the ROWID. But it might also be a WITHOUT ROWID ** table or a INTEGER PRIMARY KEY DESC column, neither of which are ** ROWID aliases. To distinguish these cases, check to see if ** there is a "pk" entry in "PRAGMA index_list". There will be ** no "pk" index if the PRIMARY KEY really is an alias for the ROWID. */ zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)" " WHERE origin='pk'", zTab); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ freeColumnList(azCol); return 0; } rc = sqlite3_step(pStmt); sqlite3_finalize(pStmt); preserveRowid = rc==SQLITE_ROW; } if( preserveRowid ){ /* Only preserve the rowid if we can find a name to use for the ** rowid */ static char *azRowid[] = { "rowid", "_rowid_", "oid" }; int i, j; for(j=0; j<3; j++){ for(i=1; i<=nCol; i++){ if( sqlite3_stricmp(azRowid[j],azCol[i])==0 ) break; } if( i>nCol ){ /* At this point, we know that azRowid[j] is not the name of any ** ordinary column in the table. Verify that azRowid[j] is a valid ** name for the rowid before adding it to azCol[0]. WITHOUT ROWID ** tables will fail this last check */ rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0); if( rc==SQLITE_OK ) azCol[0] = azRowid[j]; break; } } } return azCol; } /* ** Toggle the reverse_unordered_selects setting. */ static void toggleSelectOrder(sqlite3 *db){ sqlite3_stmt *pStmt = 0; int iSetting = 0; char zStmt[100]; sqlite3_prepare_v2(db, "PRAGMA reverse_unordered_selects", -1, &pStmt, 0); if( sqlite3_step(pStmt)==SQLITE_ROW ){ iSetting = sqlite3_column_int(pStmt, 0); } sqlite3_finalize(pStmt); sqlite3_snprintf(sizeof(zStmt), zStmt, "PRAGMA reverse_unordered_selects(%d)", !iSetting); sqlite3_exec(db, zStmt, 0, 0, 0); } /* ** This is a different callback routine used for dumping the database. ** Each row received by this callback consists of a table name, ** the table type ("index" or "table") and SQL to create the table. ** This routine should print text sufficient to recreate the table. */ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ int rc; const char *zTable; const char *zType; const char *zSql; ShellState *p = (ShellState *)pArg; UNUSED_PARAMETER(azNotUsed); if( nArg!=3 || azArg==0 ) return 0; zTable = azArg[0]; zType = azArg[1]; zSql = azArg[2]; if( strcmp(zTable, "sqlite_sequence")==0 ){ raw_printf(p->out, "DELETE FROM sqlite_sequence;\n"); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){ raw_printf(p->out, "ANALYZE sqlite_master;\n"); }else if( strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ raw_printf(p->out, "PRAGMA writable_schema=ON;\n"); p->writableSchema = 1; } zIns = sqlite3_mprintf( "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); utf8_printf(p->out, "%s\n", zIns); sqlite3_free(zIns); return 0; }else{ printSchemaLine(p->out, zSql, ";\n"); } if( strcmp(zType, "table")==0 ){ ShellText sSelect; ShellText sTable; char **azCol; int i; char *savedDestTable; int savedMode; azCol = tableColumnList(p, zTable); if( azCol==0 ){ p->nErr++; return 0; } /* Always quote the table name, even if it appears to be pure ascii, ** in case it is a keyword. Ex: INSERT INTO "table" ... */ initText(&sTable); appendText(&sTable, zTable, quoteChar(zTable)); /* If preserving the rowid, add a column list after the table name. ** In other words: "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)" ** instead of the usual "INSERT INTO tab VALUES(...)". */ if( azCol[0] ){ appendText(&sTable, "(", 0); appendText(&sTable, azCol[0], 0); for(i=1; azCol[i]; i++){ appendText(&sTable, ",", 0); appendText(&sTable, azCol[i], quoteChar(azCol[i])); } appendText(&sTable, ")", 0); } /* Build an appropriate SELECT statement */ initText(&sSelect); appendText(&sSelect, "SELECT ", 0); if( azCol[0] ){ appendText(&sSelect, azCol[0], 0); appendText(&sSelect, ",", 0); } for(i=1; azCol[i]; i++){ appendText(&sSelect, azCol[i], quoteChar(azCol[i])); if( azCol[i+1] ){ appendText(&sSelect, ",", 0); } } freeColumnList(azCol); appendText(&sSelect, " FROM ", 0); appendText(&sSelect, zTable, quoteChar(zTable)); savedDestTable = p->zDestTable; savedMode = p->mode; p->zDestTable = sTable.z; p->mode = p->cMode = MODE_Insert; rc = shell_exec(p->db, sSelect.z, shell_callback, p, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); toggleSelectOrder(p->db); shell_exec(p->db, sSelect.z, shell_callback, p, 0); toggleSelectOrder(p->db); } p->zDestTable = savedDestTable; p->mode = savedMode; freeText(&sTable); freeText(&sSelect); if( rc ) p->nErr++; } return 0; } /* ** Run zQuery. Use dump_callback() as the callback routine so that ** the contents of the query are output as SQL statements. |
︙ | ︙ | |||
2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 | static char zHelp[] = #ifndef SQLITE_OMIT_AUTHORIZATION ".auth ON|OFF Show authorizer callbacks\n" #endif ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" ".bail on|off Stop after hitting an error. Default OFF\n" ".binary on|off Turn binary output on or off. Default OFF\n" ".changes on|off Show number of rows changed by SQL\n" ".check GLOB Fail if output since .testcase does not match\n" ".clone NEWDB Clone data into NEWDB from the existing database\n" ".databases List names and files of attached databases\n" ".dbinfo ?DB? Show status information about the database\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" " If TABLE specified, only dump tables matching\n" " LIKE pattern TABLE.\n" ".echo on|off Turn command echo on or off\n" ".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN\n" ".exit Exit this program\n" | > > > | > > > > > | > | | > > | 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 | static char zHelp[] = #ifndef SQLITE_OMIT_AUTHORIZATION ".auth ON|OFF Show authorizer callbacks\n" #endif ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" ".bail on|off Stop after hitting an error. Default OFF\n" ".binary on|off Turn binary output on or off. Default OFF\n" ".cd DIRECTORY Change the working directory to DIRECTORY\n" ".changes on|off Show number of rows changed by SQL\n" ".check GLOB Fail if output since .testcase does not match\n" ".clone NEWDB Clone data into NEWDB from the existing database\n" ".databases List names and files of attached databases\n" ".dbinfo ?DB? Show status information about the database\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" " If TABLE specified, only dump tables matching\n" " LIKE pattern TABLE.\n" ".echo on|off Turn command echo on or off\n" ".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN\n" ".exit Exit this program\n" /* Because explain mode comes on automatically now, the ".explain" mode ** is removed from the help screen. It is still supported for legacy, however */ /*".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic\n"*/ ".fullschema ?--indent? Show schema and the content of sqlite_stat tables\n" ".headers on|off Turn display of headers on or off\n" ".help Show this message\n" ".import FILE TABLE Import data from FILE into TABLE\n" #ifndef SQLITE_OMIT_TEST_CONTROL ".imposter INDEX TABLE Create imposter table TABLE on index INDEX\n" #endif ".indexes ?TABLE? Show names of all indexes\n" " If TABLE specified, only show indexes for tables\n" " matching LIKE pattern TABLE.\n" #ifdef SQLITE_ENABLE_IOTRACE ".iotrace FILE Enable I/O diagnostic logging to FILE\n" #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT\n" ".lint OPTIONS Report potential schema issues. Options:\n" " fkey-indexes Find missing foreign key indexes\n" #ifndef SQLITE_OMIT_LOAD_EXTENSION ".load FILE ?ENTRY? Load an extension library\n" #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" " ascii Columns/rows delimited by 0x1F and 0x1E\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" " html HTML <table> code\n" " insert SQL insert statements for TABLE\n" " line One value per line\n" " list Values delimited by \"|\"\n" " quote Escape answers as for SQL\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" ".nullvalue STRING Use STRING in place of NULL values\n" ".once FILENAME Output for the next SQL command only to FILENAME\n" ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE\n" " The --new option starts with an empty file\n" ".output ?FILENAME? Send output to FILENAME or stdout\n" ".print STRING... Print literal STRING\n" ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".save FILE Write in-memory database into FILE\n" ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n" ".schema ?PATTERN? Show the CREATE statements matching PATTERN\n" " Add --indent for pretty-printing\n" ".selftest ?--init? Run tests defined in the SELFTEST table\n" ".separator COL ?ROW? Change the column separator and optionally the row\n" " separator for both the output mode and .import\n" #if defined(SQLITE_ENABLE_SESSION) ".session CMD ... Create or control sessions\n" #endif ".sha3sum ?OPTIONS...? Compute a SHA3 hash of database content\n" ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" ".show Show the current values for various settings\n" ".stats ?on|off? Show stats or turn stats on or off\n" ".system CMD ARGS... Run CMD ARGS... in a system shell\n" ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" |
︙ | ︙ | |||
2239 2240 2241 2242 2243 2244 2245 | } #endif /* Forward reference */ static int process_input(ShellState *p, FILE *in); | < | | > > > > > > > > | > | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 | } #endif /* Forward reference */ static int process_input(ShellState *p, FILE *in); /* ** Read the content of file zName into memory obtained from sqlite3_malloc64() ** and return a pointer to the buffer. The caller is responsible for freeing ** the memory. ** ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes ** read. ** ** For convenience, a nul-terminator byte is always appended to the data read ** from the file before the buffer is returned. This byte is not included in ** the final value of (*pnByte), if applicable. ** ** NULL is returned if any error is encountered. The final value of *pnByte ** is undefined in this case. */ static char *readFile(const char *zName, int *pnByte){ FILE *in = fopen(zName, "rb"); long nIn; size_t nRead; char *pBuf; if( in==0 ) return 0; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); pBuf = sqlite3_malloc64( nIn+1 ); if( pBuf==0 ) return 0; nRead = fread(pBuf, nIn, 1, in); fclose(in); if( nRead!=1 ){ sqlite3_free(pBuf); return 0; } pBuf[nIn] = 0; if( pnByte ) *pnByte = nIn; return pBuf; } #if defined(SQLITE_ENABLE_SESSION) /* ** Close a single OpenSession object and release all of its associated ** resources. */ static void session_close(OpenSession *pSession){ int i; |
︙ | ︙ | |||
2374 2375 2376 2377 2378 2379 2380 | ** the database fails to open, print an error message and exit. */ static void open_db(ShellState *p, int keepAlive){ if( p->db==0 ){ sqlite3_initialize(); sqlite3_open(p->zDbFilename, &p->db); globalDb = p->db; | < < < < > > > | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > | 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 | ** the database fails to open, print an error message and exit. */ static void open_db(ShellState *p, int keepAlive){ if( p->db==0 ){ sqlite3_initialize(); sqlite3_open(p->zDbFilename, &p->db); globalDb = p->db; if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", p->zDbFilename, sqlite3_errmsg(p->db)); if( keepAlive ) return; exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif sqlite3_fileio_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0); sqlite3_completion_init(p->db, 0, 0); sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0, shellAddSchemaName, 0, 0); } } #if HAVE_READLINE || HAVE_EDITLINE /* ** Readline completion callbacks */ static char *readline_completion_generator(const char *text, int state){ static sqlite3_stmt *pStmt = 0; char *zRet; if( state==0 ){ char *zSql; sqlite3_finalize(pStmt); zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" " FROM completion(%Q) ORDER BY 1", text); sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0); sqlite3_free(zSql); } if( sqlite3_step(pStmt)==SQLITE_ROW ){ zRet = strdup((const char*)sqlite3_column_text(pStmt, 0)); }else{ sqlite3_finalize(pStmt); pStmt = 0; zRet = 0; } return zRet; } static char **readline_completion(const char *zText, int iStart, int iEnd){ rl_attempted_completion_over = 1; return rl_completion_matches(zText, readline_completion_generator); } #elif HAVE_LINENOISE /* ** Linenoise completion callback */ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){ int nLine = (int)strlen(zLine); int i, iStart; sqlite3_stmt *pStmt = 0; char *zSql; char zBuf[1000]; if( nLine>sizeof(zBuf)-30 ) return; if( zLine[0]=='.' ) return; for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} if( i==nLine-1 ) return; iStart = i+1; memcpy(zBuf, zLine, iStart); zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" " FROM completion(%Q,%Q) ORDER BY 1", &zLine[iStart], zLine); sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0); sqlite3_free(zSql); sqlite3_exec(globalDb, "PRAGMA page_count", 0, 0, 0); /* Load the schema */ while( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zCompletion = (const char*)sqlite3_column_text(pStmt, 0); int nCompletion = sqlite3_column_bytes(pStmt, 0); if( iStart+nCompletion < sizeof(zBuf)-1 ){ memcpy(zBuf+iStart, zCompletion, nCompletion+1); linenoiseAddCompletion(lc, zBuf); } } sqlite3_finalize(pStmt); } #endif /* ** Do C-language style dequoting. ** ** \a -> alarm ** \b -> backspace ** \t -> tab |
︙ | ︙ | |||
2515 2516 2517 2518 2519 2520 2521 | return isNeg? -v : v; } /* ** Interpret zArg as either an integer or a boolean value. Return 1 or 0 ** for TRUE and FALSE. Return the integer value if appropriate. */ | | > > > > > > > > > > > | 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 | return isNeg? -v : v; } /* ** Interpret zArg as either an integer or a boolean value. Return 1 or 0 ** for TRUE and FALSE. Return the integer value if appropriate. */ static int booleanValue(const char *zArg){ int i; if( zArg[0]=='0' && zArg[1]=='x' ){ for(i=2; hexDigitValue(zArg[i])>=0; i++){} }else{ for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){} } if( i>0 && zArg[i]==0 ) return (int)(integerValue(zArg) & 0xffffffff); if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){ return 1; } if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ return 0; } utf8_printf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); return 0; } /* ** Set or clear a shell flag according to a boolean value. */ static void setOrClearFlag(ShellState *p, unsigned mFlag, const char *zArg){ if( booleanValue(zArg) ){ ShellSetFlag(p, mFlag); }else{ ShellClearFlag(p, mFlag); } } /* ** Close an output file, assuming it is not stderr or stdout */ static void output_file_close(FILE *f){ if( f && f!=stdout && f!=stderr ) fclose(f); } |
︙ | ︙ | |||
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 | if( f==0 ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); } } return f; } /* ** A routine for handling output from sqlite3_trace(). */ static int sql_trace_callback( unsigned mType, void *pArg, void *pP, void *pX ){ FILE *f = (FILE*)pArg; UNUSED_PARAMETER(mType); UNUSED_PARAMETER(pP); if( f ){ const char *z = (const char*)pX; int i = (int)strlen(z); while( i>0 && z[i-1]==';' ){ i--; } utf8_printf(f, "%.*s;\n", i, z); } return 0; } /* ** A no-op routine that runs with the ".breakpoint" doc-command. This is ** a useful spot to set a debugger breakpoint. */ static void test_breakpoint(void){ static int nCall = 0; | > > > > | 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 | if( f==0 ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); } } return f; } #if !defined(SQLITE_UNTESTABLE) #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) /* ** A routine for handling output from sqlite3_trace(). */ static int sql_trace_callback( unsigned mType, void *pArg, void *pP, void *pX ){ FILE *f = (FILE*)pArg; UNUSED_PARAMETER(mType); UNUSED_PARAMETER(pP); if( f ){ const char *z = (const char*)pX; int i = (int)strlen(z); while( i>0 && z[i-1]==';' ){ i--; } utf8_printf(f, "%.*s;\n", i, z); } return 0; } #endif #endif /* ** A no-op routine that runs with the ".breakpoint" doc-command. This is ** a useful spot to set a debugger breakpoint. */ static void test_breakpoint(void){ static int nCall = 0; |
︙ | ︙ | |||
2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 | struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int cTerm; /* Character that terminated the most recent field */ int cColSep; /* The column separator character. (Usually ",") */ int cRowSep; /* The row separator character. (Usually "\n") */ }; /* Append a single byte to z[] */ static void import_append_char(ImportCtx *p, int c){ | > | 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 | struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int bNotFirst; /* True if one or more bytes already read */ int cTerm; /* Character that terminated the most recent field */ int cColSep; /* The column separator character. (Usually ",") */ int cRowSep; /* The row separator character. (Usually "\n") */ }; /* Append a single byte to z[] */ static void import_append_char(ImportCtx *p, int c){ |
︙ | ︙ | |||
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 | break; } import_append_char(p, c); ppc = pc; pc = c; } }else{ while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); c = fgetc(p->in); } if( c==rSep ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } p->cTerm = c; } if( p->z ) p->z[p->n] = 0; return p->z; } /* Read a single field of ASCII delimited text. ** ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes | > > > > > > > > > > > > > > > > | 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 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 | break; } import_append_char(p, c); ppc = pc; pc = c; } }else{ /* If this is the first field being parsed and it begins with the ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ if( (c&0xff)==0xef && p->bNotFirst==0 ){ import_append_char(p, c); c = fgetc(p->in); if( (c&0xff)==0xbb ){ import_append_char(p, c); c = fgetc(p->in); if( (c&0xff)==0xbf ){ p->bNotFirst = 1; p->n = 0; return csv_read_one_field(p); } } } while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); c = fgetc(p->in); } if( c==rSep ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } p->cTerm = c; } if( p->z ) p->z[p->n] = 0; p->bNotFirst = 1; return p->z; } /* Read a single field of ASCII delimited text. ** ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes |
︙ | ︙ | |||
3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 | rc = _wunlink(z); sqlite3_free(z); #else rc = unlink(zFilename); #endif return rc; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 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 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 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 | rc = _wunlink(z); sqlite3_free(z); #else rc = unlink(zFilename); #endif return rc; } /* ** The implementation of SQL scalar function fkey_collate_clause(), used ** by the ".lint fkey-indexes" command. This scalar function is always ** called with four arguments - the parent table name, the parent column name, ** the child table name and the child column name. ** ** fkey_collate_clause('parent-tab', 'parent-col', 'child-tab', 'child-col') ** ** If either of the named tables or columns do not exist, this function ** returns an empty string. An empty string is also returned if both tables ** and columns exist but have the same default collation sequence. Or, ** if both exist but the default collation sequences are different, this ** function returns the string " COLLATE <parent-collation>", where ** <parent-collation> is the default collation sequence of the parent column. */ static void shellFkeyCollateClause( sqlite3_context *pCtx, int nVal, sqlite3_value **apVal ){ sqlite3 *db = sqlite3_context_db_handle(pCtx); const char *zParent; const char *zParentCol; const char *zParentSeq; const char *zChild; const char *zChildCol; const char *zChildSeq = 0; /* Initialize to avoid false-positive warning */ int rc; assert( nVal==4 ); zParent = (const char*)sqlite3_value_text(apVal[0]); zParentCol = (const char*)sqlite3_value_text(apVal[1]); zChild = (const char*)sqlite3_value_text(apVal[2]); zChildCol = (const char*)sqlite3_value_text(apVal[3]); sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); rc = sqlite3_table_column_metadata( db, "main", zParent, zParentCol, 0, &zParentSeq, 0, 0, 0 ); if( rc==SQLITE_OK ){ rc = sqlite3_table_column_metadata( db, "main", zChild, zChildCol, 0, &zChildSeq, 0, 0, 0 ); } if( rc==SQLITE_OK && sqlite3_stricmp(zParentSeq, zChildSeq) ){ char *z = sqlite3_mprintf(" COLLATE %s", zParentSeq); sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); sqlite3_free(z); } } /* ** The implementation of dot-command ".lint fkey-indexes". */ static int lintFkeyIndexes( ShellState *pState, /* Current shell tool state */ char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ sqlite3 *db = pState->db; /* Database handle to query "main" db of */ FILE *out = pState->out; /* Stream to write non-error output to */ int bVerbose = 0; /* If -verbose is present */ int bGroupByParent = 0; /* If -groupbyparent is present */ int i; /* To iterate through azArg[] */ const char *zIndent = ""; /* How much to indent CREATE INDEX by */ int rc; /* Return code */ sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ /* ** This SELECT statement returns one row for each foreign key constraint ** in the schema of the main database. The column values are: ** ** 0. The text of an SQL statement similar to: ** ** "EXPLAIN QUERY PLAN SELECT rowid FROM child_table WHERE child_key=?" ** ** This is the same SELECT that the foreign keys implementation needs ** to run internally on child tables. If there is an index that can ** be used to optimize this query, then it can also be used by the FK ** implementation to optimize DELETE or UPDATE statements on the parent ** table. ** ** 1. A GLOB pattern suitable for sqlite3_strglob(). If the plan output by ** the EXPLAIN QUERY PLAN command matches this pattern, then the schema ** contains an index that can be used to optimize the query. ** ** 2. Human readable text that describes the child table and columns. e.g. ** ** "child_table(child_key1, child_key2)" ** ** 3. Human readable text that describes the parent table and columns. e.g. ** ** "parent_table(parent_key1, parent_key2)" ** ** 4. A full CREATE INDEX statement for an index that could be used to ** optimize DELETE or UPDATE statements on the parent table. e.g. ** ** "CREATE INDEX child_table_child_key ON child_table(child_key)" ** ** 5. The name of the parent table. ** ** These six values are used by the C logic below to generate the report. */ const char *zSql = "SELECT " " 'EXPLAIN QUERY PLAN SELECT rowid FROM ' || quote(s.name) || ' WHERE '" " || group_concat(quote(s.name) || '.' || quote(f.[from]) || '=?' " " || fkey_collate_clause(" " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]),' AND ')" ", " " 'SEARCH TABLE ' || s.name || ' USING COVERING INDEX*('" " || group_concat('*=?', ' AND ') || ')'" ", " " s.name || '(' || group_concat(f.[from], ', ') || ')'" ", " " f.[table] || '(' || group_concat(COALESCE(f.[to], p.[name])) || ')'" ", " " 'CREATE INDEX ' || quote(s.name ||'_'|| group_concat(f.[from], '_'))" " || ' ON ' || quote(s.name) || '('" " || group_concat(quote(f.[from]) ||" " fkey_collate_clause(" " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]), ', ')" " || ');'" ", " " f.[table] " "FROM sqlite_master AS s, pragma_foreign_key_list(s.name) AS f " "LEFT JOIN pragma_table_info AS p ON (pk-1=seq AND p.arg=f.[table]) " "GROUP BY s.name, f.id " "ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)" ; const char *zGlobIPK = "SEARCH TABLE * USING INTEGER PRIMARY KEY (rowid=?)"; for(i=2; i<nArg; i++){ int n = (int)strlen(azArg[i]); if( n>1 && sqlite3_strnicmp("-verbose", azArg[i], n)==0 ){ bVerbose = 1; } else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ bGroupByParent = 1; zIndent = " "; } else{ raw_printf(stderr, "Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1] ); return SQLITE_ERROR; } } /* Register the fkey_collate_clause() SQL function */ rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8, 0, shellFkeyCollateClause, 0, 0 ); if( rc==SQLITE_OK ){ rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0); } if( rc==SQLITE_OK ){ sqlite3_bind_int(pSql, 1, bGroupByParent); } if( rc==SQLITE_OK ){ int rc2; char *zPrev = 0; while( SQLITE_ROW==sqlite3_step(pSql) ){ int res = -1; sqlite3_stmt *pExplain = 0; const char *zEQP = (const char*)sqlite3_column_text(pSql, 0); const char *zGlob = (const char*)sqlite3_column_text(pSql, 1); const char *zFrom = (const char*)sqlite3_column_text(pSql, 2); const char *zTarget = (const char*)sqlite3_column_text(pSql, 3); const char *zCI = (const char*)sqlite3_column_text(pSql, 4); const char *zParent = (const char*)sqlite3_column_text(pSql, 5); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc!=SQLITE_OK ) break; if( SQLITE_ROW==sqlite3_step(pExplain) ){ const char *zPlan = (const char*)sqlite3_column_text(pExplain, 3); res = ( 0==sqlite3_strglob(zGlob, zPlan) || 0==sqlite3_strglob(zGlobIPK, zPlan) ); } rc = sqlite3_finalize(pExplain); if( rc!=SQLITE_OK ) break; if( res<0 ){ raw_printf(stderr, "Error: internal error"); break; }else{ if( bGroupByParent && (bVerbose || res==0) && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) ){ raw_printf(out, "-- Parent table %s\n", zParent); sqlite3_free(zPrev); zPrev = sqlite3_mprintf("%s", zParent); } if( res==0 ){ raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); }else if( bVerbose ){ raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n", zIndent, zFrom, zTarget ); } } } sqlite3_free(zPrev); if( rc!=SQLITE_OK ){ raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); } rc2 = sqlite3_finalize(pSql); if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ rc = rc2; raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); } }else{ raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); } return rc; } /* ** Implementation of ".lint" dot command. */ static int lintDotCommand( ShellState *pState, /* Current shell tool state */ char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ int n; n = (nArg>=2 ? (int)strlen(azArg[1]) : 0); if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage; return lintFkeyIndexes(pState, azArg, nArg); usage: raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]); raw_printf(stderr, "Where sub-commands are:\n"); raw_printf(stderr, " fkey-indexes\n"); return SQLITE_ERROR; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ |
︙ | ︙ | |||
3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 | setTextMode(p->out, 1); } }else{ raw_printf(stderr, "Usage: .binary on|off\n"); rc = 1; } }else /* The undocumented ".breakpoint" command causes a call to the no-op ** routine named test_breakpoint(). */ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ test_breakpoint(); }else if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){ if( nArg==2 ){ | > > > > > > > > > > > > > > > > > > > | | | 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 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 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 | setTextMode(p->out, 1); } }else{ raw_printf(stderr, "Usage: .binary on|off\n"); rc = 1; } }else if( c=='c' && strcmp(azArg[0],"cd")==0 ){ if( nArg==2 ){ #if defined(_WIN32) || defined(WIN32) wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]); rc = !SetCurrentDirectoryW(z); sqlite3_free(z); #else rc = chdir(azArg[1]); #endif if( rc ){ utf8_printf(stderr, "Cannot change to directory \"%s\"\n", azArg[1]); rc = 1; } }else{ raw_printf(stderr, "Usage: .cd DIRECTORY\n"); rc = 1; } }else /* The undocumented ".breakpoint" command causes a call to the no-op ** routine named test_breakpoint(). */ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ test_breakpoint(); }else if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_CountChanges, azArg[1]); }else{ raw_printf(stderr, "Usage: .changes on|off\n"); rc = 1; } }else /* Cancel output redirection, if it is currently set (by .testcase) ** Then read the content of the testcase-out.txt file and compare against ** azArg[1]. If there are differences, report an error and exit. */ if( c=='c' && n>=3 && strncmp(azArg[0], "check", n)==0 ){ char *zRes = 0; output_reset(p); if( nArg!=2 ){ raw_printf(stderr, "Usage: .check GLOB-PATTERN\n"); rc = 2; }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ raw_printf(stderr, "Error: cannot read 'testcase-out.txt'\n"); rc = 2; }else if( testcase_glob(azArg[1],zRes)==0 ){ utf8_printf(stderr, "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", p->zTestcase, azArg[1], zRes); rc = 2; |
︙ | ︙ | |||
3402 3403 3404 3405 3406 3407 3408 | }else if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){ ShellState data; char *zErrMsg = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); | | | | < < > | | | | > > > | > > > > > > > > > > > > > > > > > > > | | > | | > > | > > > > > > > > > | | < | < | | | | > > | | | | | | < > | | 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 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 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 | }else if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){ ShellState data; char *zErrMsg = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.cMode = data.mode = MODE_List; sqlite3_snprintf(sizeof(data.colSeparator),data.colSeparator,": "); data.cnt = 0; sqlite3_exec(p->db, "SELECT name, file FROM pragma_database_list", callback, &data, &zErrMsg); if( zErrMsg ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else if( c=='d' && strncmp(azArg[0], "dbinfo", n)==0 ){ rc = shell_dbinfo_command(p, nArg, azArg); }else if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ const char *zLike = 0; int i; int savedShowHeader = p->showHeader; ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines); for(i=1; i<nArg; i++){ if( azArg[i][0]=='-' ){ const char *z = azArg[i]+1; if( z[0]=='-' ) z++; if( strcmp(z,"preserve-rowids")==0 ){ #ifdef SQLITE_OMIT_VIRTUALTABLE raw_printf(stderr, "The --preserve-rowids option is not compatible" " with SQLITE_OMIT_VIRTUALTABLE\n"); rc = 1; goto meta_command_exit; #else ShellSetFlag(p, SHFLG_PreserveRowid); #endif }else if( strcmp(z,"newlines")==0 ){ ShellSetFlag(p, SHFLG_Newlines); }else { raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); rc = 1; goto meta_command_exit; } }else if( zLike ){ raw_printf(stderr, "Usage: .dump ?--preserve-rowids? " "?--newlines? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; }else{ zLike = azArg[i]; } } open_db(p, 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. */ raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); raw_printf(p->out, "BEGIN TRANSACTION;\n"); p->writableSchema = 0; p->showHeader = 0; /* Set writable_schema=ON since doing so forces SQLite to initialize ** as much of the schema as it can even if the sqlite_master table is ** corrupt. */ sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); p->nErr = 0; if( zLike==0 ){ run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'" ); run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE name=='sqlite_sequence'" ); run_table_dump_query(p, "SELECT sql FROM sqlite_master " "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 ); }else{ char *zSql; zSql = sqlite3_mprintf( "SELECT name, type, sql FROM sqlite_master " "WHERE tbl_name LIKE %Q AND type=='table'" " AND sql NOT NULL", zLike); run_schema_dump_query(p,zSql); sqlite3_free(zSql); zSql = sqlite3_mprintf( "SELECT sql FROM sqlite_master " "WHERE sql NOT NULL" " AND type IN ('index','trigger','view')" " AND tbl_name LIKE %Q", zLike); run_table_dump_query(p, zSql, 0); sqlite3_free(zSql); } if( p->writableSchema ){ raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); raw_printf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n"); p->showHeader = savedShowHeader; }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_Echo, azArg[1]); }else{ raw_printf(stderr, "Usage: .echo on|off\n"); rc = 1; } }else if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){ |
︙ | ︙ | |||
3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 | }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); rc = 2; }else if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ int val = 1; if( nArg>=2 ){ if( strcmp(azArg[1],"auto")==0 ){ val = 99; }else{ val = booleanValue(azArg[1]); | > > | 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 | }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); rc = 2; }else /* The ".explain" command is automatic now. It is largely pointless. It ** retained purely for backwards compatibility */ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ int val = 1; if( nArg>=2 ){ if( strcmp(azArg[1],"auto")==0 ){ val = 99; }else{ val = booleanValue(azArg[1]); |
︙ | ︙ | |||
3789 3790 3791 3792 3793 3794 3795 | xCloser(sCtx.in); sqlite3_free(sCtx.z); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); }else | | | < | | < > | < > | > | | < > | | | | < | | < | > | < < < < < < | < < | > > > > > > > > | > > > > > > > > > > > > > > | > > | > > > > | > | > | < > > > > > > > | 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 | xCloser(sCtx.in); sqlite3_free(sCtx.z); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); }else #ifndef SQLITE_UNTESTABLE if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){ char *zSql; char *zCollist = 0; sqlite3_stmt *pStmt; int tnum = 0; int i; if( nArg!=3 ){ utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master" " WHERE name='%q' AND type='index'", azArg[1]); sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( sqlite3_step(pStmt)==SQLITE_ROW ){ tnum = sqlite3_column_int(pStmt, 0); } sqlite3_finalize(pStmt); if( tnum==0 ){ utf8_printf(stderr, "no such index: \"%s\"\n", azArg[1]); rc = 1; goto meta_command_exit; } zSql = sqlite3_mprintf("PRAGMA index_xinfo='%q'", azArg[1]); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); i = 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ char zLabel[20]; const char *zCol = (const char*)sqlite3_column_text(pStmt,2); i++; if( zCol==0 ){ if( sqlite3_column_int(pStmt,1)==-1 ){ zCol = "_ROWID_"; }else{ sqlite3_snprintf(sizeof(zLabel),zLabel,"expr%d",i); zCol = zLabel; } } if( zCollist==0 ){ zCollist = sqlite3_mprintf("\"%w\"", zCol); }else{ zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol); } } sqlite3_finalize(pStmt); zSql = sqlite3_mprintf( "CREATE TABLE \"%w\"(%s,PRIMARY KEY(%s))WITHOUT ROWID", azArg[2], zCollist, zCollist); sqlite3_free(zCollist); rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum); if( rc==SQLITE_OK ){ rc = sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); if( rc ){ utf8_printf(stderr, "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); }else{ utf8_printf(stdout, "%s;\n", zSql); raw_printf(stdout, "WARNING: writing to an imposter table will corrupt the index!\n" ); } }else{ raw_printf(stderr, "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' && 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 ){ |
︙ | ︙ | |||
3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 | rc = 1; }else{ sqlite3IoTrace = iotracePrintf; } } }else #endif if( c=='l' && n>=5 && strncmp(azArg[0], "limits", n)==0 ){ static const struct { const char *zLimitName; /* Name of a limit */ int limitCode; /* Integer code for that limit */ } aLimit[] = { { "length", SQLITE_LIMIT_LENGTH }, { "sql_length", SQLITE_LIMIT_SQL_LENGTH }, | > | 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 | rc = 1; }else{ sqlite3IoTrace = iotracePrintf; } } }else #endif if( c=='l' && n>=5 && strncmp(azArg[0], "limits", n)==0 ){ static const struct { const char *zLimitName; /* Name of a limit */ int limitCode; /* Integer code for that limit */ } aLimit[] = { { "length", SQLITE_LIMIT_LENGTH }, { "sql_length", SQLITE_LIMIT_SQL_LENGTH }, |
︙ | ︙ | |||
3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, (int)integerValue(azArg[2])); } printf("%20s %d\n", aLimit[iLimit].zLimitName, sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); } }else #ifndef SQLITE_OMIT_LOAD_EXTENSION if( c=='l' && strncmp(azArg[0], "load", n)==0 ){ const char *zFile, *zProc; char *zErrMsg = 0; if( nArg<2 ){ raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); | > > > > > | 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, (int)integerValue(azArg[2])); } printf("%20s %d\n", aLimit[iLimit].zLimitName, sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); } }else if( c=='l' && n>2 && strncmp(azArg[0], "lint", n)==0 ){ open_db(p, 0); lintDotCommand(p, azArg, nArg); }else #ifndef SQLITE_OMIT_LOAD_EXTENSION if( c=='l' && strncmp(azArg[0], "load", n)==0 ){ const char *zFile, *zProc; char *zErrMsg = 0; if( nArg<2 ){ raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); |
︙ | ︙ | |||
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 | if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ const char *zMode = nArg>=2 ? azArg[1] : ""; int n2 = (int)strlen(zMode); int c2 = zMode[0]; if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){ p->mode = MODE_Line; }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){ p->mode = MODE_Column; }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){ p->mode = MODE_List; }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ p->mode = MODE_Html; }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, nArg>=3 ? azArg[2] : "table"); }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ p->mode = MODE_Ascii; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); | > > > > > > > > > | | | 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 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 | if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ const char *zMode = nArg>=2 ? azArg[1] : ""; int n2 = (int)strlen(zMode); int c2 = zMode[0]; if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){ p->mode = MODE_Line; sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){ p->mode = MODE_Column; sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ p->mode = MODE_Html; }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, nArg>=3 ? azArg[2] : "table"); }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){ p->mode = MODE_Quote; }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ p->mode = MODE_Ascii; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); }else if( nArg==1 ){ raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); }else{ raw_printf(stderr, "Error: mode should be one of: " "ascii column csv html insert line list quote tabs tcl\n"); rc = 1; } p->cMode = p->mode; }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ |
︙ | ︙ | |||
4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 | char *zNewFilename; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ /* Close the existing database */ session_close_all(p); sqlite3_close(p->db); p->db = 0; sqlite3_free(p->zFreeOnClose); p->zFreeOnClose = 0; /* Check for command-line arguments */ for(iName=1; iName<nArg && azArg[iName][0]=='-'; iName++){ const char *z = azArg[iName]; if( optionMatch(z,"new") ){ newFlag = 1; | > | 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 | char *zNewFilename; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ /* Close the existing database */ session_close_all(p); sqlite3_close(p->db); p->db = 0; p->zDbFilename = 0; sqlite3_free(p->zFreeOnClose); p->zFreeOnClose = 0; /* Check for command-line arguments */ for(iName=1; iName<nArg && azArg[iName][0]=='-'; iName++){ const char *z = azArg[iName]; if( optionMatch(z,"new") ){ newFlag = 1; |
︙ | ︙ | |||
4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 | }else{ raw_printf(stderr, "Usage: .scanstats on|off\n"); rc = 1; } }else if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ ShellState data; char *zErrMsg = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.cMode = data.mode = MODE_Semi; if( nArg>=2 && optionMatch(azArg[1], "indent") ){ data.cMode = data.mode = MODE_Pretty; nArg--; if( nArg==2 ) azArg[1] = azArg[2]; } if( nArg==2 && azArg[1][0]!='-' ){ int i; | > > > > > | 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 | }else{ raw_printf(stderr, "Usage: .scanstats on|off\n"); rc = 1; } }else if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ ShellText sSelect; ShellState data; char *zErrMsg = 0; const char *zDiv = 0; int iSchema = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.cMode = data.mode = MODE_Semi; initText(&sSelect); if( nArg>=2 && optionMatch(azArg[1], "indent") ){ data.cMode = data.mode = MODE_Pretty; nArg--; if( nArg==2 ) azArg[1] = azArg[2]; } if( nArg==2 && azArg[1][0]!='-' ){ int i; |
︙ | ︙ | |||
4232 4233 4234 4235 4236 4237 4238 | ")"; new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; callback(&data, 1, new_argv, new_colv); rc = SQLITE_OK; }else{ | < < < < < < < < < < | | < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 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 | ")"; new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; callback(&data, 1, new_argv, new_colv); rc = SQLITE_OK; }else{ zDiv = "("; } }else if( nArg==1 ){ zDiv = "("; }else{ raw_printf(stderr, "Usage: .schema ?--indent? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } if( zDiv ){ sqlite3_stmt *pStmt = 0; rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", -1, &pStmt, 0); if( rc ){ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); rc = 1; goto meta_command_exit; } appendText(&sSelect, "SELECT sql FROM", 0); iSchema = 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); char zScNum[30]; sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); appendText(&sSelect, zDiv, 0); zDiv = " UNION ALL "; if( strcmp(zDb, "main")!=0 ){ appendText(&sSelect, "SELECT shell_add_schema(sql,", 0); appendText(&sSelect, zDb, '"'); appendText(&sSelect, ") AS sql, type, tbl_name, name, rowid,", 0); appendText(&sSelect, zScNum, 0); appendText(&sSelect, " AS snum, ", 0); appendText(&sSelect, zDb, '\''); appendText(&sSelect, " AS sname FROM ", 0); appendText(&sSelect, zDb, '"'); appendText(&sSelect, ".sqlite_master", 0); }else{ appendText(&sSelect, "SELECT sql, type, tbl_name, name, rowid, ", 0); appendText(&sSelect, zScNum, 0); appendText(&sSelect, " AS snum, 'main' AS sname FROM sqlite_master",0); } } sqlite3_finalize(pStmt); appendText(&sSelect, ") WHERE ", 0); if( nArg>1 ){ char *zQarg = sqlite3_mprintf("%Q", azArg[1]); if( strchr(azArg[1], '.') ){ appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0); }else{ appendText(&sSelect, "lower(tbl_name)", 0); } appendText(&sSelect, strchr(azArg[1], '*') ? " GLOB " : " LIKE ", 0); appendText(&sSelect, zQarg, 0); appendText(&sSelect, " AND ", 0); sqlite3_free(zQarg); } appendText(&sSelect, "type!='meta' AND sql IS NOT NULL" " ORDER BY snum, rowid", 0); rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); freeText(&sSelect); } if( zErrMsg ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ raw_printf(stderr,"Error: querying schema information\n"); rc = 1; }else{ rc = 0; } }else #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ sqlite3SelectTrace = (int)integerValue(azArg[1]); }else #endif #if defined(SQLITE_ENABLE_SESSION) if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){ OpenSession *pSession = &p->aSession[0]; char **azCmd = &azArg[1]; |
︙ | ︙ | |||
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 | v = integerValue(azArg[i]); sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); utf8_printf(p->out, "%s", zBuf); } } }else #endif if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ raw_printf(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); } if( nArg>=3 ){ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); } }else if( c=='s' && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) ){ char *zCmd; int i, x; if( nArg<2 ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 | v = integerValue(azArg[i]); sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); utf8_printf(p->out, "%s", zBuf); } } }else #endif if( c=='s' && n>=4 && strncmp(azArg[0],"selftest",n)==0 ){ int bIsInit = 0; /* True to initialize the SELFTEST table */ int bVerbose = 0; /* Verbose output */ int bSelftestExists; /* True if SELFTEST already exists */ int i, k; /* Loop counters */ int nTest = 0; /* Number of tests runs */ int nErr = 0; /* Number of errors seen */ ShellText str; /* Answer for a query */ sqlite3_stmt *pStmt = 0; /* Query against the SELFTEST table */ open_db(p,0); for(i=1; i<nArg; i++){ const char *z = azArg[i]; if( z[0]=='-' && z[1]=='-' ) z++; if( strcmp(z,"-init")==0 ){ bIsInit = 1; }else if( strcmp(z,"-v")==0 ){ bVerbose++; }else { utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); raw_printf(stderr, "Should be one of: --init -v\n"); rc = 1; goto meta_command_exit; } } if( sqlite3_table_column_metadata(p->db,"main","selftest",0,0,0,0,0,0) != SQLITE_OK ){ bSelftestExists = 0; }else{ bSelftestExists = 1; } if( bIsInit ){ createSelftestTable(p); bSelftestExists = 1; } initText(&str); appendText(&str, "x", 0); for(k=bSelftestExists; k>=0; k--){ if( k==1 ){ rc = sqlite3_prepare_v2(p->db, "SELECT tno,op,cmd,ans FROM selftest ORDER BY tno", -1, &pStmt, 0); }else{ rc = sqlite3_prepare_v2(p->db, "VALUES(0,'memo','Missing SELFTEST table - default checks only','')," " (1,'run','PRAGMA integrity_check','ok')", -1, &pStmt, 0); } if( rc ){ raw_printf(stderr, "Error querying the selftest table\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; } for(i=1; sqlite3_step(pStmt)==SQLITE_ROW; i++){ int tno = sqlite3_column_int(pStmt, 0); const char *zOp = (const char*)sqlite3_column_text(pStmt, 1); const char *zSql = (const char*)sqlite3_column_text(pStmt, 2); const char *zAns = (const char*)sqlite3_column_text(pStmt, 3); k = 0; if( bVerbose>0 ){ char *zQuote = sqlite3_mprintf("%q", zSql); printf("%d: %s %s\n", tno, zOp, zSql); sqlite3_free(zQuote); } if( strcmp(zOp,"memo")==0 ){ utf8_printf(p->out, "%s\n", zSql); }else if( strcmp(zOp,"run")==0 ){ char *zErrMsg = 0; str.n = 0; str.z[0] = 0; rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); nTest++; if( bVerbose ){ utf8_printf(p->out, "Result: %s\n", str.z); } if( rc || zErrMsg ){ nErr++; rc = 1; utf8_printf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); sqlite3_free(zErrMsg); }else if( strcmp(zAns,str.z)!=0 ){ nErr++; rc = 1; utf8_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); utf8_printf(p->out, "%d: Got: [%s]\n", tno, str.z); } }else { utf8_printf(stderr, "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); rc = 1; break; } } /* End loop over rows of content from SELFTEST */ sqlite3_finalize(pStmt); } /* End loop over k */ freeText(&str); utf8_printf(p->out, "%d errors out of %d tests\n", nErr, nTest); }else if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ raw_printf(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); } if( nArg>=3 ){ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); } }else if( c=='s' && n>=4 && strncmp(azArg[0],"sha3sum",n)==0 ){ const char *zLike = 0; /* Which table to checksum. 0 means everything */ int i; /* Loop counter */ int bSchema = 0; /* Also hash the schema */ int bSeparate = 0; /* Hash each table separately */ int iSize = 224; /* Hash algorithm to use */ int bDebug = 0; /* Only show the query that would have run */ sqlite3_stmt *pStmt; /* For querying tables names */ char *zSql; /* SQL to be run */ char *zSep; /* Separator */ ShellText sSql; /* Complete SQL for the query to run the hash */ ShellText sQuery; /* Set of queries used to read all content */ open_db(p, 0); for(i=1; i<nArg; i++){ const char *z = azArg[i]; if( z[0]=='-' ){ z++; if( z[0]=='-' ) z++; if( strcmp(z,"schema")==0 ){ bSchema = 1; }else if( strcmp(z,"sha3-224")==0 || strcmp(z,"sha3-256")==0 || strcmp(z,"sha3-384")==0 || strcmp(z,"sha3-512")==0 ){ iSize = atoi(&z[5]); }else if( strcmp(z,"debug")==0 ){ bDebug = 1; }else { utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); raw_printf(stderr, "Should be one of: --schema" " --sha3-224 --sha3-255 --sha3-384 --sha3-512\n"); rc = 1; goto meta_command_exit; } }else if( zLike ){ raw_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; }else{ zLike = z; bSeparate = 1; if( sqlite3_strlike("sqlite_%", zLike, 0)==0 ) bSchema = 1; } } if( bSchema ){ zSql = "SELECT lower(name) FROM sqlite_master" " WHERE type='table' AND coalesce(rootpage,0)>1" " UNION ALL SELECT 'sqlite_master'" " ORDER BY 1 collate nocase"; }else{ zSql = "SELECT lower(name) FROM sqlite_master" " WHERE type='table' AND coalesce(rootpage,0)>1" " AND name NOT LIKE 'sqlite_%'" " ORDER BY 1 collate nocase"; } sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); initText(&sQuery); initText(&sSql); appendText(&sSql, "WITH [sha3sum$query](a,b) AS(",0); zSep = "VALUES("; while( SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zTab = (const char*)sqlite3_column_text(pStmt,0); if( zLike && sqlite3_strlike(zLike, zTab, 0)!=0 ) continue; if( strncmp(zTab, "sqlite_",7)!=0 ){ appendText(&sQuery,"SELECT * FROM ", 0); appendText(&sQuery,zTab,'"'); appendText(&sQuery," NOT INDEXED;", 0); }else if( strcmp(zTab, "sqlite_master")==0 ){ appendText(&sQuery,"SELECT type,name,tbl_name,sql FROM sqlite_master" " ORDER BY name;", 0); }else if( strcmp(zTab, "sqlite_sequence")==0 ){ appendText(&sQuery,"SELECT name,seq FROM sqlite_sequence" " ORDER BY name;", 0); }else if( strcmp(zTab, "sqlite_stat1")==0 ){ appendText(&sQuery,"SELECT tbl,idx,stat FROM sqlite_stat1" " ORDER BY tbl,idx;", 0); }else if( strcmp(zTab, "sqlite_stat3")==0 || strcmp(zTab, "sqlite_stat4")==0 ){ appendText(&sQuery, "SELECT * FROM ", 0); appendText(&sQuery, zTab, 0); appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0); } appendText(&sSql, zSep, 0); appendText(&sSql, sQuery.z, '\''); sQuery.n = 0; appendText(&sSql, ",", 0); appendText(&sSql, zTab, '\''); zSep = "),("; } sqlite3_finalize(pStmt); if( bSeparate ){ zSql = sqlite3_mprintf( "%s))" " SELECT lower(hex(sha3_query(a,%d))) AS hash, b AS label" " FROM [sha3sum$query]", sSql.z, iSize); }else{ zSql = sqlite3_mprintf( "%s))" " SELECT lower(hex(sha3_query(group_concat(a,''),%d))) AS hash" " FROM [sha3sum$query]", sSql.z, iSize); } freeText(&sQuery); freeText(&sSql); if( bDebug ){ utf8_printf(p->out, "%s\n", zSql); }else{ shell_exec(p->db, zSql, shell_callback, p, 0); } sqlite3_free(zSql); }else if( c=='s' && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) ){ char *zCmd; int i, x; if( nArg<2 ){ |
︙ | ︙ | |||
4539 4540 4541 4542 4543 4544 4545 | static const char *azBool[] = { "off", "on", "full", "unk" }; int i; if( nArg!=1 ){ raw_printf(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } | | > | 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 | static const char *azBool[] = { "off", "on", "full", "unk" }; int i; if( nArg!=1 ){ raw_printf(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } utf8_printf(p->out, "%12.12s: %s\n","echo", azBool[ShellHasFlag(p, SHFLG_Echo)]); utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); utf8_printf(p->out, "%12.12s: %s\n","explain", p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]); utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); utf8_printf(p->out, "%12.12s: ", "nullvalue"); output_c_string(p->out, p->nullValue); |
︙ | ︙ | |||
4577 4578 4579 4580 4581 4582 4583 | display_stats(p->db, p, 0); }else{ raw_printf(stderr, "Usage: .stats ?on|off?\n"); rc = 1; } }else | | > > > < > > | | | > | < | | < > | | | | | > | > > > > > > | | | < < < | < | < | | < | < | 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 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 | display_stats(p->db, p, 0); }else{ raw_printf(stderr, "Usage: .stats ?on|off?\n"); rc = 1; } }else if( (c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0) || (c=='i' && (strncmp(azArg[0], "indices", n)==0 || strncmp(azArg[0], "indexes", n)==0) ) ){ sqlite3_stmt *pStmt; char **azResult; int nRow, nAlloc; int ii; ShellText s; initText(&s); open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ) return shellDatabaseError(p->db); if( nArg>2 && c=='i' ){ /* It is an historical accident that the .indexes command shows an error ** when called with the wrong number of arguments whereas the .tables ** command does not. */ raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){ const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); if( zDbName==0 ) continue; if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0); if( sqlite3_stricmp(zDbName, "main")==0 ){ appendText(&s, "SELECT name FROM ", 0); }else{ appendText(&s, "SELECT ", 0); appendText(&s, zDbName, '\''); appendText(&s, "||'.'||name FROM ", 0); } appendText(&s, zDbName, '"'); appendText(&s, ".sqlite_master ", 0); if( c=='t' ){ appendText(&s," WHERE type IN ('table','view')" " AND name NOT LIKE 'sqlite_%'" " AND name LIKE ?1", 0); }else{ appendText(&s," WHERE type='index'" " AND tbl_name LIKE ?1", 0); } } rc = sqlite3_finalize(pStmt); appendText(&s, " ORDER BY 1", 0); rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0); freeText(&s); if( rc ) return shellDatabaseError(p->db); /* Run the SQL statement prepared by the above block. Store the results ** as an array of nul-terminated strings in azResult[]. */ nRow = nAlloc = 0; azResult = 0; if( nArg>1 ){ |
︙ | ︙ | |||
4686 4687 4688 4689 4690 4691 4692 | }else /* Begin redirecting output to the file "testcase-out.txt" */ if( c=='t' && strcmp(azArg[0],"testcase")==0 ){ output_reset(p); p->out = output_file_open("testcase-out.txt"); if( p->out==0 ){ | | > < | 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 7281 7282 7283 7284 7285 7286 7287 7288 7289 | }else /* Begin redirecting output to the file "testcase-out.txt" */ if( c=='t' && strcmp(azArg[0],"testcase")==0 ){ output_reset(p); p->out = output_file_open("testcase-out.txt"); if( p->out==0 ){ raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); }else{ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); } }else #ifndef SQLITE_UNTESTABLE if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ } aCtrl[] = { { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE }, { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE }, { "prng_reset", SQLITE_TESTCTRL_PRNG_RESET }, { "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST }, { "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL }, { "benign_malloc_hooks", SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS }, { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE }, { "assert", SQLITE_TESTCTRL_ASSERT }, { "always", SQLITE_TESTCTRL_ALWAYS }, { "reserve", SQLITE_TESTCTRL_RESERVE }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, { "imposter", SQLITE_TESTCTRL_IMPOSTER }, }; int testctrl = -1; int rc2 = 0; int i, n2; |
︙ | ︙ | |||
4825 4826 4827 4828 4829 4830 4831 | raw_printf(stderr,"Usage: .testctrl imposter dbName onoff tnum\n"); } break; case SQLITE_TESTCTRL_BITVEC_TEST: case SQLITE_TESTCTRL_FAULT_INSTALL: case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: | < > | 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 | raw_printf(stderr,"Usage: .testctrl imposter dbName onoff tnum\n"); } break; case SQLITE_TESTCTRL_BITVEC_TEST: case SQLITE_TESTCTRL_FAULT_INSTALL: case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: default: utf8_printf(stderr, "Error: CLI support for testctrl %s not implemented\n", azArg[1]); break; } } }else #endif /* !defined(SQLITE_UNTESTABLE) */ if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 ){ open_db(p, 0); sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); }else if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){ |
︙ | ︙ | |||
4943 4944 4945 4946 4947 4948 4949 | if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ utf8_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); }else if( c=='v' && strncmp(azArg[0], "vfsinfo", n)==0 ){ const char *zDbName = nArg==2 ? azArg[1] : "main"; | | | 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 | if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ utf8_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); }else if( c=='v' && strncmp(azArg[0], "vfsinfo", n)==0 ){ const char *zDbName = nArg==2 ? azArg[1] : "main"; sqlite3_vfs *pVfs = 0; if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); if( pVfs ){ utf8_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
︙ | ︙ | |||
5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 | if( zSql==0 ) return 1; zSql[nSql] = ';'; zSql[nSql+1] = 0; rc = sqlite3_complete(zSql); zSql[nSql] = 0; return rc; } /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. ** ** Return the number of errors. */ static int process_input(ShellState *p, FILE *in){ char *zLine = 0; /* A single input line */ char *zSql = 0; /* Accumulated SQL text */ int nLine; /* Length of current line */ int nSql = 0; /* Bytes of zSql[] used */ int nAlloc = 0; /* Allocated zSql[] space */ int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | | 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 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 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 | 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 */ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ int rc; char *zErrMsg = 0; open_db(p, 0); if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); BEGIN_TIMER; rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg); END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; if( in!=0 || !stdin_is_interactive ){ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error: near line %d:", startline); }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:"); } if( zErrMsg!=0 ){ utf8_printf(stderr, "%s %s\n", zPrefix, zErrMsg); sqlite3_free(zErrMsg); zErrMsg = 0; }else{ utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db)); } return 1; }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ raw_printf(p->out, "changes: %3d total_changes: %d\n", sqlite3_changes(p->db), sqlite3_total_changes(p->db)); } return 0; } /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. ** ** Return the number of errors. */ static int process_input(ShellState *p, FILE *in){ char *zLine = 0; /* A single input line */ char *zSql = 0; /* Accumulated SQL text */ int nLine; /* Length of current line */ int nSql = 0; /* Bytes of zSql[] used */ int nAlloc = 0; /* Allocated zSql[] space */ int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ int rc; /* Error code */ int errCnt = 0; /* Number of errors seen */ int lineno = 0; /* Current line number */ int startline = 0; /* Line number for start of current input */ while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){ fflush(p->out); zLine = one_input_line(in, zLine, nSql>0); if( zLine==0 ){ /* End of input */ if( in==0 && stdin_is_interactive ) printf("\n"); break; } if( seenInterrupt ){ if( in!=0 ) break; seenInterrupt = 0; } lineno++; if( nSql==0 && _all_whitespace(zLine) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); continue; } if( zLine && zLine[0]=='.' && nSql==0 ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); rc = do_meta_command(zLine, p); if( rc==2 ){ /* exit requested */ break; }else if( rc ){ errCnt++; } continue; |
︙ | ︙ | |||
5154 5155 5156 5157 5158 5159 5160 | }else{ zSql[nSql++] = '\n'; memcpy(zSql+nSql, zLine, nLine+1); nSql += nLine; } if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) && sqlite3_complete(zSql) ){ | < < < < < < < < < < < < < < < < < < < < < | < < < < | < | | < < | 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 | }else{ zSql[nSql++] = '\n'; memcpy(zSql+nSql, zLine, nLine+1); nSql += nLine; } if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) && sqlite3_complete(zSql) ){ errCnt += runOneSqlLine(p, zSql, in, startline); nSql = 0; if( p->outCount ){ output_reset(p); p->outCount = 0; } }else if( nSql && _all_whitespace(zSql) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql); nSql = 0; } } if( nSql && !_all_whitespace(zSql) ){ runOneSqlLine(p, zSql, in, startline); } free(zSql); free(zLine); return errCnt>0; } /* |
︙ | ︙ | |||
5336 5337 5338 5339 5340 5341 5342 | " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" | | | 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 | " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif |
︙ | ︙ | |||
5439 5440 5441 5442 5443 5444 5445 | setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #if USE_SYSTEM_SQLITE+0!=1 | | | 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 | setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #if USE_SYSTEM_SQLITE+0!=1 if( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", sqlite3_sourceid(), SQLITE_SOURCE_ID); exit(1); } #endif main_init(&data); #if !SQLITE_SHELL_IS_UTF8 |
︙ | ︙ | |||
5534 5535 5536 5537 5538 5539 5540 | zSize = cmdline_option_value(argc, argv, ++i); szHeap = integerValue(zSize); if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #else (void)cmdline_option_value(argc, argv, ++i); #endif | < < < < < < < < < < | 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 | zSize = cmdline_option_value(argc, argv, ++i); szHeap = integerValue(zSize); if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #else (void)cmdline_option_value(argc, argv, ++i); #endif }else if( strcmp(z,"-pagecache")==0 ){ int n, sz; sz = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( sz>70000 ) sz = 70000; if( sz<0 ) sz = 0; n = (int)integerValue(cmdline_option_value(argc,argv,++i)); sqlite3_config(SQLITE_CONFIG_PAGECACHE, |
︙ | ︙ | |||
5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 | if( z[1]=='-' ){ z++; } if( strcmp(z,"-init")==0 ){ i++; }else if( strcmp(z,"-html")==0 ){ data.mode = MODE_Html; }else if( strcmp(z,"-list")==0 ){ data.mode = MODE_List; }else if( strcmp(z,"-line")==0 ){ data.mode = MODE_Line; }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); | > > | 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 | if( z[1]=='-' ){ z++; } if( strcmp(z,"-init")==0 ){ i++; }else if( strcmp(z,"-html")==0 ){ data.mode = MODE_Html; }else if( strcmp(z,"-list")==0 ){ data.mode = MODE_List; }else if( strcmp(z,"-quote")==0 ){ data.mode = MODE_Quote; }else if( strcmp(z,"-line")==0 ){ data.mode = MODE_Line; }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); |
︙ | ︙ | |||
5658 5659 5660 5661 5662 5663 5664 | sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; }else if( strcmp(z,"-echo")==0 ){ | | | < < | 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 | sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; }else if( strcmp(z,"-echo")==0 ){ ShellSetFlag(&data, SHFLG_Echo); }else if( strcmp(z,"-eqp")==0 ){ data.autoEQP = 1; }else if( strcmp(z,"-eqpfull")==0 ){ data.autoEQP = 2; }else if( strcmp(z,"-stats")==0 ){ data.statsOn = 1; }else if( strcmp(z,"-scanstats")==0 ){ data.scanstatsOn = 1; }else if( strcmp(z,"-backslash")==0 ){ /* Undocumented command-line option: -backslash ** Causes C-style backslash escapes to be evaluated in SQL statements ** 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( strcmp(z,"-bail")==0 ){ bail_on_error = 1; }else if( strcmp(z,"-version")==0 ){ printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); return 0; }else if( strcmp(z,"-interactive")==0 ){ stdin_is_interactive = 1; }else if( strcmp(z,"-batch")==0 ){ stdin_is_interactive = 0; }else if( strcmp(z,"-heap")==0 ){ i++; }else if( strcmp(z,"-pagecache")==0 ){ i+=2; }else if( strcmp(z,"-lookaside")==0 ){ i+=2; }else if( strcmp(z,"-mmap")==0 ){ i++; }else if( strcmp(z,"-vfs")==0 ){ |
︙ | ︙ | |||
5782 5783 5784 5785 5786 5787 5788 5789 5790 | if( zHome ){ nHistory = strlen30(zHome) + 20; if( (zHistory = malloc(nHistory))!=0 ){ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); } } if( zHistory ){ shell_read_history(zHistory); } rc = process_input(&data, 0); if( zHistory ){ | > > > > > | | 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 | if( zHome ){ nHistory = strlen30(zHome) + 20; if( (zHistory = malloc(nHistory))!=0 ){ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); } } if( zHistory ){ shell_read_history(zHistory); } #if HAVE_READLINE || HAVE_EDITLINE rl_attempted_completion_function = readline_completion; #elif HAVE_LINENOISE linenoiseSetCompletionCallback(linenoise_completion); #endif rc = process_input(&data, 0); if( zHistory ){ shell_stifle_history(2000); shell_write_history(zHistory); free(zHistory); } }else{ rc = process_input(&data, stdin); } } |
︙ | ︙ |
Added src/shell.c.in.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 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 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 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 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 4176 4177 4178 4179 4180 4181 4182 4183 4184 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 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 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 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 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 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 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 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 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 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 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 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 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 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 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 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 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 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 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 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 6254 6255 6256 6257 6258 6259 6260 6261 6262 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 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 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 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 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 | /* ** 2001 September 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. ** ************************************************************************* ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. */ #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) /* This needs to come before any includes for MSVC compiler */ #define _CRT_SECURE_NO_WARNINGS #endif /* ** Warning pragmas copied from msvc.h in the core. */ #if defined(_MSC_VER) #pragma warning(disable : 4054) #pragma warning(disable : 4055) #pragma warning(disable : 4100) #pragma warning(disable : 4127) #pragma warning(disable : 4130) #pragma warning(disable : 4152) #pragma warning(disable : 4189) #pragma warning(disable : 4206) #pragma warning(disable : 4210) #pragma warning(disable : 4232) #pragma warning(disable : 4244) #pragma warning(disable : 4305) #pragma warning(disable : 4306) #pragma warning(disable : 4702) #pragma warning(disable : 4706) #endif /* defined(_MSC_VER) */ /* ** No support for loadable extensions in VxWorks. */ #if (defined(__RTP__) || defined(_WRS_KERNEL)) && !SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION 1 #endif /* ** Enable large-file support for fopen() and friends on unix. */ #ifndef SQLITE_DISABLE_LFS # define _LARGE_FILE 1 # ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 # endif # define _LARGEFILE_SOURCE 1 #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include "sqlite3.h" #if SQLITE_USER_AUTHENTICATION # include "sqlite3userauth.h" #endif #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) # include <pwd.h> # endif # include <unistd.h> # include <sys/types.h> #endif #if HAVE_READLINE # include <readline/readline.h> # include <readline/history.h> #endif #if HAVE_EDITLINE # include <editline/readline.h> #endif #if HAVE_EDITLINE || HAVE_READLINE # define shell_add_history(X) add_history(X) # define shell_read_history(X) read_history(X) # define shell_write_history(X) write_history(X) # define shell_stifle_history(X) stifle_history(X) # define shell_readline(X) readline(X) #elif HAVE_LINENOISE # include "linenoise.h" # define shell_add_history(X) linenoiseHistoryAdd(X) # define shell_read_history(X) linenoiseHistoryLoad(X) # define shell_write_history(X) linenoiseHistorySave(X) # define shell_stifle_history(X) linenoiseHistorySetMaxLen(X) # define shell_readline(X) linenoise(X) #else # define shell_read_history(X) # define shell_write_history(X) # define shell_stifle_history(X) # define SHELL_USE_LOCAL_GETLINE 1 #endif #if defined(_WIN32) || defined(WIN32) # include <io.h> # include <fcntl.h> # define isatty(h) _isatty(h) # ifndef access # define access(f,m) _access((f),(m)) # endif # undef popen # define popen _popen # undef pclose # define pclose _pclose #else /* Make sure isatty() has a prototype. */ extern int isatty(int); # if !defined(__RTP__) && !defined(_WRS_KERNEL) /* popen and pclose are not C89 functions and so are ** sometimes omitted from the <stdio.h> header */ extern FILE *popen(const char*,const char*); extern int pclose(FILE*); # else # define SQLITE_OMIT_POPEN 1 # endif #endif #if defined(_WIN32_WCE) /* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() * thus we always assume that we have a console. That can be * overridden with the -batch command line option. */ #define isatty(x) 1 #endif /* ctype macros that work with signed characters */ #define IsSpace(X) isspace((unsigned char)X) #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) #if defined(_WIN32) || defined(WIN32) #include <windows.h> /* string conversion routines only needed on Win32 */ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif /* On Windows, we normally run with output mode of TEXT so that \n characters ** are automatically translated into \r\n. However, this behavior needs ** to be disabled in some cases (ex: when generating CSV output and when ** rendering quoted strings that contain \n characters). The following ** routines take care of that. */ #if defined(_WIN32) || defined(WIN32) static void setBinaryMode(FILE *file, int isOutput){ if( isOutput ) fflush(file); _setmode(_fileno(file), _O_BINARY); } static void setTextMode(FILE *file, int isOutput){ if( isOutput ) fflush(file); _setmode(_fileno(file), _O_TEXT); } #else # define setBinaryMode(X,Y) # define setTextMode(X,Y) #endif /* True if the timer is enabled */ static int enableTimer = 0; /* Return the current wall-clock time */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; sqlite3_int64 t; if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ clockVfs->xCurrentTimeInt64(clockVfs, &t); }else{ double r; clockVfs->xCurrentTime(clockVfs, &r); t = (sqlite3_int64)(r*86400000.0); } return t; } #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) #include <sys/time.h> #include <sys/resource.h> /* VxWorks does not support getrusage() as far as we can determine */ #if defined(_WRS_KERNEL) || defined(__RTP__) struct rusage { struct timeval ru_utime; /* user CPU time used */ struct timeval ru_stime; /* system CPU time used */ }; #define getrusage(A,B) memset(B,0,sizeof(*B)) #endif /* Saved resource information for the beginning of an operation */ static struct rusage sBegin; /* CPU time at start */ static sqlite3_int64 iBegin; /* Wall-clock time at start */ /* ** Begin timing an operation */ static void beginTimer(void){ if( enableTimer ){ getrusage(RUSAGE_SELF, &sBegin); iBegin = timeOfDay(); } } /* Return the difference of two time_structs in seconds */ static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + (double)(pEnd->tv_sec - pStart->tv_sec); } /* ** Print the timing results. */ static void endTimer(void){ if( enableTimer ){ sqlite3_int64 iEnd = timeOfDay(); struct rusage sEnd; getrusage(RUSAGE_SELF, &sEnd); printf("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() #define END_TIMER endTimer() #define HAS_TIMER 1 #elif (defined(_WIN32) || defined(WIN32)) /* Saved resource information for the beginning of an operation */ static HANDLE hProcess; static FILETIME ftKernelBegin; static FILETIME ftUserBegin; static sqlite3_int64 ftWallBegin; typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); static GETPROCTIMES getProcessTimesAddr = NULL; /* ** Check to see if we have timer support. Return 1 if necessary ** support found (or found previously). */ static int hasTimer(void){ if( getProcessTimesAddr ){ return 1; } else { /* GetProcessTimes() isn't supported in WIN95 and some other Windows ** versions. See if the version we are running on has it, and if it ** does, save off a pointer to it and the current process handle. */ hProcess = GetCurrentProcess(); if( hProcess ){ HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); if( NULL != hinstLib ){ getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); if( NULL != getProcessTimesAddr ){ return 1; } FreeLibrary(hinstLib); } } } return 0; } /* ** Begin timing an operation */ static void beginTimer(void){ if( enableTimer && getProcessTimesAddr ){ FILETIME ftCreation, ftExit; getProcessTimesAddr(hProcess,&ftCreation,&ftExit, &ftKernelBegin,&ftUserBegin); ftWallBegin = timeOfDay(); } } /* Return the difference of two FILETIME structs in seconds */ static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ sqlite_int64 i64Start = *((sqlite_int64 *) pStart); sqlite_int64 i64End = *((sqlite_int64 *) pEnd); return (double) ((i64End - i64Start) / 10000000.0); } /* ** 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); printf("Run Time: real %.3f user %f sys %f\n", (ftWallEnd - ftWallBegin)*0.001, timeDiff(&ftUserBegin, &ftUserEnd), timeDiff(&ftKernelBegin, &ftKernelEnd)); } } #define BEGIN_TIMER beginTimer() #define END_TIMER endTimer() #define HAS_TIMER hasTimer() #else #define BEGIN_TIMER #define END_TIMER #define HAS_TIMER 0 #endif /* ** Used to prevent warnings about unused parameters */ #define UNUSED_PARAMETER(x) (void)(x) /* ** If the following flag is set, then command execution stops ** at an error if we are not interactive. */ static int bail_on_error = 0; /* ** Threat 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 have to know if standard output is a console ** in order to translate UTF-8 into MBCS. The following variable is ** true if translation is required. */ 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. */ static sqlite3 *globalDb = 0; /* ** True if an interrupt (Control-C) has been received. */ static volatile int seenInterrupt = 0; /* ** This is the name of our program. It is set in main(), used ** in a number of other places, mostly for error messages. */ static char *Argv0; /* ** Prompt strings. Initialized in main. Settable with ** .prompt main continue */ static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ /* ** Render output like fprintf(). Except, if the output is going to the ** console and if this is running on a Windows machine, translate the ** output from UTF-8 into MBCS. */ #if defined(_WIN32) || defined(WIN32) void utf8_printf(FILE *out, const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); if( stdout_is_console && (out==stdout || out==stderr) ){ char *z1 = sqlite3_vmprintf(zFormat, ap); char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); sqlite3_free(z1); fputs(z2, out); sqlite3_free(z2); }else{ vfprintf(out, zFormat, ap); } va_end(ap); } #elif !defined(utf8_printf) # define utf8_printf fprintf #endif /* ** Render output like fprintf(). This should not be used on anything that ** includes string formatting (e.g. "%s"). */ #if !defined(raw_printf) # define raw_printf fprintf #endif /* ** Write I/O traces to the following stream. */ #ifdef SQLITE_ENABLE_IOTRACE static FILE *iotrace = 0; #endif /* ** This routine works like printf in that its first argument is a ** format string and subsequent arguments are values to be substituted ** in place of % fields. The result of formatting this string ** is written to iotrace. */ #ifdef SQLITE_ENABLE_IOTRACE static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ va_list ap; char *z; if( iotrace==0 ) return; va_start(ap, zFormat); z = sqlite3_vmprintf(zFormat, ap); va_end(ap); utf8_printf(iotrace, "%s", z); sqlite3_free(z); } #endif /* ** Output string zUtf to stream pOut as w characters. If w is negative, ** then right-justify the text. W is the width in UTF-8 characters, not ** in bytes. This is different from the %*.*s specification in printf ** since with %*.*s the width is measured in bytes, not characters. */ static void utf8_width_print(FILE *pOut, int w, const char *zUtf){ int i; int n; int aw = w<0 ? -w : w; char zBuf[1000]; if( aw>(int)sizeof(zBuf)/3 ) aw = (int)sizeof(zBuf)/3; for(i=n=0; zUtf[i]; i++){ if( (zUtf[i]&0xc0)!=0x80 ){ n++; if( n==aw ){ do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); break; } } } if( n>=aw ){ utf8_printf(pOut, "%.*s", i, zUtf); }else if( w<0 ){ utf8_printf(pOut, "%*s%s", aw-n, "", zUtf); }else{ utf8_printf(pOut, "%s%*s", zUtf, aw-n, ""); } } /* ** Determines if a string is a number of not. */ static int isNumber(const char *z, int *realnum){ if( *z=='-' || *z=='+' ) z++; if( !IsDigit(*z) ){ return 0; } z++; if( realnum ) *realnum = 0; while( IsDigit(*z) ){ z++; } if( *z=='.' ){ z++; if( !IsDigit(*z) ) return 0; while( IsDigit(*z) ){ z++; } if( realnum ) *realnum = 1; } if( *z=='e' || *z=='E' ){ z++; if( *z=='+' || *z=='-' ) z++; if( !IsDigit(*z) ) return 0; while( IsDigit(*z) ){ z++; } if( realnum ) *realnum = 1; } return *z==0; } /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. */ static int strlen30(const char *z){ const char *z2 = z; while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } /* ** Return the length of a string in characters. Multibyte UTF8 characters ** count as a single character. */ static int strlenChar(const char *z){ int n = 0; while( *z ){ if( (0xc0&*(z++))!=0x80 ) n++; } return n; } /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. ** ** If zLine is not NULL then it is a malloced buffer returned from ** a previous call to this routine that may be reused. */ static char *local_getline(char *zLine, FILE *in){ int nLine = zLine==0 ? 0 : 100; int n = 0; while( 1 ){ if( n+100>nLine ){ nLine = nLine*2 + 100; zLine = realloc(zLine, nLine); if( zLine==0 ) return 0; } if( fgets(&zLine[n], nLine - n, in)==0 ){ if( n==0 ){ free(zLine); return 0; } zLine[n] = 0; break; } while( zLine[n] ) n++; if( n>0 && zLine[n-1]=='\n' ){ n--; if( n>0 && zLine[n-1]=='\r' ) n--; zLine[n] = 0; break; } } #if defined(_WIN32) || defined(WIN32) /* For interactive input on Windows systems, translate the ** multi-byte characterset characters into UTF-8. */ if( stdin_is_interactive && in==stdin ){ char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); if( zTrans ){ int nTrans = strlen30(zTrans)+1; if( nTrans>nLine ){ zLine = realloc(zLine, nTrans); if( zLine==0 ){ sqlite3_free(zTrans); return 0; } } memcpy(zLine, zTrans, nTrans); sqlite3_free(zTrans); } } #endif /* defined(_WIN32) || defined(WIN32) */ return zLine; } /* ** Retrieve a single line of input text. ** ** If in==0 then read from standard input and prompt before each line. ** If isContinuation is true, then a continuation prompt is appropriate. ** If isContinuation is zero, then the main prompt should be used. ** ** If zPrior is not NULL then it is a buffer from a prior call to this ** routine that can be reused. ** ** The result is stored in space obtained from malloc() and must either ** be freed by the caller or else passed back into this routine via the ** zPrior argument for reuse. */ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ char *zPrompt; char *zResult; if( in!=0 ){ zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; #if SHELL_USE_LOCAL_GETLINE printf("%s", zPrompt); fflush(stdout); zResult = local_getline(zPrior, stdin); #else free(zPrior); zResult = shell_readline(zPrompt); if( zResult && *zResult ) shell_add_history(zResult); #endif } return zResult; } /* ** A variable length string to which one can append text. */ typedef struct ShellText ShellText; struct ShellText { char *z; int n; int nAlloc; }; /* ** Initialize and destroy a ShellText object */ static void initText(ShellText *p){ memset(p, 0, sizeof(*p)); } static void freeText(ShellText *p){ free(p->z); initText(p); } /* zIn is either a pointer to a NULL-terminated string in memory obtained ** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** added to zIn, and the result returned in memory obtained from malloc(). ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ static void appendText(ShellText *p, char const *zAppend, char quote){ int len; int i; int nAppend = strlen30(zAppend); len = nAppend+p->n+1; if( quote ){ len += 2; for(i=0; i<nAppend; i++){ if( zAppend[i]==quote ) len++; } } if( p->n+len>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + len + 20; p->z = realloc(p->z, p->nAlloc); if( p->z==0 ){ memset(p, 0, sizeof(*p)); return; } } if( quote ){ char *zCsr = p->z+p->n; *zCsr++ = quote; for(i=0; i<nAppend; i++){ *zCsr++ = zAppend[i]; if( zAppend[i]==quote ) *zCsr++ = quote; } *zCsr++ = quote; p->n = (int)(zCsr - p->z); *zCsr = '\0'; }else{ memcpy(p->z+p->n, zAppend, nAppend); p->n += nAppend; p->z[p->n] = '\0'; } } /* ** Attempt to determine if identifier zName needs to be quoted, either ** because it contains non-alphanumeric characters, or because it is an ** SQLite keyword. Be conservative in this estimate: When in doubt assume ** that quoting is required. ** ** Return '"' if quoting is required. Return 0 if no quoting is required. */ static char quoteChar(const char *zName){ /* All SQLite keywords, in alphabetical order */ static const char *azKeywords[] = { "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH", "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA", "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE", "WITH", "WITHOUT", }; int i, lwr, upr, mid, c; if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; for(i=0; zName[i]; i++){ if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; } lwr = 0; upr = sizeof(azKeywords)/sizeof(azKeywords[0]) - 1; while( lwr<=upr ){ mid = (lwr+upr)/2; c = sqlite3_stricmp(azKeywords[mid], zName); if( c==0 ) return '"'; if( c<0 ){ lwr = mid+1; }else{ upr = mid-1; } } return 0; } /* ** SQL function: shell_add_schema(S,X) ** ** Add the schema name X to the CREATE statement in S and return the result. ** Examples: ** ** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); ** ** Also works on ** ** CREATE INDEX ** CREATE UNIQUE INDEX ** CREATE VIEW ** CREATE TRIGGER ** CREATE VIRTUAL TABLE ** ** This UDF is used by the .schema command to insert the schema name of ** attached databases into the middle of the sqlite_master.sql field. */ static void shellAddSchemaName( sqlite3_context *pCtx, int nVal, sqlite3_value **apVal ){ static const char *aPrefix[] = { "TABLE", "INDEX", "UNIQUE INDEX", "VIEW", "TRIGGER", "VIRTUAL TABLE" }; int i = 0; const char *zIn = (const char*)sqlite3_value_text(apVal[0]); const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); assert( nVal==2 ); if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){ for(i=0; i<(int)(sizeof(aPrefix)/sizeof(aPrefix[0])); i++){ int n = strlen30(aPrefix[i]); if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){ char cQuote = quoteChar(zSchema); char *z; if( cQuote ){ z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8); }else{ z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8); } sqlite3_result_text(pCtx, z, -1, sqlite3_free); return; } } } sqlite3_result_value(pCtx, apVal[0]); } /* ** The source code for several run-time loadable extensions is inserted ** below by the ../tool/mkshellc.tcl script. Before processing that included ** code, we need to override some macros to make the included program code ** work here in the middle of this regular program. */ #define SQLITE_EXTENSION_INIT1 #define SQLITE_EXTENSION_INIT2(X) (void)(X) INCLUDE ../ext/misc/shathree.c INCLUDE ../ext/misc/fileio.c INCLUDE ../ext/misc/completion.c #if defined(SQLITE_ENABLE_SESSION) /* ** State information for a single open session */ typedef struct OpenSession OpenSession; struct OpenSession { char *zName; /* Symbolic name for this session */ int nFilter; /* Number of xFilter rejection GLOB patterns */ char **azFilter; /* Array of xFilter rejection GLOB patterns */ sqlite3_session *p; /* The open session */ }; #endif /* ** Shell output mode information from before ".explain on", ** saved so that it can be restored by ".explain off" */ typedef struct SavedModeInfo SavedModeInfo; struct SavedModeInfo { int valid; /* Is there legit data in here? */ int mode; /* Mode prior to ".explain on" */ int showHeader; /* The ".header" setting prior to ".explain on" */ int colWidth[100]; /* Column widths prior to ".explain on" */ }; /* ** State information about the database connection is contained in an ** instance of the following structure. */ typedef struct ShellState ShellState; struct ShellState { sqlite3 *db; /* The database */ int autoExplain; /* Automatically turn on .explain mode */ int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ int statsOn; /* True to display memory stats before each finalize */ int scanstatsOn; /* True to display scan stats before each finalize */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ int cMode; /* temporary output mode for the current query */ int normalMode; /* Output mode before ".explain on" */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int showHeader; /* True to show column names in List or Column mode */ int nCheck; /* Number of ".check" commands run */ unsigned shellFlgs; /* Various flags */ char *zDestTable; /* Name of destination table when MODE_Insert */ char zTestcase[30]; /* Name of current test case */ char colSeparator[20]; /* Column separator character for several modes */ char rowSeparator[20]; /* Row separator character for MODE_Ascii */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ char nullValue[20]; /* The text to print when a NULL comes back from ** the database */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ char *zFreeOnClose; /* Filename to free when closing */ const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ #if defined(SQLITE_ENABLE_SESSION) int nSession; /* Number of active sessions */ OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */ #endif }; /* ** These are the allowed shellFlgs values */ #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ #define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ #define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */ #define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ #define SHFLG_CountChanges 0x00000020 /* .changes setting */ #define SHFLG_Echo 0x00000040 /* .echo or --echo setting */ /* ** Macros for testing and setting shellFlgs */ #define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) #define ShellSetFlag(P,X) ((P)->shellFlgs|=(X)) #define ShellClearFlag(P,X) ((P)->shellFlgs&=(~(X))) /* ** These are the allowed modes. */ #define MODE_Line 0 /* One column per line. Blank line between records */ #define MODE_Column 1 /* One record per line in neat columns */ #define MODE_List 2 /* One record per line with a separator */ #define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ #define MODE_Html 4 /* Generate an XHTML table */ #define MODE_Insert 5 /* Generate SQL "insert" statements */ #define MODE_Quote 6 /* Quote values as for SQL */ #define MODE_Tcl 7 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 8 /* Quote strings, numbers are plain */ #define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */ #define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */ #define MODE_Pretty 11 /* Pretty-print schemas */ static const char *modeDescr[] = { "line", "column", "list", "semi", "html", "insert", "quote", "tcl", "csv", "explain", "ascii", "prettyprint", }; /* ** These are the column/row/line separators used by the various ** import/export modes. */ #define SEP_Column "|" #define SEP_Row "\n" #define SEP_Tab "\t" #define SEP_Space " " #define SEP_Comma "," #define SEP_CrLf "\r\n" #define SEP_Unit "\x1F" #define SEP_Record "\x1E" /* ** Number of elements in an array */ #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) /* ** A callback for the sqlite3_log() interface. */ static void shellLog(void *pArg, int iErrCode, const char *zMsg){ ShellState *p = (ShellState*)pArg; if( p->pLog==0 ) return; utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg); fflush(p->pLog); } /* ** Output the given string as a hex-encoded blob (eg. X'1234' ) */ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ int i; char *zBlob = (char *)pBlob; raw_printf(out,"X'"); for(i=0; i<nBlob; i++){ raw_printf(out,"%02x",zBlob[i]&0xff); } raw_printf(out,"'"); } /* ** Find a string that is not found anywhere in z[]. Return a pointer ** to that string. ** ** Try to use zA and zB first. If both of those are already found in z[] ** then make up some string and store it in the buffer zBuf. */ static const char *unused_string( const char *z, /* Result must not appear anywhere in z */ const char *zA, const char *zB, /* Try these first */ char *zBuf /* Space to store a generated string */ ){ unsigned i = 0; if( strstr(z, zA)==0 ) return zA; if( strstr(z, zB)==0 ) return zB; do{ sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++); }while( strstr(z,zBuf)!=0 ); return zBuf; } /* ** Output the given string as a quoted string using SQL quoting conventions. ** ** See also: output_quoted_escaped_string() */ static void output_quoted_string(FILE *out, const char *z){ int i; char c; setBinaryMode(out, 1); for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c==0 ){ utf8_printf(out,"'%s'",z); }else{ raw_printf(out, "'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c=='\'' ) i++; if( i ){ utf8_printf(out, "%.*s", i, z); z += i; } if( c=='\'' ){ raw_printf(out, "'"); continue; } if( c==0 ){ break; } z++; } raw_printf(out, "'"); } setTextMode(out, 1); } /* ** Output the given string as a quoted string using SQL quoting conventions. ** Additionallly , escape the "\n" and "\r" characters so that they do not ** get corrupted by end-of-line translation facilities in some operating ** systems. ** ** This is like output_quoted_string() but with the addition of the \r\n ** escape mechanism. */ static void output_quoted_escaped_string(FILE *out, const char *z){ int i; char c; setBinaryMode(out, 1); for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} if( c==0 ){ utf8_printf(out,"'%s'",z); }else{ const char *zNL = 0; const char *zCR = 0; int nNL = 0; int nCR = 0; char zBuf1[20], zBuf2[20]; for(i=0; z[i]; i++){ if( z[i]=='\n' ) nNL++; if( z[i]=='\r' ) nCR++; } if( nNL ){ raw_printf(out, "replace("); zNL = unused_string(z, "\\n", "\\012", zBuf1); } if( nCR ){ raw_printf(out, "replace("); zCR = unused_string(z, "\\r", "\\015", zBuf2); } raw_printf(out, "'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} if( c=='\'' ) i++; if( i ){ utf8_printf(out, "%.*s", i, z); z += i; } if( c=='\'' ){ raw_printf(out, "'"); continue; } if( c==0 ){ break; } z++; if( c=='\n' ){ raw_printf(out, "%s", zNL); continue; } raw_printf(out, "%s", zCR); } raw_printf(out, "'"); if( nCR ){ raw_printf(out, ",'%s',char(13))", zCR); } if( nNL ){ raw_printf(out, ",'%s',char(10))", zNL); } } setTextMode(out, 1); } /* ** Output the given string as a quoted according to C or TCL quoting rules. */ static void output_c_string(FILE *out, const char *z){ unsigned int c; fputc('"', out); while( (c = *(z++))!=0 ){ if( c=='\\' ){ fputc(c, out); fputc(c, out); }else if( c=='"' ){ fputc('\\', out); fputc('"', out); }else if( c=='\t' ){ fputc('\\', out); fputc('t', out); }else if( c=='\n' ){ fputc('\\', out); fputc('n', out); }else if( c=='\r' ){ fputc('\\', out); fputc('r', out); }else if( !isprint(c&0xff) ){ raw_printf(out, "\\%03o", c&0xff); }else{ fputc(c, out); } } fputc('"', out); } /* ** Output the given string with characters that are special to ** HTML escaped. */ static void output_html_string(FILE *out, const char *z){ int i; if( z==0 ) z = ""; while( *z ){ for(i=0; z[i] && z[i]!='<' && z[i]!='&' && z[i]!='>' && z[i]!='\"' && z[i]!='\''; i++){} if( i>0 ){ utf8_printf(out,"%.*s",i,z); } if( z[i]=='<' ){ raw_printf(out,"<"); }else if( z[i]=='&' ){ raw_printf(out,"&"); }else if( z[i]=='>' ){ raw_printf(out,">"); }else if( z[i]=='\"' ){ raw_printf(out,"""); }else if( z[i]=='\'' ){ raw_printf(out,"'"); }else{ break; } z += i + 1; } } /* ** If a field contains any character identified by a 1 in the following ** array, then the string must be quoted for CSV. */ static const char needCsvQuote[] = { 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, 0, 1, 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, 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, 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, }; /* ** Output a single term of CSV. Actually, p->colSeparator is used for ** the separator, which may or may not be a comma. p->nullValue is ** the null value. Strings are quoted if necessary. The separator ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ FILE *out = p->out; if( z==0 ){ utf8_printf(out,"%s",p->nullValue); }else{ int i; int nSep = strlen30(p->colSeparator); for(i=0; z[i]; i++){ if( needCsvQuote[((unsigned char*)z)[i]] || (z[i]==p->colSeparator[0] && (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ i = 0; break; } } if( i==0 ){ putc('"', out); for(i=0; z[i]; i++){ if( z[i]=='"' ) putc('"', out); putc(z[i], out); } putc('"', out); }else{ utf8_printf(out, "%s", z); } } if( bSep ){ utf8_printf(p->out, "%s", p->colSeparator); } } #ifdef SIGINT /* ** This routine runs when the user presses Ctrl-C */ static void interrupt_handler(int NotUsed){ UNUSED_PARAMETER(NotUsed); seenInterrupt++; if( seenInterrupt>2 ) exit(1); if( globalDb ) sqlite3_interrupt(globalDb); } #endif #ifndef SQLITE_OMIT_AUTHORIZATION /* ** When the ".auth ON" is set, the following authorizer callback is ** invoked. It always returns SQLITE_OK. */ static int shellAuth( void *pClientData, int op, const char *zA1, const char *zA2, const char *zA3, const char *zA4 ){ ShellState *p = (ShellState*)pClientData; static const char *azAction[] = { 0, "CREATE_INDEX", "CREATE_TABLE", "CREATE_TEMP_INDEX", "CREATE_TEMP_TABLE", "CREATE_TEMP_TRIGGER", "CREATE_TEMP_VIEW", "CREATE_TRIGGER", "CREATE_VIEW", "DELETE", "DROP_INDEX", "DROP_TABLE", "DROP_TEMP_INDEX", "DROP_TEMP_TABLE", "DROP_TEMP_TRIGGER", "DROP_TEMP_VIEW", "DROP_TRIGGER", "DROP_VIEW", "INSERT", "PRAGMA", "READ", "SELECT", "TRANSACTION", "UPDATE", "ATTACH", "DETACH", "ALTER_TABLE", "REINDEX", "ANALYZE", "CREATE_VTABLE", "DROP_VTABLE", "FUNCTION", "SAVEPOINT", "RECURSIVE" }; int i; const char *az[4]; az[0] = zA1; az[1] = zA2; az[2] = zA3; az[3] = zA4; utf8_printf(p->out, "authorizer: %s", azAction[op]); for(i=0; i<4; i++){ raw_printf(p->out, " "); if( az[i] ){ output_c_string(p->out, az[i]); }else{ raw_printf(p->out, "NULL"); } } raw_printf(p->out, "\n"); return SQLITE_OK; } #endif /* ** Print a schema statement. Part of MODE_Semi and MODE_Pretty output. ** ** This routine converts some CREATE TABLE statements for shadow tables ** in FTS3/4/5 into CREATE TABLE IF NOT EXISTS statements. */ static void printSchemaLine(FILE *out, const char *z, const char *zTail){ if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ utf8_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); }else{ utf8_printf(out, "%s%s", z, zTail); } } static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){ char c = z[n]; z[n] = 0; printSchemaLine(out, z, zTail); z[n] = c; } /* ** This is the callback routine that the shell ** invokes for each row of a query result. */ static int shell_callback( void *pArg, int nArg, /* Number of result columns */ char **azArg, /* Text of each result column */ char **azCol, /* Column names */ int *aiType /* Column types */ ){ int i; ShellState *p = (ShellState*)pArg; if( azArg==0 ) return 0; switch( p->cMode ){ case MODE_Line: { int w = 5; if( azArg==0 ) break; for(i=0; i<nArg; i++){ int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator); for(i=0; i<nArg; i++){ utf8_printf(p->out,"%*s = %s%s", w, azCol[i], azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); } break; } case MODE_Explain: case MODE_Column: { static const int aExplainWidths[] = {4, 13, 4, 4, 4, 13, 2, 13}; const int *colWidth; int showHdr; char *rowSep; if( p->cMode==MODE_Column ){ colWidth = p->colWidth; showHdr = p->showHeader; rowSep = p->rowSeparator; }else{ colWidth = aExplainWidths; showHdr = 1; rowSep = SEP_Row; } if( p->cnt++==0 ){ for(i=0; i<nArg; i++){ int w, n; if( i<ArraySize(p->colWidth) ){ w = colWidth[i]; }else{ w = 0; } if( w==0 ){ w = strlenChar(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; n = strlenChar(azArg && azArg[i] ? azArg[i] : p->nullValue); if( w<n ) w = n; } if( i<ArraySize(p->actualWidth) ){ p->actualWidth[i] = w; } if( showHdr ){ utf8_width_print(p->out, w, azCol[i]); utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " "); } } if( showHdr ){ for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; if( w<0 ) w = -w; }else{ w = 10; } utf8_printf(p->out,"%-*.*s%s",w,w, "----------------------------------------------------------" "----------------------------------------------------------", i==nArg-1 ? rowSep : " "); } } } if( azArg==0 ) break; for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; }else{ w = 10; } if( p->cMode==MODE_Explain && azArg[i] && strlenChar(azArg[i])>w ){ w = strlenChar(azArg[i]); } if( i==1 && p->aiIndent && p->pStmt ){ if( p->iIndent<p->nIndent ){ utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); } p->iIndent++; } utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue); utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " "); } break; } case MODE_Semi: { /* .schema and .fullschema output */ printSchemaLine(p->out, azArg[0], ";\n"); break; } case MODE_Pretty: { /* .schema and .fullschema with --indent */ char *z; int j; int nParen = 0; char cEnd = 0; char c; int nLine = 0; assert( nArg==1 ); if( azArg[0]==0 ) break; if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 ){ utf8_printf(p->out, "%s;\n", azArg[0]); break; } z = sqlite3_mprintf("%s", azArg[0]); j = 0; for(i=0; IsSpace(z[i]); i++){} for(; (c = z[i])!=0; i++){ if( IsSpace(c) ){ if( IsSpace(z[j-1]) || z[j-1]=='(' ) continue; }else if( (c=='(' || c==')') && j>0 && IsSpace(z[j-1]) ){ j--; } z[j++] = c; } while( j>0 && IsSpace(z[j-1]) ){ j--; } z[j] = 0; if( strlen30(z)>=79 ){ for(i=j=0; (c = z[i])!=0; i++){ if( c==cEnd ){ cEnd = 0; }else if( c=='"' || c=='\'' || c=='`' ){ cEnd = c; }else if( c=='[' ){ cEnd = ']'; }else if( c=='(' ){ nParen++; }else if( c==')' ){ nParen--; if( nLine>0 && nParen==0 && j>0 ){ printSchemaLineN(p->out, z, j, "\n"); j = 0; } } z[j++] = c; if( nParen==1 && (c=='(' || c==',' || c=='\n') ){ if( c=='\n' ) j--; printSchemaLineN(p->out, z, j, "\n "); j = 0; nLine++; while( IsSpace(z[i+1]) ){ i++; } } } z[j] = 0; } printSchemaLine(p->out, z, ";\n"); sqlite3_free(z); break; } case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ utf8_printf(p->out,"%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator); } } if( azArg==0 ) break; for(i=0; i<nArg; i++){ char *z = azArg[i]; if( z==0 ) z = p->nullValue; utf8_printf(p->out, "%s", z); if( i<nArg-1 ){ utf8_printf(p->out, "%s", p->colSeparator); }else{ utf8_printf(p->out, "%s", p->rowSeparator); } } break; } case MODE_Html: { if( p->cnt++==0 && p->showHeader ){ raw_printf(p->out,"<TR>"); for(i=0; i<nArg; i++){ raw_printf(p->out,"<TH>"); output_html_string(p->out, azCol[i]); raw_printf(p->out,"</TH>\n"); } raw_printf(p->out,"</TR>\n"); } if( azArg==0 ) break; raw_printf(p->out,"<TR>"); for(i=0; i<nArg; i++){ raw_printf(p->out,"<TD>"); output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); raw_printf(p->out,"</TD>\n"); } raw_printf(p->out,"</TR>\n"); break; } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ output_c_string(p->out,azCol[i] ? azCol[i] : ""); if(i<nArg-1) utf8_printf(p->out, "%s", p->colSeparator); } utf8_printf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; i<nArg; i++){ output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); if(i<nArg-1) utf8_printf(p->out, "%s", p->colSeparator); } utf8_printf(p->out, "%s", p->rowSeparator); break; } case MODE_Csv: { setBinaryMode(p->out, 1); if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); } utf8_printf(p->out, "%s", p->rowSeparator); } if( nArg>0 ){ for(i=0; i<nArg; i++){ output_csv(p, azArg[i], i<nArg-1); } utf8_printf(p->out, "%s", p->rowSeparator); } setTextMode(p->out, 1); break; } case MODE_Insert: { if( azArg==0 ) break; utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); if( p->showHeader ){ raw_printf(p->out,"("); for(i=0; i<nArg; i++){ if( i>0 ) raw_printf(p->out, ","); if( quoteChar(azCol[i]) ){ char *z = sqlite3_mprintf("\"%w\"", azCol[i]); utf8_printf(p->out, "%s", z); sqlite3_free(z); }else{ raw_printf(p->out, "%s", azCol[i]); } } raw_printf(p->out,")"); } p->cnt++; for(i=0; i<nArg; i++){ raw_printf(p->out, i>0 ? "," : " VALUES("); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ utf8_printf(p->out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ if( ShellHasFlag(p, SHFLG_Newlines) ){ output_quoted_string(p->out, azArg[i]); }else{ output_quoted_escaped_string(p->out, azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ utf8_printf(p->out,"%s", azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); raw_printf(p->out, "%s", z); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ utf8_printf(p->out,"%s", azArg[i]); }else if( ShellHasFlag(p, SHFLG_Newlines) ){ output_quoted_string(p->out, azArg[i]); }else{ output_quoted_escaped_string(p->out, azArg[i]); } } raw_printf(p->out,");\n"); break; } case MODE_Quote: { if( azArg==0 ) break; if( p->cnt==0 && p->showHeader ){ for(i=0; i<nArg; i++){ if( i>0 ) raw_printf(p->out, ","); output_quoted_string(p->out, azCol[i]); } raw_printf(p->out,"\n"); } p->cnt++; for(i=0; i<nArg; i++){ if( i>0 ) raw_printf(p->out, ","); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ utf8_printf(p->out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ output_quoted_string(p->out, azArg[i]); }else if( aiType && aiType[i]==SQLITE_INTEGER ){ utf8_printf(p->out,"%s", azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); raw_printf(p->out, "%s", z); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ utf8_printf(p->out,"%s", azArg[i]); }else{ output_quoted_string(p->out, azArg[i]); } } raw_printf(p->out,"\n"); break; } case MODE_Ascii: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator); utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : ""); } utf8_printf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; i<nArg; i++){ if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator); utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); } utf8_printf(p->out, "%s", p->rowSeparator); break; } } return 0; } /* ** This is the callback routine that the SQLite library ** invokes for each row of a query result. */ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ /* since we don't have type info, call the shell_callback with a NULL value */ return shell_callback(pArg, nArg, azArg, azCol, NULL); } /* ** This is the callback routine from sqlite3_exec() that appends all ** output onto the end of a ShellText object. */ static int captureOutputCallback(void *pArg, int nArg, char **azArg, char **az){ ShellText *p = (ShellText*)pArg; int i; UNUSED_PARAMETER(az); if( azArg==0 ) return 0; if( p->n ) appendText(p, "|", 0); for(i=0; i<nArg; i++){ if( i ) appendText(p, ",", 0); if( azArg[i] ) appendText(p, azArg[i], 0); } return 0; } /* ** Generate an appropriate SELFTEST table in the main database. */ static void createSelftestTable(ShellState *p){ char *zErrMsg = 0; sqlite3_exec(p->db, "SAVEPOINT selftest_init;\n" "CREATE TABLE IF NOT EXISTS selftest(\n" " tno INTEGER PRIMARY KEY,\n" /* Test number */ " op TEXT,\n" /* Operator: memo run */ " cmd TEXT,\n" /* Command text */ " ans TEXT\n" /* Desired answer */ ");" "CREATE TEMP TABLE [_shell$self](op,cmd,ans);\n" "INSERT INTO [_shell$self](rowid,op,cmd)\n" " VALUES(coalesce((SELECT (max(tno)+100)/10 FROM selftest),10),\n" " 'memo','Tests generated by --init');\n" "INSERT INTO [_shell$self]\n" " SELECT 'run',\n" " 'SELECT hex(sha3_query(''SELECT type,name,tbl_name,sql " "FROM sqlite_master ORDER BY 2'',224))',\n" " hex(sha3_query('SELECT type,name,tbl_name,sql " "FROM sqlite_master ORDER BY 2',224));\n" "INSERT INTO [_shell$self]\n" " SELECT 'run'," " 'SELECT hex(sha3_query(''SELECT * FROM \"' ||" " printf('%w',name) || '\" NOT INDEXED'',224))',\n" " hex(sha3_query(printf('SELECT * FROM \"%w\" NOT INDEXED',name),224))\n" " FROM (\n" " SELECT name FROM sqlite_master\n" " WHERE type='table'\n" " AND name<>'selftest'\n" " AND coalesce(rootpage,0)>0\n" " )\n" " ORDER BY name;\n" "INSERT INTO [_shell$self]\n" " VALUES('run','PRAGMA integrity_check','ok');\n" "INSERT INTO selftest(tno,op,cmd,ans)" " SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n" "DROP TABLE [_shell$self];" ,0,0,&zErrMsg); if( zErrMsg ){ utf8_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); sqlite3_free(zErrMsg); } sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); } /* ** Set the destination table field of the ShellState structure to ** the name of the table given. Escape any quote characters in the ** table name. */ static void set_table_name(ShellState *p, const char *zName){ int i, n; char cQuote; char *z; if( p->zDestTable ){ free(p->zDestTable); p->zDestTable = 0; } if( zName==0 ) return; cQuote = quoteChar(zName); n = strlen30(zName); if( cQuote ) n += n+2; z = p->zDestTable = malloc( n+1 ); if( z==0 ){ raw_printf(stderr,"Error: out of memory\n"); exit(1); } n = 0; if( cQuote ) z[n++] = cQuote; for(i=0; zName[i]; i++){ z[n++] = zName[i]; if( zName[i]==cQuote ) z[n++] = cQuote; } if( cQuote ) z[n++] = cQuote; z[n] = 0; } /* ** Execute a query statement that will generate SQL output. Print ** the result columns, comma-separated, on a line and then add a ** semicolon terminator to the end of that line. ** ** If the number of columns is 1 and that column contains text "--" ** then write the semicolon on a separate line. That way, if a ** "--" comment occurs at the end of the statement, the comment ** won't consume the semicolon terminator. */ static int run_table_dump_query( ShellState *p, /* Query context */ const char *zSelect, /* SELECT statement to extract content */ const char *zFirstRow /* Print before first row, if not NULL */ ){ sqlite3_stmt *pSelect; int rc; int nResult; int i; const char *z; rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; return rc; } rc = sqlite3_step(pSelect); nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ if( zFirstRow ){ utf8_printf(p->out, "%s", zFirstRow); zFirstRow = 0; } z = (const char*)sqlite3_column_text(pSelect, 0); utf8_printf(p->out, "%s", z); for(i=1; i<nResult; i++){ utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i)); } if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ raw_printf(p->out, "\n;\n"); }else{ raw_printf(p->out, ";\n"); } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; } return rc; } /* ** Allocate space and save off current error string. */ static char *save_err_msg( sqlite3 *db /* Database to query */ ){ int nErrMsg = 1+strlen30(sqlite3_errmsg(db)); char *zErrMsg = sqlite3_malloc64(nErrMsg); if( zErrMsg ){ memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg); } return zErrMsg; } #ifdef __linux__ /* ** Attempt to display I/O stats on Linux using /proc/PID/io */ static void displayLinuxIoStats(FILE *out){ FILE *in; char z[200]; sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); in = fopen(z, "rb"); if( in==0 ) return; while( fgets(z, sizeof(z), in)!=0 ){ static const struct { const char *zPattern; const char *zDesc; } aTrans[] = { { "rchar: ", "Bytes received by read():" }, { "wchar: ", "Bytes sent to write():" }, { "syscr: ", "Read() system calls:" }, { "syscw: ", "Write() system calls:" }, { "read_bytes: ", "Bytes read from storage:" }, { "write_bytes: ", "Bytes written to storage:" }, { "cancelled_write_bytes: ", "Cancelled write bytes:" }, }; int i; for(i=0; i<ArraySize(aTrans); i++){ int n = (int)strlen(aTrans[i].zPattern); if( strncmp(aTrans[i].zPattern, z, n)==0 ){ utf8_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]); break; } } } fclose(in); } #endif /* ** Display a single line of status using 64-bit values. */ static void displayStatLine( ShellState *p, /* The shell context */ char *zLabel, /* Label for this one line */ char *zFormat, /* Format for the result */ int iStatusCtrl, /* Which status to display */ int bReset /* True to reset the stats */ ){ sqlite3_int64 iCur = -1; sqlite3_int64 iHiwtr = -1; int i, nPercent; char zLine[200]; sqlite3_status64(iStatusCtrl, &iCur, &iHiwtr, bReset); for(i=0, nPercent=0; zFormat[i]; i++){ if( zFormat[i]=='%' ) nPercent++; } if( nPercent>1 ){ sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iCur, iHiwtr); }else{ sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); } raw_printf(p->out, "%-36s %s\n", zLabel, zLine); } /* ** Display memory stats. */ static int display_stats( sqlite3 *db, /* Database to query */ ShellState *pArg, /* Pointer to ShellState */ int bReset /* True to reset the stats */ ){ int iCur; int iHiwtr; if( pArg && pArg->out ){ displayStatLine(pArg, "Memory Used:", "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); displayStatLine(pArg, "Number of Outstanding Allocations:", "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); if( pArg->shellFlgs & SHFLG_Pagecache ){ displayStatLine(pArg, "Number of Pcache Pages Used:", "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); } displayStatLine(pArg, "Number of Pcache Overflow Bytes:", "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); displayStatLine(pArg, "Largest Allocation:", "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); displayStatLine(pArg, "Largest Pcache Allocation:", "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); #ifdef YYTRACKMAXSTACKDEPTH displayStatLine(pArg, "Deepest Parser Stack:", "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); #endif } if( pArg && pArg->out && db ){ if( pArg->shellFlgs & SHFLG_Lookaside ){ iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); raw_printf(pArg->out, "Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); raw_printf(pArg->out, "Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); raw_printf(pArg->out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur); } if( pArg && pArg->out && db && pArg->pStmt ){ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); raw_printf(pArg->out, "Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur); } #ifdef __linux__ displayLinuxIoStats(pArg->out); #endif /* Do not remove this machine readable comment: extra-stats-output-here */ return 0; } /* ** Display scan stats. */ static void display_scanstats( sqlite3 *db, /* Database to query */ ShellState *pArg /* Pointer to ShellState */ ){ #ifndef SQLITE_ENABLE_STMT_SCANSTATUS UNUSED_PARAMETER(db); UNUSED_PARAMETER(pArg); #else int i, k, n, mx; raw_printf(pArg->out, "-------- scanstats --------\n"); mx = 0; for(k=0; k<=mx; k++){ double rEstLoop = 1.0; for(i=n=0; 1; i++){ sqlite3_stmt *p = pArg->pStmt; sqlite3_int64 nLoop, nVisit; double rEst; int iSid; const char *zExplain; if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){ break; } sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_SELECTID, (void*)&iSid); if( iSid>mx ) mx = iSid; if( iSid!=k ) continue; if( n==0 ){ rEstLoop = (double)nLoop; if( k>0 ) raw_printf(pArg->out, "-------- subquery %d -------\n", k); } n++; sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); utf8_printf(pArg->out, "Loop %2d: %s\n", n, zExplain); rEstLoop *= rEst; raw_printf(pArg->out, " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst ); } } raw_printf(pArg->out, "---------------------------\n"); #endif } /* ** Parameter azArray points to a zero-terminated array of strings. zStr ** points to a single nul-terminated string. Return non-zero if zStr ** is equal, according to strcmp(), to any of the strings in the array. ** Otherwise, return zero. */ static int str_in_array(const char *zStr, const char **azArray){ int i; for(i=0; azArray[i]; i++){ if( 0==strcmp(zStr, azArray[i]) ) return 1; } return 0; } /* ** If compiled statement pSql appears to be an EXPLAIN statement, allocate ** and populate the ShellState.aiIndent[] array with the number of ** spaces each opcode should be indented before it is output. ** ** The indenting rules are: ** ** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent ** all opcodes that occur between the p2 jump destination and the opcode ** itself by 2 spaces. ** ** * For each "Goto", if the jump destination is earlier in the program ** and ends on one of: ** Yield SeekGt SeekLt RowSetRead Rewind ** or if the P1 parameter is one instead of zero, ** then indent all opcodes between the earlier instruction ** and "Goto" by 2 spaces. */ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ const char *zSql; /* The text of the SQL statement */ const char *z; /* Used to check if this is an EXPLAIN */ int *abYield = 0; /* True if op is an OP_Yield */ int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ int iOp; /* Index of operation in p->aiIndent[] */ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", "NextIfOpen", "PrevIfOpen", 0 }; const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 }; const char *azGoto[] = { "Goto", 0 }; /* Try to figure out if this is really an EXPLAIN statement. If this ** cannot be verified, return early. */ if( sqlite3_column_count(pSql)!=8 ){ p->cMode = p->mode; return; } zSql = sqlite3_sql(pSql); if( zSql==0 ) return; for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++); if( sqlite3_strnicmp(z, "explain", 7) ){ p->cMode = p->mode; return; } for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ int i; int iAddr = sqlite3_column_int(pSql, 0); const char *zOp = (const char*)sqlite3_column_text(pSql, 1); /* Set p2 to the P2 field of the current opcode. Then, assuming that ** p2 is an instruction address, set variable p2op to the index of that ** instruction in the aiIndent[] array. p2 and p2op may be different if ** the current instruction is part of a sub-program generated by an ** SQL trigger or foreign key. */ int p2 = sqlite3_column_int(pSql, 3); int p2op = (p2 + (iOp-iAddr)); /* Grow the p->aiIndent array as required */ if( iOp>=nAlloc ){ if( iOp==0 ){ /* Do further verfication that this is explain output. Abort if ** it is not */ static const char *explainCols[] = { "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment" }; int jj; for(jj=0; jj<ArraySize(explainCols); jj++){ if( strcmp(sqlite3_column_name(pSql,jj),explainCols[jj])!=0 ){ p->cMode = p->mode; sqlite3_reset(pSql); return; } } } nAlloc += 100; p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); } abYield[iOp] = str_in_array(zOp, azYield); p->aiIndent[iOp] = 0; p->nIndent = iOp+1; if( str_in_array(zOp, azNext) ){ for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; } if( str_in_array(zOp, azGoto) && p2op<p->nIndent && (abYield[p2op] || sqlite3_column_int(pSql, 2)) ){ for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; } } p->iIndent = 0; sqlite3_free(abYield); sqlite3_reset(pSql); } /* ** Free the array allocated by explain_data_prepare(). */ static void explain_data_delete(ShellState *p){ sqlite3_free(p->aiIndent); p->aiIndent = 0; p->nIndent = 0; p->iIndent = 0; } /* ** Disable and restore .wheretrace and .selecttrace settings. */ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) extern int sqlite3SelectTrace; static int savedSelectTrace; #endif #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) extern int sqlite3WhereTrace; static int savedWhereTrace; #endif static void disable_debug_trace_modes(void){ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) savedSelectTrace = sqlite3SelectTrace; sqlite3SelectTrace = 0; #endif #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) savedWhereTrace = sqlite3WhereTrace; sqlite3WhereTrace = 0; #endif } static void restore_debug_trace_modes(void){ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) sqlite3SelectTrace = savedSelectTrace; #endif #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) sqlite3WhereTrace = savedWhereTrace; #endif } /* ** Run a prepared statement */ static void exec_prepared_stmt( ShellState *pArg, /* Pointer to ShellState */ sqlite3_stmt *pStmt, /* Statment to run */ int (*xCallback)(void*,int,char**,char**,int*) /* Callback function */ ){ int rc; /* perform the first step. this will tell us if we ** have a result set or not and how wide it is. */ rc = sqlite3_step(pStmt); /* if we have a result set... */ if( SQLITE_ROW == rc ){ /* if we have a callback... */ if( xCallback ){ /* allocate space for col name ptr, value ptr, and type */ int nCol = sqlite3_column_count(pStmt); void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1); if( !pData ){ rc = SQLITE_NOMEM; }else{ char **azCols = (char **)pData; /* Names of result columns */ char **azVals = &azCols[nCol]; /* Results */ int *aiTypes = (int *)&azVals[nCol]; /* Result types */ int i, x; assert(sizeof(int) <= sizeof(char *)); /* save off ptrs to column names */ for(i=0; i<nCol; i++){ azCols[i] = (char *)sqlite3_column_name(pStmt, i); } do{ /* extract the data and data types */ for(i=0; i<nCol; i++){ aiTypes[i] = x = sqlite3_column_type(pStmt, i); if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){ azVals[i] = ""; }else{ azVals[i] = (char*)sqlite3_column_text(pStmt, i); } if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ rc = SQLITE_NOMEM; break; /* from for */ } } /* end for */ /* if data and types extracted successfully... */ if( SQLITE_ROW == rc ){ /* call the supplied callback with the result row data */ if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){ rc = SQLITE_ABORT; }else{ rc = sqlite3_step(pStmt); } } } while( SQLITE_ROW == rc ); sqlite3_free(pData); } }else{ do{ rc = sqlite3_step(pStmt); } while( rc == SQLITE_ROW ); } } } /* ** Execute a statement or set of statements. Print ** any result rows/columns depending on the current mode ** set via the supplied callback. ** ** This is very similar to SQLite's built-in sqlite3_exec() ** function except it takes a slightly different callback ** and callback data argument. */ static int shell_exec( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */ /* (not the same as sqlite3_exec) */ ShellState *pArg, /* Pointer to ShellState */ char **pzErrMsg /* Error msg written here */ ){ sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ int rc = SQLITE_OK; /* Return Code */ int rc2; const char *zLeftover; /* Tail of unprocessed SQL */ if( pzErrMsg ){ *pzErrMsg = NULL; } while( zSql[0] && (SQLITE_OK == rc) ){ static const char *zStmtSql; rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); if( SQLITE_OK != rc ){ if( pzErrMsg ){ *pzErrMsg = save_err_msg(db); } }else{ if( !pStmt ){ /* this happens for a comment or white-space */ zSql = zLeftover; while( IsSpace(zSql[0]) ) zSql++; continue; } zStmtSql = sqlite3_sql(pStmt); if( zStmtSql==0 ) zStmtSql = ""; while( IsSpace(zStmtSql[0]) ) zStmtSql++; /* save off the prepared statment handle and reset row count */ if( pArg ){ pArg->pStmt = pStmt; pArg->cnt = 0; } /* echo the sql statement if echo on */ if( pArg && ShellHasFlag(pArg, SHFLG_Echo) ){ utf8_printf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); } /* Show the EXPLAIN QUERY PLAN if .eqp is on */ if( pArg && pArg->autoEQP && sqlite3_strlike("EXPLAIN%",zStmtSql,0)!=0 ){ sqlite3_stmt *pExplain; char *zEQP; disable_debug_trace_modes(); zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc==SQLITE_OK ){ while( sqlite3_step(pExplain)==SQLITE_ROW ){ raw_printf(pArg->out,"--EQP-- %d,",sqlite3_column_int(pExplain, 0)); raw_printf(pArg->out,"%d,", sqlite3_column_int(pExplain, 1)); raw_printf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2)); utf8_printf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3)); } } sqlite3_finalize(pExplain); sqlite3_free(zEQP); if( pArg->autoEQP>=2 ){ /* Also do an EXPLAIN for ".eqp full" mode */ zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc==SQLITE_OK ){ pArg->cMode = MODE_Explain; explain_data_prepare(pArg, pExplain); exec_prepared_stmt(pArg, pExplain, xCallback); explain_data_delete(pArg); } sqlite3_finalize(pExplain); sqlite3_free(zEQP); } restore_debug_trace_modes(); } if( pArg ){ pArg->cMode = pArg->mode; if( pArg->autoExplain && sqlite3_column_count(pStmt)==8 && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0 ){ pArg->cMode = MODE_Explain; } /* If the shell is currently in ".explain" mode, gather the extra ** data required to add indents to the output.*/ if( pArg->cMode==MODE_Explain ){ explain_data_prepare(pArg, pStmt); } } exec_prepared_stmt(pArg, pStmt, xCallback); explain_data_delete(pArg); /* print usage stats if stats on */ if( pArg && pArg->statsOn ){ display_stats(db, pArg, 0); } /* print loop-counters if required */ if( pArg && pArg->scanstatsOn ){ display_scanstats(db, pArg); } /* Finalize the statement just executed. If this fails, save a ** copy of the error message. Otherwise, set zSql to point to the ** next statement to execute. */ rc2 = sqlite3_finalize(pStmt); if( rc!=SQLITE_NOMEM ) rc = rc2; if( rc==SQLITE_OK ){ zSql = zLeftover; while( IsSpace(zSql[0]) ) zSql++; }else if( pzErrMsg ){ *pzErrMsg = save_err_msg(db); } /* clear saved stmt handle */ if( pArg ){ pArg->pStmt = NULL; } } } /* end while */ return rc; } /* ** Release memory previously allocated by tableColumnList(). */ static void freeColumnList(char **azCol){ int i; for(i=1; azCol[i]; i++){ sqlite3_free(azCol[i]); } /* azCol[0] is a static string */ sqlite3_free(azCol); } /* ** Return a list of pointers to strings which are the names of all ** columns in table zTab. The memory to hold the names is dynamically ** allocated and must be released by the caller using a subsequent call ** to freeColumnList(). ** ** The azCol[0] entry is usually NULL. However, if zTab contains a rowid ** value that needs to be preserved, then azCol[0] is filled in with the ** name of the rowid column. ** ** The first regular column in the table is azCol[1]. The list is terminated ** by an entry with azCol[i]==0. */ static char **tableColumnList(ShellState *p, const char *zTab){ char **azCol = 0; sqlite3_stmt *pStmt; char *zSql; int nCol = 0; int nAlloc = 0; int nPK = 0; /* Number of PRIMARY KEY columns seen */ int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */ int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid); int rc; zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ) return 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nCol>=nAlloc-2 ){ nAlloc = nAlloc*2 + nCol + 10; azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0])); if( azCol==0 ){ raw_printf(stderr, "Error: out of memory\n"); exit(1); } } azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); if( sqlite3_column_int(pStmt, 5) ){ nPK++; if( nPK==1 && sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2), "INTEGER")==0 ){ isIPK = 1; }else{ isIPK = 0; } } } sqlite3_finalize(pStmt); azCol[0] = 0; azCol[nCol+1] = 0; /* The decision of whether or not a rowid really needs to be preserved ** is tricky. We never need to preserve a rowid for a WITHOUT ROWID table ** or a table with an INTEGER PRIMARY KEY. We are unable to preserve ** rowids on tables where the rowid is inaccessible because there are other ** columns in the table named "rowid", "_rowid_", and "oid". */ if( preserveRowid && isIPK ){ /* If a single PRIMARY KEY column with type INTEGER was seen, then it ** might be an alise for the ROWID. But it might also be a WITHOUT ROWID ** table or a INTEGER PRIMARY KEY DESC column, neither of which are ** ROWID aliases. To distinguish these cases, check to see if ** there is a "pk" entry in "PRAGMA index_list". There will be ** no "pk" index if the PRIMARY KEY really is an alias for the ROWID. */ zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)" " WHERE origin='pk'", zTab); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ freeColumnList(azCol); return 0; } rc = sqlite3_step(pStmt); sqlite3_finalize(pStmt); preserveRowid = rc==SQLITE_ROW; } if( preserveRowid ){ /* Only preserve the rowid if we can find a name to use for the ** rowid */ static char *azRowid[] = { "rowid", "_rowid_", "oid" }; int i, j; for(j=0; j<3; j++){ for(i=1; i<=nCol; i++){ if( sqlite3_stricmp(azRowid[j],azCol[i])==0 ) break; } if( i>nCol ){ /* At this point, we know that azRowid[j] is not the name of any ** ordinary column in the table. Verify that azRowid[j] is a valid ** name for the rowid before adding it to azCol[0]. WITHOUT ROWID ** tables will fail this last check */ rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0); if( rc==SQLITE_OK ) azCol[0] = azRowid[j]; break; } } } return azCol; } /* ** Toggle the reverse_unordered_selects setting. */ static void toggleSelectOrder(sqlite3 *db){ sqlite3_stmt *pStmt = 0; int iSetting = 0; char zStmt[100]; sqlite3_prepare_v2(db, "PRAGMA reverse_unordered_selects", -1, &pStmt, 0); if( sqlite3_step(pStmt)==SQLITE_ROW ){ iSetting = sqlite3_column_int(pStmt, 0); } sqlite3_finalize(pStmt); sqlite3_snprintf(sizeof(zStmt), zStmt, "PRAGMA reverse_unordered_selects(%d)", !iSetting); sqlite3_exec(db, zStmt, 0, 0, 0); } /* ** This is a different callback routine used for dumping the database. ** Each row received by this callback consists of a table name, ** the table type ("index" or "table") and SQL to create the table. ** This routine should print text sufficient to recreate the table. */ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ int rc; const char *zTable; const char *zType; const char *zSql; ShellState *p = (ShellState *)pArg; UNUSED_PARAMETER(azNotUsed); if( nArg!=3 || azArg==0 ) return 0; zTable = azArg[0]; zType = azArg[1]; zSql = azArg[2]; if( strcmp(zTable, "sqlite_sequence")==0 ){ raw_printf(p->out, "DELETE FROM sqlite_sequence;\n"); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){ raw_printf(p->out, "ANALYZE sqlite_master;\n"); }else if( strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ raw_printf(p->out, "PRAGMA writable_schema=ON;\n"); p->writableSchema = 1; } zIns = sqlite3_mprintf( "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); utf8_printf(p->out, "%s\n", zIns); sqlite3_free(zIns); return 0; }else{ printSchemaLine(p->out, zSql, ";\n"); } if( strcmp(zType, "table")==0 ){ ShellText sSelect; ShellText sTable; char **azCol; int i; char *savedDestTable; int savedMode; azCol = tableColumnList(p, zTable); if( azCol==0 ){ p->nErr++; return 0; } /* Always quote the table name, even if it appears to be pure ascii, ** in case it is a keyword. Ex: INSERT INTO "table" ... */ initText(&sTable); appendText(&sTable, zTable, quoteChar(zTable)); /* If preserving the rowid, add a column list after the table name. ** In other words: "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)" ** instead of the usual "INSERT INTO tab VALUES(...)". */ if( azCol[0] ){ appendText(&sTable, "(", 0); appendText(&sTable, azCol[0], 0); for(i=1; azCol[i]; i++){ appendText(&sTable, ",", 0); appendText(&sTable, azCol[i], quoteChar(azCol[i])); } appendText(&sTable, ")", 0); } /* Build an appropriate SELECT statement */ initText(&sSelect); appendText(&sSelect, "SELECT ", 0); if( azCol[0] ){ appendText(&sSelect, azCol[0], 0); appendText(&sSelect, ",", 0); } for(i=1; azCol[i]; i++){ appendText(&sSelect, azCol[i], quoteChar(azCol[i])); if( azCol[i+1] ){ appendText(&sSelect, ",", 0); } } freeColumnList(azCol); appendText(&sSelect, " FROM ", 0); appendText(&sSelect, zTable, quoteChar(zTable)); savedDestTable = p->zDestTable; savedMode = p->mode; p->zDestTable = sTable.z; p->mode = p->cMode = MODE_Insert; rc = shell_exec(p->db, sSelect.z, shell_callback, p, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); toggleSelectOrder(p->db); shell_exec(p->db, sSelect.z, shell_callback, p, 0); toggleSelectOrder(p->db); } p->zDestTable = savedDestTable; p->mode = savedMode; freeText(&sTable); freeText(&sSelect); if( rc ) p->nErr++; } return 0; } /* ** Run zQuery. Use dump_callback() as the callback routine so that ** the contents of the query are output as SQL statements. ** ** If we get a SQLITE_CORRUPT error, rerun the query after appending ** "ORDER BY rowid DESC" to the end. */ static int run_schema_dump_query( ShellState *p, const char *zQuery ){ int rc; char *zErr = 0; rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); if( zErr ){ utf8_printf(p->out, "/****** %s ******/\n", zErr); sqlite3_free(zErr); zErr = 0; } zQ2 = malloc( len+100 ); if( zQ2==0 ) return rc; sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); if( rc ){ utf8_printf(p->out, "/****** ERROR: %s ******/\n", zErr); }else{ rc = SQLITE_CORRUPT; } sqlite3_free(zErr); free(zQ2); } return rc; } /* ** Text of a help message */ static char zHelp[] = #ifndef SQLITE_OMIT_AUTHORIZATION ".auth ON|OFF Show authorizer callbacks\n" #endif ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" ".bail on|off Stop after hitting an error. Default OFF\n" ".binary on|off Turn binary output on or off. Default OFF\n" ".cd DIRECTORY Change the working directory to DIRECTORY\n" ".changes on|off Show number of rows changed by SQL\n" ".check GLOB Fail if output since .testcase does not match\n" ".clone NEWDB Clone data into NEWDB from the existing database\n" ".databases List names and files of attached databases\n" ".dbinfo ?DB? Show status information about the database\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" " If TABLE specified, only dump tables matching\n" " LIKE pattern TABLE.\n" ".echo on|off Turn command echo on or off\n" ".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN\n" ".exit Exit this program\n" /* Because explain mode comes on automatically now, the ".explain" mode ** is removed from the help screen. It is still supported for legacy, however */ /*".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic\n"*/ ".fullschema ?--indent? Show schema and the content of sqlite_stat tables\n" ".headers on|off Turn display of headers on or off\n" ".help Show this message\n" ".import FILE TABLE Import data from FILE into TABLE\n" #ifndef SQLITE_OMIT_TEST_CONTROL ".imposter INDEX TABLE Create imposter table TABLE on index INDEX\n" #endif ".indexes ?TABLE? Show names of all indexes\n" " If TABLE specified, only show indexes for tables\n" " matching LIKE pattern TABLE.\n" #ifdef SQLITE_ENABLE_IOTRACE ".iotrace FILE Enable I/O diagnostic logging to FILE\n" #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT\n" ".lint OPTIONS Report potential schema issues. Options:\n" " fkey-indexes Find missing foreign key indexes\n" #ifndef SQLITE_OMIT_LOAD_EXTENSION ".load FILE ?ENTRY? Load an extension library\n" #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" " ascii Columns/rows delimited by 0x1F and 0x1E\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" " html HTML <table> code\n" " insert SQL insert statements for TABLE\n" " line One value per line\n" " list Values delimited by \"|\"\n" " quote Escape answers as for SQL\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" ".nullvalue STRING Use STRING in place of NULL values\n" ".once FILENAME Output for the next SQL command only to FILENAME\n" ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE\n" " The --new option starts with an empty file\n" ".output ?FILENAME? Send output to FILENAME or stdout\n" ".print STRING... Print literal STRING\n" ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".save FILE Write in-memory database into FILE\n" ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n" ".schema ?PATTERN? Show the CREATE statements matching PATTERN\n" " Add --indent for pretty-printing\n" ".selftest ?--init? Run tests defined in the SELFTEST table\n" ".separator COL ?ROW? Change the column separator and optionally the row\n" " separator for both the output mode and .import\n" #if defined(SQLITE_ENABLE_SESSION) ".session CMD ... Create or control sessions\n" #endif ".sha3sum ?OPTIONS...? Compute a SHA3 hash of database content\n" ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" ".show Show the current values for various settings\n" ".stats ?on|off? Show stats or turn stats on or off\n" ".system CMD ARGS... Run CMD ARGS... in a system shell\n" ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" ".testcase NAME Begin redirecting output to 'testcase-out.txt'\n" ".timeout MS Try opening locked tables for MS milliseconds\n" ".timer on|off Turn SQL timer on or off\n" ".trace FILE|off Output each SQL statement as it is run\n" ".vfsinfo ?AUX? Information about the top-level VFS\n" ".vfslist List all available VFSes\n" ".vfsname ?AUX? Print the name of the VFS stack\n" ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" " Negative values right-justify\n" ; #if defined(SQLITE_ENABLE_SESSION) /* ** Print help information for the ".sessions" command */ void session_help(ShellState *p){ raw_printf(p->out, ".session ?NAME? SUBCOMMAND ?ARGS...?\n" "If ?NAME? is omitted, the first defined session is used.\n" "Subcommands:\n" " attach TABLE Attach TABLE\n" " changeset FILE Write a changeset into FILE\n" " close Close one session\n" " enable ?BOOLEAN? Set or query the enable bit\n" " filter GLOB... Reject tables matching GLOBs\n" " indirect ?BOOLEAN? Mark or query the indirect status\n" " isempty Query whether the session is empty\n" " list List currently open session names\n" " open DB NAME Open a new session on DB\n" " patchset FILE Write a patchset into FILE\n" ); } #endif /* Forward reference */ static int process_input(ShellState *p, FILE *in); /* ** Read the content of file zName into memory obtained from sqlite3_malloc64() ** and return a pointer to the buffer. The caller is responsible for freeing ** the memory. ** ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes ** read. ** ** For convenience, a nul-terminator byte is always appended to the data read ** from the file before the buffer is returned. This byte is not included in ** the final value of (*pnByte), if applicable. ** ** NULL is returned if any error is encountered. The final value of *pnByte ** is undefined in this case. */ static char *readFile(const char *zName, int *pnByte){ FILE *in = fopen(zName, "rb"); long nIn; size_t nRead; char *pBuf; if( in==0 ) return 0; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); pBuf = sqlite3_malloc64( nIn+1 ); if( pBuf==0 ) return 0; nRead = fread(pBuf, nIn, 1, in); fclose(in); if( nRead!=1 ){ sqlite3_free(pBuf); return 0; } pBuf[nIn] = 0; if( pnByte ) *pnByte = nIn; return pBuf; } #if defined(SQLITE_ENABLE_SESSION) /* ** Close a single OpenSession object and release all of its associated ** resources. */ static void session_close(OpenSession *pSession){ int i; sqlite3session_delete(pSession->p); sqlite3_free(pSession->zName); for(i=0; i<pSession->nFilter; i++){ sqlite3_free(pSession->azFilter[i]); } sqlite3_free(pSession->azFilter); memset(pSession, 0, sizeof(OpenSession)); } #endif /* ** Close all OpenSession objects and release all associated resources. */ #if defined(SQLITE_ENABLE_SESSION) static void session_close_all(ShellState *p){ int i; for(i=0; i<p->nSession; i++){ session_close(&p->aSession[i]); } p->nSession = 0; } #else # define session_close_all(X) #endif /* ** Implementation of the xFilter function for an open session. Omit ** any tables named by ".session filter" but let all other table through. */ #if defined(SQLITE_ENABLE_SESSION) static int session_filter(void *pCtx, const char *zTab){ OpenSession *pSession = (OpenSession*)pCtx; int i; for(i=0; i<pSession->nFilter; i++){ if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0; } return 1; } #endif /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. */ static void open_db(ShellState *p, int keepAlive){ if( p->db==0 ){ sqlite3_initialize(); sqlite3_open(p->zDbFilename, &p->db); globalDb = p->db; if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", p->zDbFilename, sqlite3_errmsg(p->db)); if( keepAlive ) return; exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif sqlite3_fileio_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0); sqlite3_completion_init(p->db, 0, 0); sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0, shellAddSchemaName, 0, 0); } } #if HAVE_READLINE || HAVE_EDITLINE /* ** Readline completion callbacks */ static char *readline_completion_generator(const char *text, int state){ static sqlite3_stmt *pStmt = 0; char *zRet; if( state==0 ){ char *zSql; sqlite3_finalize(pStmt); zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" " FROM completion(%Q) ORDER BY 1", text); sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0); sqlite3_free(zSql); } if( sqlite3_step(pStmt)==SQLITE_ROW ){ zRet = strdup((const char*)sqlite3_column_text(pStmt, 0)); }else{ sqlite3_finalize(pStmt); pStmt = 0; zRet = 0; } return zRet; } static char **readline_completion(const char *zText, int iStart, int iEnd){ rl_attempted_completion_over = 1; return rl_completion_matches(zText, readline_completion_generator); } #elif HAVE_LINENOISE /* ** Linenoise completion callback */ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){ int nLine = (int)strlen(zLine); int i, iStart; sqlite3_stmt *pStmt = 0; char *zSql; char zBuf[1000]; if( nLine>sizeof(zBuf)-30 ) return; if( zLine[0]=='.' ) return; for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} if( i==nLine-1 ) return; iStart = i+1; memcpy(zBuf, zLine, iStart); zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" " FROM completion(%Q,%Q) ORDER BY 1", &zLine[iStart], zLine); sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0); sqlite3_free(zSql); sqlite3_exec(globalDb, "PRAGMA page_count", 0, 0, 0); /* Load the schema */ while( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zCompletion = (const char*)sqlite3_column_text(pStmt, 0); int nCompletion = sqlite3_column_bytes(pStmt, 0); if( iStart+nCompletion < sizeof(zBuf)-1 ){ memcpy(zBuf+iStart, zCompletion, nCompletion+1); linenoiseAddCompletion(lc, zBuf); } } sqlite3_finalize(pStmt); } #endif /* ** Do C-language style dequoting. ** ** \a -> alarm ** \b -> backspace ** \t -> tab ** \n -> newline ** \v -> vertical tab ** \f -> form feed ** \r -> carriage return ** \s -> space ** \" -> " ** \' -> ' ** \\ -> backslash ** \NNN -> ascii character NNN in octal */ static void resolve_backslashes(char *z){ int i, j; char c; while( *z && *z!='\\' ) z++; for(i=j=0; (c = z[i])!=0; i++, j++){ if( c=='\\' && z[i+1]!=0 ){ c = z[++i]; if( c=='a' ){ c = '\a'; }else if( c=='b' ){ c = '\b'; }else if( c=='t' ){ c = '\t'; }else if( c=='n' ){ c = '\n'; }else if( c=='v' ){ c = '\v'; }else if( c=='f' ){ c = '\f'; }else if( c=='r' ){ c = '\r'; }else if( c=='"' ){ c = '"'; }else if( c=='\'' ){ c = '\''; }else if( c=='\\' ){ c = '\\'; }else if( c>='0' && c<='7' ){ c -= '0'; if( z[i+1]>='0' && z[i+1]<='7' ){ i++; c = (c<<3) + z[i] - '0'; if( z[i+1]>='0' && z[i+1]<='7' ){ i++; c = (c<<3) + z[i] - '0'; } } } } z[j] = c; } if( j<i ) z[j] = 0; } /* ** Return the value of a hexadecimal digit. Return -1 if the input ** is not a hex digit. */ static int hexDigitValue(char c){ if( c>='0' && c<='9' ) return c - '0'; if( c>='a' && c<='f' ) return c - 'a' + 10; if( c>='A' && c<='F' ) return c - 'A' + 10; return -1; } /* ** Interpret zArg as an integer value, possibly with suffixes. */ static sqlite3_int64 integerValue(const char *zArg){ sqlite3_int64 v = 0; static const struct { char *zSuffix; int iMult; } aMult[] = { { "KiB", 1024 }, { "MiB", 1024*1024 }, { "GiB", 1024*1024*1024 }, { "KB", 1000 }, { "MB", 1000000 }, { "GB", 1000000000 }, { "K", 1000 }, { "M", 1000000 }, { "G", 1000000000 }, }; int i; int isNeg = 0; if( zArg[0]=='-' ){ isNeg = 1; zArg++; }else if( zArg[0]=='+' ){ zArg++; } if( zArg[0]=='0' && zArg[1]=='x' ){ int x; zArg += 2; while( (x = hexDigitValue(zArg[0]))>=0 ){ v = (v<<4) + x; zArg++; } }else{ while( IsDigit(zArg[0]) ){ v = v*10 + zArg[0] - '0'; zArg++; } } for(i=0; i<ArraySize(aMult); i++){ if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ v *= aMult[i].iMult; break; } } return isNeg? -v : v; } /* ** Interpret zArg as either an integer or a boolean value. Return 1 or 0 ** for TRUE and FALSE. Return the integer value if appropriate. */ static int booleanValue(const char *zArg){ int i; if( zArg[0]=='0' && zArg[1]=='x' ){ for(i=2; hexDigitValue(zArg[i])>=0; i++){} }else{ for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){} } if( i>0 && zArg[i]==0 ) return (int)(integerValue(zArg) & 0xffffffff); if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){ return 1; } if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ return 0; } utf8_printf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); return 0; } /* ** Set or clear a shell flag according to a boolean value. */ static void setOrClearFlag(ShellState *p, unsigned mFlag, const char *zArg){ if( booleanValue(zArg) ){ ShellSetFlag(p, mFlag); }else{ ShellClearFlag(p, mFlag); } } /* ** Close an output file, assuming it is not stderr or stdout */ static void output_file_close(FILE *f){ if( f && f!=stdout && f!=stderr ) fclose(f); } /* ** Try to open an output file. The names "stdout" and "stderr" are ** recognized and do the right thing. NULL is returned if the output ** filename is "off". */ static FILE *output_file_open(const char *zFile){ FILE *f; if( strcmp(zFile,"stdout")==0 ){ f = stdout; }else if( strcmp(zFile, "stderr")==0 ){ f = stderr; }else if( strcmp(zFile, "off")==0 ){ f = 0; }else{ f = fopen(zFile, "wb"); if( f==0 ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); } } return f; } #if !defined(SQLITE_UNTESTABLE) #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) /* ** A routine for handling output from sqlite3_trace(). */ static int sql_trace_callback( unsigned mType, void *pArg, void *pP, void *pX ){ FILE *f = (FILE*)pArg; UNUSED_PARAMETER(mType); UNUSED_PARAMETER(pP); if( f ){ const char *z = (const char*)pX; int i = (int)strlen(z); while( i>0 && z[i-1]==';' ){ i--; } utf8_printf(f, "%.*s;\n", i, z); } return 0; } #endif #endif /* ** A no-op routine that runs with the ".breakpoint" doc-command. This is ** a useful spot to set a debugger breakpoint. */ static void test_breakpoint(void){ static int nCall = 0; nCall++; } /* ** An object used to read a CSV and other files for import. */ typedef struct ImportCtx ImportCtx; struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int bNotFirst; /* True if one or more bytes already read */ int cTerm; /* Character that terminated the most recent field */ int cColSep; /* The column separator character. (Usually ",") */ int cRowSep; /* The row separator character. (Usually "\n") */ }; /* Append a single byte to z[] */ static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; p->z = sqlite3_realloc64(p->z, p->nAlloc); if( p->z==0 ){ raw_printf(stderr, "out of memory\n"); exit(1); } } p->z[p->n++] = (char)c; } /* Read a single field of CSV text. Compatible with rfc4180 and extended ** with the option of having a separator other than ",". ** ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc64(). ** + Use p->cSep as the column separator. The default is ",". ** + Use p->rSep as the row separator. The default is "\n". ** + Keep track of the line number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. ** + Report syntax errors on stderr */ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ int c; int cSep = p->cColSep; int rSep = p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ p->cTerm = EOF; return 0; } if( c=='"' ){ int pc, ppc; int startLine = p->nLine; int cQuote = c; pc = ppc = 0; while( 1 ){ c = fgetc(p->in); if( c==rSep ) p->nLine++; if( c==cQuote ){ if( pc==cQuote ){ pc = 0; continue; } } if( (c==cSep && pc==cQuote) || (c==rSep && pc==cQuote) || (c==rSep && pc=='\r' && ppc==cQuote) || (c==EOF && pc==cQuote) ){ do{ p->n--; }while( p->z[p->n]!=cQuote ); p->cTerm = c; break; } if( pc==cQuote && c!='\r' ){ utf8_printf(stderr, "%s:%d: unescaped %c character\n", p->zFile, p->nLine, cQuote); } if( c==EOF ){ utf8_printf(stderr, "%s:%d: unterminated %c-quoted field\n", p->zFile, startLine, cQuote); p->cTerm = c; break; } import_append_char(p, c); ppc = pc; pc = c; } }else{ /* If this is the first field being parsed and it begins with the ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ if( (c&0xff)==0xef && p->bNotFirst==0 ){ import_append_char(p, c); c = fgetc(p->in); if( (c&0xff)==0xbb ){ import_append_char(p, c); c = fgetc(p->in); if( (c&0xff)==0xbf ){ p->bNotFirst = 1; p->n = 0; return csv_read_one_field(p); } } } while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); c = fgetc(p->in); } if( c==rSep ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } p->cTerm = c; } if( p->z ) p->z[p->n] = 0; p->bNotFirst = 1; return p->z; } /* Read a single field of ASCII delimited text. ** ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc64(). ** + Use p->cSep as the column separator. The default is "\x1F". ** + Use p->rSep as the row separator. The default is "\x1E". ** + Keep track of the row number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. ** + Report syntax errors on stderr */ static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ int c; int cSep = p->cColSep; int rSep = p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ p->cTerm = EOF; return 0; } while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); c = fgetc(p->in); } if( c==rSep ){ p->nLine++; } p->cTerm = c; if( p->z ) p->z[p->n] = 0; return p->z; } /* ** Try to transfer data for table zTable. If an error is seen while ** moving forward, try to go backwards. The backwards movement won't ** work for WITHOUT ROWID tables. */ static void tryToCloneData( ShellState *p, sqlite3 *newDb, const char *zTable ){ sqlite3_stmt *pQuery = 0; sqlite3_stmt *pInsert = 0; char *zQuery = 0; char *zInsert = 0; int rc; int i, j, n; int nTable = (int)strlen(zTable); int k = 0; int cnt = 0; const int spinRate = 10000; zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ utf8_printf(stderr, "Error %d: %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_data_xfer; } n = sqlite3_column_count(pQuery); zInsert = sqlite3_malloc64(200 + nTable + n*3); if( zInsert==0 ){ raw_printf(stderr, "out of memory\n"); goto end_data_xfer; } sqlite3_snprintf(200+nTable,zInsert, "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable); i = (int)strlen(zInsert); for(j=1; j<n; j++){ memcpy(zInsert+i, ",?", 2); i += 2; } memcpy(zInsert+i, ");", 3); rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); if( rc ){ utf8_printf(stderr, "Error %d: %s on [%s]\n", sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zQuery); goto end_data_xfer; } for(k=0; k<2; k++){ while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ for(i=0; i<n; i++){ switch( sqlite3_column_type(pQuery, i) ){ case SQLITE_NULL: { sqlite3_bind_null(pInsert, i+1); break; } case SQLITE_INTEGER: { sqlite3_bind_int64(pInsert, i+1, sqlite3_column_int64(pQuery,i)); break; } case SQLITE_FLOAT: { sqlite3_bind_double(pInsert, i+1, sqlite3_column_double(pQuery,i)); break; } case SQLITE_TEXT: { sqlite3_bind_text(pInsert, i+1, (const char*)sqlite3_column_text(pQuery,i), -1, SQLITE_STATIC); break; } case SQLITE_BLOB: { sqlite3_bind_blob(pInsert, i+1, sqlite3_column_blob(pQuery,i), sqlite3_column_bytes(pQuery,i), SQLITE_STATIC); break; } } } /* End for */ rc = sqlite3_step(pInsert); if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ utf8_printf(stderr, "Error %d: %s\n", sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb)); } sqlite3_reset(pInsert); cnt++; if( (cnt%spinRate)==0 ){ printf("%c\b", "|/-\\"[(cnt/spinRate)%4]); fflush(stdout); } } /* End while */ if( rc==SQLITE_DONE ) break; sqlite3_finalize(pQuery); sqlite3_free(zQuery); zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", zTable); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ utf8_printf(stderr, "Warning: cannot step \"%s\" backwards", zTable); break; } } /* End for(k=0...) */ end_data_xfer: sqlite3_finalize(pQuery); sqlite3_finalize(pInsert); sqlite3_free(zQuery); sqlite3_free(zInsert); } /* ** Try to transfer all rows of the schema that match zWhere. For ** each row, invoke xForEach() on the object defined by that row. ** If an error is encountered while moving forward through the ** sqlite_master table, try again moving backwards. */ static void tryToCloneSchema( ShellState *p, sqlite3 *newDb, const char *zWhere, void (*xForEach)(ShellState*,sqlite3*,const char*) ){ sqlite3_stmt *pQuery = 0; char *zQuery = 0; int rc; const unsigned char *zName; const unsigned char *zSql; char *zErrMsg = 0; zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" " WHERE %s", zWhere); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ utf8_printf(stderr, "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_schema_xfer; } while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); printf("%s... ", zName); fflush(stdout); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); sqlite3_free(zErrMsg); zErrMsg = 0; } if( xForEach ){ xForEach(p, newDb, (const char*)zName); } printf("done\n"); } if( rc!=SQLITE_DONE ){ sqlite3_finalize(pQuery); sqlite3_free(zQuery); zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" " WHERE %s ORDER BY rowid DESC", zWhere); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ utf8_printf(stderr, "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_schema_xfer; } while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); printf("%s... ", zName); fflush(stdout); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); sqlite3_free(zErrMsg); zErrMsg = 0; } if( xForEach ){ xForEach(p, newDb, (const char*)zName); } printf("done\n"); } } end_schema_xfer: sqlite3_finalize(pQuery); sqlite3_free(zQuery); } /* ** Open a new database file named "zNewDb". Try to recover as much information ** as possible out of the main database (which might be corrupt) and write it ** into zNewDb. */ static void tryToClone(ShellState *p, const char *zNewDb){ int rc; sqlite3 *newDb = 0; if( access(zNewDb,0)==0 ){ utf8_printf(stderr, "File \"%s\" already exists.\n", zNewDb); return; } rc = sqlite3_open(zNewDb, &newDb); if( rc ){ utf8_printf(stderr, "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); }else{ sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); tryToCloneSchema(p, newDb, "type!='table'", 0); sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); } sqlite3_close(newDb); } /* ** Change the output file back to stdout */ static void output_reset(ShellState *p){ if( p->outfile[0]=='|' ){ #ifndef SQLITE_OMIT_POPEN pclose(p->out); #endif }else{ output_file_close(p->out); } p->outfile[0] = 0; p->out = stdout; } /* ** Run an SQL command and return the single integer result. */ static int db_int(ShellState *p, const char *zSql){ sqlite3_stmt *pStmt; int res = 0; sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ res = sqlite3_column_int(pStmt,0); } sqlite3_finalize(pStmt); return res; } /* ** Convert a 2-byte or 4-byte big-endian integer into a native integer */ static unsigned int get2byteInt(unsigned char *a){ return (a[0]<<8) + a[1]; } static unsigned int get4byteInt(unsigned char *a){ return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3]; } /* ** Implementation of the ".info" command. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ static const struct { const char *zName; int ofst; } aField[] = { { "file change counter:", 24 }, { "database page count:", 28 }, { "freelist page count:", 36 }, { "schema cookie:", 40 }, { "schema format:", 44 }, { "default cache size:", 48 }, { "autovacuum top root:", 52 }, { "incremental vacuum:", 64 }, { "text encoding:", 56 }, { "user version:", 60 }, { "application id:", 68 }, { "software version:", 96 }, }; static const struct { const char *zName; const char *zSql; } aQuery[] = { { "number of tables:", "SELECT count(*) FROM %s WHERE type='table'" }, { "number of indexes:", "SELECT count(*) FROM %s WHERE type='index'" }, { "number of triggers:", "SELECT count(*) FROM %s WHERE type='trigger'" }, { "number of views:", "SELECT count(*) FROM %s WHERE type='view'" }, { "schema size:", "SELECT total(length(sql)) FROM %s" }, }; sqlite3_file *pFile = 0; int i; char *zSchemaTab; char *zDb = nArg>=2 ? azArg[1] : "main"; unsigned char aHdr[100]; open_db(p, 0); if( p->db==0 ) return 1; sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFile); if( pFile==0 || pFile->pMethods==0 || pFile->pMethods->xRead==0 ){ return 1; } i = pFile->pMethods->xRead(pFile, aHdr, 100, 0); if( i!=SQLITE_OK ){ raw_printf(stderr, "unable to read database header\n"); return 1; } i = get2byteInt(aHdr+16); if( i==1 ) i = 65536; utf8_printf(p->out, "%-20s %d\n", "database page size:", i); utf8_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]); utf8_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]); utf8_printf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); for(i=0; i<ArraySize(aField); i++){ int ofst = aField[i].ofst; unsigned int val = get4byteInt(aHdr + ofst); utf8_printf(p->out, "%-20s %u", aField[i].zName, val); switch( ofst ){ case 56: { if( val==1 ) raw_printf(p->out, " (utf8)"); if( val==2 ) raw_printf(p->out, " (utf16le)"); if( val==3 ) raw_printf(p->out, " (utf16be)"); } } raw_printf(p->out, "\n"); } if( zDb==0 ){ zSchemaTab = sqlite3_mprintf("main.sqlite_master"); }else if( strcmp(zDb,"temp")==0 ){ zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_master"); }else{ zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_master", zDb); } for(i=0; i<ArraySize(aQuery); i++){ char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); int val = db_int(p, zSql); sqlite3_free(zSql); utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); return 0; } /* ** Print the current sqlite3_errmsg() value to stderr and return 1. */ static int shellDatabaseError(sqlite3 *db){ const char *zErr = sqlite3_errmsg(db); utf8_printf(stderr, "Error: %s\n", zErr); return 1; } /* ** Print an out-of-memory message to stderr and return 1. */ static int shellNomemError(void){ raw_printf(stderr, "Error: out of memory\n"); return 1; } /* ** Compare the pattern in zGlob[] against the text in z[]. Return TRUE ** if they match and FALSE (0) if they do not match. ** ** Globbing rules: ** ** '*' Matches any sequence of zero or more characters. ** ** '?' Matches exactly one character. ** ** [...] Matches one character from the enclosed list of ** characters. ** ** [^...] Matches one character not in the enclosed list. ** ** '#' Matches any sequence of one or more digits with an ** optional + or - sign in front ** ** ' ' Any span of whitespace matches any other span of ** whitespace. ** ** Extra whitespace at the end of z[] is ignored. */ static int testcase_glob(const char *zGlob, const char *z){ int c, c2; int invert; int seen; while( (c = (*(zGlob++)))!=0 ){ if( IsSpace(c) ){ if( !IsSpace(*z) ) return 0; while( IsSpace(*zGlob) ) zGlob++; while( IsSpace(*z) ) z++; }else if( c=='*' ){ while( (c=(*(zGlob++))) == '*' || c=='?' ){ if( c=='?' && (*(z++))==0 ) return 0; } if( c==0 ){ return 1; }else if( c=='[' ){ while( *z && testcase_glob(zGlob-1,z)==0 ){ z++; } return (*z)!=0; } while( (c2 = (*(z++)))!=0 ){ while( c2!=c ){ c2 = *(z++); if( c2==0 ) return 0; } if( testcase_glob(zGlob,z) ) return 1; } return 0; }else if( c=='?' ){ if( (*(z++))==0 ) return 0; }else if( c=='[' ){ int prior_c = 0; seen = 0; invert = 0; c = *(z++); if( c==0 ) return 0; c2 = *(zGlob++); if( c2=='^' ){ invert = 1; c2 = *(zGlob++); } if( c2==']' ){ if( c==']' ) seen = 1; c2 = *(zGlob++); } while( c2 && c2!=']' ){ if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){ c2 = *(zGlob++); if( c>=prior_c && c<=c2 ) seen = 1; prior_c = 0; }else{ if( c==c2 ){ seen = 1; } prior_c = c2; } c2 = *(zGlob++); } if( c2==0 || (seen ^ invert)==0 ) return 0; }else if( c=='#' ){ if( (z[0]=='-' || z[0]=='+') && IsDigit(z[1]) ) z++; if( !IsDigit(z[0]) ) return 0; z++; while( IsDigit(z[0]) ){ z++; } }else{ if( c!=(*(z++)) ) return 0; } } while( IsSpace(*z) ){ z++; } return *z==0; } /* ** Compare the string as a command-line option with either one or two ** initial "-" characters. */ static int optionMatch(const char *zStr, const char *zOpt){ if( zStr[0]!='-' ) return 0; zStr++; if( zStr[0]=='-' ) zStr++; return strcmp(zStr, zOpt)==0; } /* ** Delete a file. */ int shellDeleteFile(const char *zFilename){ int rc; #ifdef _WIN32 wchar_t *z = sqlite3_win32_utf8_to_unicode(zFilename); rc = _wunlink(z); sqlite3_free(z); #else rc = unlink(zFilename); #endif return rc; } /* ** The implementation of SQL scalar function fkey_collate_clause(), used ** by the ".lint fkey-indexes" command. This scalar function is always ** called with four arguments - the parent table name, the parent column name, ** the child table name and the child column name. ** ** fkey_collate_clause('parent-tab', 'parent-col', 'child-tab', 'child-col') ** ** If either of the named tables or columns do not exist, this function ** returns an empty string. An empty string is also returned if both tables ** and columns exist but have the same default collation sequence. Or, ** if both exist but the default collation sequences are different, this ** function returns the string " COLLATE <parent-collation>", where ** <parent-collation> is the default collation sequence of the parent column. */ static void shellFkeyCollateClause( sqlite3_context *pCtx, int nVal, sqlite3_value **apVal ){ sqlite3 *db = sqlite3_context_db_handle(pCtx); const char *zParent; const char *zParentCol; const char *zParentSeq; const char *zChild; const char *zChildCol; const char *zChildSeq = 0; /* Initialize to avoid false-positive warning */ int rc; assert( nVal==4 ); zParent = (const char*)sqlite3_value_text(apVal[0]); zParentCol = (const char*)sqlite3_value_text(apVal[1]); zChild = (const char*)sqlite3_value_text(apVal[2]); zChildCol = (const char*)sqlite3_value_text(apVal[3]); sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); rc = sqlite3_table_column_metadata( db, "main", zParent, zParentCol, 0, &zParentSeq, 0, 0, 0 ); if( rc==SQLITE_OK ){ rc = sqlite3_table_column_metadata( db, "main", zChild, zChildCol, 0, &zChildSeq, 0, 0, 0 ); } if( rc==SQLITE_OK && sqlite3_stricmp(zParentSeq, zChildSeq) ){ char *z = sqlite3_mprintf(" COLLATE %s", zParentSeq); sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); sqlite3_free(z); } } /* ** The implementation of dot-command ".lint fkey-indexes". */ static int lintFkeyIndexes( ShellState *pState, /* Current shell tool state */ char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ sqlite3 *db = pState->db; /* Database handle to query "main" db of */ FILE *out = pState->out; /* Stream to write non-error output to */ int bVerbose = 0; /* If -verbose is present */ int bGroupByParent = 0; /* If -groupbyparent is present */ int i; /* To iterate through azArg[] */ const char *zIndent = ""; /* How much to indent CREATE INDEX by */ int rc; /* Return code */ sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ /* ** This SELECT statement returns one row for each foreign key constraint ** in the schema of the main database. The column values are: ** ** 0. The text of an SQL statement similar to: ** ** "EXPLAIN QUERY PLAN SELECT rowid FROM child_table WHERE child_key=?" ** ** This is the same SELECT that the foreign keys implementation needs ** to run internally on child tables. If there is an index that can ** be used to optimize this query, then it can also be used by the FK ** implementation to optimize DELETE or UPDATE statements on the parent ** table. ** ** 1. A GLOB pattern suitable for sqlite3_strglob(). If the plan output by ** the EXPLAIN QUERY PLAN command matches this pattern, then the schema ** contains an index that can be used to optimize the query. ** ** 2. Human readable text that describes the child table and columns. e.g. ** ** "child_table(child_key1, child_key2)" ** ** 3. Human readable text that describes the parent table and columns. e.g. ** ** "parent_table(parent_key1, parent_key2)" ** ** 4. A full CREATE INDEX statement for an index that could be used to ** optimize DELETE or UPDATE statements on the parent table. e.g. ** ** "CREATE INDEX child_table_child_key ON child_table(child_key)" ** ** 5. The name of the parent table. ** ** These six values are used by the C logic below to generate the report. */ const char *zSql = "SELECT " " 'EXPLAIN QUERY PLAN SELECT rowid FROM ' || quote(s.name) || ' WHERE '" " || group_concat(quote(s.name) || '.' || quote(f.[from]) || '=?' " " || fkey_collate_clause(" " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]),' AND ')" ", " " 'SEARCH TABLE ' || s.name || ' USING COVERING INDEX*('" " || group_concat('*=?', ' AND ') || ')'" ", " " s.name || '(' || group_concat(f.[from], ', ') || ')'" ", " " f.[table] || '(' || group_concat(COALESCE(f.[to], p.[name])) || ')'" ", " " 'CREATE INDEX ' || quote(s.name ||'_'|| group_concat(f.[from], '_'))" " || ' ON ' || quote(s.name) || '('" " || group_concat(quote(f.[from]) ||" " fkey_collate_clause(" " f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]), ', ')" " || ');'" ", " " f.[table] " "FROM sqlite_master AS s, pragma_foreign_key_list(s.name) AS f " "LEFT JOIN pragma_table_info AS p ON (pk-1=seq AND p.arg=f.[table]) " "GROUP BY s.name, f.id " "ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)" ; const char *zGlobIPK = "SEARCH TABLE * USING INTEGER PRIMARY KEY (rowid=?)"; for(i=2; i<nArg; i++){ int n = (int)strlen(azArg[i]); if( n>1 && sqlite3_strnicmp("-verbose", azArg[i], n)==0 ){ bVerbose = 1; } else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ bGroupByParent = 1; zIndent = " "; } else{ raw_printf(stderr, "Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1] ); return SQLITE_ERROR; } } /* Register the fkey_collate_clause() SQL function */ rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8, 0, shellFkeyCollateClause, 0, 0 ); if( rc==SQLITE_OK ){ rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0); } if( rc==SQLITE_OK ){ sqlite3_bind_int(pSql, 1, bGroupByParent); } if( rc==SQLITE_OK ){ int rc2; char *zPrev = 0; while( SQLITE_ROW==sqlite3_step(pSql) ){ int res = -1; sqlite3_stmt *pExplain = 0; const char *zEQP = (const char*)sqlite3_column_text(pSql, 0); const char *zGlob = (const char*)sqlite3_column_text(pSql, 1); const char *zFrom = (const char*)sqlite3_column_text(pSql, 2); const char *zTarget = (const char*)sqlite3_column_text(pSql, 3); const char *zCI = (const char*)sqlite3_column_text(pSql, 4); const char *zParent = (const char*)sqlite3_column_text(pSql, 5); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc!=SQLITE_OK ) break; if( SQLITE_ROW==sqlite3_step(pExplain) ){ const char *zPlan = (const char*)sqlite3_column_text(pExplain, 3); res = ( 0==sqlite3_strglob(zGlob, zPlan) || 0==sqlite3_strglob(zGlobIPK, zPlan) ); } rc = sqlite3_finalize(pExplain); if( rc!=SQLITE_OK ) break; if( res<0 ){ raw_printf(stderr, "Error: internal error"); break; }else{ if( bGroupByParent && (bVerbose || res==0) && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) ){ raw_printf(out, "-- Parent table %s\n", zParent); sqlite3_free(zPrev); zPrev = sqlite3_mprintf("%s", zParent); } if( res==0 ){ raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); }else if( bVerbose ){ raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n", zIndent, zFrom, zTarget ); } } } sqlite3_free(zPrev); if( rc!=SQLITE_OK ){ raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); } rc2 = sqlite3_finalize(pSql); if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ rc = rc2; raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); } }else{ raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); } return rc; } /* ** Implementation of ".lint" dot command. */ static int lintDotCommand( ShellState *pState, /* Current shell tool state */ char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ int n; n = (nArg>=2 ? (int)strlen(azArg[1]) : 0); if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage; return lintFkeyIndexes(pState, azArg, nArg); usage: raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]); raw_printf(stderr, "Where sub-commands are:\n"); raw_printf(stderr, " fkey-indexes\n"); return SQLITE_ERROR; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ static int do_meta_command(char *zLine, ShellState *p){ int h = 1; int nArg = 0; int n, c; int rc = 0; char *azArg[50]; /* Parse the input line into tokens. */ while( zLine[h] && nArg<ArraySize(azArg) ){ while( IsSpace(zLine[h]) ){ h++; } if( zLine[h]==0 ) break; if( zLine[h]=='\'' || zLine[h]=='"' ){ int delim = zLine[h++]; azArg[nArg++] = &zLine[h]; while( zLine[h] && zLine[h]!=delim ){ if( zLine[h]=='\\' && delim=='"' && zLine[h+1]!=0 ) h++; h++; } if( zLine[h]==delim ){ zLine[h++] = 0; } if( delim=='"' ) resolve_backslashes(azArg[nArg-1]); }else{ azArg[nArg++] = &zLine[h]; while( zLine[h] && !IsSpace(zLine[h]) ){ h++; } if( zLine[h] ) zLine[h++] = 0; resolve_backslashes(azArg[nArg-1]); } } /* Process the input line. */ if( nArg==0 ) return 0; /* no tokens, no error */ n = strlen30(azArg[0]); c = azArg[0][0]; #ifndef SQLITE_OMIT_AUTHORIZATION if( c=='a' && strncmp(azArg[0], "auth", n)==0 ){ if( nArg!=2 ){ raw_printf(stderr, "Usage: .auth ON|OFF\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); if( booleanValue(azArg[1]) ){ sqlite3_set_authorizer(p->db, shellAuth, p); }else{ sqlite3_set_authorizer(p->db, 0, 0); } }else #endif if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0) || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0) ){ const char *zDestFile = 0; const char *zDb = 0; sqlite3 *pDest; sqlite3_backup *pBackup; int j; for(j=1; j<nArg; j++){ const char *z = azArg[j]; if( z[0]=='-' ){ while( z[0]=='-' ) z++; /* No options to process at this time */ { utf8_printf(stderr, "unknown option: %s\n", azArg[j]); return 1; } }else if( zDestFile==0 ){ zDestFile = azArg[j]; }else if( zDb==0 ){ zDb = zDestFile; zDestFile = azArg[j]; }else{ raw_printf(stderr, "too many arguments to .backup\n"); return 1; } } if( zDestFile==0 ){ raw_printf(stderr, "missing FILENAME argument on .backup\n"); return 1; } if( zDb==0 ) zDb = "main"; rc = sqlite3_open(zDestFile, &pDest); if( rc!=SQLITE_OK ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile); sqlite3_close(pDest); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); sqlite3_close(pDest); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); rc = 1; } sqlite3_close(pDest); }else if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){ if( nArg==2 ){ bail_on_error = booleanValue(azArg[1]); }else{ raw_printf(stderr, "Usage: .bail on|off\n"); rc = 1; } }else if( c=='b' && n>=3 && strncmp(azArg[0], "binary", n)==0 ){ if( nArg==2 ){ if( booleanValue(azArg[1]) ){ setBinaryMode(p->out, 1); }else{ setTextMode(p->out, 1); } }else{ raw_printf(stderr, "Usage: .binary on|off\n"); rc = 1; } }else if( c=='c' && strcmp(azArg[0],"cd")==0 ){ if( nArg==2 ){ #if defined(_WIN32) || defined(WIN32) wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]); rc = !SetCurrentDirectoryW(z); sqlite3_free(z); #else rc = chdir(azArg[1]); #endif if( rc ){ utf8_printf(stderr, "Cannot change to directory \"%s\"\n", azArg[1]); rc = 1; } }else{ raw_printf(stderr, "Usage: .cd DIRECTORY\n"); rc = 1; } }else /* The undocumented ".breakpoint" command causes a call to the no-op ** routine named test_breakpoint(). */ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ test_breakpoint(); }else if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_CountChanges, azArg[1]); }else{ raw_printf(stderr, "Usage: .changes on|off\n"); rc = 1; } }else /* Cancel output redirection, if it is currently set (by .testcase) ** Then read the content of the testcase-out.txt file and compare against ** azArg[1]. If there are differences, report an error and exit. */ if( c=='c' && n>=3 && strncmp(azArg[0], "check", n)==0 ){ char *zRes = 0; output_reset(p); if( nArg!=2 ){ raw_printf(stderr, "Usage: .check GLOB-PATTERN\n"); rc = 2; }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ raw_printf(stderr, "Error: cannot read 'testcase-out.txt'\n"); rc = 2; }else if( testcase_glob(azArg[1],zRes)==0 ){ utf8_printf(stderr, "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", p->zTestcase, azArg[1], zRes); rc = 2; }else{ utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase); p->nCheck++; } sqlite3_free(zRes); }else if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){ if( nArg==2 ){ tryToClone(p, azArg[1]); }else{ raw_printf(stderr, "Usage: .clone FILENAME\n"); rc = 1; } }else if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){ ShellState data; char *zErrMsg = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.cMode = data.mode = MODE_List; sqlite3_snprintf(sizeof(data.colSeparator),data.colSeparator,": "); data.cnt = 0; sqlite3_exec(p->db, "SELECT name, file FROM pragma_database_list", callback, &data, &zErrMsg); if( zErrMsg ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else if( c=='d' && strncmp(azArg[0], "dbinfo", n)==0 ){ rc = shell_dbinfo_command(p, nArg, azArg); }else if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ const char *zLike = 0; int i; int savedShowHeader = p->showHeader; ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines); for(i=1; i<nArg; i++){ if( azArg[i][0]=='-' ){ const char *z = azArg[i]+1; if( z[0]=='-' ) z++; if( strcmp(z,"preserve-rowids")==0 ){ #ifdef SQLITE_OMIT_VIRTUALTABLE raw_printf(stderr, "The --preserve-rowids option is not compatible" " with SQLITE_OMIT_VIRTUALTABLE\n"); rc = 1; goto meta_command_exit; #else ShellSetFlag(p, SHFLG_PreserveRowid); #endif }else if( strcmp(z,"newlines")==0 ){ ShellSetFlag(p, SHFLG_Newlines); }else { raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); rc = 1; goto meta_command_exit; } }else if( zLike ){ raw_printf(stderr, "Usage: .dump ?--preserve-rowids? " "?--newlines? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; }else{ zLike = azArg[i]; } } open_db(p, 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. */ raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); raw_printf(p->out, "BEGIN TRANSACTION;\n"); p->writableSchema = 0; p->showHeader = 0; /* Set writable_schema=ON since doing so forces SQLite to initialize ** as much of the schema as it can even if the sqlite_master table is ** corrupt. */ sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); p->nErr = 0; if( zLike==0 ){ run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'" ); run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE name=='sqlite_sequence'" ); run_table_dump_query(p, "SELECT sql FROM sqlite_master " "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 ); }else{ char *zSql; zSql = sqlite3_mprintf( "SELECT name, type, sql FROM sqlite_master " "WHERE tbl_name LIKE %Q AND type=='table'" " AND sql NOT NULL", zLike); run_schema_dump_query(p,zSql); sqlite3_free(zSql); zSql = sqlite3_mprintf( "SELECT sql FROM sqlite_master " "WHERE sql NOT NULL" " AND type IN ('index','trigger','view')" " AND tbl_name LIKE %Q", zLike); run_table_dump_query(p, zSql, 0); sqlite3_free(zSql); } if( p->writableSchema ){ raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); raw_printf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n"); p->showHeader = savedShowHeader; }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_Echo, azArg[1]); }else{ raw_printf(stderr, "Usage: .echo on|off\n"); rc = 1; } }else if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){ if( nArg==2 ){ if( strcmp(azArg[1],"full")==0 ){ p->autoEQP = 2; }else{ p->autoEQP = booleanValue(azArg[1]); } }else{ raw_printf(stderr, "Usage: .eqp on|off|full\n"); rc = 1; } }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); rc = 2; }else /* The ".explain" command is automatic now. It is largely pointless. It ** retained purely for backwards compatibility */ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ int val = 1; if( nArg>=2 ){ if( strcmp(azArg[1],"auto")==0 ){ val = 99; }else{ val = booleanValue(azArg[1]); } } if( val==1 && p->mode!=MODE_Explain ){ p->normalMode = p->mode; p->mode = MODE_Explain; p->autoExplain = 0; }else if( val==0 ){ if( p->mode==MODE_Explain ) p->mode = p->normalMode; p->autoExplain = 0; }else if( val==99 ){ if( p->mode==MODE_Explain ) p->mode = p->normalMode; p->autoExplain = 1; } }else if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ ShellState data; char *zErrMsg = 0; int doStats = 0; memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.cMode = data.mode = MODE_Semi; if( nArg==2 && optionMatch(azArg[1], "indent") ){ data.cMode = data.mode = MODE_Pretty; nArg = 1; } if( nArg!=1 ){ raw_printf(stderr, "Usage: .fullschema ?--indent?\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); rc = sqlite3_exec(p->db, "SELECT sql FROM" " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " FROM sqlite_master UNION ALL" " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " "ORDER BY rowid", callback, &data, &zErrMsg ); if( rc==SQLITE_OK ){ sqlite3_stmt *pStmt; rc = sqlite3_prepare_v2(p->db, "SELECT rowid FROM sqlite_master" " WHERE name GLOB 'sqlite_stat[134]'", -1, &pStmt, 0); doStats = sqlite3_step(pStmt)==SQLITE_ROW; sqlite3_finalize(pStmt); } if( doStats==0 ){ raw_printf(p->out, "/* No STAT tables available */\n"); }else{ raw_printf(p->out, "ANALYZE sqlite_master;\n"); sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", callback, &data, &zErrMsg); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(p->db, "SELECT * FROM sqlite_stat1", shell_callback, &data,&zErrMsg); data.zDestTable = "sqlite_stat3"; shell_exec(p->db, "SELECT * FROM sqlite_stat3", shell_callback, &data,&zErrMsg); data.zDestTable = "sqlite_stat4"; shell_exec(p->db, "SELECT * FROM sqlite_stat4", shell_callback, &data, &zErrMsg); raw_printf(p->out, "ANALYZE sqlite_master;\n"); } }else if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ if( nArg==2 ){ p->showHeader = booleanValue(azArg[1]); }else{ raw_printf(stderr, "Usage: .headers on|off\n"); rc = 1; } }else if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ utf8_printf(p->out, "%s", zHelp); }else if( c=='i' && strncmp(azArg[0], "import", n)==0 ){ char *zTable; /* Insert data into this table */ char *zFile; /* Name of file to extra content from */ sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ int 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; /* An SQL statement */ ImportCtx sCtx; /* Reader context */ char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close file */ if( nArg!=3 ){ raw_printf(stderr, "Usage: .import FILE TABLE\n"); goto meta_command_exit; } zFile = azArg[1]; zTable = azArg[2]; seenInterrupt = 0; memset(&sCtx, 0, sizeof(sCtx)); open_db(p, 0); nSep = strlen30(p->colSeparator); if( nSep==0 ){ raw_printf(stderr, "Error: non-null column separator required for import\n"); return 1; } if( nSep>1 ){ raw_printf(stderr, "Error: multi-character column separators not allowed" " for import\n"); return 1; } nSep = strlen30(p->rowSeparator); if( nSep==0 ){ raw_printf(stderr, "Error: non-null row separator required for import\n"); return 1; } if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){ /* When importing CSV (only), if the row separator is set to the ** default output row separator, change it to the default input ** row separator. This avoids having to maintain different input ** and output row separators. */ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); nSep = strlen30(p->rowSeparator); } if( nSep>1 ){ raw_printf(stderr, "Error: multi-character row separators not allowed" " for import\n"); return 1; } sCtx.zFile = zFile; sCtx.nLine = 1; if( sCtx.zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN raw_printf(stderr, "Error: pipes are not supported in this OS\n"); return 1; #else sCtx.in = popen(sCtx.zFile+1, "r"); sCtx.zFile = "<pipe>"; xCloser = pclose; #endif }else{ sCtx.in = fopen(sCtx.zFile, "rb"); xCloser = fclose; } if( p->mode==MODE_Ascii ){ xRead = ascii_read_one_field; }else{ xRead = csv_read_one_field; } if( sCtx.in==0 ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); return 1; } sCtx.cColSep = p->colSeparator[0]; sCtx.cRowSep = p->rowSeparator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ raw_printf(stderr, "Error: out of memory\n"); xCloser(sCtx.in); return 1; } nByte = strlen30(zSql); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); char cSep = '('; while( xRead(&sCtx) ){ zCreate = sqlite3_mprintf("%z%c\n \"%w\" TEXT", zCreate, cSep, sCtx.z); cSep = ','; if( sCtx.cTerm!=sCtx.cColSep ) break; } if( cSep=='(' ){ sqlite3_free(zCreate); sqlite3_free(sCtx.z); xCloser(sCtx.in); utf8_printf(stderr,"%s: empty file\n", sCtx.zFile); return 1; } zCreate = sqlite3_mprintf("%z\n)", zCreate); rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); sqlite3_free(zCreate); if( rc ){ utf8_printf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, sqlite3_errmsg(p->db)); sqlite3_free(sCtx.z); xCloser(sCtx.in); return 1; } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); } sqlite3_free(zSql); if( rc ){ if (pStmt) sqlite3_finalize(pStmt); utf8_printf(stderr,"Error: %s\n", sqlite3_errmsg(p->db)); xCloser(sCtx.in); return 1; } nCol = sqlite3_column_count(pStmt); 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 ){ raw_printf(stderr, "Error: out of memory\n"); xCloser(sCtx.in); return 1; } 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; rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); if (pStmt) sqlite3_finalize(pStmt); xCloser(sCtx.in); return 1; } 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); /* ** Did we reach end-of-file before finding any columns? ** If so, stop instead of NULL filling the remaining columns. */ if( z==0 && i==0 ) break; /* ** Did we reach end-of-file OR end-of-line before finding any ** columns in ASCII mode? If so, stop instead of NULL filling ** the remaining columns. */ if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ utf8_printf(stderr, "%s:%d: expected %d columns but found %d - " "filling the rest with NULL\n", sCtx.zFile, startLine, nCol, i+1); i += 2; while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } } } if( sCtx.cTerm==sCtx.cColSep ){ do{ xRead(&sCtx); i++; }while( sCtx.cTerm==sCtx.cColSep ); utf8_printf(stderr, "%s:%d: expected %d columns but found %d - " "extras ignored\n", sCtx.zFile, startLine, nCol, i); } if( i>=nCol ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ utf8_printf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, sqlite3_errmsg(p->db)); } } }while( sCtx.cTerm!=EOF ); xCloser(sCtx.in); sqlite3_free(sCtx.z); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); }else #ifndef SQLITE_UNTESTABLE if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){ char *zSql; char *zCollist = 0; sqlite3_stmt *pStmt; int tnum = 0; int i; if( nArg!=3 ){ utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master" " WHERE name='%q' AND type='index'", azArg[1]); sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( sqlite3_step(pStmt)==SQLITE_ROW ){ tnum = sqlite3_column_int(pStmt, 0); } sqlite3_finalize(pStmt); if( tnum==0 ){ utf8_printf(stderr, "no such index: \"%s\"\n", azArg[1]); rc = 1; goto meta_command_exit; } zSql = sqlite3_mprintf("PRAGMA index_xinfo='%q'", azArg[1]); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); i = 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ char zLabel[20]; const char *zCol = (const char*)sqlite3_column_text(pStmt,2); i++; if( zCol==0 ){ if( sqlite3_column_int(pStmt,1)==-1 ){ zCol = "_ROWID_"; }else{ sqlite3_snprintf(sizeof(zLabel),zLabel,"expr%d",i); zCol = zLabel; } } if( zCollist==0 ){ zCollist = sqlite3_mprintf("\"%w\"", zCol); }else{ zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol); } } sqlite3_finalize(pStmt); zSql = sqlite3_mprintf( "CREATE TABLE \"%w\"(%s,PRIMARY KEY(%s))WITHOUT ROWID", azArg[2], zCollist, zCollist); sqlite3_free(zCollist); rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum); if( rc==SQLITE_OK ){ rc = sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); if( rc ){ utf8_printf(stderr, "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); }else{ utf8_printf(stdout, "%s;\n", zSql); raw_printf(stdout, "WARNING: writing to an imposter table will corrupt the index!\n" ); } }else{ raw_printf(stderr, "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' && 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 ){ sqlite3IoTrace = 0; }else if( strcmp(azArg[1], "-")==0 ){ sqlite3IoTrace = iotracePrintf; iotrace = stdout; }else{ iotrace = fopen(azArg[1], "w"); if( iotrace==0 ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); sqlite3IoTrace = 0; rc = 1; }else{ sqlite3IoTrace = iotracePrintf; } } }else #endif if( c=='l' && n>=5 && strncmp(azArg[0], "limits", n)==0 ){ static const struct { const char *zLimitName; /* Name of a limit */ int limitCode; /* Integer code for that limit */ } aLimit[] = { { "length", SQLITE_LIMIT_LENGTH }, { "sql_length", SQLITE_LIMIT_SQL_LENGTH }, { "column", SQLITE_LIMIT_COLUMN }, { "expr_depth", SQLITE_LIMIT_EXPR_DEPTH }, { "compound_select", SQLITE_LIMIT_COMPOUND_SELECT }, { "vdbe_op", SQLITE_LIMIT_VDBE_OP }, { "function_arg", SQLITE_LIMIT_FUNCTION_ARG }, { "attached", SQLITE_LIMIT_ATTACHED }, { "like_pattern_length", SQLITE_LIMIT_LIKE_PATTERN_LENGTH }, { "variable_number", SQLITE_LIMIT_VARIABLE_NUMBER }, { "trigger_depth", SQLITE_LIMIT_TRIGGER_DEPTH }, { "worker_threads", SQLITE_LIMIT_WORKER_THREADS }, }; int i, n2; open_db(p, 0); if( nArg==1 ){ for(i=0; i<ArraySize(aLimit); i++){ printf("%20s %d\n", aLimit[i].zLimitName, sqlite3_limit(p->db, aLimit[i].limitCode, -1)); } }else if( nArg>3 ){ raw_printf(stderr, "Usage: .limit NAME ?NEW-VALUE?\n"); rc = 1; goto meta_command_exit; }else{ int iLimit = -1; n2 = strlen30(azArg[1]); for(i=0; i<ArraySize(aLimit); i++){ if( sqlite3_strnicmp(aLimit[i].zLimitName, azArg[1], n2)==0 ){ if( iLimit<0 ){ iLimit = i; }else{ utf8_printf(stderr, "ambiguous limit: \"%s\"\n", azArg[1]); rc = 1; goto meta_command_exit; } } } if( iLimit<0 ){ utf8_printf(stderr, "unknown limit: \"%s\"\n" "enter \".limits\" with no arguments for a list.\n", azArg[1]); rc = 1; goto meta_command_exit; } if( nArg==3 ){ sqlite3_limit(p->db, aLimit[iLimit].limitCode, (int)integerValue(azArg[2])); } printf("%20s %d\n", aLimit[iLimit].zLimitName, sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); } }else if( c=='l' && n>2 && strncmp(azArg[0], "lint", n)==0 ){ open_db(p, 0); lintDotCommand(p, azArg, nArg); }else #ifndef SQLITE_OMIT_LOAD_EXTENSION if( c=='l' && strncmp(azArg[0], "load", n)==0 ){ const char *zFile, *zProc; char *zErrMsg = 0; if( nArg<2 ){ raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); rc = 1; goto meta_command_exit; } zFile = azArg[1]; zProc = nArg>=3 ? azArg[2] : 0; open_db(p, 0); rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg); if( rc!=SQLITE_OK ){ utf8_printf(stderr, "Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif if( c=='l' && strncmp(azArg[0], "log", n)==0 ){ if( nArg!=2 ){ raw_printf(stderr, "Usage: .log FILENAME\n"); rc = 1; }else{ const char *zFile = azArg[1]; output_file_close(p->pLog); p->pLog = output_file_open(zFile); } }else if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ const char *zMode = nArg>=2 ? azArg[1] : ""; int n2 = (int)strlen(zMode); int c2 = zMode[0]; if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){ p->mode = MODE_Line; sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){ p->mode = MODE_Column; sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ p->mode = MODE_Html; }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, nArg>=3 ? azArg[2] : "table"); }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){ p->mode = MODE_Quote; }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ p->mode = MODE_Ascii; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); }else if( nArg==1 ){ raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); }else{ raw_printf(stderr, "Error: mode should be one of: " "ascii column csv html insert line list quote tabs tcl\n"); rc = 1; } p->cMode = p->mode; }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ raw_printf(stderr, "Usage: .nullvalue STRING\n"); rc = 1; } }else if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){ char *zNewFilename; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ /* Close the existing database */ session_close_all(p); sqlite3_close(p->db); p->db = 0; p->zDbFilename = 0; sqlite3_free(p->zFreeOnClose); p->zFreeOnClose = 0; /* Check for command-line arguments */ for(iName=1; iName<nArg && azArg[iName][0]=='-'; iName++){ const char *z = azArg[iName]; if( optionMatch(z,"new") ){ newFlag = 1; }else if( z[0]=='-' ){ utf8_printf(stderr, "unknown option: %s\n", z); rc = 1; goto meta_command_exit; } } /* If a filename is specified, try to open it first */ zNewFilename = nArg>iName ? sqlite3_mprintf("%s", azArg[iName]) : 0; if( zNewFilename ){ if( newFlag ) shellDeleteFile(zNewFilename); p->zDbFilename = zNewFilename; open_db(p, 1); if( p->db==0 ){ utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename); sqlite3_free(zNewFilename); }else{ p->zFreeOnClose = zNewFilename; } } if( p->db==0 ){ /* As a fall-back open a TEMP database */ p->zDbFilename = 0; open_db(p, 0); } }else if( c=='o' && (strncmp(azArg[0], "output", n)==0 || strncmp(azArg[0], "once", n)==0) ){ const char *zFile = nArg>=2 ? azArg[1] : "stdout"; if( nArg>2 ){ utf8_printf(stderr, "Usage: .%s FILE\n", azArg[0]); rc = 1; goto meta_command_exit; } if( n>1 && strncmp(azArg[0], "once", n)==0 ){ if( nArg<2 ){ raw_printf(stderr, "Usage: .once FILE\n"); rc = 1; goto meta_command_exit; } p->outCount = 2; }else{ p->outCount = 0; } output_reset(p); if( zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN raw_printf(stderr, "Error: pipes are not supported in this OS\n"); rc = 1; p->out = stdout; #else p->out = popen(zFile + 1, "w"); if( p->out==0 ){ utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); p->out = stdout; rc = 1; }else{ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } #endif }else{ p->out = output_file_open(zFile); if( p->out==0 ){ if( strcmp(zFile,"off")!=0 ){ utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); } p->out = stdout; rc = 1; } else { sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } }else if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i<nArg; i++){ if( i>1 ) raw_printf(p->out, " "); utf8_printf(p->out, "%s", azArg[i]); } raw_printf(p->out, "\n"); }else if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){ if( nArg >= 2) { strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); } if( nArg >= 3) { strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); } }else if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ rc = 2; }else if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){ FILE *alt; if( nArg!=2 ){ raw_printf(stderr, "Usage: .read FILE\n"); rc = 1; goto meta_command_exit; } alt = fopen(azArg[1], "rb"); if( alt==0 ){ utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ rc = process_input(p, alt); fclose(alt); } }else if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){ const char *zSrcFile; const char *zDb; sqlite3 *pSrc; sqlite3_backup *pBackup; int nTimeout = 0; if( nArg==2 ){ zSrcFile = azArg[1]; zDb = "main"; }else if( nArg==3 ){ zSrcFile = azArg[2]; zDb = azArg[1]; }else{ raw_printf(stderr, "Usage: .restore ?DB? FILE\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); sqlite3_close(pSrc); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_close(pSrc); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK || rc==SQLITE_BUSY ){ if( rc==SQLITE_BUSY ){ if( nTimeout++ >= 3 ) break; sqlite3_sleep(100); } } sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ raw_printf(stderr, "Error: source database is busy\n"); rc = 1; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } sqlite3_close(pSrc); }else if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ if( nArg==2 ){ p->scanstatsOn = booleanValue(azArg[1]); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); #endif }else{ raw_printf(stderr, "Usage: .scanstats on|off\n"); rc = 1; } }else if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ ShellText sSelect; ShellState data; char *zErrMsg = 0; const char *zDiv = 0; int iSchema = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.cMode = data.mode = MODE_Semi; initText(&sSelect); if( nArg>=2 && optionMatch(azArg[1], "indent") ){ data.cMode = data.mode = MODE_Pretty; nArg--; if( nArg==2 ) azArg[1] = azArg[2]; } if( nArg==2 && azArg[1][0]!='-' ){ int i; for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]); if( strcmp(azArg[1],"sqlite_master")==0 ){ char *new_argv[2], *new_colv[2]; new_argv[0] = "CREATE TABLE sqlite_master (\n" " type text,\n" " name text,\n" " tbl_name text,\n" " rootpage integer,\n" " sql text\n" ")"; new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; callback(&data, 1, new_argv, new_colv); rc = SQLITE_OK; }else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){ char *new_argv[2], *new_colv[2]; new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n" " type text,\n" " name text,\n" " tbl_name text,\n" " rootpage integer,\n" " sql text\n" ")"; new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; callback(&data, 1, new_argv, new_colv); rc = SQLITE_OK; }else{ zDiv = "("; } }else if( nArg==1 ){ zDiv = "("; }else{ raw_printf(stderr, "Usage: .schema ?--indent? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } if( zDiv ){ sqlite3_stmt *pStmt = 0; rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", -1, &pStmt, 0); if( rc ){ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); rc = 1; goto meta_command_exit; } appendText(&sSelect, "SELECT sql FROM", 0); iSchema = 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); char zScNum[30]; sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); appendText(&sSelect, zDiv, 0); zDiv = " UNION ALL "; if( strcmp(zDb, "main")!=0 ){ appendText(&sSelect, "SELECT shell_add_schema(sql,", 0); appendText(&sSelect, zDb, '"'); appendText(&sSelect, ") AS sql, type, tbl_name, name, rowid,", 0); appendText(&sSelect, zScNum, 0); appendText(&sSelect, " AS snum, ", 0); appendText(&sSelect, zDb, '\''); appendText(&sSelect, " AS sname FROM ", 0); appendText(&sSelect, zDb, '"'); appendText(&sSelect, ".sqlite_master", 0); }else{ appendText(&sSelect, "SELECT sql, type, tbl_name, name, rowid, ", 0); appendText(&sSelect, zScNum, 0); appendText(&sSelect, " AS snum, 'main' AS sname FROM sqlite_master",0); } } sqlite3_finalize(pStmt); appendText(&sSelect, ") WHERE ", 0); if( nArg>1 ){ char *zQarg = sqlite3_mprintf("%Q", azArg[1]); if( strchr(azArg[1], '.') ){ appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0); }else{ appendText(&sSelect, "lower(tbl_name)", 0); } appendText(&sSelect, strchr(azArg[1], '*') ? " GLOB " : " LIKE ", 0); appendText(&sSelect, zQarg, 0); appendText(&sSelect, " AND ", 0); sqlite3_free(zQarg); } appendText(&sSelect, "type!='meta' AND sql IS NOT NULL" " ORDER BY snum, rowid", 0); rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); freeText(&sSelect); } if( zErrMsg ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ raw_printf(stderr,"Error: querying schema information\n"); rc = 1; }else{ rc = 0; } }else #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ sqlite3SelectTrace = (int)integerValue(azArg[1]); }else #endif #if defined(SQLITE_ENABLE_SESSION) if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){ OpenSession *pSession = &p->aSession[0]; char **azCmd = &azArg[1]; int iSes = 0; int nCmd = nArg - 1; int i; if( nArg<=1 ) goto session_syntax_error; open_db(p, 0); if( nArg>=3 ){ for(iSes=0; iSes<p->nSession; iSes++){ if( strcmp(p->aSession[iSes].zName, azArg[1])==0 ) break; } if( iSes<p->nSession ){ pSession = &p->aSession[iSes]; azCmd++; nCmd--; }else{ pSession = &p->aSession[0]; iSes = 0; } } /* .session attach TABLE ** Invoke the sqlite3session_attach() interface to attach a particular ** table so that it is never filtered. */ if( strcmp(azCmd[0],"attach")==0 ){ if( nCmd!=2 ) goto session_syntax_error; if( pSession->p==0 ){ session_not_open: raw_printf(stderr, "ERROR: No sessions are open\n"); }else{ rc = sqlite3session_attach(pSession->p, azCmd[1]); if( rc ){ raw_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc); rc = 0; } } }else /* .session changeset FILE ** .session patchset FILE ** Write a changeset or patchset into a file. The file is overwritten. */ if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){ FILE *out = 0; if( nCmd!=2 ) goto session_syntax_error; if( pSession->p==0 ) goto session_not_open; out = fopen(azCmd[1], "wb"); if( out==0 ){ utf8_printf(stderr, "ERROR: cannot open \"%s\" for writing\n", azCmd[1]); }else{ int szChng; void *pChng; if( azCmd[0][0]=='c' ){ rc = sqlite3session_changeset(pSession->p, &szChng, &pChng); }else{ rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); } if( rc ){ printf("Error: error code %d\n", rc); rc = 0; } if( pChng && fwrite(pChng, szChng, 1, out)!=1 ){ raw_printf(stderr, "ERROR: Failed to write entire %d-byte output\n", szChng); } sqlite3_free(pChng); fclose(out); } }else /* .session close ** Close the identified session */ if( strcmp(azCmd[0], "close")==0 ){ if( nCmd!=1 ) goto session_syntax_error; if( p->nSession ){ session_close(pSession); p->aSession[iSes] = p->aSession[--p->nSession]; } }else /* .session enable ?BOOLEAN? ** Query or set the enable flag */ if( strcmp(azCmd[0], "enable")==0 ){ int ii; if( nCmd>2 ) goto session_syntax_error; ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( p->nSession ){ ii = sqlite3session_enable(pSession->p, ii); utf8_printf(p->out, "session %s enable flag = %d\n", pSession->zName, ii); } }else /* .session filter GLOB .... ** Set a list of GLOB patterns of table names to be excluded. */ if( strcmp(azCmd[0], "filter")==0 ){ int ii, nByte; if( nCmd<2 ) goto session_syntax_error; if( p->nSession ){ for(ii=0; ii<pSession->nFilter; ii++){ sqlite3_free(pSession->azFilter[ii]); } sqlite3_free(pSession->azFilter); nByte = sizeof(pSession->azFilter[0])*(nCmd-1); pSession->azFilter = sqlite3_malloc( nByte ); if( pSession->azFilter==0 ){ raw_printf(stderr, "Error: out or memory\n"); exit(1); } for(ii=1; ii<nCmd; ii++){ pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]); } pSession->nFilter = ii-1; } }else /* .session indirect ?BOOLEAN? ** Query or set the indirect flag */ if( strcmp(azCmd[0], "indirect")==0 ){ int ii; if( nCmd>2 ) goto session_syntax_error; ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( p->nSession ){ ii = sqlite3session_indirect(pSession->p, ii); utf8_printf(p->out, "session %s indirect flag = %d\n", pSession->zName, ii); } }else /* .session isempty ** Determine if the session is empty */ if( strcmp(azCmd[0], "isempty")==0 ){ int ii; if( nCmd!=1 ) goto session_syntax_error; if( p->nSession ){ ii = sqlite3session_isempty(pSession->p); utf8_printf(p->out, "session %s isempty flag = %d\n", pSession->zName, ii); } }else /* .session list ** List all currently open sessions */ if( strcmp(azCmd[0],"list")==0 ){ for(i=0; i<p->nSession; i++){ utf8_printf(p->out, "%d %s\n", i, p->aSession[i].zName); } }else /* .session open DB NAME ** Open a new session called NAME on the attached database DB. ** DB is normally "main". */ if( strcmp(azCmd[0],"open")==0 ){ char *zName; if( nCmd!=3 ) goto session_syntax_error; zName = azCmd[2]; if( zName[0]==0 ) goto session_syntax_error; for(i=0; i<p->nSession; i++){ if( strcmp(p->aSession[i].zName,zName)==0 ){ utf8_printf(stderr, "Session \"%s\" already exists\n", zName); goto meta_command_exit; } } if( p->nSession>=ArraySize(p->aSession) ){ raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession)); goto meta_command_exit; } pSession = &p->aSession[p->nSession]; rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); if( rc ){ raw_printf(stderr, "Cannot open session: error code=%d\n", rc); rc = 0; goto meta_command_exit; } pSession->nFilter = 0; sqlite3session_table_filter(pSession->p, session_filter, pSession); p->nSession++; pSession->zName = sqlite3_mprintf("%s", zName); }else /* If no command name matches, show a syntax error */ session_syntax_error: session_help(p); }else #endif #ifdef SQLITE_DEBUG /* Undocumented commands for internal testing. Subject to change ** without notice. */ if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){ if( strncmp(azArg[0]+9, "boolean", n-9)==0 ){ int i, v; for(i=1; i<nArg; i++){ v = booleanValue(azArg[i]); utf8_printf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); } } if( strncmp(azArg[0]+9, "integer", n-9)==0 ){ int i; sqlite3_int64 v; for(i=1; i<nArg; i++){ char zBuf[200]; v = integerValue(azArg[i]); sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); utf8_printf(p->out, "%s", zBuf); } } }else #endif if( c=='s' && n>=4 && strncmp(azArg[0],"selftest",n)==0 ){ int bIsInit = 0; /* True to initialize the SELFTEST table */ int bVerbose = 0; /* Verbose output */ int bSelftestExists; /* True if SELFTEST already exists */ int i, k; /* Loop counters */ int nTest = 0; /* Number of tests runs */ int nErr = 0; /* Number of errors seen */ ShellText str; /* Answer for a query */ sqlite3_stmt *pStmt = 0; /* Query against the SELFTEST table */ open_db(p,0); for(i=1; i<nArg; i++){ const char *z = azArg[i]; if( z[0]=='-' && z[1]=='-' ) z++; if( strcmp(z,"-init")==0 ){ bIsInit = 1; }else if( strcmp(z,"-v")==0 ){ bVerbose++; }else { utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); raw_printf(stderr, "Should be one of: --init -v\n"); rc = 1; goto meta_command_exit; } } if( sqlite3_table_column_metadata(p->db,"main","selftest",0,0,0,0,0,0) != SQLITE_OK ){ bSelftestExists = 0; }else{ bSelftestExists = 1; } if( bIsInit ){ createSelftestTable(p); bSelftestExists = 1; } initText(&str); appendText(&str, "x", 0); for(k=bSelftestExists; k>=0; k--){ if( k==1 ){ rc = sqlite3_prepare_v2(p->db, "SELECT tno,op,cmd,ans FROM selftest ORDER BY tno", -1, &pStmt, 0); }else{ rc = sqlite3_prepare_v2(p->db, "VALUES(0,'memo','Missing SELFTEST table - default checks only','')," " (1,'run','PRAGMA integrity_check','ok')", -1, &pStmt, 0); } if( rc ){ raw_printf(stderr, "Error querying the selftest table\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; } for(i=1; sqlite3_step(pStmt)==SQLITE_ROW; i++){ int tno = sqlite3_column_int(pStmt, 0); const char *zOp = (const char*)sqlite3_column_text(pStmt, 1); const char *zSql = (const char*)sqlite3_column_text(pStmt, 2); const char *zAns = (const char*)sqlite3_column_text(pStmt, 3); k = 0; if( bVerbose>0 ){ char *zQuote = sqlite3_mprintf("%q", zSql); printf("%d: %s %s\n", tno, zOp, zSql); sqlite3_free(zQuote); } if( strcmp(zOp,"memo")==0 ){ utf8_printf(p->out, "%s\n", zSql); }else if( strcmp(zOp,"run")==0 ){ char *zErrMsg = 0; str.n = 0; str.z[0] = 0; rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); nTest++; if( bVerbose ){ utf8_printf(p->out, "Result: %s\n", str.z); } if( rc || zErrMsg ){ nErr++; rc = 1; utf8_printf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); sqlite3_free(zErrMsg); }else if( strcmp(zAns,str.z)!=0 ){ nErr++; rc = 1; utf8_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); utf8_printf(p->out, "%d: Got: [%s]\n", tno, str.z); } }else { utf8_printf(stderr, "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); rc = 1; break; } } /* End loop over rows of content from SELFTEST */ sqlite3_finalize(pStmt); } /* End loop over k */ freeText(&str); utf8_printf(p->out, "%d errors out of %d tests\n", nErr, nTest); }else if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ raw_printf(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); } if( nArg>=3 ){ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); } }else if( c=='s' && n>=4 && strncmp(azArg[0],"sha3sum",n)==0 ){ const char *zLike = 0; /* Which table to checksum. 0 means everything */ int i; /* Loop counter */ int bSchema = 0; /* Also hash the schema */ int bSeparate = 0; /* Hash each table separately */ int iSize = 224; /* Hash algorithm to use */ int bDebug = 0; /* Only show the query that would have run */ sqlite3_stmt *pStmt; /* For querying tables names */ char *zSql; /* SQL to be run */ char *zSep; /* Separator */ ShellText sSql; /* Complete SQL for the query to run the hash */ ShellText sQuery; /* Set of queries used to read all content */ open_db(p, 0); for(i=1; i<nArg; i++){ const char *z = azArg[i]; if( z[0]=='-' ){ z++; if( z[0]=='-' ) z++; if( strcmp(z,"schema")==0 ){ bSchema = 1; }else if( strcmp(z,"sha3-224")==0 || strcmp(z,"sha3-256")==0 || strcmp(z,"sha3-384")==0 || strcmp(z,"sha3-512")==0 ){ iSize = atoi(&z[5]); }else if( strcmp(z,"debug")==0 ){ bDebug = 1; }else { utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); raw_printf(stderr, "Should be one of: --schema" " --sha3-224 --sha3-255 --sha3-384 --sha3-512\n"); rc = 1; goto meta_command_exit; } }else if( zLike ){ raw_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; }else{ zLike = z; bSeparate = 1; if( sqlite3_strlike("sqlite_%", zLike, 0)==0 ) bSchema = 1; } } if( bSchema ){ zSql = "SELECT lower(name) FROM sqlite_master" " WHERE type='table' AND coalesce(rootpage,0)>1" " UNION ALL SELECT 'sqlite_master'" " ORDER BY 1 collate nocase"; }else{ zSql = "SELECT lower(name) FROM sqlite_master" " WHERE type='table' AND coalesce(rootpage,0)>1" " AND name NOT LIKE 'sqlite_%'" " ORDER BY 1 collate nocase"; } sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); initText(&sQuery); initText(&sSql); appendText(&sSql, "WITH [sha3sum$query](a,b) AS(",0); zSep = "VALUES("; while( SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zTab = (const char*)sqlite3_column_text(pStmt,0); if( zLike && sqlite3_strlike(zLike, zTab, 0)!=0 ) continue; if( strncmp(zTab, "sqlite_",7)!=0 ){ appendText(&sQuery,"SELECT * FROM ", 0); appendText(&sQuery,zTab,'"'); appendText(&sQuery," NOT INDEXED;", 0); }else if( strcmp(zTab, "sqlite_master")==0 ){ appendText(&sQuery,"SELECT type,name,tbl_name,sql FROM sqlite_master" " ORDER BY name;", 0); }else if( strcmp(zTab, "sqlite_sequence")==0 ){ appendText(&sQuery,"SELECT name,seq FROM sqlite_sequence" " ORDER BY name;", 0); }else if( strcmp(zTab, "sqlite_stat1")==0 ){ appendText(&sQuery,"SELECT tbl,idx,stat FROM sqlite_stat1" " ORDER BY tbl,idx;", 0); }else if( strcmp(zTab, "sqlite_stat3")==0 || strcmp(zTab, "sqlite_stat4")==0 ){ appendText(&sQuery, "SELECT * FROM ", 0); appendText(&sQuery, zTab, 0); appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0); } appendText(&sSql, zSep, 0); appendText(&sSql, sQuery.z, '\''); sQuery.n = 0; appendText(&sSql, ",", 0); appendText(&sSql, zTab, '\''); zSep = "),("; } sqlite3_finalize(pStmt); if( bSeparate ){ zSql = sqlite3_mprintf( "%s))" " SELECT lower(hex(sha3_query(a,%d))) AS hash, b AS label" " FROM [sha3sum$query]", sSql.z, iSize); }else{ zSql = sqlite3_mprintf( "%s))" " SELECT lower(hex(sha3_query(group_concat(a,''),%d))) AS hash" " FROM [sha3sum$query]", sSql.z, iSize); } freeText(&sQuery); freeText(&sSql); if( bDebug ){ utf8_printf(p->out, "%s\n", zSql); }else{ shell_exec(p->db, zSql, shell_callback, p, 0); } sqlite3_free(zSql); }else if( c=='s' && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) ){ char *zCmd; int i, x; if( nArg<2 ){ raw_printf(stderr, "Usage: .system COMMAND\n"); rc = 1; goto meta_command_exit; } zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]); for(i=2; i<nArg; i++){ zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", zCmd, azArg[i]); } x = system(zCmd); sqlite3_free(zCmd); if( x ) raw_printf(stderr, "System command returns %d\n", x); }else if( c=='s' && strncmp(azArg[0], "show", n)==0 ){ static const char *azBool[] = { "off", "on", "full", "unk" }; int i; if( nArg!=1 ){ raw_printf(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } utf8_printf(p->out, "%12.12s: %s\n","echo", azBool[ShellHasFlag(p, SHFLG_Echo)]); utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); utf8_printf(p->out, "%12.12s: %s\n","explain", p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]); utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); utf8_printf(p->out, "%12.12s: ", "nullvalue"); output_c_string(p->out, p->nullValue); raw_printf(p->out, "\n"); utf8_printf(p->out,"%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); utf8_printf(p->out,"%12.12s: ", "colseparator"); output_c_string(p->out, p->colSeparator); raw_printf(p->out, "\n"); utf8_printf(p->out,"%12.12s: ", "rowseparator"); output_c_string(p->out, p->rowSeparator); raw_printf(p->out, "\n"); utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]); utf8_printf(p->out, "%12.12s: ", "width"); for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { raw_printf(p->out, "%d ", p->colWidth[i]); } raw_printf(p->out, "\n"); utf8_printf(p->out, "%12.12s: %s\n", "filename", p->zDbFilename ? p->zDbFilename : ""); }else if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){ if( nArg==2 ){ p->statsOn = booleanValue(azArg[1]); }else if( nArg==1 ){ display_stats(p->db, p, 0); }else{ raw_printf(stderr, "Usage: .stats ?on|off?\n"); rc = 1; } }else if( (c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0) || (c=='i' && (strncmp(azArg[0], "indices", n)==0 || strncmp(azArg[0], "indexes", n)==0) ) ){ sqlite3_stmt *pStmt; char **azResult; int nRow, nAlloc; int ii; ShellText s; initText(&s); open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ) return shellDatabaseError(p->db); if( nArg>2 && c=='i' ){ /* It is an historical accident that the .indexes command shows an error ** when called with the wrong number of arguments whereas the .tables ** command does not. */ raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){ const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); if( zDbName==0 ) continue; if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0); if( sqlite3_stricmp(zDbName, "main")==0 ){ appendText(&s, "SELECT name FROM ", 0); }else{ appendText(&s, "SELECT ", 0); appendText(&s, zDbName, '\''); appendText(&s, "||'.'||name FROM ", 0); } appendText(&s, zDbName, '"'); appendText(&s, ".sqlite_master ", 0); if( c=='t' ){ appendText(&s," WHERE type IN ('table','view')" " AND name NOT LIKE 'sqlite_%'" " AND name LIKE ?1", 0); }else{ appendText(&s," WHERE type='index'" " AND tbl_name LIKE ?1", 0); } } rc = sqlite3_finalize(pStmt); appendText(&s, " ORDER BY 1", 0); rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0); freeText(&s); if( rc ) return shellDatabaseError(p->db); /* Run the SQL statement prepared by the above block. Store the results ** as an array of nul-terminated strings in azResult[]. */ nRow = nAlloc = 0; azResult = 0; if( nArg>1 ){ sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT); }else{ sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC); } while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nRow>=nAlloc ){ char **azNew; int n2 = nAlloc*2 + 10; azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2); if( azNew==0 ){ rc = shellNomemError(); break; } nAlloc = n2; azResult = azNew; } azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); if( 0==azResult[nRow] ){ rc = shellNomemError(); break; } nRow++; } if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ rc = shellDatabaseError(p->db); } /* Pretty-print the contents of array azResult[] to the output */ if( rc==0 && nRow>0 ){ int len, maxlen = 0; int i, j; int nPrintCol, nPrintRow; for(i=0; i<nRow; i++){ len = strlen30(azResult[i]); if( len>maxlen ) maxlen = len; } nPrintCol = 80/(maxlen+2); if( nPrintCol<1 ) nPrintCol = 1; nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; for(i=0; i<nPrintRow; i++){ for(j=i; j<nRow; j+=nPrintRow){ char *zSp = j<nPrintRow ? "" : " "; utf8_printf(p->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } raw_printf(p->out, "\n"); } } for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]); sqlite3_free(azResult); }else /* Begin redirecting output to the file "testcase-out.txt" */ if( c=='t' && strcmp(azArg[0],"testcase")==0 ){ output_reset(p); p->out = output_file_open("testcase-out.txt"); if( p->out==0 ){ raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); }else{ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); } }else #ifndef SQLITE_UNTESTABLE if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ } aCtrl[] = { { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE }, { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE }, { "prng_reset", SQLITE_TESTCTRL_PRNG_RESET }, { "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST }, { "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL }, { "benign_malloc_hooks", SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS }, { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE }, { "assert", SQLITE_TESTCTRL_ASSERT }, { "always", SQLITE_TESTCTRL_ALWAYS }, { "reserve", SQLITE_TESTCTRL_RESERVE }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, { "imposter", SQLITE_TESTCTRL_IMPOSTER }, }; int testctrl = -1; int rc2 = 0; int i, n2; open_db(p, 0); /* convert testctrl text option to value. allow any unique prefix ** of the option name, or a numerical value. */ n2 = strlen30(azArg[1]); for(i=0; i<ArraySize(aCtrl); i++){ if( strncmp(azArg[1], aCtrl[i].zCtrlName, n2)==0 ){ if( testctrl<0 ){ testctrl = aCtrl[i].ctrlCode; }else{ utf8_printf(stderr, "ambiguous option name: \"%s\"\n", azArg[1]); testctrl = -1; break; } } } if( testctrl<0 ) testctrl = (int)integerValue(azArg[1]); if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){ utf8_printf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]); }else{ switch(testctrl){ /* sqlite3_test_control(int, db, int) */ case SQLITE_TESTCTRL_OPTIMIZATIONS: case SQLITE_TESTCTRL_RESERVE: if( nArg==3 ){ int opt = (int)strtol(azArg[2], 0, 0); rc2 = sqlite3_test_control(testctrl, p->db, opt); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); } else { utf8_printf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); } break; /* sqlite3_test_control(int) */ case SQLITE_TESTCTRL_PRNG_SAVE: case SQLITE_TESTCTRL_PRNG_RESTORE: case SQLITE_TESTCTRL_PRNG_RESET: case SQLITE_TESTCTRL_BYTEORDER: if( nArg==2 ){ rc2 = sqlite3_test_control(testctrl); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); } else { utf8_printf(stderr,"Error: testctrl %s takes no options\n", azArg[1]); } break; /* sqlite3_test_control(int, uint) */ case SQLITE_TESTCTRL_PENDING_BYTE: if( nArg==3 ){ unsigned int opt = (unsigned int)integerValue(azArg[2]); rc2 = sqlite3_test_control(testctrl, opt); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); } else { utf8_printf(stderr,"Error: testctrl %s takes a single unsigned" " int option\n", azArg[1]); } break; /* sqlite3_test_control(int, int) */ case SQLITE_TESTCTRL_ASSERT: case SQLITE_TESTCTRL_ALWAYS: case SQLITE_TESTCTRL_NEVER_CORRUPT: if( nArg==3 ){ int opt = booleanValue(azArg[2]); rc2 = sqlite3_test_control(testctrl, opt); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); } else { utf8_printf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); } break; /* sqlite3_test_control(int, char *) */ #ifdef SQLITE_N_KEYWORD case SQLITE_TESTCTRL_ISKEYWORD: if( nArg==3 ){ const char *opt = azArg[2]; rc2 = sqlite3_test_control(testctrl, opt); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); } else { utf8_printf(stderr, "Error: testctrl %s takes a single char * option\n", azArg[1]); } break; #endif case SQLITE_TESTCTRL_IMPOSTER: if( nArg==5 ){ rc2 = sqlite3_test_control(testctrl, p->db, azArg[2], integerValue(azArg[3]), integerValue(azArg[4])); raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2); }else{ raw_printf(stderr,"Usage: .testctrl imposter dbName onoff tnum\n"); } break; case SQLITE_TESTCTRL_BITVEC_TEST: case SQLITE_TESTCTRL_FAULT_INSTALL: case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: default: utf8_printf(stderr, "Error: CLI support for testctrl %s not implemented\n", azArg[1]); break; } } }else #endif /* !defined(SQLITE_UNTESTABLE) */ if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 ){ open_db(p, 0); sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); }else if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){ if( nArg==2 ){ enableTimer = booleanValue(azArg[1]); if( enableTimer && !HAS_TIMER ){ raw_printf(stderr, "Error: timer not available on this system.\n"); enableTimer = 0; } }else{ raw_printf(stderr, "Usage: .timer on|off\n"); rc = 1; } }else if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){ open_db(p, 0); if( nArg!=2 ){ raw_printf(stderr, "Usage: .trace FILE|off\n"); rc = 1; goto meta_command_exit; } output_file_close(p->traceOut); p->traceOut = output_file_open(azArg[1]); #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) if( p->traceOut==0 ){ sqlite3_trace_v2(p->db, 0, 0, 0); }else{ sqlite3_trace_v2(p->db, SQLITE_TRACE_STMT, sql_trace_callback,p->traceOut); } #endif }else #if SQLITE_USER_AUTHENTICATION if( c=='u' && strncmp(azArg[0], "user", n)==0 ){ if( nArg<2 ){ raw_printf(stderr, "Usage: .user SUBCOMMAND ...\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); if( strcmp(azArg[1],"login")==0 ){ if( nArg!=4 ){ raw_printf(stderr, "Usage: .user login USER PASSWORD\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], (int)strlen(azArg[3])); if( rc ){ utf8_printf(stderr, "Authentication failed for user %s\n", azArg[2]); rc = 1; } }else if( strcmp(azArg[1],"add")==0 ){ if( nArg!=5 ){ raw_printf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_add(p->db, azArg[2], azArg[3], (int)strlen(azArg[3]), booleanValue(azArg[4])); if( rc ){ raw_printf(stderr, "User-Add failed: %d\n", rc); rc = 1; } }else if( strcmp(azArg[1],"edit")==0 ){ if( nArg!=5 ){ raw_printf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_change(p->db, azArg[2], azArg[3], (int)strlen(azArg[3]), booleanValue(azArg[4])); if( rc ){ raw_printf(stderr, "User-Edit failed: %d\n", rc); rc = 1; } }else if( strcmp(azArg[1],"delete")==0 ){ if( nArg!=3 ){ raw_printf(stderr, "Usage: .user delete USER\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_delete(p->db, azArg[2]); if( rc ){ raw_printf(stderr, "User-Delete failed: %d\n", rc); rc = 1; } }else{ raw_printf(stderr, "Usage: .user login|add|edit|delete ...\n"); rc = 1; goto meta_command_exit; } }else #endif /* SQLITE_USER_AUTHENTICATION */ if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ utf8_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); }else if( c=='v' && strncmp(azArg[0], "vfsinfo", n)==0 ){ const char *zDbName = nArg==2 ? azArg[1] : "main"; sqlite3_vfs *pVfs = 0; if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); if( pVfs ){ utf8_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); } } }else if( c=='v' && strncmp(azArg[0], "vfslist", n)==0 ){ sqlite3_vfs *pVfs; sqlite3_vfs *pCurrent = 0; if( p->db ){ sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); } for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ utf8_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, pVfs==pCurrent ? " <--- CURRENT" : ""); raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); if( pVfs->pNext ){ raw_printf(p->out, "-----------------------------------\n"); } } }else if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){ const char *zDbName = nArg==2 ? azArg[1] : "main"; char *zVfsName = 0; if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); if( zVfsName ){ utf8_printf(p->out, "%s\n", zVfsName); sqlite3_free(zVfsName); } } }else #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){ sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff; }else #endif if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ int j; assert( nArg<=ArraySize(azArg) ); for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ p->colWidth[j-1] = (int)integerValue(azArg[j]); } }else { utf8_printf(stderr, "Error: unknown command or invalid arguments: " " \"%s\". Enter \".help\" for help\n", azArg[0]); rc = 1; } meta_command_exit: if( p->outCount ){ p->outCount--; if( p->outCount==0 ) output_reset(p); } return rc; } /* ** Return TRUE if a semicolon occurs anywhere in the first N characters ** of string z[]. */ static int line_contains_semicolon(const char *z, int N){ int i; for(i=0; i<N; i++){ if( z[i]==';' ) return 1; } return 0; } /* ** Test to see if a line consists entirely of whitespace. */ static int _all_whitespace(const char *z){ for(; *z; z++){ if( IsSpace(z[0]) ) continue; if( *z=='/' && z[1]=='*' ){ z += 2; while( *z && (*z!='*' || z[1]!='/') ){ z++; } if( *z==0 ) return 0; z++; continue; } if( *z=='-' && z[1]=='-' ){ z += 2; while( *z && *z!='\n' ){ z++; } if( *z==0 ) return 1; continue; } return 0; } return 1; } /* ** Return TRUE if the line typed in is an SQL command terminator other ** than a semi-colon. The SQL Server style "go" command is understood ** as is the Oracle "/". */ static int line_is_command_terminator(const char *zLine){ while( IsSpace(zLine[0]) ){ zLine++; }; if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){ return 1; /* Oracle */ } if( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o' && _all_whitespace(&zLine[2]) ){ return 1; /* SQL Server */ } return 0; } /* ** Return true if zSql is a complete SQL statement. Return false if it ** ends in the middle of a string literal or C-style comment. */ static int line_is_complete(char *zSql, int nSql){ int rc; 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 */ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ int rc; char *zErrMsg = 0; open_db(p, 0); if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); BEGIN_TIMER; rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg); END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; if( in!=0 || !stdin_is_interactive ){ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error: near line %d:", startline); }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:"); } if( zErrMsg!=0 ){ utf8_printf(stderr, "%s %s\n", zPrefix, zErrMsg); sqlite3_free(zErrMsg); zErrMsg = 0; }else{ utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db)); } return 1; }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ raw_printf(p->out, "changes: %3d total_changes: %d\n", sqlite3_changes(p->db), sqlite3_total_changes(p->db)); } return 0; } /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. ** ** Return the number of errors. */ static int process_input(ShellState *p, FILE *in){ char *zLine = 0; /* A single input line */ char *zSql = 0; /* Accumulated SQL text */ int nLine; /* Length of current line */ int nSql = 0; /* Bytes of zSql[] used */ int nAlloc = 0; /* Allocated zSql[] space */ int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ int rc; /* Error code */ int errCnt = 0; /* Number of errors seen */ int lineno = 0; /* Current line number */ int startline = 0; /* Line number for start of current input */ while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){ fflush(p->out); zLine = one_input_line(in, zLine, nSql>0); if( zLine==0 ){ /* End of input */ if( in==0 && stdin_is_interactive ) printf("\n"); break; } if( seenInterrupt ){ if( in!=0 ) break; seenInterrupt = 0; } lineno++; if( nSql==0 && _all_whitespace(zLine) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); continue; } if( zLine && zLine[0]=='.' && nSql==0 ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); rc = do_meta_command(zLine, p); if( rc==2 ){ /* exit requested */ break; }else if( rc ){ errCnt++; } continue; } if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){ memcpy(zLine,";",2); } nLine = strlen30(zLine); if( nSql+nLine+2>=nAlloc ){ nAlloc = nSql+nLine+100; zSql = realloc(zSql, nAlloc); if( zSql==0 ){ raw_printf(stderr, "Error: out of memory\n"); exit(1); } } nSqlPrior = nSql; if( nSql==0 ){ int i; for(i=0; zLine[i] && IsSpace(zLine[i]); i++){} assert( nAlloc>0 && zSql!=0 ); memcpy(zSql, zLine+i, nLine+1-i); startline = lineno; nSql = nLine-i; }else{ zSql[nSql++] = '\n'; memcpy(zSql+nSql, zLine, nLine+1); nSql += nLine; } if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) && sqlite3_complete(zSql) ){ errCnt += runOneSqlLine(p, zSql, in, startline); nSql = 0; if( p->outCount ){ output_reset(p); p->outCount = 0; } }else if( nSql && _all_whitespace(zSql) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql); nSql = 0; } } if( nSql && !_all_whitespace(zSql) ){ runOneSqlLine(p, zSql, in, startline); } free(zSql); free(zLine); return errCnt>0; } /* ** Return a pathname which is the user's home directory. A ** 0 return indicates an error of some kind. */ static char *find_home_dir(int clearFlag){ static char *home_dir = NULL; if( clearFlag ){ free(home_dir); home_dir = 0; return 0; } if( home_dir ) return home_dir; #if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \ && !defined(__RTP__) && !defined(_WRS_KERNEL) { struct passwd *pwent; uid_t uid = getuid(); if( (pwent=getpwuid(uid)) != NULL) { home_dir = pwent->pw_dir; } } #endif #if defined(_WIN32_WCE) /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv() */ home_dir = "/"; #else #if defined(_WIN32) || defined(WIN32) if (!home_dir) { home_dir = getenv("USERPROFILE"); } #endif if (!home_dir) { home_dir = getenv("HOME"); } #if defined(_WIN32) || defined(WIN32) if (!home_dir) { char *zDrive, *zPath; int n; zDrive = getenv("HOMEDRIVE"); zPath = getenv("HOMEPATH"); if( zDrive && zPath ){ n = strlen30(zDrive) + strlen30(zPath) + 1; home_dir = malloc( n ); if( home_dir==0 ) return 0; sqlite3_snprintf(n, home_dir, "%s%s", zDrive, zPath); return home_dir; } home_dir = "c:\\"; } #endif #endif /* !_WIN32_WCE */ if( home_dir ){ int n = strlen30(home_dir) + 1; char *z = malloc( n ); if( z ) memcpy(z, home_dir, n); home_dir = z; } return home_dir; } /* ** Read input from the file given by sqliterc_override. Or if that ** parameter is NULL, take input from ~/.sqliterc ** ** Returns the number of errors. */ static void process_sqliterc( ShellState *p, /* Configuration data */ const char *sqliterc_override /* Name of config file. NULL to use default */ ){ char *home_dir = NULL; const char *sqliterc = sqliterc_override; char *zBuf = 0; FILE *in = NULL; if (sqliterc == NULL) { home_dir = find_home_dir(0); if( home_dir==0 ){ raw_printf(stderr, "-- warning: cannot find home directory;" " cannot read ~/.sqliterc\n"); return; } sqlite3_initialize(); zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); sqliterc = zBuf; } in = fopen(sqliterc,"rb"); if( in ){ if( stdin_is_interactive ){ utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); } process_input(p,in); fclose(in); } sqlite3_free(zBuf); } /* ** Show available command line options */ static const char zOptions[] = " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" " -echo print commands before execution\n" " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) " -heap SIZE Size of heap for memsys3 or memsys5\n" #endif " -help show this message\n" " -html set output mode to HTML\n" " -interactive force interactive I/O\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif ; static void usage(int showDetail){ utf8_printf(stderr, "Usage: %s [OPTIONS] FILENAME [SQL]\n" "FILENAME is the name of an SQLite database. A new database is created\n" "if the file does not previously exist.\n", Argv0); if( showDetail ){ utf8_printf(stderr, "OPTIONS include:\n%s", zOptions); }else{ raw_printf(stderr, "Use the -help option for additional information\n"); } exit(1); } /* ** Initialize the state information in data */ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->normalMode = data->cMode = data->mode = MODE_List; data->autoExplain = 1; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); } /* ** Output text to the console in a font that attracts extra attention. */ #ifdef _WIN32 static void printBold(const char *zText){ HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; GetConsoleScreenBufferInfo(out, &defaultScreenInfo); SetConsoleTextAttribute(out, FOREGROUND_RED|FOREGROUND_INTENSITY ); printf("%s", zText); SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); } #else static void printBold(const char *zText){ printf("\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 ){ utf8_printf(stderr, "%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); exit(1); } return argv[i]; } #ifndef SQLITE_SHELL_IS_UTF8 # if (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER) # define SQLITE_SHELL_IS_UTF8 (0) # else # define SQLITE_SHELL_IS_UTF8 (1) # endif #endif #if SQLITE_SHELL_IS_UTF8 int SQLITE_CDECL main(int argc, char **argv){ #else int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char **argv; #endif char *zErrMsg = 0; ShellState data; const char *zInitFile = 0; int i; int rc = 0; int warnInmemoryDb = 0; int readStdin = 1; int nCmd = 0; char **azCmd = 0; setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #if USE_SYSTEM_SQLITE+0!=1 if( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", sqlite3_sourceid(), SQLITE_SOURCE_ID); exit(1); } #endif main_init(&data); #if !SQLITE_SHELL_IS_UTF8 sqlite3_initialize(); argv = sqlite3_malloc64(sizeof(argv[0])*argc); if( argv==0 ){ raw_printf(stderr, "out of memory\n"); exit(1); } for(i=0; i<argc; i++){ argv[i] = sqlite3_win32_unicode_to_utf8(wargv[i]); if( argv[i]==0 ){ raw_printf(stderr, "out of memory\n"); exit(1); } } #endif assert( argc>=1 && argv && argv[0] ); Argv0 = argv[0]; /* Make sure we have a valid signal handler early, before anything ** else is done. */ #ifdef SIGINT signal(SIGINT, interrupt_handler); #endif #ifdef SQLITE_SHELL_DBNAME_PROC { /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name ** of a C-function that will provide the name of the database file. Use ** this compile-time option to embed this shell program in larger ** applications. */ extern void SQLITE_SHELL_DBNAME_PROC(const char**); SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename); warnInmemoryDb = 0; } #endif /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** the size of the alternative malloc heap, ** and the first command to execute. */ for(i=1; i<argc; i++){ char *z; z = argv[i]; if( z[0]!='-' ){ if( data.zDbFilename==0 ){ data.zDbFilename = z; }else{ /* Excesss arguments are interpreted as SQL (or dot-commands) and ** mean that nothing is read from stdin */ readStdin = 0; nCmd++; azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd); if( azCmd==0 ){ raw_printf(stderr, "out of memory\n"); exit(1); } azCmd[nCmd-1] = z; } } if( z[1]=='-' ) z++; if( strcmp(z,"-separator")==0 || strcmp(z,"-nullvalue")==0 || strcmp(z,"-newline")==0 || strcmp(z,"-cmd")==0 ){ (void)cmdline_option_value(argc, argv, ++i); }else if( strcmp(z,"-init")==0 ){ zInitFile = cmdline_option_value(argc, argv, ++i); }else if( 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( strcmp(z,"-heap")==0 ){ #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) const char *zSize; sqlite3_int64 szHeap; zSize = cmdline_option_value(argc, argv, ++i); szHeap = integerValue(zSize); if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #else (void)cmdline_option_value(argc, argv, ++i); #endif }else if( strcmp(z,"-pagecache")==0 ){ int n, sz; sz = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( sz>70000 ) sz = 70000; if( sz<0 ) sz = 0; n = (int)integerValue(cmdline_option_value(argc,argv,++i)); sqlite3_config(SQLITE_CONFIG_PAGECACHE, (n>0 && sz>0) ? malloc(n*sz) : 0, sz, n); data.shellFlgs |= SHFLG_Pagecache; }else if( strcmp(z,"-lookaside")==0 ){ int n, sz; sz = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( sz<0 ) sz = 0; n = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( n<0 ) n = 0; sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n); if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside; #ifdef SQLITE_ENABLE_VFSTRACE }else if( strcmp(z,"-vfstrace")==0 ){ extern int vfstrace_register( const char *zTraceName, const char *zOldVfsName, int (*xOut)(const char*,void*), void *pOutArg, int makeDefault ); vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); #endif #ifdef SQLITE_ENABLE_MULTIPLEX }else if( strcmp(z,"-multiplex")==0 ){ extern int sqlite3_multiple_initialize(const char*,int); sqlite3_multiplex_initialize(0, 1); #endif }else if( strcmp(z,"-mmap")==0 ){ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz); }else if( strcmp(z,"-vfs")==0 ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i)); if( pVfs ){ sqlite3_vfs_register(pVfs, 1); }else{ utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]); exit(1); } } } if( data.zDbFilename==0 ){ #ifndef SQLITE_OMIT_MEMORYDB data.zDbFilename = ":memory:"; warnInmemoryDb = argc==1; #else utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0); return 1; #endif } data.out = stdout; /* Go ahead and open the database file if it already exists. If the ** file does not exist, delay opening it. This prevents empty database ** files from being created if a user mistypes the database name argument ** to the sqlite command-line tool. */ if( access(data.zDbFilename, 0)==0 ){ open_db(&data, 0); } /* Process the initialization file if there is one. If no -init option ** is given on the command line, look for a file named ~/.sqliterc and ** try to process it. */ process_sqliterc(&data,zInitFile); /* Make a second pass through the command-line argument and set ** options. This second pass is delayed until after the initialization ** file is processed so that the command-line arguments will override ** settings in the initialization file. */ for(i=1; i<argc; i++){ char *z = argv[i]; if( z[0]!='-' ) continue; if( z[1]=='-' ){ z++; } if( strcmp(z,"-init")==0 ){ i++; }else if( strcmp(z,"-html")==0 ){ data.mode = MODE_Html; }else if( strcmp(z,"-list")==0 ){ data.mode = MODE_List; }else if( strcmp(z,"-quote")==0 ){ data.mode = MODE_Quote; }else if( strcmp(z,"-line")==0 ){ data.mode = MODE_Line; }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); }else if( strcmp(z,"-ascii")==0 ){ data.mode = MODE_Ascii; sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Record); }else if( strcmp(z,"-separator")==0 ){ sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-newline")==0 ){ sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; }else if( strcmp(z,"-echo")==0 ){ ShellSetFlag(&data, SHFLG_Echo); }else if( strcmp(z,"-eqp")==0 ){ data.autoEQP = 1; }else if( strcmp(z,"-eqpfull")==0 ){ data.autoEQP = 2; }else if( strcmp(z,"-stats")==0 ){ data.statsOn = 1; }else if( strcmp(z,"-scanstats")==0 ){ data.scanstatsOn = 1; }else if( strcmp(z,"-backslash")==0 ){ /* Undocumented command-line option: -backslash ** Causes C-style backslash escapes to be evaluated in SQL statements ** 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( strcmp(z,"-bail")==0 ){ bail_on_error = 1; }else if( strcmp(z,"-version")==0 ){ printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); return 0; }else if( strcmp(z,"-interactive")==0 ){ stdin_is_interactive = 1; }else if( strcmp(z,"-batch")==0 ){ stdin_is_interactive = 0; }else if( strcmp(z,"-heap")==0 ){ i++; }else if( strcmp(z,"-pagecache")==0 ){ i+=2; }else if( strcmp(z,"-lookaside")==0 ){ i+=2; }else if( strcmp(z,"-mmap")==0 ){ i++; }else if( strcmp(z,"-vfs")==0 ){ i++; #ifdef SQLITE_ENABLE_VFSTRACE }else if( strcmp(z,"-vfstrace")==0 ){ i++; #endif #ifdef SQLITE_ENABLE_MULTIPLEX }else if( strcmp(z,"-multiplex")==0 ){ i++; #endif }else if( strcmp(z,"-help")==0 ){ usage(1); }else if( strcmp(z,"-cmd")==0 ){ /* Run commands that follow -cmd first and separately from commands ** that simply appear on the command-line. This seems goofy. It would ** be better if all commands ran in the order that they appear. But ** we retain the goofy behavior for historical compatibility. */ if( i==argc-1 ) break; z = cmdline_option_value(argc,argv,++i); if( z[0]=='.' ){ rc = do_meta_command(z, &data); if( rc && bail_on_error ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg); if( zErrMsg!=0 ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); if( bail_on_error ) return rc!=0 ? rc : 1; }else if( rc!=0 ){ utf8_printf(stderr,"Error: unable to process SQL \"%s\"\n", z); if( bail_on_error ) return rc; } } }else{ utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); raw_printf(stderr,"Use -help for a list of options.\n"); return 1; } data.cMode = data.mode; } if( !readStdin ){ /* Run all arguments that do not begin with '-' as if they were separate ** command-line inputs, except for the argToSkip argument which contains ** the database filename. */ for(i=0; i<nCmd; i++){ if( azCmd[i][0]=='.' ){ rc = do_meta_command(azCmd[i], &data); if( rc ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); rc = shell_exec(data.db, azCmd[i], shell_callback, &data, &zErrMsg); if( zErrMsg!=0 ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); return rc!=0 ? rc : 1; }else if( rc!=0 ){ utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); return rc; } } } free(azCmd); }else{ /* Run commands received from standard input */ if( stdin_is_interactive ){ char *zHome; char *zHistory = 0; int nHistory; printf( "SQLite version %s %.19s\n" /*extra-version-info*/ "Enter \".help\" for usage hints.\n", sqlite3_libversion(), sqlite3_sourceid() ); if( warnInmemoryDb ){ printf("Connected to a "); printBold("transient in-memory database"); printf(".\nUse \".open FILENAME\" to reopen on a " "persistent database.\n"); } zHome = find_home_dir(0); if( zHome ){ nHistory = strlen30(zHome) + 20; if( (zHistory = malloc(nHistory))!=0 ){ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); } } if( zHistory ){ shell_read_history(zHistory); } #if HAVE_READLINE || HAVE_EDITLINE rl_attempted_completion_function = readline_completion; #elif HAVE_LINENOISE linenoiseSetCompletionCallback(linenoise_completion); #endif rc = process_input(&data, 0); if( zHistory ){ shell_stifle_history(2000); shell_write_history(zHistory); free(zHistory); } }else{ rc = process_input(&data, stdin); } } set_table_name(&data, 0); if( data.db ){ session_close_all(&data); sqlite3_close(data.db); } sqlite3_free(data.zFreeOnClose); find_home_dir(1); #if !SQLITE_SHELL_IS_UTF8 for(i=0; i<argc; i++) sqlite3_free(argv[i]); sqlite3_free(argv); #endif return rc; } |
Changes to src/sqlite.h.in.
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* ** 2001-09-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. |
︙ | ︙ | |||
110 111 112 113 114 115 116 | ** ** Since [version 3.6.18] ([dateof:3.6.18]), ** SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID | | | > > | | | | > > | 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 | ** ** Since [version 3.6.18] ([dateof:3.6.18]), ** SQLite source code has been stored in the ** <a href="http://www.fossil-scm.org/">Fossil configuration management ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID ** string contains the date and time of the check-in (UTC) and a SHA1 ** or SHA3-256 hash of the entire source tree. If the source code has ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "--VERS--" #define SQLITE_VERSION_NUMBER --VERSION-NUMBER-- #define SQLITE_SOURCE_ID "--SOURCE-ID--" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros ** but are associated with the library instead of the header file. ^(Cautious ** programmers might include assert() statements in their application to ** verify that values returned by these interfaces match the macros in ** the header, and thus ensure that the application is ** compiled with matching library and header files. ** ** <blockquote><pre> ** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER ); ** assert( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,80)==0 ); ** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 ); ** </pre></blockquote>)^ ** ** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] ** macro. ^The sqlite3_libversion() function returns a pointer to the ** to the sqlite3_version[] string constant. The sqlite3_libversion() ** function is provided for use in DLLs since DLL users usually do not have ** direct access to string constants within the DLL. ^The ** sqlite3_libversion_number() function returns an integer equal to ** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns ** a pointer to a string constant whose value is the same as the ** [SQLITE_SOURCE_ID] C preprocessor macro. Except if SQLite is built ** using an edited copy of [the amalgamation], then the last four characters ** of the hash might be different from [SQLITE_SOURCE_ID].)^ ** ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_EXTERN const char sqlite3_version[]; const char *sqlite3_libversion(void); const char *sqlite3_sourceid(void); int sqlite3_libversion_number(void); |
︙ | ︙ | |||
255 256 257 258 259 260 261 | ** ^The sqlite3_int64 and sqlite_int64 types can store integer values ** between -9223372036854775808 and +9223372036854775807 inclusive. ^The ** sqlite3_uint64 and sqlite_uint64 types can store integer values ** between 0 and +18446744073709551615 inclusive. */ #ifdef SQLITE_INT64_TYPE typedef SQLITE_INT64_TYPE sqlite_int64; | > > > | > | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | ** ^The sqlite3_int64 and sqlite_int64 types can store integer values ** between -9223372036854775808 and +9223372036854775807 inclusive. ^The ** sqlite3_uint64 and sqlite_uint64 types can store integer values ** between 0 and +18446744073709551615 inclusive. */ #ifdef SQLITE_INT64_TYPE typedef SQLITE_INT64_TYPE sqlite_int64; # ifdef SQLITE_UINT64_TYPE typedef SQLITE_UINT64_TYPE sqlite_uint64; # else typedef unsigned SQLITE_INT64_TYPE sqlite_uint64; # endif #elif defined(_MSC_VER) || defined(__BORLANDC__) typedef __int64 sqlite_int64; typedef unsigned __int64 sqlite_uint64; #else typedef long long int sqlite_int64; typedef unsigned long long int sqlite_uint64; #endif |
︙ | ︙ | |||
409 410 411 412 413 414 415 | ** ** New error codes may be added in future versions of SQLite. ** ** See also: [extended result code definitions] */ #define SQLITE_OK 0 /* Successful result */ /* beginning-of-error-codes */ | | | | | 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 | ** ** New error codes may be added in future versions of SQLite. ** ** See also: [extended result code definitions] */ #define SQLITE_OK 0 /* Successful result */ /* beginning-of-error-codes */ #define SQLITE_ERROR 1 /* Generic error */ #define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ #define SQLITE_PERM 3 /* Access permission denied */ #define SQLITE_ABORT 4 /* Callback routine requested an abort */ #define SQLITE_BUSY 5 /* The database file is locked */ #define SQLITE_LOCKED 6 /* A table in the database is locked */ #define SQLITE_NOMEM 7 /* A malloc() failed */ #define SQLITE_READONLY 8 /* Attempt to write a readonly database */ #define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ #define SQLITE_CORRUPT 11 /* The database disk image is malformed */ #define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ #define SQLITE_FULL 13 /* Insertion failed because database is full */ #define SQLITE_CANTOPEN 14 /* Unable to open the database file */ #define SQLITE_PROTOCOL 15 /* Database lock protocol error */ #define SQLITE_EMPTY 16 /* Internal use only */ #define SQLITE_SCHEMA 17 /* The database schema changed */ #define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ #define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ #define SQLITE_MISMATCH 20 /* Data type mismatch */ #define SQLITE_MISUSE 21 /* Library used incorrectly */ #define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ #define SQLITE_AUTH 23 /* Authorization denied */ #define SQLITE_FORMAT 24 /* Not used */ #define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ #define SQLITE_NOTADB 26 /* File opened that is not a database file */ #define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */ #define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ #define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ #define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ /* end-of-error-codes */ |
︙ | ︙ | |||
486 487 488 489 490 491 492 493 494 495 496 497 498 499 | #define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) #define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) #define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8)) #define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8)) #define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8)) #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) | > > > | 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | #define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) #define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) #define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8)) #define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8)) #define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8)) #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) |
︙ | ︙ | |||
568 569 570 571 572 573 574 | ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls ** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that ** after reboot following a crash or power loss, the only bytes in a ** file that were written at the application level might have changed ** and that adjacent bytes, even bytes within the same sector are ** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | | > > > > > > | 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 | ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls ** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that ** after reboot following a crash or power loss, the only bytes in a ** file that were written at the application level might have changed ** and that adjacent bytes, even bytes within the same sector are ** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN ** flag indicates that a file cannot be deleted when open. The ** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. |
︙ | ︙ | |||
718 719 720 721 722 723 724 725 726 727 728 729 730 731 | ** <li> [SQLITE_IOCAP_ATOMIC4K] ** <li> [SQLITE_IOCAP_ATOMIC8K] ** <li> [SQLITE_IOCAP_ATOMIC16K] ** <li> [SQLITE_IOCAP_ATOMIC32K] ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means | > > > > | 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 | ** <li> [SQLITE_IOCAP_ATOMIC4K] ** <li> [SQLITE_IOCAP_ATOMIC8K] ** <li> [SQLITE_IOCAP_ATOMIC16K] ** <li> [SQLITE_IOCAP_ATOMIC32K] ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means |
︙ | ︙ | |||
846 847 848 849 850 851 852 | ** anti-virus programs. By default, the windows VFS will retry file read, ** file write, and file delete operations up to 10 times, with a delay ** of 25 milliseconds before the first retry and with the delay increasing ** by an additional 25 milliseconds with each subsequent retry. This ** opcode allows these two values (10 retries and 25 milliseconds of delay) ** to be adjusted. The values are changed for all database connections ** within the same process. The argument is a pointer to an array of two | | | 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 | ** anti-virus programs. By default, the windows VFS will retry file read, ** file write, and file delete operations up to 10 times, with a delay ** of 25 milliseconds before the first retry and with the delay increasing ** by an additional 25 milliseconds with each subsequent retry. This ** opcode allows these two values (10 retries and 25 milliseconds of delay) ** to be adjusted. The values are changed for all database connections ** within the same process. The argument is a pointer to an array of two ** integers where the first integer is the new retry count and the second ** integer is the delay. If either integer is negative, then the setting ** is not changed but instead the prior value of that setting is written ** into the array entry, allowing the current retry settings to be ** interrogated. The zDbName parameter is ignored. ** ** <li>[[SQLITE_FCNTL_PERSIST_WAL]] ** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the |
︙ | ︙ | |||
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 | ** The [SQLITE_FCNTL_ZIPVFS] opcode is implemented by zipvfs only. All other ** VFS should return SQLITE_NOTFOUND for this opcode. ** ** <li>[[SQLITE_FCNTL_RBU]] ** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by ** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for ** this opcode. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 #define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 #define SQLITE_FCNTL_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** The [SQLITE_FCNTL_ZIPVFS] opcode is implemented by zipvfs only. All other ** VFS should return SQLITE_NOTFOUND for this opcode. ** ** <li>[[SQLITE_FCNTL_RBU]] ** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by ** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for ** this opcode. ** ** <li>[[SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]] ** If the [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] opcode returns SQLITE_OK, then ** the file descriptor is placed in "batch write mode", which ** means all subsequent write operations will be deferred and done ** atomically at the next [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. Systems ** that do not support batch atomic writes will return SQLITE_NOTFOUND. ** ^Following a successful SQLITE_FCNTL_BEGIN_ATOMIC_WRITE and prior to ** the closing [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] or ** [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE], SQLite will make ** no VFS interface calls on the same [sqlite3_file] file descriptor ** except for calls to the xWrite method and the xFileControl method ** with [SQLITE_FCNTL_SIZE_HINT]. ** ** <li>[[SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]] ** The [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] opcode causes all write ** operations since the previous successful call to ** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be performed atomically. ** This file control returns [SQLITE_OK] if and only if the writes were ** all performed successfully and have been committed to persistent storage. ** ^Regardless of whether or not it is successful, this file control takes ** the file descriptor out of batch write mode so that all subsequent ** write operations are independent. ** ^SQLite will never invoke SQLITE_FCNTL_COMMIT_ATOMIC_WRITE without ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. ** ** <li>[[SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE]] ** The [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE] opcode causes all write ** operations since the previous successful call to ** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be rolled back. ** ^This file control takes the file descriptor out of batch write mode ** so that all subsequent write operations are independent. ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 #define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 #define SQLITE_FCNTL_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 |
︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 | #define SQLITE_FCNTL_WAL_BLOCK 24 #define SQLITE_FCNTL_ZIPVFS 25 #define SQLITE_FCNTL_RBU 26 #define SQLITE_FCNTL_VFS_POINTER 27 #define SQLITE_FCNTL_JOURNAL_POINTER 28 #define SQLITE_FCNTL_WIN32_GET_HANDLE 29 #define SQLITE_FCNTL_PDB 30 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO | > > > | 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | #define SQLITE_FCNTL_WAL_BLOCK 24 #define SQLITE_FCNTL_ZIPVFS 25 #define SQLITE_FCNTL_RBU 26 #define SQLITE_FCNTL_VFS_POINTER 27 #define SQLITE_FCNTL_JOURNAL_POINTER 28 #define SQLITE_FCNTL_WIN32_GET_HANDLE 29 #define SQLITE_FCNTL_PDB 30 #define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
︙ | ︙ | |||
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 | ** <dd> ^(The SQLITE_CONFIG_GETMALLOC option takes a single argument which ** is a pointer to an instance of the [sqlite3_mem_methods] structure. ** The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ ** This option can be used to overload the default memory allocation ** routines with a wrapper that simulations memory allocation failure or ** tracks memory usage, for example. </dd> ** ** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt> ** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int, ** interpreted as a boolean, which enables or disables the collection of ** memory allocation statistics. ^(When memory allocation statistics are ** disabled, the following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] ** <li> [sqlite3_soft_heap_limit64()] ** <li> [sqlite3_status64()] ** </ul>)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory ** allocation statistics are disabled by default. ** </dd> ** ** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt> | > > > > > > > > > > | < < < < < < < < < < < < < < < < < < | 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 | ** <dd> ^(The SQLITE_CONFIG_GETMALLOC option takes a single argument which ** is a pointer to an instance of the [sqlite3_mem_methods] structure. ** The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ ** This option can be used to overload the default memory allocation ** routines with a wrapper that simulations memory allocation failure or ** tracks memory usage, for example. </dd> ** ** [[SQLITE_CONFIG_SMALL_MALLOC]] <dt>SQLITE_CONFIG_SMALL_MALLOC</dt> ** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes single argument of ** type int, interpreted as a boolean, which if true provides a hint to ** SQLite that it should avoid large memory allocations if possible. ** SQLite will run faster if it is free to make large memory allocations, ** but some application might prefer to run slower in exchange for ** guarantees about memory fragmentation that are possible if large ** allocations are avoided. This hint is normally off. ** </dd> ** ** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt> ** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int, ** interpreted as a boolean, which enables or disables the collection of ** memory allocation statistics. ^(When memory allocation statistics are ** disabled, the following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] ** <li> [sqlite3_soft_heap_limit64()] ** <li> [sqlite3_status64()] ** </ul>)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory ** allocation statistics are disabled by default. ** </dd> ** ** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt> ** <dd> The SQLITE_CONFIG_SCRATCH option is no longer used. ** </dd> ** ** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt> ** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a memory pool ** that SQLite can use for the database page cache with the default page ** cache implementation. ** This configuration option is a no-op if an application-define page |
︙ | ︙ | |||
1673 1674 1675 1676 1677 1678 1679 | ** page cache memory is needed beyond what is provided by the initial ** allocation, then SQLite goes to [sqlite3_malloc()] separately for each ** additional cache line. </dd> ** ** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer ** that SQLite will use for all of its dynamic memory allocation needs | | < | 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 | ** page cache memory is needed beyond what is provided by the initial ** allocation, then SQLite goes to [sqlite3_malloc()] separately for each ** additional cache line. </dd> ** ** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer ** that SQLite will use for all of its dynamic memory allocation needs ** beyond those provided for by [SQLITE_CONFIG_PAGECACHE]. ** ^The SQLITE_CONFIG_HEAP option is only available if SQLite is compiled ** with either [SQLITE_ENABLE_MEMSYS3] or [SQLITE_ENABLE_MEMSYS5] and returns ** [SQLITE_ERROR] if invoked otherwise. ** ^There are three arguments to SQLITE_CONFIG_HEAP: ** An 8-byte aligned pointer to the memory, ** the number of bytes in the memory buffer, and the minimum allocation size. ** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts |
︙ | ︙ | |||
1867 1868 1869 1870 1871 1872 1873 | ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | | > | 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 | ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ #define SQLITE_CONFIG_PCACHE 14 /* no-op */ #define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** |
︙ | ︙ | |||
1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 | ** schema. ^The sole argument is a pointer to a constant UTF8 string ** which will become the new schema name in place of "main". ^SQLite ** does not make a copy of the new main schema name string, so the application ** must ensure that the argument passed into this DBCONFIG option is unchanged ** until after the database connection closes. ** </dd> ** ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** schema. ^The sole argument is a pointer to a constant UTF8 string ** which will become the new schema name in place of "main". ^SQLite ** does not make a copy of the new main schema name string, so the application ** must ensure that the argument passed into this DBCONFIG option is unchanged ** until after the database connection closes. ** </dd> ** ** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt> ** <dd> Usually, when a database in wal mode is closed or detached from a ** database handle, SQLite checks if this will mean that there are now no ** connections at all to the database. If so, it performs a checkpoint ** operation before closing the connection. This option may be used to ** override this behaviour. The first parameter passed to this operation ** is an integer - non-zero to disable checkpoints-on-close, or zero (the ** default) to enable them. The second parameter is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. ** </dd> ** ** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt> ** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, ** a single SQL query statement will always use the same algorithm regardless ** of values of [bound parameters].)^ The QPSG disables some query optimizations ** that look at the values of bound parameters, which can make some queries ** slower. But the QPSG has the advantage of more predictable behavior. With ** the QPSG active, SQLite will always use the same query plan in the field as ** was used during testing in the lab. ** </dd> ** ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the |
︙ | ︙ | |||
2016 2017 2018 2019 2020 2021 2022 | ** has a unique 64-bit signed ** integer key called the [ROWID | "rowid"]. ^The rowid is always available ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those ** names are not also used by explicitly declared columns. ^If ** the table has a column of type [INTEGER PRIMARY KEY] then that column ** is another alias for the rowid. ** | | | | < | | > > > > | > > > > > > > > > | | < | | < | 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 | ** has a unique 64-bit signed ** integer key called the [ROWID | "rowid"]. ^The rowid is always available ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those ** names are not also used by explicitly declared columns. ^If ** the table has a column of type [INTEGER PRIMARY KEY] then that column ** is another alias for the rowid. ** ** ^The sqlite3_last_insert_rowid(D) interface usually returns the [rowid] of ** the most recent successful [INSERT] into a rowid table or [virtual table] ** on database connection D. ^Inserts into [WITHOUT ROWID] tables are not ** recorded. ^If no successful [INSERT]s into rowid tables have ever occurred ** on the database connection D, then sqlite3_last_insert_rowid(D) returns ** zero. ** ** As well as being set automatically as rows are inserted into database ** tables, the value returned by this function may be set explicitly by ** [sqlite3_set_last_insert_rowid()] ** ** Some virtual table implementations may INSERT rows into rowid tables as ** part of committing a transaction (e.g. to flush data accumulated in memory ** to disk). In this case subsequent calls to this function return the rowid ** associated with these internal INSERT operations, which leads to ** unintuitive results. Virtual table implementations that do write to rowid ** tables in this way can avoid this problem by restoring the original ** rowid value using [sqlite3_set_last_insert_rowid()] before returning ** control to the user. ** ** ^(If an [INSERT] occurs within a trigger then this routine will ** return the [rowid] of the inserted row as long as the trigger is ** running. Once the trigger program ends, the value returned ** by this routine reverts to what it was before the trigger was fired.)^ ** ** ^An [INSERT] that fails due to a constraint violation is not a ** successful [INSERT] and does not change the value returned by this ** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, ** and INSERT OR ABORT make no changes to the return value of this ** routine when their insertion fails. ^(When INSERT OR REPLACE ** encounters a constraint violation, it does not fail. The |
︙ | ︙ | |||
2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 | ** function is running and thus changes the last insert [rowid], ** then the value returned by [sqlite3_last_insert_rowid()] is ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); /* ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^This function returns the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. | > > > > > > > > > > | 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 | ** function is running and thus changes the last insert [rowid], ** then the value returned by [sqlite3_last_insert_rowid()] is ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); /* ** CAPI3REF: Set the Last Insert Rowid value. ** METHOD: sqlite3 ** ** The sqlite3_set_last_insert_rowid(D, R) method allows the application to ** set the value returned by calling sqlite3_last_insert_rowid(D) to R ** without inserting a row into the database. */ void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); /* ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^This function returns the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. |
︙ | ︙ | |||
2167 2168 2169 2170 2171 2172 2173 | ** running statements reaches zero are interrupted as if they had been ** running prior to the sqlite3_interrupt() call. ^New SQL statements ** that are started after the running statement count reaches zero are ** not effected by the sqlite3_interrupt(). ** ^A call to sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the sqlite3_interrupt() call returns. | < < < | 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 | ** running statements reaches zero are interrupted as if they had been ** running prior to the sqlite3_interrupt() call. ^New SQL statements ** that are started after the running statement count reaches zero are ** not effected by the sqlite3_interrupt(). ** ^A call to sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the sqlite3_interrupt() call returns. */ void sqlite3_interrupt(sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete ** ** These routines are useful during command-line input to determine if the |
︙ | ︙ | |||
2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 | ** method. */ void sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** METHOD: sqlite3 ** ** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], | > > | | 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 | ** method. */ void sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** METHOD: sqlite3 ** KEYWORDS: {authorizer callback} ** ** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], ** [sqlite3_prepare_v3()], [sqlite3_prepare16()], [sqlite3_prepare16_v2()], ** and [sqlite3_prepare16_v3()]. ^At various ** points during the compilation process, as logic is being created ** to perform various actions, the authorizer callback is invoked to ** see if those actions are allowed. ^The authorizer callback should ** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the ** specific action but allow the SQL statement to continue to be ** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be ** rejected with an error. ^If the authorizer callback returns |
︙ | ︙ | |||
2659 2660 2661 2662 2663 2664 2665 | ** authorizer will fail with an error message explaining that ** access is denied. ** ** ^The first parameter to the authorizer callback is a copy of the third ** parameter to the sqlite3_set_authorizer() interface. ^The second parameter ** to the callback is an integer [SQLITE_COPY | action code] that specifies ** the particular action to be authorized. ^The third through sixth parameters | | | > > > > > > | 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 | ** authorizer will fail with an error message explaining that ** access is denied. ** ** ^The first parameter to the authorizer callback is a copy of the third ** parameter to the sqlite3_set_authorizer() interface. ^The second parameter ** to the callback is an integer [SQLITE_COPY | action code] that specifies ** the particular action to be authorized. ^The third through sixth parameters ** to the callback are either NULL pointers or zero-terminated strings ** that contain additional details about the action to be authorized. ** Applications must always be prepared to encounter a NULL pointer in any ** of the third through the sixth parameters of the authorization callback. ** ** ^If the action code is [SQLITE_READ] ** and the callback returns [SQLITE_IGNORE] then the ** [prepared statement] statement is constructed to substitute ** a NULL value in place of the table column that would have ** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] ** return can be used to deny an untrusted user access to individual ** columns of a table. ** ^When a table is referenced by a [SELECT] but no column values are ** extracted from that table (for example in a query like ** "SELECT count(*) FROM tab") then the [SQLITE_READ] authorizer callback ** is invoked once for that table with a column name that is an empty string. ** ^If the action code is [SQLITE_DELETE] and the callback returns ** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the ** [truncate optimization] is disabled and all rows are deleted individually. ** ** An authorizer is used when [sqlite3_prepare | preparing] ** SQL statements from an untrusted source, to ensure that the SQL statements ** do not try to access data they are not allowed to see, or that they do not |
︙ | ︙ | |||
3038 3039 3040 3041 3042 3043 3044 | ** automatically deleted as soon as the database connection is closed. ** ** [[URI filenames in sqlite3_open()]] <h3>URI Filenames</h3> ** ** ^If [URI filename] interpretation is enabled, and the filename argument ** begins with "file:", then the filename is interpreted as a URI. ^URI ** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is | | | | 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 | ** automatically deleted as soon as the database connection is closed. ** ** [[URI filenames in sqlite3_open()]] <h3>URI Filenames</h3> ** ** ^If [URI filename] interpretation is enabled, and the filename argument ** begins with "file:", then the filename is interpreted as a URI. ^URI ** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is ** set in the third argument to sqlite3_open_v2(), or if it has ** been enabled globally using the [SQLITE_CONFIG_URI] option with the ** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. ** URI filename interpretation is turned off ** by default, but future releases of SQLite might enable URI filename ** interpretation by default. See "[URI filenames]" for additional ** information. ** ** URI filenames are parsed according to RFC 3986. ^If the URI contains an ** authority, then it must be either an empty string or the string ** "localhost". ^If the authority is not an empty string or "localhost", an |
︙ | ︙ | |||
3380 3381 3382 3383 3384 3385 3386 | ** <dd>The maximum depth of the parse tree on any expression.</dd>)^ ** ** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^ ** ** [[SQLITE_LIMIT_VDBE_OP]] ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> ** <dd>The maximum number of instructions in a virtual machine program | | | | | 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 | ** <dd>The maximum depth of the parse tree on any expression.</dd>)^ ** ** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^ ** ** [[SQLITE_LIMIT_VDBE_OP]] ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> ** <dd>The maximum number of instructions in a virtual machine program ** used to implement an SQL statement. If [sqlite3_prepare_v2()] or ** the equivalent tries to allocate space for more than this many opcodes ** in a single prepared statement, an SQLITE_NOMEM error is returned.</dd>)^ ** ** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt> ** <dd>The maximum number of arguments on a function.</dd>)^ ** ** [[SQLITE_LIMIT_ATTACHED]] ^(<dt>SQLITE_LIMIT_ATTACHED</dt> ** <dd>The maximum number of [ATTACH | attached databases].)^</dd> ** |
︙ | ︙ | |||
3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 | #define SQLITE_LIMIT_FUNCTION_ARG 6 #define SQLITE_LIMIT_ATTACHED 7 #define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 #define SQLITE_LIMIT_WORKER_THREADS 11 /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} ** METHOD: sqlite3 ** CONSTRUCTOR: sqlite3_stmt ** | > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > | > | | | 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 | #define SQLITE_LIMIT_FUNCTION_ARG 6 #define SQLITE_LIMIT_ATTACHED 7 #define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 #define SQLITE_LIMIT_WORKER_THREADS 11 /* ** CAPI3REF: Prepare Flags ** ** These constants define various flags that can be passed into ** "prepFlags" parameter of the [sqlite3_prepare_v3()] and ** [sqlite3_prepare16_v3()] interfaces. ** ** New flags may be added in future releases of SQLite. ** ** <dl> ** [[SQLITE_PREPARE_PERSISTENT]] ^(<dt>SQLITE_PREPARE_PERSISTENT</dt> ** <dd>The SQLITE_PREPARE_PERSISTENT flag is a hint to the query planner ** that the prepared statement will be retained for a long time and ** probably reused many times.)^ ^Without this flag, [sqlite3_prepare_v3()] ** and [sqlite3_prepare16_v3()] assume that the prepared statement will ** be used just once or at most a few times and then destroyed using ** [sqlite3_finalize()] relatively soon. The current implementation acts ** on this hint by avoiding the use of [lookaside memory] so as not to ** deplete the limited store of lookaside memory. Future versions of ** SQLite may act on this hint differently. ** </dl> */ #define SQLITE_PREPARE_PERSISTENT 0x01 /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} ** METHOD: sqlite3 ** CONSTRUCTOR: sqlite3_stmt ** ** To execute an SQL statement, it must first be compiled into a byte-code ** program using one of these routines. Or, in other words, these routines ** are constructors for the [prepared statement] object. ** ** The preferred routine to use is [sqlite3_prepare_v2()]. The ** [sqlite3_prepare()] interface is legacy and should be avoided. ** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used ** for special purposes. ** ** The use of the UTF-8 interfaces is preferred, as SQLite currently ** does all parsing using UTF-8. The UTF-16 interfaces are provided ** as a convenience. The UTF-16 interfaces work by converting the ** input text into UTF-8, then invoking the corresponding UTF-8 interface. ** ** The first argument, "db", is a [database connection] obtained from a ** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or ** [sqlite3_open16()]. The database connection must not have been closed. ** ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare(), sqlite3_prepare_v2(), ** and sqlite3_prepare_v3() ** interfaces use UTF-8, and sqlite3_prepare16(), sqlite3_prepare16_v2(), ** and sqlite3_prepare16_v3() use UTF-16. ** ** ^If the nByte argument is negative, then zSql is read up to the ** first zero terminator. ^If nByte is positive, then it is the ** number of bytes read from zSql. ^If nByte is zero, then no prepared ** statement is generated. ** If the caller knows that the supplied string is nul-terminated, then ** there is a small performance advantage to passing an nByte parameter that |
︙ | ︙ | |||
3463 3464 3465 3466 3467 3468 3469 | ** The calling procedure is responsible for deleting the compiled ** SQL statement using [sqlite3_finalize()] after it has finished with it. ** ppStmt may not be NULL. ** ** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; ** otherwise an [error code] is returned. ** | | | > | | | 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 | ** The calling procedure is responsible for deleting the compiled ** SQL statement using [sqlite3_finalize()] after it has finished with it. ** ppStmt may not be NULL. ** ** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; ** otherwise an [error code] is returned. ** ** The sqlite3_prepare_v2(), sqlite3_prepare_v3(), sqlite3_prepare16_v2(), ** and sqlite3_prepare16_v3() interfaces are recommended for all new programs. ** The older interfaces (sqlite3_prepare() and sqlite3_prepare16()) ** are retained for backwards compatibility, but their use is discouraged. ** ^In the "vX" interfaces, the prepared statement ** that is returned (the [sqlite3_stmt] object) contains a copy of the ** original SQL text. This causes the [sqlite3_step()] interface to ** behave differently in three ways: ** ** <ol> ** <li> ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it |
︙ | ︙ | |||
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 | ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** </li> ** </ol> */ int sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); int sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); int sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); int sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); /* ** CAPI3REF: Retrieving Statement SQL ** METHOD: sqlite3_stmt ** ** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8 ** SQL text used to create [prepared statement] P if P was | > > > > > > > > > > > > > > > > > > > > > > > | | 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 | ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** </li> ** ** <p>^sqlite3_prepare_v3() differs from sqlite3_prepare_v2() only in having ** the extra prepFlags parameter, which is a bit array consisting of zero or ** more of the [SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_*] flags. ^The ** sqlite3_prepare_v2() interface works exactly the same as ** sqlite3_prepare_v3() with a zero prepFlags parameter. ** </ol> */ int sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); int sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); int sqlite3_prepare_v3( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); int sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); int sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); int sqlite3_prepare16_v3( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); /* ** CAPI3REF: Retrieving Statement SQL ** METHOD: sqlite3_stmt ** ** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8 ** SQL text used to create [prepared statement] P if P was ** created by [sqlite3_prepare_v2()], [sqlite3_prepare_v3()], ** [sqlite3_prepare16_v2()], or [sqlite3_prepare16_v3()]. ** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8 ** string containing the SQL text of prepared statement P with ** [bound parameters] expanded. ** ** ^(For example, if a prepared statement is created using the SQL ** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345 ** and parameter :xyz is unbound, then sqlite3_sql() will return |
︙ | ︙ | |||
3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 | ** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, ** since the statements themselves do not actually modify the database but ** rather they control the timing of when other statements modify the ** database. ^The [ATTACH] and [DETACH] statements also cause ** sqlite3_stmt_readonly() to return true since, while those statements ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt ** | > > > > | 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 | ** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, ** since the statements themselves do not actually modify the database but ** rather they control the timing of when other statements modify the ** database. ^The [ATTACH] and [DETACH] statements also cause ** sqlite3_stmt_readonly() to return true since, while those statements ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. ** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** sqlite3_stmt_readonly() returns false for those commands. */ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt ** |
︙ | ︙ | |||
3651 3652 3653 3654 3655 3656 3657 | ** still make the distinction between protected and unprotected ** sqlite3_value objects even when not strictly required. ** ** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. ** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. | | | > | | 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 | ** still make the distinction between protected and unprotected ** sqlite3_value objects even when not strictly required. ** ** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. ** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. ** Unprotected sqlite3_value objects may only be used as arguments ** to [sqlite3_result_value()], [sqlite3_bind_value()], and ** [sqlite3_value_dup()]. ** The [sqlite3_value_blob | sqlite3_value_type()] family of ** interfaces require protected sqlite3_value objects. */ typedef struct sqlite3_value sqlite3_value; /* ** CAPI3REF: SQL Function Context Object ** ** The context in which an SQL function executes is stored in an ** sqlite3_context object. ^A pointer to an sqlite3_context object ** is always first parameter to [application-defined SQL functions]. |
︙ | ︙ | |||
3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 | ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that ** is filled with zeroes. ^A zeroblob uses a fixed amount of memory ** (just an integer to hold its size) while it is being processed. ** Zeroblobs are intended to serve as placeholders for BLOBs whose ** content is later written using ** [sqlite3_blob_open | incremental BLOB I/O] routines. ** ^A negative value for the zeroblob results in a zero-length BLOB. ** ** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer ** for the [prepared statement] or with a prepared statement for which ** [sqlite3_step()] has been called more recently than [sqlite3_reset()], ** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() ** routine is passed a [prepared statement] that has been finalized, the ** result is undefined and probably harmful. | > > > > > > > > > | 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 | ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that ** is filled with zeroes. ^A zeroblob uses a fixed amount of memory ** (just an integer to hold its size) while it is being processed. ** Zeroblobs are intended to serve as placeholders for BLOBs whose ** content is later written using ** [sqlite3_blob_open | incremental BLOB I/O] routines. ** ^A negative value for the zeroblob results in a zero-length BLOB. ** ** ^The sqlite3_bind_pointer(S,I,P,T,D) routine causes the I-th parameter in ** [prepared statement] S to have an SQL value of NULL, but to also be ** associated with the pointer P of type T. ^D is either a NULL pointer or ** a pointer to a destructor function for P. ^SQLite will invoke the ** destructor D with a single argument of P when it is finished using ** P. The T parameter should be a static string, preferably a string ** literal. The sqlite3_bind_pointer() routine is part of the ** [pointer passing interface] added for SQLite 3.20.0. ** ** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer ** for the [prepared statement] or with a prepared statement for which ** [sqlite3_step()] has been called more recently than [sqlite3_reset()], ** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() ** routine is passed a [prepared statement] that has been finalized, the ** result is undefined and probably harmful. |
︙ | ︙ | |||
3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 | int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); int sqlite3_bind_null(sqlite3_stmt*, int); int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, void(*)(void*), unsigned char encoding); int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64); /* ** CAPI3REF: Number Of SQL Parameters ** METHOD: sqlite3_stmt ** | > | 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 | int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); int sqlite3_bind_null(sqlite3_stmt*, int); int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, void(*)(void*), unsigned char encoding); int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); int sqlite3_bind_pointer(sqlite3_stmt*, int, void*, const char*,void(*)(void*)); int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64); /* ** CAPI3REF: Number Of SQL Parameters ** METHOD: sqlite3_stmt ** |
︙ | ︙ | |||
3834 3835 3836 3837 3838 3839 3840 | ** and are referred to as "nameless" or "anonymous parameters". ** ** ^The first host parameter has an index of 1, not 0. ** ** ^If the value N is out of range or if the N-th parameter is ** nameless, then NULL is returned. ^The returned string is ** always in UTF-8 encoding even if the named parameter was | | | | > | 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 | ** and are referred to as "nameless" or "anonymous parameters". ** ** ^The first host parameter has an index of 1, not 0. ** ** ^If the value N is out of range or if the N-th parameter is ** nameless, then NULL is returned. ^The returned string is ** always in UTF-8 encoding even if the named parameter was ** originally specified as UTF-16 in [sqlite3_prepare16()], ** [sqlite3_prepare16_v2()], or [sqlite3_prepare16_v3()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* ** CAPI3REF: Index Of A Parameter With A Given Name ** METHOD: sqlite3_stmt ** ** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second ** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero ** is returned if no matching parameter is found. ^The parameter ** name must be given in UTF-8 even if the original statement ** was prepared from UTF-16 text using [sqlite3_prepare16_v2()] or ** [sqlite3_prepare16_v3()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_name()]. */ int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); |
︙ | ︙ | |||
3875 3876 3877 3878 3879 3880 3881 | int sqlite3_clear_bindings(sqlite3_stmt*); /* ** CAPI3REF: Number Of Columns In A Result Set ** METHOD: sqlite3_stmt ** ** ^Return the number of columns in the result set returned by the | | | > > > > | 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 | int sqlite3_clear_bindings(sqlite3_stmt*); /* ** CAPI3REF: Number Of Columns In A Result Set ** METHOD: sqlite3_stmt ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^If this routine returns 0, that means the ** [prepared statement] returns no data (for example an [UPDATE]). ** ^However, just because this routine returns a positive number does not ** mean that one or more rows of data will be returned. ^A SELECT statement ** will always have a positive sqlite3_column_count() but depending on the ** WHERE clause constraints and the table content, it might return no rows. ** ** See also: [sqlite3_data_count()] */ int sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set |
︙ | ︙ | |||
4002 4003 4004 4005 4006 4007 4008 | const char *sqlite3_column_decltype(sqlite3_stmt*,int); const void *sqlite3_column_decltype16(sqlite3_stmt*,int); /* ** CAPI3REF: Evaluate An SQL Statement ** METHOD: sqlite3_stmt ** | | > | | > | | | | 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 | const char *sqlite3_column_decltype(sqlite3_stmt*,int); const void *sqlite3_column_decltype16(sqlite3_stmt*,int); /* ** CAPI3REF: Evaluate An SQL Statement ** METHOD: sqlite3_stmt ** ** After a [prepared statement] has been prepared using any of ** [sqlite3_prepare_v2()], [sqlite3_prepare_v3()], [sqlite3_prepare16_v2()], ** or [sqlite3_prepare16_v3()] or one of the legacy ** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function ** must be called one or more times to evaluate the statement. ** ** The details of the behavior of the sqlite3_step() interface depend ** on whether the statement was prepared using the newer "vX" interfaces ** [sqlite3_prepare_v3()], [sqlite3_prepare_v2()], [sqlite3_prepare16_v3()], ** [sqlite3_prepare16_v2()] or the older legacy ** interfaces [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the ** new "vX" interface is recommended for new applications but the legacy ** interface will continue to be supported. ** ** ^In the legacy interface, the return value will be either [SQLITE_BUSY], ** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. ** ^With the "v2" interface, any of the other [result codes] or ** [extended result codes] might be returned as well. ** |
︙ | ︙ | |||
4072 4073 4074 4075 4076 4077 4078 | ** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step() ** API always returns a generic error code, [SQLITE_ERROR], following any ** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call ** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the ** specific [error codes] that better describes the error. ** We admit that this is a goofy design. The problem has been fixed ** with the "v2" interface. If you prepare all of your SQL statements | > | | | 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 | ** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step() ** API always returns a generic error code, [SQLITE_ERROR], following any ** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call ** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the ** specific [error codes] that better describes the error. ** We admit that this is a goofy design. The problem has been fixed ** with the "v2" interface. If you prepare all of your SQL statements ** using [sqlite3_prepare_v3()] or [sqlite3_prepare_v2()] ** or [sqlite3_prepare16_v2()] or [sqlite3_prepare16_v3()] instead ** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "vX" interfaces is recommended. */ int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** METHOD: sqlite3_stmt ** |
︙ | ︙ | |||
4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 | #endif #define SQLITE3_TEXT 3 /* ** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} ** METHOD: sqlite3_stmt ** ** ^These routines return information about a single column of the current ** result row of a query. ^In every case the first argument is a pointer ** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] ** that was returned from [sqlite3_prepare_v2()] or one of its variants) ** and the second argument is the index of the column for which information ** should be returned. ^The leftmost column of the result set has the index 0. | > > > > > > > > > > > > > > > > > > > > > > | 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 | #endif #define SQLITE3_TEXT 3 /* ** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} ** METHOD: sqlite3_stmt ** ** <b>Summary:</b> ** <blockquote><table border=0 cellpadding=0 cellspacing=0> ** <tr><td><b>sqlite3_column_blob</b><td>→<td>BLOB result ** <tr><td><b>sqlite3_column_double</b><td>→<td>REAL result ** <tr><td><b>sqlite3_column_int</b><td>→<td>32-bit INTEGER result ** <tr><td><b>sqlite3_column_int64</b><td>→<td>64-bit INTEGER result ** <tr><td><b>sqlite3_column_text</b><td>→<td>UTF-8 TEXT result ** <tr><td><b>sqlite3_column_text16</b><td>→<td>UTF-16 TEXT result ** <tr><td><b>sqlite3_column_value</b><td>→<td>The result as an ** [sqlite3_value|unprotected sqlite3_value] object. ** <tr><td> <td> <td> ** <tr><td><b>sqlite3_column_bytes</b><td>→<td>Size of a BLOB ** or a UTF-8 TEXT result in bytes ** <tr><td><b>sqlite3_column_bytes16 </b> ** <td>→ <td>Size of UTF-16 ** TEXT in bytes ** <tr><td><b>sqlite3_column_type</b><td>→<td>Default ** datatype of the result ** </table></blockquote> ** ** <b>Details:</b> ** ** ^These routines return information about a single column of the current ** result row of a query. ^In every case the first argument is a pointer ** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] ** that was returned from [sqlite3_prepare_v2()] or one of its variants) ** and the second argument is the index of the column for which information ** should be returned. ^The leftmost column of the result set has the index 0. |
︙ | ︙ | |||
4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 | ** If any of these routines are called after [sqlite3_reset()] or ** [sqlite3_finalize()] or after [sqlite3_step()] has returned ** something other than [SQLITE_ROW], the results are undefined. ** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] ** are called from a different thread while any of these routines ** are pending, then the results are undefined. ** ** ^The sqlite3_column_type() routine returns the ** [SQLITE_INTEGER | datatype code] for the initial data type ** of the result column. ^The returned value is one of [SQLITE_INTEGER], | > > > > > > | > > | | | > > > > > | 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 | ** If any of these routines are called after [sqlite3_reset()] or ** [sqlite3_finalize()] or after [sqlite3_step()] has returned ** something other than [SQLITE_ROW], the results are undefined. ** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] ** are called from a different thread while any of these routines ** are pending, then the results are undefined. ** ** The first six interfaces (_blob, _double, _int, _int64, _text, and _text16) ** each return the value of a result column in a specific data format. If ** the result column is not initially in the requested format (for example, ** if the query returns an integer but the sqlite3_column_text() interface ** is used to extract the value) then an automatic type conversion is performed. ** ** ^The sqlite3_column_type() routine returns the ** [SQLITE_INTEGER | datatype code] for the initial data type ** of the result column. ^The returned value is one of [SQLITE_INTEGER], ** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. ** The return value of sqlite3_column_type() can be used to decide which ** of the first six interface should be used to extract the column value. ** The value returned by sqlite3_column_type() is only meaningful if no ** automatic type conversions have occurred for the value in question. ** After a type conversion, the result of calling sqlite3_column_type() ** is undefined, though harmless. Future ** versions of SQLite may change the behavior of sqlite3_column_type() ** following a type conversion. ** ** If the result is a BLOB or a TEXT string, then the sqlite3_column_bytes() ** or sqlite3_column_bytes16() interfaces can be used to determine the size ** of that BLOB or string. ** ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns |
︙ | ︙ | |||
4204 4205 4206 4207 4208 4209 4210 4211 | ** [unprotected sqlite3_value] object. In a multithreaded environment, ** an unprotected sqlite3_value object may only be used safely with ** [sqlite3_bind_value()] and [sqlite3_result_value()]. ** If the [unprotected sqlite3_value] object returned by ** [sqlite3_column_value()] is used in any other way, including calls ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], ** or [sqlite3_value_bytes()], the behavior is not threadsafe. ** | > > > > | | | 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 | ** [unprotected sqlite3_value] object. In a multithreaded environment, ** an unprotected sqlite3_value object may only be used safely with ** [sqlite3_bind_value()] and [sqlite3_result_value()]. ** If the [unprotected sqlite3_value] object returned by ** [sqlite3_column_value()] is used in any other way, including calls ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], ** or [sqlite3_value_bytes()], the behavior is not threadsafe. ** Hence, the sqlite3_column_value() interface ** is normally only useful within the implementation of ** [application-defined SQL functions] or [virtual tables], not within ** top-level application code. ** ** The these routines may attempt to convert the datatype of the result. ** ^For example, if the internal representation is FLOAT and a text result ** is requested, [sqlite3_snprintf()] is used internally to perform the ** conversion automatically. ^(The following table details the conversions ** that are applied: ** ** <blockquote> ** <table border="1"> ** <tr><th> Internal<br>Type <th> Requested<br>Type <th> Conversion |
︙ | ︙ | |||
4278 4279 4280 4281 4282 4283 4284 | ** to sqlite3_column_text() or sqlite3_column_blob() with calls to ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() ** with calls to sqlite3_column_bytes(). ** ** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings | | < < < > > > | 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 | ** to sqlite3_column_text() or sqlite3_column_blob() with calls to ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() ** with calls to sqlite3_column_bytes(). ** ** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do not pass the pointers returned ** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** ** ^(If a memory allocation error occurs during the evaluation of any ** of these routines, a default value is returned. The default value ** is either the integer 0, the floating point number 0.0, or a NULL ** pointer. Subsequent calls to [sqlite3_errcode()] will return ** [SQLITE_NOMEM].)^ */ const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); double sqlite3_column_double(sqlite3_stmt*, int iCol); int sqlite3_column_int(sqlite3_stmt*, int iCol); sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); int sqlite3_column_bytes(sqlite3_stmt*, int iCol); int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); int sqlite3_column_type(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** DESTRUCTOR: sqlite3_stmt ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors |
︙ | ︙ | |||
4531 4532 4533 4534 4535 4536 4537 | void*,sqlite3_int64); #endif /* ** CAPI3REF: Obtaining SQL Values ** METHOD: sqlite3_value ** | | | > > > > > > > | > > > > > > > > > > > > > < < < < > > > | > | < | > > > > > > > > > > > > > > > > > > < < > > > < < < < | 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 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 | void*,sqlite3_int64); #endif /* ** CAPI3REF: Obtaining SQL Values ** METHOD: sqlite3_value ** ** <b>Summary:</b> ** <blockquote><table border=0 cellpadding=0 cellspacing=0> ** <tr><td><b>sqlite3_value_blob</b><td>→<td>BLOB value ** <tr><td><b>sqlite3_value_double</b><td>→<td>REAL value ** <tr><td><b>sqlite3_value_int</b><td>→<td>32-bit INTEGER value ** <tr><td><b>sqlite3_value_int64</b><td>→<td>64-bit INTEGER value ** <tr><td><b>sqlite3_value_pointer</b><td>→<td>Pointer value ** <tr><td><b>sqlite3_value_text</b><td>→<td>UTF-8 TEXT value ** <tr><td><b>sqlite3_value_text16</b><td>→<td>UTF-16 TEXT value in ** the native byteorder ** <tr><td><b>sqlite3_value_text16be</b><td>→<td>UTF-16be TEXT value ** <tr><td><b>sqlite3_value_text16le</b><td>→<td>UTF-16le TEXT value ** <tr><td> <td> <td> ** <tr><td><b>sqlite3_value_bytes</b><td>→<td>Size of a BLOB ** or a UTF-8 TEXT in bytes ** <tr><td><b>sqlite3_value_bytes16 </b> ** <td>→ <td>Size of UTF-16 ** TEXT in bytes ** <tr><td><b>sqlite3_value_type</b><td>→<td>Default ** datatype of the value ** <tr><td><b>sqlite3_value_numeric_type </b> ** <td>→ <td>Best numeric datatype of the value ** </table></blockquote> ** ** <b>Details:</b> ** ** These routines extract type, size, and content information from ** [protected sqlite3_value] objects. Protected sqlite3_value objects ** are used to pass parameter information into implementation of ** [application-defined SQL functions] and [virtual tables]. ** ** These routines work only with [protected sqlite3_value] objects. ** Any attempt to use these routines on an [unprotected sqlite3_value] ** is not threadsafe. ** ** ^These routines work just like the corresponding [column access functions] ** except that these routines take a single [protected sqlite3_value] object ** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. ** ** ^The sqlite3_value_text16() interface extracts a UTF-16 string ** in the native byte-order of the host machine. ^The ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces ** extract UTF-16 strings as big-endian and little-endian respectively. ** ** ^If [sqlite3_value] object V was initialized ** using [sqlite3_bind_pointer(S,I,P,X,D)] or [sqlite3_result_pointer(C,P,X,D)] ** and if X and Y are strings that compare equal according to strcmp(X,Y), ** then sqlite3_value_pointer(V,Y) will return the pointer P. ^Otherwise, ** sqlite3_value_pointer(V,Y) returns a NULL. The sqlite3_bind_pointer() ** routine is part of the [pointer passing interface] added for SQLite 3.20.0. ** ** ^(The sqlite3_value_type(V) interface returns the ** [SQLITE_INTEGER | datatype code] for the initial datatype of the ** [sqlite3_value] object V. The returned value is one of [SQLITE_INTEGER], ** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL].)^ ** Other interfaces might change the datatype for an sqlite3_value object. ** For example, if the datatype is initially SQLITE_INTEGER and ** sqlite3_value_text(V) is called to extract a text value for that ** integer, then subsequent calls to sqlite3_value_type(V) might return ** SQLITE_TEXT. Whether or not a persistent internal datatype conversion ** occurs is undefined and may change from one release of SQLite to the next. ** ** ^(The sqlite3_value_numeric_type() interface attempts to apply ** numeric affinity to the value. This means that an attempt is ** made to convert the value to an integer or floating point. If ** such a conversion is possible without loss of information (in other ** words, if the value is a string that looks like a number) ** then the conversion is performed. Otherwise no conversion occurs. ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. ** ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ const void *sqlite3_value_blob(sqlite3_value*); double sqlite3_value_double(sqlite3_value*); int sqlite3_value_int(sqlite3_value*); sqlite3_int64 sqlite3_value_int64(sqlite3_value*); void *sqlite3_value_pointer(sqlite3_value*, const char*); const unsigned char *sqlite3_value_text(sqlite3_value*); const void *sqlite3_value_text16(sqlite3_value*); const void *sqlite3_value_text16le(sqlite3_value*); const void *sqlite3_value_text16be(sqlite3_value*); int sqlite3_value_bytes(sqlite3_value*); int sqlite3_value_bytes16(sqlite3_value*); int sqlite3_value_type(sqlite3_value*); int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values ** METHOD: sqlite3_value ** ** The sqlite3_value_subtype(V) function returns the subtype for ** an [application-defined SQL function] argument V. The subtype ** information can be used to pass a limited amount of context from ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. */ unsigned int sqlite3_value_subtype(sqlite3_value*); /* ** CAPI3REF: Copy And Free SQL Values ** METHOD: sqlite3_value ** |
︙ | ︙ | |||
4706 4707 4708 4709 4710 4711 4712 | ** of where this might be useful is in a regular-expression matching ** function. The compiled version of the regular expression can be stored as ** metadata associated with the pattern string. ** Then as long as the pattern string remains the same, ** the compiled regular expression can be reused on multiple ** invocations of the same function. ** | | | | > | | 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 | ** of where this might be useful is in a regular-expression matching ** function. The compiled version of the regular expression can be stored as ** metadata associated with the pattern string. ** Then as long as the pattern string remains the same, ** the compiled regular expression can be reused on multiple ** invocations of the same function. ** ** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument ** value to the application-defined function. ^N is zero for the left-most ** function argument. ^If there is no metadata ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface ** returns a NULL pointer. ** ** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th ** argument of the application-defined function. ^Subsequent ** calls to sqlite3_get_auxdata(C,N) return P from the most recent ** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or ** NULL if the metadata has been discarded. |
︙ | ︙ | |||
4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 | ** should be called near the end of the function implementation and the ** function implementation should not make any use of P after ** sqlite3_set_auxdata() has been called. ** ** ^(In practice, metadata is preserved between function calls for ** function parameters that are compile-time constants, including literal ** values and [parameters] and expressions composed from the same.)^ ** ** These routines must be called from the same thread in which ** the SQL function is running. */ void *sqlite3_get_auxdata(sqlite3_context*, int N); void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); | > > > > | 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 | ** should be called near the end of the function implementation and the ** function implementation should not make any use of P after ** sqlite3_set_auxdata() has been called. ** ** ^(In practice, metadata is preserved between function calls for ** function parameters that are compile-time constants, including literal ** values and [parameters] and expressions composed from the same.)^ ** ** 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. ** ** These routines must be called from the same thread in which ** the SQL function is running. */ void *sqlite3_get_auxdata(sqlite3_context*, int N); void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); |
︙ | ︙ | |||
4863 4864 4865 4866 4867 4868 4869 | ** ^If the 4th parameter to the sqlite3_result_text* interfaces or to ** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite ** assumes that the text or BLOB result is in constant space and does not ** copy the content of the parameter nor call a destructor on the content ** when it has finished using that result. ** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT | | > > > > > > > > > > > | 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 | ** ^If the 4th parameter to the sqlite3_result_text* interfaces or to ** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite ** assumes that the text or BLOB result is in constant space and does not ** copy the content of the parameter nor call a destructor on the content ** when it has finished using that result. ** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT ** then SQLite makes a copy of the result into space obtained ** from [sqlite3_malloc()] before it returns. ** ** ^The sqlite3_result_value() interface sets the result of ** the application-defined function to be a copy of the ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] ** so that the [sqlite3_value] specified in the parameter may change or ** be deallocated after sqlite3_result_value() returns without harm. ** ^A [protected sqlite3_value] object may always be used where an ** [unprotected sqlite3_value] object is required, so either ** kind of [sqlite3_value] object can be used with this interface. ** ** ^The sqlite3_result_pointer(C,P,T,D) interface sets the result to an ** SQL NULL value, just like [sqlite3_result_null(C)], except that it ** also associates the host-language pointer P or type T with that ** NULL value such that the pointer can be retrieved within an ** [application-defined SQL function] using [sqlite3_value_pointer()]. ** ^If the D parameter is not NULL, then it is a pointer to a destructor ** for the P parameter. ^SQLite invokes D with P as its only argument ** when SQLite is finished with P. The T parameter should be a static ** string and preferably a string literal. The sqlite3_result_pointer() ** routine is part of the [pointer passing interface] added for SQLite 3.20.0. ** ** If these routines are called from within the different thread ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. */ void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); void sqlite3_result_blob64(sqlite3_context*,const void*, sqlite3_uint64,void(*)(void*)); |
︙ | ︙ | |||
4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 | void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, void(*)(void*), unsigned char encoding); void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); void sqlite3_result_value(sqlite3_context*, sqlite3_value*); void sqlite3_result_zeroblob(sqlite3_context*, int n); int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); /* ** CAPI3REF: Setting The Subtype Of An SQL Function ** METHOD: sqlite3_context | > | 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 | void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, void(*)(void*), unsigned char encoding); void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); void sqlite3_result_value(sqlite3_context*, sqlite3_value*); void sqlite3_result_pointer(sqlite3_context*, void*,const char*,void(*)(void*)); void sqlite3_result_zeroblob(sqlite3_context*, int n); int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); /* ** CAPI3REF: Setting The Subtype Of An SQL Function ** METHOD: sqlite3_context |
︙ | ︙ | |||
5385 5386 5387 5388 5389 5390 5391 | ** ^In the case of an update, this is the [rowid] after the update takes place. ** ** ^(The update hook is not invoked when internal system tables are ** modified (i.e. sqlite_master and sqlite_sequence).)^ ** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified. ** ** ^In the current implementation, the update hook | | | 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 | ** ^In the case of an update, this is the [rowid] after the update takes place. ** ** ^(The update hook is not invoked when internal system tables are ** modified (i.e. sqlite_master and sqlite_sequence).)^ ** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified. ** ** ^In the current implementation, the update hook ** is not invoked when conflicting rows are deleted because of an ** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions |
︙ | ︙ | |||
5558 5559 5560 5561 5562 5563 5564 | ** interface returns SQLITE_OK and fills in the non-NULL pointers in ** the final five arguments with appropriate values if the specified ** column exists. ^The sqlite3_table_column_metadata() interface returns ** SQLITE_ERROR and if the specified column does not exist. ** ^If the column-name parameter to sqlite3_table_column_metadata() is a ** NULL pointer, then this routine simply checks for the existence of the ** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it | | > > | 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 | ** interface returns SQLITE_OK and fills in the non-NULL pointers in ** the final five arguments with appropriate values if the specified ** column exists. ^The sqlite3_table_column_metadata() interface returns ** SQLITE_ERROR and if the specified column does not exist. ** ^If the column-name parameter to sqlite3_table_column_metadata() is a ** NULL pointer, then this routine simply checks for the existence of the ** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it ** does not. If the table name parameter T in a call to ** sqlite3_table_column_metadata(X,D,T,C,...) is NULL then the result is ** undefined behavior. ** ** ^The column is identified by the second, third and fourth parameters to ** this function. ^(The second parameter is either the name of the database ** (i.e. "main", "temp", or an attached database) containing the specified ** table or NULL.)^ ^If it is NULL, then all attached databases are searched ** for the table using the same algorithm used by the database engine to ** resolve unqualified table references. |
︙ | ︙ | |||
5968 5969 5970 5971 5972 5973 5974 | ** CAPI3REF: Virtual Table Constraint Operator Codes ** ** These macros defined the allowed values for the ** [sqlite3_index_info].aConstraint[].op field. Each value represents ** an operator that is part of a constraint term in the wHERE clause of ** a query that uses a [virtual table]. */ | | | | | | | | | | > > > > > | 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 | ** CAPI3REF: Virtual Table Constraint Operator Codes ** ** These macros defined the allowed values for the ** [sqlite3_index_info].aConstraint[].op field. Each value represents ** an operator that is part of a constraint term in the wHERE clause of ** a query that uses a [virtual table]. */ #define SQLITE_INDEX_CONSTRAINT_EQ 2 #define SQLITE_INDEX_CONSTRAINT_GT 4 #define SQLITE_INDEX_CONSTRAINT_LE 8 #define SQLITE_INDEX_CONSTRAINT_LT 16 #define SQLITE_INDEX_CONSTRAINT_GE 32 #define SQLITE_INDEX_CONSTRAINT_MATCH 64 #define SQLITE_INDEX_CONSTRAINT_LIKE 65 #define SQLITE_INDEX_CONSTRAINT_GLOB 66 #define SQLITE_INDEX_CONSTRAINT_REGEXP 67 #define SQLITE_INDEX_CONSTRAINT_NE 68 #define SQLITE_INDEX_CONSTRAINT_ISNOT 69 #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 #define SQLITE_INDEX_CONSTRAINT_IS 72 /* ** CAPI3REF: Register A Virtual Table Implementation ** METHOD: sqlite3 ** ** ^These routines are used to register a new [virtual table module] name. ** ^Module names must be registered before |
︙ | ︙ | |||
6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 | ** being opened for read/write access)^. ** </ul> ** ** ^Unless it returns SQLITE_MISUSE, this function sets the ** [database connection] error code and message accessible via ** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. ** ** ** ^(If the row that a BLOB handle points to is modified by an ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects ** then the BLOB handle is marked as "expired". ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for | > > > > > > | 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 | ** being opened for read/write access)^. ** </ul> ** ** ^Unless it returns SQLITE_MISUSE, this function sets the ** [database connection] error code and message accessible via ** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. ** ** A BLOB referenced by sqlite3_blob_open() may be read using the ** [sqlite3_blob_read()] interface and modified by using ** [sqlite3_blob_write()]. The [BLOB handle] can be moved to a ** different row of the same table using the [sqlite3_blob_reopen()] ** interface. However, the column, table, or database of a [BLOB handle] ** cannot be changed after the [BLOB handle] is opened. ** ** ^(If the row that a BLOB handle points to is modified by an ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects ** then the BLOB handle is marked as "expired". ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for |
︙ | ︙ | |||
6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 | ** ** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function may be used to create a ** zero-filled blob to read or write using the incremental-blob interface. ** ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. */ int sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); /* ** CAPI3REF: Move a BLOB Handle to a New Row ** METHOD: sqlite3_blob ** | > > > > | | | 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 | ** ** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function may be used to create a ** zero-filled blob to read or write using the incremental-blob interface. ** ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. ** ** See also: [sqlite3_blob_close()], ** [sqlite3_blob_reopen()], [sqlite3_blob_read()], ** [sqlite3_blob_bytes()], [sqlite3_blob_write()]. */ int sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); /* ** CAPI3REF: Move a BLOB Handle to a New Row ** METHOD: sqlite3_blob ** ** ^This function is used to move an existing [BLOB handle] so that it points ** to a different row of the same database table. ^The new row is identified ** by the rowid value passed as the second argument. Only the row can be ** changed. ^The database, table and column on which the blob handle is open ** remain the same. Moving an existing [BLOB handle] to a new row is ** faster than closing the existing handle and opening a new one. ** ** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - ** it must exist and there must be either a blob or text value stored in ** the nominated column.)^ ^If the new row is not present in the table, or if ** it does not contain a blob or text value, or if another error occurs, an ** SQLite error code is returned and the blob handle is considered aborted. |
︙ | ︙ | |||
6718 6719 6720 6721 6722 6723 6724 | #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 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 | | | 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 | #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 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #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 #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 |
︙ | ︙ | |||
6777 6778 6779 6780 6781 6782 6783 | ** that can be returned by [sqlite3_status()]. ** ** <dl> ** [[SQLITE_STATUS_MEMORY_USED]] ^(<dt>SQLITE_STATUS_MEMORY_USED</dt> ** <dd>This parameter is the current amount of memory checked out ** using [sqlite3_malloc()], either directly or indirectly. The ** figure includes calls made to [sqlite3_malloc()] by the application | | < | 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 | ** that can be returned by [sqlite3_status()]. ** ** <dl> ** [[SQLITE_STATUS_MEMORY_USED]] ^(<dt>SQLITE_STATUS_MEMORY_USED</dt> ** <dd>This parameter is the current amount of memory checked out ** using [sqlite3_malloc()], either directly or indirectly. The ** figure includes calls made to [sqlite3_malloc()] by the application ** and internal memory usage by the SQLite library. Auxiliary page-cache ** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in ** this parameter. The amount returned is the sum of the allocation ** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^ ** ** [[SQLITE_STATUS_MALLOC_SIZE]] ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their |
︙ | ︙ | |||
6816 6817 6818 6819 6820 6821 6822 | ** ** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.</dd>)^ ** | | < < < < < | < < < < < < < | | | < < < | | | | 7105 7106 7107 7108 7109 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 | ** ** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.</dd>)^ ** ** [[SQLITE_STATUS_SCRATCH_USED]] <dt>SQLITE_STATUS_SCRATCH_USED</dt> ** <dd>No longer used.</dd> ** ** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>No longer used.</dd> ** ** [[SQLITE_STATUS_SCRATCH_SIZE]] <dt>SQLITE_STATUS_SCRATCH_SIZE</dt> ** <dd>No longer used.</dd> ** ** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt> ** <dd>The *pHighwater parameter records the deepest parser stack. ** The *pCurrent value is undefined. The *pHighwater value is only ** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^ ** </dl> ** ** New status parameters may be added from time to time. */ #define SQLITE_STATUS_MEMORY_USED 0 #define SQLITE_STATUS_PAGECACHE_USED 1 #define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 #define SQLITE_STATUS_SCRATCH_USED 3 /* NOT USED */ #define SQLITE_STATUS_SCRATCH_OVERFLOW 4 /* NOT USED */ #define SQLITE_STATUS_MALLOC_SIZE 5 #define SQLITE_STATUS_PARSER_STACK 6 #define SQLITE_STATUS_PAGECACHE_SIZE 7 #define SQLITE_STATUS_SCRATCH_SIZE 8 /* NOT USED */ #define SQLITE_STATUS_MALLOC_COUNT 9 /* ** CAPI3REF: Database Connection Status ** METHOD: sqlite3 ** ** ^This interface is used to retrieve runtime status information |
︙ | ︙ | |||
7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 | ** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt> ** <dd>^This is the number of virtual machine operations executed ** by the prepared statement if that number is less than or equal ** to 2147483647. The number of virtual machine operations can be ** used as a proxy for the total work done by the prepared statement. ** If the number of virtual machine operations exceeds 2147483647 ** then the value returned by this statement status code is undefined. ** </dd> ** </dl> */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 #define SQLITE_STMTSTATUS_AUTOINDEX 3 #define SQLITE_STMTSTATUS_VM_STEP 4 /* ** CAPI3REF: Custom Page Cache Object ** ** The sqlite3_pcache type is opaque. It is implemented by ** the pluggable module. The SQLite core has no knowledge of ** its size or internal structure and never deals with the | > > > > > > > > > > > > > > > > > > > > > | 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 | ** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt> ** <dd>^This is the number of virtual machine operations executed ** by the prepared statement if that number is less than or equal ** to 2147483647. The number of virtual machine operations can be ** used as a proxy for the total work done by the prepared statement. ** If the number of virtual machine operations exceeds 2147483647 ** then the value returned by this statement status code is undefined. ** ** [[SQLITE_STMTSTATUS_REPREPARE]] <dt>SQLITE_STMTSTATUS_REPREPARE</dt> ** <dd>^This is the number of times that the prepare statement has been ** automatically regenerated due to schema changes or change to ** [bound parameters] that might affect the query plan. ** ** [[SQLITE_STMTSTATUS_RUN]] <dt>SQLITE_STMTSTATUS_RUN</dt> ** <dd>^This is the number of times that the prepared statement has ** been run. A single "run" for the purposes of this counter is one ** or more calls to [sqlite3_step()] followed by a call to [sqlite3_reset()]. ** The counter is incremented on the first [sqlite3_step()] call of each ** cycle. ** ** [[SQLITE_STMTSTATUS_MEMUSED]] <dt>SQLITE_STMTSTATUS_MEMUSED</dt> ** <dd>^This is the approximate number of bytes of heap memory ** used to store the prepared statement. ^This value is not actually ** a counter, and so the resetFlg parameter to sqlite3_stmt_status() ** is ignored when the opcode is SQLITE_STMTSTATUS_MEMUSED. ** </dd> ** </dl> */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 #define SQLITE_STMTSTATUS_AUTOINDEX 3 #define SQLITE_STMTSTATUS_VM_STEP 4 #define SQLITE_STMTSTATUS_REPREPARE 5 #define SQLITE_STMTSTATUS_RUN 6 #define SQLITE_STMTSTATUS_MEMUSED 99 /* ** CAPI3REF: Custom Page Cache Object ** ** The sqlite3_pcache type is opaque. It is implemented by ** the pluggable module. The SQLite core has no knowledge of ** its size or internal structure and never deals with the |
︙ | ︙ | |||
8138 8139 8140 8141 8142 8143 8144 | ** CAPI3REF: The pre-update hook. ** ** ^These interfaces are only available if SQLite is compiled using the ** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option. ** ** ^The [sqlite3_preupdate_hook()] interface registers a callback function ** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation | | | | | > > | | > | | < > | > | 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 | ** CAPI3REF: The pre-update hook. ** ** ^These interfaces are only available if SQLite is compiled using the ** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option. ** ** ^The [sqlite3_preupdate_hook()] interface registers a callback function ** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation ** on a database table. ** ^At most one preupdate hook may be registered at a time on a single ** [database connection]; each call to [sqlite3_preupdate_hook()] overrides ** the previous setting. ** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()] ** with a NULL pointer as the second parameter. ** ^The third parameter to [sqlite3_preupdate_hook()] is passed through as ** the first parameter to callbacks. ** ** ^The preupdate hook only fires for changes to real database tables; the ** preupdate hook is not invoked for changes to [virtual tables] or to ** system tables like sqlite_master or sqlite_stat1. ** ** ^The second parameter to the preupdate callback is a pointer to ** the [database connection] that registered the preupdate hook. ** ^The third parameter to the preupdate callback is one of the constants ** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to identify the ** kind of update operation that is about to occur. ** ^(The fourth parameter to the preupdate callback is the name of the ** database within the database connection that is being modified. This ** will be "main" for the main database or "temp" for TEMP tables or ** the name given after the AS keyword in the [ATTACH] statement for attached ** databases.)^ ** ^The fifth parameter to the preupdate callback is the name of the ** table that is being modified. ** ** For an UPDATE or DELETE operation on a [rowid table], the sixth ** parameter passed to the preupdate callback is the initial [rowid] of the ** row being modified or deleted. For an INSERT operation on a rowid table, ** or any operation on a WITHOUT ROWID table, the value of the sixth ** parameter is undefined. For an INSERT or UPDATE on a rowid table the ** seventh parameter is the final rowid value of the row being inserted ** or updated. The value of the seventh parameter passed to the callback ** function is not defined for operations on WITHOUT ROWID tables, or for ** INSERT operations on rowid tables. ** ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces ** provide additional information about a preupdate event. These routines ** may only be called from within a preupdate callback. Invoking any of ** these routines from outside of a preupdate callback or with a ** [database connection] pointer that is different from the one supplied |
︙ | ︙ | |||
8208 8209 8210 8211 8212 8213 8214 | ** callback was invoked as a result of a direct insert, update, or delete ** operation; or 1 for inserts, updates, or deletes invoked by top-level ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** ** See also: [sqlite3_update_hook()] */ | > | | | | | > | | 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 | ** callback was invoked as a result of a direct insert, update, or delete ** operation; or 1 for inserts, updates, or deletes invoked by top-level ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** ** See also: [sqlite3_update_hook()] */ #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) void *sqlite3_preupdate_hook( sqlite3 *db, void(*xPreUpdate)( void *pCtx, /* Copy of third arg to preupdate_hook() */ sqlite3 *db, /* Database handle */ int op, /* SQLITE_UPDATE, DELETE or INSERT */ char const *zDb, /* Database name */ char const *zName, /* Table name */ sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */ sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */ ), void* ); int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **); int sqlite3_preupdate_count(sqlite3 *); int sqlite3_preupdate_depth(sqlite3 *); int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **); #endif /* ** CAPI3REF: Low-level system error code ** ** ^Attempt to return the underlying operating system error code or error ** number that caused the most recent I/O error or failure to open a file. ** The return value is OS-dependent. For example, on unix systems, after ** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be ** called to get back the underlying "errno" that caused the problem, such ** as ENOSPC, EAUTH, EISDIR, and so forth. */ int sqlite3_system_errno(sqlite3*); /* ** CAPI3REF: Database Snapshot ** KEYWORDS: {snapshot} {sqlite3_snapshot} ** EXPERIMENTAL ** ** An instance of the snapshot object records the state of a [WAL mode] ** database for some specific point in history. ** ** In [WAL mode], multiple [database connections] that are open on the ** same database file can each be reading a different historical version |
︙ | ︙ | |||
8264 8265 8266 8267 8268 8269 8270 | ** the most recent version. ** ** The constructor for this object is [sqlite3_snapshot_get()]. The ** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer ** to an historical snapshot (if possible). The destructor for ** sqlite3_snapshot objects is [sqlite3_snapshot_free()]. */ | | > > > > > > > > > > > > > | > > > > > > > > > > > > > | < | 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 | ** the most recent version. ** ** The constructor for this object is [sqlite3_snapshot_get()]. The ** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer ** to an historical snapshot (if possible). The destructor for ** sqlite3_snapshot objects is [sqlite3_snapshot_free()]. */ typedef struct sqlite3_snapshot { unsigned char hidden[48]; } sqlite3_snapshot; /* ** CAPI3REF: Record A Database Snapshot ** EXPERIMENTAL ** ** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a ** new [sqlite3_snapshot] object that records the current state of ** schema S in database connection D. ^On success, the ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. ** If there is not already a read-transaction open on schema S when ** this function is called, one is opened automatically. ** ** The following must be true for this function to succeed. If any of ** the following statements are false when sqlite3_snapshot_get() is ** called, SQLITE_ERROR is returned. The final value of *P is undefined ** in this case. ** ** <ul> ** <li> The database handle must be in [autocommit mode]. ** ** <li> Schema S of [database connection] D must be a [WAL mode] database. ** ** <li> There must not be a write transaction open on schema S of database ** connection D. ** ** <li> One or more transactions must have been written to the current wal ** file since it was created on disk (by any connection). This means ** that a snapshot cannot be taken on a wal mode database with no wal ** file immediately after it is first opened. At least one transaction ** must be written to it first. ** </ul> ** ** This function may also return SQLITE_NOMEM. If it is called with the ** database handle in autocommit mode but fails for some other reason, ** whether or not a read transaction is opened on schema S is undefined. ** ** The [sqlite3_snapshot] object returned from a successful call to ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] ** to avoid a memory leak. ** ** The [sqlite3_snapshot_get()] interface is only available when the ** SQLITE_ENABLE_SNAPSHOT compile-time option is used. |
︙ | ︙ | |||
8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 | ** snapshot, and a positive value if P1 is a newer snapshot than P2. */ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( sqlite3_snapshot *p1, sqlite3_snapshot *p2 ); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif /* SQLITE3_H */ | > > > > > > > > > > > > > > > > > > > > > > | 8696 8697 8698 8699 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 | ** snapshot, and a positive value if P1 is a newer snapshot than P2. */ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( sqlite3_snapshot *p1, sqlite3_snapshot *p2 ); /* ** CAPI3REF: Recover snapshots from a wal file ** EXPERIMENTAL ** ** If all connections disconnect from a database file but do not perform ** a checkpoint, the existing wal file is opened along with the database ** file the next time the database is opened. At this point it is only ** possible to successfully call sqlite3_snapshot_open() to open the most ** recent snapshot of the database (the one at the head of the wal file), ** even though the wal file may contain other valid snapshots for which ** clients have sqlite3_snapshot handles. ** ** This function attempts to scan the wal file associated with database zDb ** of database handle db and make all valid snapshots available to ** sqlite3_snapshot_open(). It is an error if there is already a read ** transaction open on the database, or if the database is not a wal mode ** database. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif /* SQLITE3_H */ |
Changes to src/sqlite3ext.h.
︙ | ︙ | |||
242 243 244 245 246 247 248 | const char *(*errstr)(int); int (*stmt_busy)(sqlite3_stmt*); int (*stmt_readonly)(sqlite3_stmt*); int (*stricmp)(const char*,const char*); int (*uri_boolean)(const char*,const char*,int); sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); const char *(*uri_parameter)(const char*,const char*); | | | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | const char *(*errstr)(int); int (*stmt_busy)(sqlite3_stmt*); int (*stmt_readonly)(sqlite3_stmt*); int (*stricmp)(const char*,const char*); int (*uri_boolean)(const char*,const char*,int); sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); const char *(*uri_parameter)(const char*,const char*); char *(*xvsnprintf)(int,char*,const char*,va_list); int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); /* Version 3.8.7 and later */ int (*auto_extension)(void(*)(void)); int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64, void(*)(void*)); int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64, void(*)(void*),unsigned char); |
︙ | ︙ | |||
278 279 280 281 282 283 284 285 286 287 288 289 290 291 | int (*strlike)(const char*,const char*,unsigned int); int (*db_cacheflush)(sqlite3*); /* Version 3.12.0 and later */ int (*system_errno)(sqlite3*); /* Version 3.14.0 and later */ int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*); char *(*expanded_sql)(sqlite3_stmt*); }; /* ** This is the function signature used for all extension entry points. It ** is also defined in the file "loadext.c". */ typedef int (*sqlite3_loadext_entry)( | > > > > > > > > > > | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | int (*strlike)(const char*,const char*,unsigned int); int (*db_cacheflush)(sqlite3*); /* Version 3.12.0 and later */ int (*system_errno)(sqlite3*); /* Version 3.14.0 and later */ int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*); char *(*expanded_sql)(sqlite3_stmt*); /* Version 3.18.0 and later */ void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64); /* Version 3.20.0 and later */ int (*prepare_v3)(sqlite3*,const char*,int,unsigned int, sqlite3_stmt**,const char**); int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int, sqlite3_stmt**,const void**); int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*)); void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*)); void *(*value_pointer)(sqlite3_value*,const char*); }; /* ** This is the function signature used for all extension entry points. It ** is also defined in the file "loadext.c". */ typedef int (*sqlite3_loadext_entry)( |
︙ | ︙ | |||
428 429 430 431 432 433 434 | #define sqlite3_value_numeric_type sqlite3_api->value_numeric_type #define sqlite3_value_text sqlite3_api->value_text #define sqlite3_value_text16 sqlite3_api->value_text16 #define sqlite3_value_text16be sqlite3_api->value_text16be #define sqlite3_value_text16le sqlite3_api->value_text16le #define sqlite3_value_type sqlite3_api->value_type #define sqlite3_vmprintf sqlite3_api->vmprintf | | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | #define sqlite3_value_numeric_type sqlite3_api->value_numeric_type #define sqlite3_value_text sqlite3_api->value_text #define sqlite3_value_text16 sqlite3_api->value_text16 #define sqlite3_value_text16be sqlite3_api->value_text16be #define sqlite3_value_text16le sqlite3_api->value_text16le #define sqlite3_value_type sqlite3_api->value_type #define sqlite3_vmprintf sqlite3_api->vmprintf #define sqlite3_vsnprintf sqlite3_api->xvsnprintf #define sqlite3_overload_function sqlite3_api->overload_function #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 #define sqlite3_clear_bindings sqlite3_api->clear_bindings #define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob #define sqlite3_blob_bytes sqlite3_api->blob_bytes #define sqlite3_blob_close sqlite3_api->blob_close |
︙ | ︙ | |||
504 505 506 507 508 509 510 | #define sqlite3_errstr sqlite3_api->errstr #define sqlite3_stmt_busy sqlite3_api->stmt_busy #define sqlite3_stmt_readonly sqlite3_api->stmt_readonly #define sqlite3_stricmp sqlite3_api->stricmp #define sqlite3_uri_boolean sqlite3_api->uri_boolean #define sqlite3_uri_int64 sqlite3_api->uri_int64 #define sqlite3_uri_parameter sqlite3_api->uri_parameter | | | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 | #define sqlite3_errstr sqlite3_api->errstr #define sqlite3_stmt_busy sqlite3_api->stmt_busy #define sqlite3_stmt_readonly sqlite3_api->stmt_readonly #define sqlite3_stricmp sqlite3_api->stricmp #define sqlite3_uri_boolean sqlite3_api->uri_boolean #define sqlite3_uri_int64 sqlite3_api->uri_int64 #define sqlite3_uri_parameter sqlite3_api->uri_parameter #define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf #define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 /* Version 3.8.7 and later */ #define sqlite3_auto_extension sqlite3_api->auto_extension #define sqlite3_bind_blob64 sqlite3_api->bind_blob64 #define sqlite3_bind_text64 sqlite3_api->bind_text64 #define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension #define sqlite3_load_extension sqlite3_api->load_extension |
︙ | ︙ | |||
536 537 538 539 540 541 542 543 544 545 546 547 548 549 | #define sqlite3_strlike sqlite3_api->strlike #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush /* Version 3.12.0 and later */ #define sqlite3_system_errno sqlite3_api->system_errno /* Version 3.14.0 and later */ #define sqlite3_trace_v2 sqlite3_api->trace_v2 #define sqlite3_expanded_sql sqlite3_api->expanded_sql #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) /* This case when the file really is being compiled as a loadable ** extension */ # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; | > > > > > > > > | 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 | #define sqlite3_strlike sqlite3_api->strlike #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush /* Version 3.12.0 and later */ #define sqlite3_system_errno sqlite3_api->system_errno /* Version 3.14.0 and later */ #define sqlite3_trace_v2 sqlite3_api->trace_v2 #define sqlite3_expanded_sql sqlite3_api->expanded_sql /* Version 3.18.0 and later */ #define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid /* Version 3.20.0 and later */ #define sqlite3_prepare_v3 sqlite3_api->prepare_v3 #define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3 #define sqlite3_bind_pointer sqlite3_api->bind_pointer #define sqlite3_result_pointer sqlite3_api->result_pointer #define sqlite3_value_pointer sqlite3_api->value_pointer #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) /* This case when the file really is being compiled as a loadable ** extension */ # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
46 47 48 49 50 51 52 | ** Make sure the Tcl calling convention macro is defined. This macro is ** only used by test code and Tcl integration code. */ #ifndef SQLITE_TCLAPI # define SQLITE_TCLAPI #endif | < < < < < < < < | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | ** Make sure the Tcl calling convention macro is defined. This macro is ** only used by test code and Tcl integration code. */ #ifndef SQLITE_TCLAPI # define SQLITE_TCLAPI #endif /* ** Include the header file used to customize the compiler options for MSVC. ** This should be done first so that it can successfully prevent spurious ** compiler warnings due to subsequent content in this file and other files ** that are included by this file. */ #include "msvc.h" |
︙ | ︙ | |||
99 100 101 102 103 104 105 | # define _LARGE_FILE 1 # ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 # endif # define _LARGEFILE_SOURCE 1 #endif | > > | > > > > > > > > > > | > > > > > | 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 | # define _LARGE_FILE 1 # ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 # endif # define _LARGEFILE_SOURCE 1 #endif /* The GCC_VERSION and MSVC_VERSION macros are used to ** conditionally include optimizations for each of these compilers. A ** value of 0 means that compiler is not being used. The ** SQLITE_DISABLE_INTRINSIC macro means do not use any compiler-specific ** optimizations, and hence set all compiler macros to 0 ** ** There was once also a CLANG_VERSION macro. However, we learn that the ** version numbers in clang are for "marketing" only and are inconsistent ** and unreliable. Fortunately, all versions of clang also recognize the ** gcc version numbers and have reasonable settings for gcc version numbers, ** so the GCC_VERSION macro will be set to a correct non-zero value even ** when compiling with clang. */ #if defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC) # define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__) #else # define GCC_VERSION 0 #endif #if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC) # define MSVC_VERSION _MSC_VER #else # define MSVC_VERSION 0 #endif /* Needed for various definitions... */ #if defined(__GNUC__) && !defined(_GNU_SOURCE) # define _GNU_SOURCE #endif |
︙ | ︙ | |||
157 158 159 160 161 162 163 | */ #include "sqlite3.h" /* ** Include the configuration header output by 'configure' if we're using the ** autoconf-based build */ | | > | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | */ #include "sqlite3.h" /* ** Include the configuration header output by 'configure' if we're using the ** autoconf-based build */ #if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) #include "config.h" #define SQLITECONFIG_H 1 #endif #include "sqliteLimit.h" /* Disable nuisance warnings on Borland compilers */ #if defined(__BORLANDC__) #pragma warn -rch /* unreachable code */ |
︙ | ︙ | |||
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 | */ #if !defined(SQLITE_DISABLE_INTRINSIC) # if defined(_MSC_VER) && _MSC_VER>=1400 # if !defined(_WIN32_WCE) # include <intrin.h> # pragma intrinsic(_byteswap_ushort) # pragma intrinsic(_byteswap_ulong) # pragma intrinsic(_ReadWriteBarrier) # else # include <cmnintrin.h> # endif # endif #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 ** database connection at the same time. ** ** Older versions of SQLite used an optional THREADSAFE macro. ** We support that for legacy. */ #if !defined(SQLITE_THREADSAFE) # if defined(THREADSAFE) # define SQLITE_THREADSAFE THREADSAFE # else # define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ # endif | > > > > > > | 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 | */ #if !defined(SQLITE_DISABLE_INTRINSIC) # if defined(_MSC_VER) && _MSC_VER>=1400 # if !defined(_WIN32_WCE) # include <intrin.h> # pragma intrinsic(_byteswap_ushort) # pragma intrinsic(_byteswap_ulong) # pragma intrinsic(_byteswap_uint64) # pragma intrinsic(_ReadWriteBarrier) # else # include <cmnintrin.h> # endif # endif #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 ** database connection at the same time. ** ** Older versions of SQLite used an optional THREADSAFE macro. ** We support that for legacy. ** ** To ensure that the correct value of "THREADSAFE" is reported when querying ** for compile-time options at runtime (e.g. "PRAGMA compile_options"), this ** logic is partially replicated in ctime.c. If it is updated here, it should ** also be updated there. */ #if !defined(SQLITE_THREADSAFE) # if defined(THREADSAFE) # define SQLITE_THREADSAFE THREADSAFE # else # define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ # endif |
︙ | ︙ | |||
499 500 501 502 503 504 505 506 507 508 509 510 511 512 | #include "parse.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <stddef.h> /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define float sqlite_int64 | > > > > > > > > > > > > | 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 | #include "parse.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <stddef.h> /* ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. ** This allows better measurements of where memcpy() is used when running ** cachegrind. But this macro version of memcpy() is very slow so it ** should not be used in production. This is a performance measurement ** hack only. */ #ifdef SQLITE_INLINE_MEMCPY # define memcpy(D,S,N) {char*xxd=(char*)(D);const char*xxs=(const char*)(S);\ int xxn=(N);while(xxn-->0)*(xxd++)=*(xxs++);} #endif /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define float sqlite_int64 |
︙ | ︙ | |||
555 556 557 558 559 560 561 | /* ** Provide a default value for SQLITE_TEMP_STORE in case it is not specified ** on the command-line */ #ifndef SQLITE_TEMP_STORE # define SQLITE_TEMP_STORE 1 | < | 583 584 585 586 587 588 589 590 591 592 593 594 595 596 | /* ** Provide a default value for SQLITE_TEMP_STORE in case it is not specified ** on the command-line */ #ifndef SQLITE_TEMP_STORE # define SQLITE_TEMP_STORE 1 #endif /* ** If no value has been provided for SQLITE_MAX_WORKER_THREADS, or if ** SQLITE_TEMP_STORE is set to 3 (never use temporary files), set it ** to zero. */ |
︙ | ︙ | |||
583 584 585 586 587 588 589 590 591 | #endif /* ** The default initial allocation for the pagecache when using separate ** pagecaches for each database connection. A positive number is the ** number of pages. A negative number N translations means that a buffer ** of -1024*N bytes is allocated and used for as many pages as it will hold. */ #ifndef SQLITE_DEFAULT_PCACHE_INITSZ | > > > | > > > > > > > > > | 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 | #endif /* ** The default initial allocation for the pagecache when using separate ** pagecaches for each database connection. A positive number is the ** number of pages. A negative number N translations means that a buffer ** of -1024*N bytes is allocated and used for as many pages as it will hold. ** ** The default value of "20" was choosen to minimize the run-time of the ** speedtest1 test program with options: --shrink-memory --reprepare */ #ifndef SQLITE_DEFAULT_PCACHE_INITSZ # define SQLITE_DEFAULT_PCACHE_INITSZ 20 #endif /* ** The compile-time options SQLITE_MMAP_READWRITE and ** SQLITE_ENABLE_BATCH_ATOMIC_WRITE are not compatible with one another. ** You must choose one or the other (or neither) but not both. */ #if defined(SQLITE_MMAP_READWRITE) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) #error Cannot use both SQLITE_MMAP_READWRITE and SQLITE_ENABLE_BATCH_ATOMIC_WRITE #endif /* ** GCC does not define the offsetof() macro so we'll have to do it ** ourselves. */ #ifndef offsetof |
︙ | ︙ | |||
760 761 762 763 764 765 766 | /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** ** For best performance, an attempt is made to guess at the byte-order ** using C-preprocessor macros. If that is unsuccessful, or if | | > | | | | | > | > < < | < | > > > > < | 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 | /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** ** For best performance, an attempt is made to guess at the byte-order ** using C-preprocessor macros. If that is unsuccessful, or if ** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined ** at run-time. */ #ifndef SQLITE_BYTEORDER # if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__arm__) # define SQLITE_BYTEORDER 1234 # elif defined(sparc) || defined(__ppc__) # define SQLITE_BYTEORDER 4321 # else # define SQLITE_BYTEORDER 0 # endif #endif #if SQLITE_BYTEORDER==4321 # define SQLITE_BIGENDIAN 1 # define SQLITE_LITTLEENDIAN 0 # define SQLITE_UTF16NATIVE SQLITE_UTF16BE #elif SQLITE_BYTEORDER==1234 # define SQLITE_BIGENDIAN 0 # define SQLITE_LITTLEENDIAN 1 # define SQLITE_UTF16NATIVE SQLITE_UTF16LE #else # ifdef SQLITE_AMALGAMATION const int sqlite3one = 1; # else extern const int sqlite3one; # endif # define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) # define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) # define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) #endif /* ** Constants for the largest and smallest possible 64-bit signed integers. |
︙ | ︙ | |||
850 851 852 853 854 855 856 | || defined(__sun) \ || defined(__FreeBSD__) \ || defined(__DragonFly__) # define SQLITE_MAX_MMAP_SIZE 0x7fff0000 /* 2147418112 */ # else # define SQLITE_MAX_MMAP_SIZE 0 # endif | < < | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 | || defined(__sun) \ || defined(__FreeBSD__) \ || defined(__DragonFly__) # define SQLITE_MAX_MMAP_SIZE 0x7fff0000 /* 2147418112 */ # else # define SQLITE_MAX_MMAP_SIZE 0 # endif #endif /* ** The default MMAP_SIZE is zero on all platforms. Or, even if a larger ** default MMAP_SIZE is specified at compile-time, make sure that it does ** not exceed the maximum mmap size. */ #ifndef SQLITE_DEFAULT_MMAP_SIZE # define SQLITE_DEFAULT_MMAP_SIZE 0 #endif #if SQLITE_DEFAULT_MMAP_SIZE>SQLITE_MAX_MMAP_SIZE # undef SQLITE_DEFAULT_MMAP_SIZE # define SQLITE_DEFAULT_MMAP_SIZE SQLITE_MAX_MMAP_SIZE #endif /* |
︙ | ︙ | |||
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | typedef struct UnpackedRecord UnpackedRecord; typedef struct VTable VTable; typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; typedef struct WhereInfo WhereInfo; typedef struct With With; /* ** Defer sourcing vdbe.h and btree.h until after the "u8" and ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ #include "btree.h" #include "vdbe.h" | > > > > > > > > | 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | typedef struct UnpackedRecord UnpackedRecord; typedef struct VTable VTable; typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; typedef struct WhereInfo WhereInfo; typedef struct With With; /* A VList object records a mapping between parameters/variables/wildcards ** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer ** variable number associated with that parameter. See the format description ** on the sqlite3VListAdd() routine for more information. A VList is really ** just an array of integers. */ typedef int VList; /* ** Defer sourcing vdbe.h and btree.h until after the "u8" and ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ #include "btree.h" #include "vdbe.h" |
︙ | ︙ | |||
1079 1080 1081 1082 1083 1084 1085 | ** EXTRA 4 3 ** ** The "PRAGMA synchronous" statement also uses the zero-based numbers. ** In other words, the zero-based numbers are used for all external interfaces ** and the one-based values are used internally. */ #ifndef SQLITE_DEFAULT_SYNCHRONOUS | | | 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 | ** EXTRA 4 3 ** ** The "PRAGMA synchronous" statement also uses the zero-based numbers. ** In other words, the zero-based numbers are used for all external interfaces ** and the one-based values are used internally. */ #ifndef SQLITE_DEFAULT_SYNCHRONOUS # define SQLITE_DEFAULT_SYNCHRONOUS 2 #endif #ifndef SQLITE_DEFAULT_WAL_SYNCHRONOUS # define SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DEFAULT_SYNCHRONOUS #endif /* ** Each database file to be accessed by the system is an instance |
︙ | ︙ | |||
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 | ** DB_UnresetViews means that one or more views have column names that ** have been filled out. If the schema changes, these column names might ** changes and so the view will need to be reset. */ #define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ #define DB_UnresetViews 0x0002 /* Some views have defined column names */ #define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ /* ** The number of different kinds of things that can be limited ** using the sqlite3_limit() interface. */ #define SQLITE_N_LIMIT (SQLITE_LIMIT_WORKER_THREADS+1) | > | 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 | ** DB_UnresetViews means that one or more views have column names that ** have been filled out. If the schema changes, these column names might ** changes and so the view will need to be reset. */ #define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ #define DB_UnresetViews 0x0002 /* Some views have defined column names */ #define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ #define DB_ResetWanted 0x0008 /* Reset the schema when nSchemaLock==0 */ /* ** The number of different kinds of things that can be limited ** using the sqlite3_limit() interface. */ #define SQLITE_N_LIMIT (SQLITE_LIMIT_WORKER_THREADS+1) |
︙ | ︙ | |||
1184 1185 1186 1187 1188 1189 1190 | ** schema information, the Lookaside.bEnabled flag is cleared so that ** lookaside allocations are not used to construct the schema objects. */ struct Lookaside { u32 bDisable; /* Only operate the lookaside when zero */ u16 sz; /* Size of each buffer in bytes */ u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ | < | | > | 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 | ** schema information, the Lookaside.bEnabled flag is cleared so that ** lookaside allocations are not used to construct the schema objects. */ struct Lookaside { u32 bDisable; /* Only operate the lookaside when zero */ u16 sz; /* Size of each buffer in bytes */ u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ u32 nSlot; /* Number of lookaside slots allocated */ u32 anStat[3]; /* 0: hits. 1: size misses. 2: full misses */ LookasideSlot *pInit; /* List of buffers not previously used */ LookasideSlot *pFree; /* List of available buffers */ void *pStart; /* First byte of available memory space */ void *pEnd; /* First byte past end of available space */ }; struct LookasideSlot { LookasideSlot *pNext; /* Next buffer in the list of free buffers */ }; |
︙ | ︙ | |||
1265 1266 1267 1268 1269 1270 1271 | struct sqlite3 { sqlite3_vfs *pVfs; /* OS Interface */ struct Vdbe *pVdbe; /* List of active virtual machines */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ sqlite3_mutex *mutex; /* Connection mutex */ Db *aDb; /* All backends */ int nDb; /* Number of backends currently in use */ | > | > > > | 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 | struct sqlite3 { sqlite3_vfs *pVfs; /* OS Interface */ struct Vdbe *pVdbe; /* List of active virtual machines */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ sqlite3_mutex *mutex; /* Connection mutex */ Db *aDb; /* All backends */ int nDb; /* Number of backends currently in use */ u32 mDbFlags; /* flags recording internal state */ u32 flags; /* flags settable by pragmas. See below */ i64 lastRowid; /* ROWID of most recent insert (see above) */ i64 szMmap; /* Default mmap_size setting */ u32 nSchemaLock; /* Do not reset the schema when non-zero */ unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ int iSysErrno; /* Errno value from last system error */ u16 dbOptFlags; /* Flags to enable/disable optimizations */ u8 enc; /* Text encoding */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 bBenignMalloc; /* Do not require OOMs if true */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ u8 mTrace; /* zero or more SQLITE_TRACE flags */ u8 skipBtreeMutex; /* True if no shared-cache backends */ u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */ int nextPagesize; /* Pagesize after VACUUM if >0 */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite3_changes() */ int nTotalChange; /* Value returned by sqlite3_total_changes() */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ struct sqlite3InitInfo { /* Information used during initialization */ |
︙ | ︙ | |||
1397 1398 1399 1400 1401 1402 1403 | ** Possible values for the sqlite3.flags. ** ** Value constraints (enforced via assert()): ** SQLITE_FullFSync == PAGER_FULLFSYNC ** SQLITE_CkptFullFSync == PAGER_CKPT_FULLFSYNC ** SQLITE_CacheSpill == PAGER_CACHE_SPILL */ | | | < < < < | | < | | | | | < | | | | | > > > > > | > | | | > > > > > > > > < < < < < | 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 | ** Possible values for the sqlite3.flags. ** ** Value constraints (enforced via assert()): ** SQLITE_FullFSync == PAGER_FULLFSYNC ** SQLITE_CkptFullFSync == PAGER_CKPT_FULLFSYNC ** SQLITE_CacheSpill == PAGER_CACHE_SPILL */ #define SQLITE_WriteSchema 0x00000001 /* OK to update SQLITE_MASTER */ #define SQLITE_LegacyFileFmt 0x00000002 /* Create new databases in format 1 */ #define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */ #define SQLITE_FullFSync 0x00000008 /* Use full fsync on the backend */ #define SQLITE_CkptFullFSync 0x00000010 /* Use full fsync for checkpoint */ #define SQLITE_CacheSpill 0x00000020 /* OK to spill pager cache */ #define SQLITE_ShortColNames 0x00000040 /* Show short columns names */ #define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */ /* DELETE, or UPDATE and return */ /* the count using a callback. */ #define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ /* result set is empty */ #define SQLITE_IgnoreChecks 0x00000200 /* Do not enforce check constraints */ #define SQLITE_ReadUncommit 0x00000400 /* READ UNCOMMITTED in shared-cache */ #define SQLITE_NoCkptOnClose 0x00000800 /* No checkpoint on close()/DETACH */ #define SQLITE_ReverseOrder 0x00001000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x00002000 /* Enable recursive triggers */ #define SQLITE_ForeignKeys 0x00004000 /* Enforce foreign key constraints */ #define SQLITE_AutoIndex 0x00008000 /* Enable automatic indexes */ #define SQLITE_LoadExtension 0x00010000 /* Enable load_extension */ #define SQLITE_LoadExtFunc 0x00020000 /* Enable load_extension() SQL func */ #define SQLITE_EnableTrigger 0x00040000 /* True to enable triggers */ #define SQLITE_DeferFKs 0x00080000 /* Defer all FK constraints */ #define SQLITE_QueryOnly 0x00100000 /* Disable database changes */ #define SQLITE_CellSizeCk 0x00200000 /* Check btree cell sizes on load */ #define SQLITE_Fts3Tokenizer 0x00400000 /* Enable fts3_tokenizer(2) */ #define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG #define SQLITE_SqlTrace 0x08000000 /* Debug print SQL as it executes */ #define SQLITE_VdbeListing 0x10000000 /* Debug listings of VDBE programs */ #define SQLITE_VdbeTrace 0x20000000 /* True to trace VDBE execution */ #define SQLITE_VdbeAddopTrace 0x40000000 /* Trace sqlite3VdbeAddOp() calls */ #define SQLITE_VdbeEQP 0x80000000 /* Debug EXPLAIN QUERY PLAN */ #endif /* ** Allowed values for sqlite3.mDbFlags */ #define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */ #define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */ #define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */ /* ** Bits of the sqlite3.dbOptFlags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to ** selectively disable various optimizations. */ #define SQLITE_QueryFlattener 0x0001 /* Query flattening */ #define SQLITE_ColumnCache 0x0002 /* Column cache */ #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ /* not used 0x0010 // Was: SQLITE_IdxRealAsInt */ #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ #define SQLITE_Transitive 0x0200 /* Transitive constraints */ #define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */ #define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */ #define SQLITE_CountOfView 0x1000 /* The count-of-view optimization */ #define SQLITE_CursorHints 0x2000 /* Add OP_CursorHint opcodes */ #define SQLITE_AllOpts 0xffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. */ #define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) #define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0) /* ** Return true if it OK to factor constant expressions into the initialization ** code. The argument is a Parse object for the code generator. */ #define ConstFactorOk(P) ((P)->okConstFactor) |
︙ | ︙ | |||
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 | #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ #define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */ #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ #define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */ #define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** used to create the initializers for the FuncDef structures. ** ** FUNCTION(zName, nArg, iArg, bNC, xFunc) ** Used to create a scalar function definition of a function zName ** implemented by C function xFunc that accepts nArg arguments. The ** value passed as iArg is cast to a (void*) and made available ** as the user-data (sqlite3_user_data()) for the function. If ** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. ** ** VFUNCTION(zName, nArg, iArg, bNC, xFunc) ** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag. ** ** DFUNCTION(zName, nArg, iArg, bNC, xFunc) ** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and ** adds the SQLITE_FUNC_SLOCHNG flag. Used for date & time functions ** and functions like sqlite_version() that can change, but not during | > | > > > > > > > | 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 | #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ #define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */ #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ #define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */ #define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ #define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** used to create the initializers for the FuncDef structures. ** ** FUNCTION(zName, nArg, iArg, bNC, xFunc) ** Used to create a scalar function definition of a function zName ** implemented by C function xFunc that accepts nArg arguments. The ** value passed as iArg is cast to a (void*) and made available ** as the user-data (sqlite3_user_data()) for the function. If ** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. ** ** VFUNCTION(zName, nArg, iArg, bNC, xFunc) ** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag. ** ** DFUNCTION(zName, nArg, iArg, bNC, xFunc) ** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and ** adds the SQLITE_FUNC_SLOCHNG flag. Used for date & time functions ** and functions like sqlite_version() that can change, but not during ** a single query. The iArg is ignored. The user-data is always set ** to a NULL pointer. The bNC parameter is not used. ** ** PURE_DATE(zName, nArg, iArg, bNC, xFunc) ** Used for "pure" date/time functions, this macro is like DFUNCTION ** except that it does set the SQLITE_FUNC_CONSTANT flags. iArg is ** ignored and the user-data for these functions is set to an ** arbitrary non-NULL pointer. The bNC parameter is not used. ** ** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) ** Used to create an aggregate function definition implemented by ** the C functions xStep and xFinal. The first four parameters ** are interpreted in the same way as the first 4 parameters to ** FUNCTION(). ** |
︙ | ︙ | |||
1595 1596 1597 1598 1599 1600 1601 | #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ | | | > > > | 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 | #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ 0, 0, xFunc, 0, #zName, {0} } #define PURE_DATE(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ (void*)&sqlite3Config, 0, xFunc, 0, #zName, {0} } #define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ pArg, 0, xFunc, 0, #zName, } #define LIKEFUNC(zName, nArg, arg, flags) \ |
︙ | ︙ | |||
1801 1802 1803 1804 1805 1806 1807 1808 1809 | Index *pIndex; /* List of SQL indexes on this table. */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ ExprList *pCheck; /* All CHECK constraints */ /* ... also used as column name list in a VIEW */ int tnum; /* Root BTree page for this table */ i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ i16 nCol; /* Number of columns in this table */ | > > < < | 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 | Index *pIndex; /* List of SQL indexes on this table. */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ ExprList *pCheck; /* All CHECK constraints */ /* ... also used as column name list in a VIEW */ int tnum; /* Root BTree page for this table */ u32 nTabRef; /* Number of pointers to this Table */ u32 tabFlags; /* Mask of TF_* values */ i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ i16 nCol; /* Number of columns in this table */ LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */ LogEst szTabRow; /* Estimated size of each table row in bytes */ #ifdef SQLITE_ENABLE_COSTMULT LogEst costMult; /* Cost multiplier for using this table */ #endif u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE int nModuleArg; /* Number of arguments to the module */ char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */ |
︙ | ︙ | |||
1833 1834 1835 1836 1837 1838 1839 | ** ** TF_OOOHidden applies to tables or view that have hidden columns that are ** followed by non-hidden columns. Example: "CREATE VIRTUAL TABLE x USING ** vtab1(a HIDDEN, b);". Since "b" is a non-hidden column but "a" is hidden, ** the TF_OOOHidden attribute would apply in this case. Such tables require ** special handling during INSERT processing. */ | | | | | | | | | | > > | | 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 | ** ** TF_OOOHidden applies to tables or view that have hidden columns that are ** followed by non-hidden columns. Example: "CREATE VIRTUAL TABLE x USING ** vtab1(a HIDDEN, b);". Since "b" is a non-hidden column but "a" is hidden, ** the TF_OOOHidden attribute would apply in this case. Such tables require ** special handling during INSERT processing. */ #define TF_Readonly 0x0001 /* Read-only system table */ #define TF_Ephemeral 0x0002 /* An ephemeral table */ #define TF_HasPrimaryKey 0x0004 /* Table has a primary key */ #define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */ #define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */ #define TF_WithoutRowid 0x0020 /* No rowid. PRIMARY KEY is the key */ #define TF_NoVisibleRowid 0x0040 /* No user-visible "rowid" column */ #define TF_OOOHidden 0x0080 /* Out-of-Order hidden columns */ #define TF_StatsUsed 0x0100 /* Query planner decisions affected by ** Index.aiRowLogEst[] values */ #define TF_HasNotNull 0x0200 /* Contains NOT NULL constraints */ /* ** Test to see whether or not a table is a virtual table. This is ** done as a macro so that it will be optimized out when virtual ** table support is omitted from the build. */ #ifndef SQLITE_OMIT_VIRTUALTABLE # define IsVirtual(X) ((X)->nModuleArg) #else # define IsVirtual(X) 0 #endif /* ** Macros to determine if a column is hidden. IsOrdinaryHiddenColumn() ** only works for non-virtual tables (ordinary tables and views) and is |
︙ | ︙ | |||
1973 1974 1975 1976 1977 1978 1979 | ** Note that aSortOrder[] and aColl[] have nField+1 slots. There ** are nField slots for the columns of an index then one extra slot ** for the rowid at the end. */ struct KeyInfo { u32 nRef; /* Number of references to this KeyInfo object */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ | | | | 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 | ** Note that aSortOrder[] and aColl[] have nField+1 slots. There ** are nField slots for the columns of an index then one extra slot ** for the rowid at the end. */ struct KeyInfo { u32 nRef; /* Number of references to this KeyInfo object */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ u16 nKeyField; /* Number of key columns in the index */ u16 nAllField; /* Total columns, including key plus others */ sqlite3 *db; /* The database connection */ u8 *aSortOrder; /* Sort order for each column. */ CollSeq *aColl[1]; /* Collating sequence for each term of the key */ }; /* ** This object holds a record which has been parsed out into individual |
︙ | ︙ | |||
2021 2022 2023 2024 2025 2026 2027 | */ struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ Mem *aMem; /* Values */ u16 nField; /* Number of entries in apMem[] */ i8 default_rc; /* Comparison result if keys are equal */ u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */ | | | | 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 | */ struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ Mem *aMem; /* Values */ u16 nField; /* Number of entries in apMem[] */ i8 default_rc; /* Comparison result if keys are equal */ u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */ i8 r1; /* Value to return if (lhs < rhs) */ i8 r2; /* Value to return if (lhs > rhs) */ u8 eqSeen; /* True if an equality comparison has been seen */ }; /* ** Each SQL index is represented in memory by an ** instance of the following structure. |
︙ | ︙ | |||
2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ 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 */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 int nSample; /* Number of elements in aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ IndexSample *aSample; /* Samples of the left-most key */ tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */ tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */ | > | 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ 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 */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 int nSample; /* Number of elements in aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ IndexSample *aSample; /* Samples of the left-most key */ tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */ tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */ |
︙ | ︙ | |||
2305 2306 2307 2308 2309 2310 2311 | ** TK_SELECT_COLUMN: column of the result vector */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ u8 op2; /* TK_REGISTER: original value of Expr.op ** TK_COLUMN: the value of p5 for OP_Column ** TK_AGG_FUNCTION: nesting depth */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ | | > | | | 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 | ** TK_SELECT_COLUMN: column of the result vector */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ u8 op2; /* TK_REGISTER: original value of Expr.op ** TK_COLUMN: the value of p5 for OP_Column ** TK_AGG_FUNCTION: nesting depth */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ Table *pTab; /* Table for TK_COLUMN expressions. Can be NULL ** for a column of an index on an expression */ }; /* ** The following are the meanings of bits in the Expr.flags field. */ #define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */ #define EP_Agg 0x000002 /* Contains one or more aggregate functions */ /* 0x000004 // available for use */ /* 0x000008 // available for use */ #define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */ #define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */ #define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ #define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ #define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */ #define EP_Generic 0x000200 /* Ignore COLLATE or affinity on this tree */ #define EP_IntValue 0x000400 /* Integer value contained in u.iValue */ |
︙ | ︙ | |||
2394 2395 2396 2397 2398 2399 2400 | ** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name ** of the result column in the form: DATABASE.TABLE.COLUMN. This later ** form is used for name resolution with nested FROM clauses. */ struct ExprList { int nExpr; /* Number of expressions on the list */ struct ExprList_item { /* For each expression in the list */ | | | | 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 | ** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name ** of the result column in the form: DATABASE.TABLE.COLUMN. This later ** form is used for name resolution with nested FROM clauses. */ struct ExprList { int nExpr; /* Number of expressions on the list */ struct ExprList_item { /* For each expression in the list */ Expr *pExpr; /* The parse tree for this expression */ char *zName; /* Token associated with this expression */ char *zSpan; /* Original text of the expression */ u8 sortOrder; /* 1 for DESC or 0 for ASC */ unsigned done :1; /* A flag to indicate when processing is finished */ unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ unsigned reusable :1; /* Constant expression is reusable */ union { struct { u16 iOrderByCol; /* For ORDER BY, column number in result set */ u16 iAlias; /* Index into Parse.aAlias[] for zName */ } x; int iConstExprReg; /* Register in which Expr value is cached */ } u; } a[1]; /* One slot for each expression in the list */ }; /* ** An instance of this structure is used by the parser to record both ** the parse tree for an expression and the span of input text for an ** expression. */ |
︙ | ︙ | |||
2559 2560 2561 2562 2563 2564 2565 | ** the OR optimization */ #define WHERE_GROUPBY 0x0040 /* pOrderBy is really a GROUP BY */ #define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */ #define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */ #define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */ #define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */ #define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */ | | | 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 | ** the OR optimization */ #define WHERE_GROUPBY 0x0040 /* pOrderBy is really a GROUP BY */ #define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */ #define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */ #define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */ #define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */ #define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */ #define WHERE_SEEK_UNIQ_TABLE 0x1000 /* Do not defer seeks if unique */ /* 0x2000 not currently used */ #define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */ /* 0x8000 not currently used */ /* Allowed return values from sqlite3WhereIsDistinct() */ #define WHERE_DISTINCT_NOOP 0 /* DISTINCT keyword not used */ |
︙ | ︙ | |||
2780 2781 2782 2783 2784 2785 2786 | /* ** An instance of this object describes where to put of the results of ** a SELECT statement. */ struct SelectDest { u8 eDest; /* How to dispose of the results. On of SRT_* above. */ | < > | 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 | /* ** An instance of this object describes where to put of the results of ** a SELECT statement. */ struct SelectDest { u8 eDest; /* How to dispose of the results. On of SRT_* above. */ int iSDParm; /* A parameter used by the eDest disposal method */ int iSdst; /* Base register where results are written */ int nSdst; /* Number of registers allocated */ char *zAffSdst; /* Affinity used when eDest==SRT_Set */ ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */ }; /* ** During code generation of statements that do inserts into AUTOINCREMENT ** tables, the following information is attached to the Table.u.autoInc.p ** pointer of each autoincrement table to record some side information that |
︙ | ︙ | |||
2893 2894 2895 2896 2897 2898 2899 | int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */ int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */ | < | > | 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 | int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */ int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */ int iSelfTab; /* Table for associated with an index on expr, or negative ** of the base register during check-constraint eval */ int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ int iCacheCnt; /* Counter used to generate aColCache[].lru values */ int nLabel; /* Number of labels used */ int *aLabel; /* Space to hold the labels */ ExprList *pConstExpr;/* Constant expressions */ Token constraintName;/* Name of the constraint currently being parsed */ yDbMask writeMask; /* Start a write transaction on these databases */ |
︙ | ︙ | |||
2917 2918 2919 2920 2921 2922 2923 | #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ #endif AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ | | | 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 | #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ #endif AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 disableTriggers; /* True to disable triggers */ |
︙ | ︙ | |||
2942 2943 2944 2945 2946 2947 2948 | u8 tempReg; /* iReg is a temp register that needs to be freed */ int iLevel; /* Nesting level */ int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ int aTempReg[8]; /* Holding area for temporary registers */ Token sNameToken; /* Token with unqualified schema object name */ | < | | > < | | | 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 | u8 tempReg; /* iReg is a temp register that needs to be freed */ int iLevel; /* Nesting level */ int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ int aTempReg[8]; /* Holding area for temporary registers */ Token sNameToken; /* Token with unqualified schema object name */ /************************************************************************ ** Above is constant between recursions. Below is reset before and after ** each recursion. The boundary between these two regions is determined ** using offsetof(Parse,sLastToken) so the sLastToken field must be the ** first field in the recursive region. ************************************************************************/ Token sLastToken; /* The last token parsed */ ynVar nVar; /* Number of '?' variables seen in the SQL so far */ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ u8 explain; /* True if the EXPLAIN flag is found on the query */ #ifndef SQLITE_OMIT_VIRTUALTABLE u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ int nVtabLock; /* Number of virtual tables to lock */ #endif int nHeight; /* Expression tree height of current sub-select */ #ifndef SQLITE_OMIT_EXPLAIN int iSelectId; /* ID of current select for EXPLAIN output */ int iNextSelectId; /* Next available select ID for EXPLAIN output */ #endif VList *pVList; /* Mapping between variable names and numbers */ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ #ifndef SQLITE_OMIT_VIRTUALTABLE Token sArg; /* Complete text of a module argument */ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ With *pWith; /* Current WITH clause, or NULL */ With *pWithToFree; /* Free this WITH object at the end of the parse */ }; /* ** Sizes and pointers of various parts of the Parse object. */ #define PARSE_HDR_SZ offsetof(Parse,aColCache) /* Recursive part w/o aColCache*/ #define PARSE_RECURSE_SZ offsetof(Parse,sLastToken) /* Recursive part */ #define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */ #define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */ /* ** Return true if currently inside an sqlite3_declare_vtab() call. */ #ifdef SQLITE_OMIT_VIRTUALTABLE |
︙ | ︙ | |||
3021 3022 3023 3024 3025 3026 3027 | ** OPFLAG_FORDELETE == BTREE_FORDELETE ** OPFLAG_SAVEPOSITION == BTREE_SAVEPOSITION ** OPFLAG_AUXDELETE == BTREE_AUXDELETE */ #define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */ /* Also used in P2 (not P5) of OP_Delete */ #define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */ | | < < | | 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 | ** OPFLAG_FORDELETE == BTREE_FORDELETE ** OPFLAG_SAVEPOSITION == BTREE_SAVEPOSITION ** OPFLAG_AUXDELETE == BTREE_AUXDELETE */ #define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */ /* Also used in P2 (not P5) of OP_Delete */ #define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */ #define OPFLAG_LASTROWID 0x20 /* Set to update db->lastRowid */ #define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ #define OPFLAG_APPEND 0x08 /* This is likely to be an append */ #define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ #define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */ #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ #define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */ #define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */ #define OPFLAG_P2ISREG 0x10 /* P2 to OP_Open** is a register number */ #define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */ #define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */ #define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */ /* * Each trigger present in the database schema is stored as an instance of * struct Trigger. * * Pointers to instances of struct Trigger are stored in two ways. |
︙ | ︙ | |||
3149 3150 3151 3152 3153 3154 3155 | /* ** An objected used to accumulate the text of a string where we ** do not necessarily know how big the string will be in the end. */ struct StrAccum { sqlite3 *db; /* Optional database for lookaside. Can be NULL */ | < < > | 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 | /* ** An objected used to accumulate the text of a string where we ** do not necessarily know how big the string will be in the end. */ struct StrAccum { sqlite3 *db; /* Optional database for lookaside. Can be NULL */ char *zText; /* The string collected so far */ u32 nAlloc; /* Amount of space allocated in zText */ u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */ u32 nChar; /* Length of the string so far */ u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */ u8 printfFlags; /* SQLITE_PRINTF flags below */ }; #define STRACCUM_NOMEM 1 #define STRACCUM_TOOBIG 2 #define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */ #define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */ |
︙ | ︙ | |||
3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 | */ struct Sqlite3Config { int bMemstat; /* True to enable memory status */ int bCoreMutex; /* True to enable core mutexing */ int bFullMutex; /* True to enable full mutexing */ int bOpenUri; /* True to interpret filenames as URIs */ int bUseCis; /* Use covering indices for full-scans */ 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 */ sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */ void *pHeap; /* Heap storage space */ int nHeap; /* Size of pHeap[] */ int mnReq, mxReq; /* Min and max heap requests sizes */ sqlite3_int64 szMmap; /* mmap() space per open file */ sqlite3_int64 mxMmap; /* Maximum value for szMmap */ | > < < < | 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 | */ struct Sqlite3Config { int bMemstat; /* True to enable memory status */ int bCoreMutex; /* True to enable core mutexing */ int bFullMutex; /* True to enable full mutexing */ int bOpenUri; /* True to interpret filenames as URIs */ int bUseCis; /* Use covering indices for full-scans */ int bSmallMalloc; /* Avoid large memory allocations if true */ 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 */ sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */ void *pHeap; /* Heap storage space */ int nHeap; /* Size of pHeap[] */ int mnReq, mxReq; /* Min and max heap requests sizes */ sqlite3_int64 szMmap; /* mmap() space per open file */ sqlite3_int64 mxMmap; /* Maximum value for szMmap */ void *pPage; /* Page cache memory */ int szPage; /* Size of each page in pPage[] */ int nPage; /* Number of pages in pPage[] */ int mxParserStack; /* maximum depth of the parser stack */ int sharedCacheEnabled; /* true if shared-cache mode enabled */ u32 szPma; /* Maximum Sorter PMA size */ /* The above might be initialized to non-zero. The following need to always |
︙ | ︙ | |||
3232 3233 3234 3235 3236 3237 3238 | #ifdef SQLITE_VDBE_COVERAGE /* The following callback (if not NULL) is invoked on every VDBE branch ** operation. Set the callback using SQLITE_TESTCTRL_VDBE_COVERAGE. */ void (*xVdbeBranch)(void*,int iSrcLine,u8 eThis,u8 eMx); /* Callback */ void *pVdbeBranchArg; /* 1st argument */ #endif | | | 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 | #ifdef SQLITE_VDBE_COVERAGE /* The following callback (if not NULL) is invoked on every VDBE branch ** operation. Set the callback using SQLITE_TESTCTRL_VDBE_COVERAGE. */ void (*xVdbeBranch)(void*,int iSrcLine,u8 eThis,u8 eMx); /* Callback */ void *pVdbeBranchArg; /* 1st argument */ #endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ int iOnceResetThreshold; /* When to reset OP_Once counters */ }; /* |
︙ | ︙ | |||
3268 3269 3270 3271 3272 3273 3274 | Parse *pParse; /* Parser context. */ int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ int walkerDepth; /* Number of subqueries */ u8 eCode; /* A small processing code */ union { /* Extra data for callback */ | | | | | | | | | > > > > > > > > | 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 | Parse *pParse; /* Parser context. */ int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ int walkerDepth; /* Number of subqueries */ u8 eCode; /* A small processing code */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int n; /* A counter */ int iCur; /* A cursor number */ SrcList *pSrcList; /* FROM clause */ struct SrcCount *pSrcCount; /* Counting column references */ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ int *aiCol; /* array of column indexes */ struct IdxCover *pIdxCover; /* Check for index coverage */ struct IdxExprTrans *pIdxTrans; /* Convert indexed expr to column */ ExprList *pGroupBy; /* GROUP BY clause */ struct HavingToWhereCtx *pHavingCtx; /* HAVING to WHERE clause ctx */ } u; }; /* Forward declarations */ int sqlite3WalkExpr(Walker*, Expr*); int sqlite3WalkExprList(Walker*, ExprList*); int sqlite3WalkSelect(Walker*, Select*); int sqlite3WalkSelectExpr(Walker*, Select*); int sqlite3WalkSelectFrom(Walker*, Select*); int sqlite3ExprWalkNoop(Walker*, Expr*); int sqlite3SelectWalkNoop(Walker*, Select*); int sqlite3SelectWalkFail(Walker*, Select*); #ifdef SQLITE_DEBUG void sqlite3SelectWalkAssert2(Walker*, Select*); #endif /* ** Return code from the parse-tree walking primitives and their ** callbacks. */ #define WRC_Continue 0 /* Continue down into children */ #define WRC_Prune 1 /* Omit children but continue walking siblings */ |
︙ | ︙ | |||
3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 | int sqlite3CantopenError(int); #define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__) #define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__) #define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__) #ifdef SQLITE_DEBUG int sqlite3NomemError(int); int sqlite3IoerrnomemError(int); # define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__) # define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__) #else # define SQLITE_NOMEM_BKPT SQLITE_NOMEM # define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM #endif /* ** FTS3 and FTS4 both require virtual table support */ #if defined(SQLITE_OMIT_VIRTUALTABLE) # undef SQLITE_ENABLE_FTS3 | > > > | 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 | int sqlite3CantopenError(int); #define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__) #define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__) #define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__) #ifdef SQLITE_DEBUG int sqlite3NomemError(int); int sqlite3IoerrnomemError(int); int sqlite3CorruptPgnoError(int,Pgno); # define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__) # define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__) # define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptPgnoError(__LINE__,(P)) #else # define SQLITE_NOMEM_BKPT SQLITE_NOMEM # define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM # define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptError(__LINE__) #endif /* ** FTS3 and FTS4 both require virtual table support */ #if defined(SQLITE_OMIT_VIRTUALTABLE) # undef SQLITE_ENABLE_FTS3 |
︙ | ︙ | |||
3429 3430 3431 3432 3433 3434 3435 3436 3437 | void *sqlite3DbMallocRawNN(sqlite3*, u64); char *sqlite3DbStrDup(sqlite3*,const char*); char *sqlite3DbStrNDup(sqlite3*,const char*, u64); void *sqlite3Realloc(void*, u64); void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64); void *sqlite3DbRealloc(sqlite3 *, void *, u64); void sqlite3DbFree(sqlite3*, void*); int sqlite3MallocSize(void*); int sqlite3DbMallocSize(sqlite3*, void*); | > < < | | 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 | void *sqlite3DbMallocRawNN(sqlite3*, u64); char *sqlite3DbStrDup(sqlite3*,const char*); char *sqlite3DbStrNDup(sqlite3*,const char*, u64); void *sqlite3Realloc(void*, u64); void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64); void *sqlite3DbRealloc(sqlite3 *, void *, u64); void sqlite3DbFree(sqlite3*, void*); void sqlite3DbFreeNN(sqlite3*, void*); int sqlite3MallocSize(void*); int sqlite3DbMallocSize(sqlite3*, void*); void *sqlite3PageMalloc(int); void sqlite3PageFree(void*); void sqlite3MemSetDefault(void); #ifndef SQLITE_UNTESTABLE void sqlite3BenignMallocHooks(void (*)(void), void (*)(void)); #endif int sqlite3HeapNearlyFull(void); /* ** On systems with ample stack space and that support alloca(), make ** use of alloca() to obtain space for large automatic objects. By default, |
︙ | ︙ | |||
3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 | # define sqlite3MemoryBarrier() #endif sqlite3_int64 sqlite3StatusValue(int); void sqlite3StatusUp(int, int); void sqlite3StatusDown(int, int); void sqlite3StatusHighwater(int, int); /* Access to mutexes used by sqlite3_status() */ sqlite3_mutex *sqlite3Pcache1Mutex(void); sqlite3_mutex *sqlite3MallocMutex(void); #ifndef SQLITE_OMIT_FLOATING_POINT int sqlite3IsNaN(double); | > | 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 | # define sqlite3MemoryBarrier() #endif sqlite3_int64 sqlite3StatusValue(int); void sqlite3StatusUp(int, int); void sqlite3StatusDown(int, int); void sqlite3StatusHighwater(int, int); int sqlite3LookasideUsed(sqlite3*,int*); /* Access to mutexes used by sqlite3_status() */ sqlite3_mutex *sqlite3Pcache1Mutex(void); sqlite3_mutex *sqlite3MallocMutex(void); #ifndef SQLITE_OMIT_FLOATING_POINT int sqlite3IsNaN(double); |
︙ | ︙ | |||
3547 3548 3549 3550 3551 3552 3553 | void sqlite3ClearTempRegCache(Parse*); #ifdef SQLITE_DEBUG int sqlite3NoTempsInRange(Parse*,int,int); #endif Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); Expr *sqlite3Expr(sqlite3*,int,const char*); void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); | | > > > | 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 | void sqlite3ClearTempRegCache(Parse*); #ifdef SQLITE_DEBUG int sqlite3NoTempsInRange(Parse*,int,int); #endif Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); Expr *sqlite3Expr(sqlite3*,int,const char*); void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); void sqlite3PExprAddSelect(Parse*, Expr*, Select*); Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*); Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*); void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); void sqlite3ExprDelete(sqlite3*, Expr*); ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); void sqlite3ExprListSetSortOrder(ExprList*,int); void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*); void sqlite3ExprListDelete(sqlite3*, ExprList*); u32 sqlite3ExprListFlags(const ExprList*); int sqlite3Init(sqlite3*, char**); int sqlite3InitCallback(void*, int, char**, char**); void sqlite3Pragma(Parse*,Token*,Token*,IdList*); #ifndef SQLITE_OMIT_VIRTUALTABLE Module *sqlite3PragmaVtabRegister(sqlite3*,const char *zName); #endif void sqlite3ResetAllSchemasOfConnection(sqlite3*); void sqlite3ResetOneSchema(sqlite3*,int); void sqlite3CollapseDatabaseArray(sqlite3*); void sqlite3CommitInternalChanges(sqlite3*); void sqlite3DeleteColumnNames(sqlite3*,Table*); int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*); |
︙ | ︙ | |||
3591 3592 3593 3594 3595 3596 3597 | void sqlite3AddDefaultValue(Parse*,ExprSpan*); void sqlite3AddCollateType(Parse*, Token*); void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); Btree *sqlite3DbNameToBtree(sqlite3*,const char*); | | | | 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 | void sqlite3AddDefaultValue(Parse*,ExprSpan*); void sqlite3AddCollateType(Parse*, Token*); void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); Btree *sqlite3DbNameToBtree(sqlite3*,const char*); #ifdef SQLITE_UNTESTABLE # define sqlite3FaultSim(X) SQLITE_OK #else int sqlite3FaultSim(int); #endif Bitvec *sqlite3BitvecCreate(u32); int sqlite3BitvecTest(Bitvec*, u32); int sqlite3BitvecTestNotNull(Bitvec*, u32); int sqlite3BitvecSet(Bitvec*, u32); void sqlite3BitvecClear(Bitvec*, u32, void*); void sqlite3BitvecDestroy(Bitvec*); u32 sqlite3BitvecSize(Bitvec*); #ifndef SQLITE_UNTESTABLE int sqlite3BitvecBuiltinTest(int,int*); #endif RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int); void sqlite3RowSetClear(RowSet*); void sqlite3RowSetInsert(RowSet*, i64); int sqlite3RowSetTest(RowSet*, int iBatch, i64); |
︙ | ︙ | |||
3693 3694 3695 3696 3697 3698 3699 | void sqlite3ExprCachePop(Parse*); void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); void sqlite3ExprCacheAffinityChange(Parse*, int, int); void sqlite3ExprCode(Parse*, Expr*, int); void sqlite3ExprCodeCopy(Parse*, Expr*, int); void sqlite3ExprCodeFactorable(Parse*, Expr*, int); | | > | > | | | < > > > > > > | 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 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 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 | void sqlite3ExprCachePop(Parse*); void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); void sqlite3ExprCacheAffinityChange(Parse*, int, int); void sqlite3ExprCode(Parse*, Expr*, int); void sqlite3ExprCodeCopy(Parse*, Expr*, int); void sqlite3ExprCodeFactorable(Parse*, Expr*, int); int sqlite3ExprCodeAtInit(Parse*, Expr*, int); int sqlite3ExprCodeTemp(Parse*, Expr*, int*); int sqlite3ExprCodeTarget(Parse*, Expr*, int); void sqlite3ExprCodeAndCache(Parse*, Expr*, int); int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8); #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */ #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */ #define SQLITE_ECEL_REF 0x04 /* Use ExprList.u.x.iOrderByCol */ #define SQLITE_ECEL_OMITREF 0x08 /* Omit if ExprList.u.x.iOrderByCol */ void sqlite3ExprIfTrue(Parse*, Expr*, int, int); void sqlite3ExprIfFalse(Parse*, Expr*, int, int); void sqlite3ExprIfFalseDup(Parse*, Expr*, int, int); Table *sqlite3FindTable(sqlite3*,const char*, const char*); #define LOCATE_VIEW 0x01 #define LOCATE_NOERR 0x02 Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*); Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *); Index *sqlite3FindIndex(sqlite3*,const char*, const char*); void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); void sqlite3Vacuum(Parse*,Token*); int sqlite3RunVacuum(char**, sqlite3*, int); char *sqlite3NameFromToken(sqlite3*, Token*); int sqlite3ExprCompare(Parse*,Expr*, Expr*, int); int sqlite3ExprCompareSkip(Expr*, Expr*, int); int sqlite3ExprListCompare(ExprList*, ExprList*, int); int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); Vdbe *sqlite3GetVdbe(Parse*); #ifndef SQLITE_UNTESTABLE void sqlite3PrngSaveState(void); void sqlite3PrngRestoreState(void); #endif void sqlite3RollbackAll(sqlite3*,int); void sqlite3CodeVerifySchema(Parse*, int); void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb); void sqlite3BeginTransaction(Parse*, int); void sqlite3EndTransaction(Parse*,int); void sqlite3Savepoint(Parse*, int, Token*); void sqlite3CloseSavepoints(sqlite3 *); void sqlite3LeaveMutexAndCloseZombie(sqlite3*); int sqlite3ExprIsConstant(Expr*); int sqlite3ExprIsConstantNotJoin(Expr*); int sqlite3ExprIsConstantOrFunction(Expr*, u8); int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); int sqlite3ExprIsTableConstant(Expr*,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS int sqlite3ExprContainsSubquery(Expr*); #endif int sqlite3ExprIsInteger(Expr*, int*); int sqlite3ExprCanBeNull(const Expr*); int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); int sqlite3IsRowid(const char*); void sqlite3GenerateRowDelete( Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); void sqlite3ResolvePartIdxLabel(Parse*,int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, u8,u8,int,int*,int*); #ifdef SQLITE_ENABLE_NULL_TRIM void sqlite3SetMakeRecordP5(Vdbe*,Table*); #else # define sqlite3SetMakeRecordP5(A,B) #endif void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int); int sqlite3OpenTableAndIndices(Parse*, Table*, int, u8, int, u8*, int*, int*); void sqlite3BeginWriteOperation(Parse*, int, int); void sqlite3MultiWrite(Parse*); void sqlite3MayAbort(Parse*); void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); void sqlite3UniqueConstraint(Parse*, int, Index*); |
︙ | ︙ | |||
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 | int sqlite3FixSelect(DbFixer*, Select*); int sqlite3FixExpr(DbFixer*, Expr*); int sqlite3FixExprList(DbFixer*, ExprList*); int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); int sqlite3AtoF(const char *z, double*, int, u8); int sqlite3GetInt32(const char *, int*); int sqlite3Atoi(const char*); int sqlite3Utf16ByteLen(const void *pData, int nChar); int sqlite3Utf8CharLen(const char *pData, int nByte); u32 sqlite3Utf8Read(const u8**); LogEst sqlite3LogEst(u64); LogEst sqlite3LogEstAdd(LogEst,LogEst); #ifndef SQLITE_OMIT_VIRTUALTABLE LogEst sqlite3LogEstFromDouble(double); #endif #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) u64 sqlite3LogEstToInt(LogEst); #endif /* ** Routines to read and write variable-length integers. These used to ** be defined locally, but now we use the varint routines in the util.c ** file. */ int sqlite3PutVarint(unsigned char*, u64); | > > > > > | 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 | int sqlite3FixSelect(DbFixer*, Select*); int sqlite3FixExpr(DbFixer*, Expr*); int sqlite3FixExprList(DbFixer*, ExprList*); int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); int sqlite3AtoF(const char *z, double*, int, u8); int sqlite3GetInt32(const char *, int*); 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); #ifndef SQLITE_OMIT_VIRTUALTABLE LogEst sqlite3LogEstFromDouble(double); #endif #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) u64 sqlite3LogEstToInt(LogEst); #endif VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int); const char *sqlite3VListNumToName(VList*,int); int sqlite3VListNameToNum(VList*,const char*,int); /* ** Routines to read and write variable-length integers. These used to ** be defined locally, but now we use the varint routines in the util.c ** file. */ int sqlite3PutVarint(unsigned char*, u64); |
︙ | ︙ | |||
3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 | const void *sqlite3ValueText(sqlite3_value*, u8); int sqlite3ValueBytes(sqlite3_value*, u8); void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); void sqlite3ValueSetNull(sqlite3_value*); void sqlite3ValueFree(sqlite3_value*); sqlite3_value *sqlite3ValueNew(sqlite3 *); char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION extern const unsigned char sqlite3OpcodeProperty[]; extern const char sqlite3StrBINARY[]; extern const unsigned char sqlite3UpperToLower[]; extern const unsigned char sqlite3CtypeMap[]; | > > | 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 | const void *sqlite3ValueText(sqlite3_value*, u8); int sqlite3ValueBytes(sqlite3_value*, u8); void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); void sqlite3ValueSetNull(sqlite3_value*); void sqlite3ValueFree(sqlite3_value*); sqlite3_value *sqlite3ValueNew(sqlite3 *); #ifndef SQLITE_OMIT_UTF16 char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); #endif int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION extern const unsigned char sqlite3OpcodeProperty[]; extern const char sqlite3StrBINARY[]; extern const unsigned char sqlite3UpperToLower[]; extern const unsigned char sqlite3CtypeMap[]; |
︙ | ︙ | |||
4029 4030 4031 4032 4033 4034 4035 | int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**); char sqlite3IndexColumnAffinity(sqlite3*, Index*, int); #endif /* ** The interface to the LEMON-generated parser */ | > | | > | 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 | int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**); char sqlite3IndexColumnAffinity(sqlite3*, Index*, int); #endif /* ** The interface to the LEMON-generated parser */ #ifndef SQLITE_AMALGAMATION void *sqlite3ParserAlloc(void*(*)(u64)); void sqlite3ParserFree(void*, void(*)(void*)); #endif void sqlite3Parser(void*, int, Token, Parse*); #ifdef YYTRACKMAXSTACKDEPTH int sqlite3ParserStackPeak(void*); #endif void sqlite3AutoLoadExtensions(sqlite3*); #ifndef SQLITE_OMIT_LOAD_EXTENSION |
︙ | ︙ | |||
4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 | int sqlite3VtabCommit(sqlite3 *db); void sqlite3VtabLock(VTable *); void sqlite3VtabUnlock(VTable *); void sqlite3VtabUnlockList(sqlite3*); int sqlite3VtabSavepoint(sqlite3 *, int, int); void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*); VTable *sqlite3GetVTable(sqlite3*, Table*); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif int sqlite3VtabEponymousTableInit(Parse*,Module*); void sqlite3VtabEponymousTableClear(sqlite3*,Module*); void sqlite3VtabMakeWritable(Parse*,Table*); void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); void sqlite3VtabFinishParse(Parse*, Token*); | > > > > > > > | 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 | int sqlite3VtabCommit(sqlite3 *db); void sqlite3VtabLock(VTable *); void sqlite3VtabUnlock(VTable *); void sqlite3VtabUnlockList(sqlite3*); int sqlite3VtabSavepoint(sqlite3 *, int, int); void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*); VTable *sqlite3GetVTable(sqlite3*, Table*); Module *sqlite3VtabCreateModule( sqlite3*, const char*, const sqlite3_module*, void*, void(*)(void*) ); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif int sqlite3VtabEponymousTableInit(Parse*,Module*); void sqlite3VtabEponymousTableClear(sqlite3*,Module*); void sqlite3VtabMakeWritable(Parse*,Table*); void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); void sqlite3VtabFinishParse(Parse*, Token*); |
︙ | ︙ | |||
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 | FKey *sqlite3FkReferences(Table *); #else #define sqlite3FkActions(a,b,c,d,e,f) #define sqlite3FkCheck(a,b,c,d,e,f) #define sqlite3FkDropTable(a,b,c) #define sqlite3FkOldmask(a,b) 0 #define sqlite3FkRequired(a,b,c,d) 0 #endif #ifndef SQLITE_OMIT_FOREIGN_KEY void sqlite3FkDelete(sqlite3 *, Table*); int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**); #else #define sqlite3FkDelete(a,b) #define sqlite3FkLocateIndex(a,b,c,d,e) #endif /* ** Available fault injectors. Should be numbered beginning with 0. */ #define SQLITE_FAULTINJECTOR_MALLOC 0 #define SQLITE_FAULTINJECTOR_COUNT 1 /* ** The interface to the code in fault.c used for identifying "benign" | > | | | 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 | FKey *sqlite3FkReferences(Table *); #else #define sqlite3FkActions(a,b,c,d,e,f) #define sqlite3FkCheck(a,b,c,d,e,f) #define sqlite3FkDropTable(a,b,c) #define sqlite3FkOldmask(a,b) 0 #define sqlite3FkRequired(a,b,c,d) 0 #define sqlite3FkReferences(a) 0 #endif #ifndef SQLITE_OMIT_FOREIGN_KEY void sqlite3FkDelete(sqlite3 *, Table*); int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**); #else #define sqlite3FkDelete(a,b) #define sqlite3FkLocateIndex(a,b,c,d,e) #endif /* ** Available fault injectors. Should be numbered beginning with 0. */ #define SQLITE_FAULTINJECTOR_MALLOC 0 #define SQLITE_FAULTINJECTOR_COUNT 1 /* ** The interface to the code in fault.c used for identifying "benign" ** malloc failures. This is only present if SQLITE_UNTESTABLE ** is not defined. */ #ifndef SQLITE_UNTESTABLE void sqlite3BeginBenignMalloc(void); void sqlite3EndBenignMalloc(void); #else #define sqlite3BeginBenignMalloc() #define sqlite3EndBenignMalloc() #endif |
︙ | ︙ | |||
4180 4181 4182 4183 4184 4185 4186 | #define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */ #define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */ #define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */ int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*); int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); int sqlite3JournalSize(sqlite3_vfs *); | | > | 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 | #define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */ #define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */ #define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */ int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*); int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); int sqlite3JournalSize(sqlite3_vfs *); #if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) int sqlite3JournalCreate(sqlite3_file *); #endif int sqlite3JournalIsInMemory(sqlite3_file *p); void sqlite3MemJournalOpen(sqlite3_file *); void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); |
︙ | ︙ | |||
4266 4267 4268 4269 4270 4271 4272 | #else # define sqlite3MemdebugSetType(X,Y) /* no-op */ # define sqlite3MemdebugHasType(X,Y) 1 # define sqlite3MemdebugNoType(X,Y) 1 #endif #define MEMTYPE_HEAP 0x01 /* General heap allocations */ #define MEMTYPE_LOOKASIDE 0x02 /* Heap that might have been lookaside */ | < | > > > > > | 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 | #else # define sqlite3MemdebugSetType(X,Y) /* no-op */ # define sqlite3MemdebugHasType(X,Y) 1 # define sqlite3MemdebugNoType(X,Y) 1 #endif #define MEMTYPE_HEAP 0x01 /* General heap allocations */ #define MEMTYPE_LOOKASIDE 0x02 /* Heap that might have been lookaside */ #define MEMTYPE_PCACHE 0x04 /* Page cache allocations */ /* ** Threading interface */ #if SQLITE_MAX_WORKER_THREADS>0 int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*); int sqlite3ThreadJoin(SQLiteThread*, void**); #endif #if defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST) int sqlite3DbstatRegister(sqlite3*); #endif int sqlite3ExprVectorSize(Expr *pExpr); int sqlite3ExprIsVector(Expr *pExpr); Expr *sqlite3VectorFieldSubexpr(Expr*, int); Expr *sqlite3ExprForVectorField(Parse*,Expr*,int); void sqlite3VectorErrorMsg(Parse*, Expr*); #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS const char **sqlite3CompileOptions(int *pnOpt); #endif #endif /* SQLITEINT_H */ |
Changes to src/sqliteLimit.h.
︙ | ︙ | |||
83 84 85 86 87 88 89 | #endif /* ** The maximum number of opcodes in a VDBE program. ** Not currently enforced. */ #ifndef SQLITE_MAX_VDBE_OP | | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | #endif /* ** The maximum number of opcodes in a VDBE program. ** Not currently enforced. */ #ifndef SQLITE_MAX_VDBE_OP # define SQLITE_MAX_VDBE_OP 250000000 #endif /* ** The maximum number of arguments to an SQL function. */ #ifndef SQLITE_MAX_FUNCTION_ARG # define SQLITE_MAX_FUNCTION_ARG 127 |
︙ | ︙ |
Changes to src/status.c.
︙ | ︙ | |||
118 119 120 121 122 123 124 | newValue = (sqlite3StatValueType)X; assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); assert( op>=0 && op<ArraySize(statMutex) ); assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() : sqlite3MallocMutex()) ); assert( op==SQLITE_STATUS_MALLOC_SIZE || op==SQLITE_STATUS_PAGECACHE_SIZE | < | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | newValue = (sqlite3StatValueType)X; assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); assert( op>=0 && op<ArraySize(statMutex) ); assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() : sqlite3MallocMutex()) ); assert( op==SQLITE_STATUS_MALLOC_SIZE || op==SQLITE_STATUS_PAGECACHE_SIZE || op==SQLITE_STATUS_PARSER_STACK ); if( newValue>wsdStat.mxValue[op] ){ wsdStat.mxValue[op] = newValue; } } /* |
︙ | ︙ | |||
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 | rc = sqlite3_status64(op, &iCur, &iHwtr, resetFlag); if( rc==0 ){ *pCurrent = (int)iCur; *pHighwater = (int)iHwtr; } return rc; } /* ** Query status information for a single database connection */ int sqlite3_db_status( sqlite3 *db, /* The database connection whose status is desired */ int op, /* Status verb */ int *pCurrent, /* Write current value here */ int *pHighwater, /* Write high-water mark here */ int resetFlag /* Reset high-water mark if true */ ){ int rc = SQLITE_OK; /* Return code */ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){ return SQLITE_MISUSE_BKPT; } #endif sqlite3_mutex_enter(db->mutex); switch( op ){ case SQLITE_DBSTATUS_LOOKASIDE_USED: { | > > > > > > > > > > > > > > > > > > > > > > | < > > > > > | > | 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 | rc = sqlite3_status64(op, &iCur, &iHwtr, resetFlag); if( rc==0 ){ *pCurrent = (int)iCur; *pHighwater = (int)iHwtr; } return rc; } /* ** Return the number of LookasideSlot elements on the linked list */ static u32 countLookasideSlots(LookasideSlot *p){ u32 cnt = 0; while( p ){ p = p->pNext; cnt++; } return cnt; } /* ** Count the number of slots of lookaside memory that are outstanding */ int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){ u32 nInit = countLookasideSlots(db->lookaside.pInit); u32 nFree = countLookasideSlots(db->lookaside.pFree); if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit; return db->lookaside.nSlot - (nInit+nFree); } /* ** Query status information for a single database connection */ int sqlite3_db_status( sqlite3 *db, /* The database connection whose status is desired */ int op, /* Status verb */ int *pCurrent, /* Write current value here */ int *pHighwater, /* Write high-water mark here */ int resetFlag /* Reset high-water mark if true */ ){ int rc = SQLITE_OK; /* Return code */ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){ return SQLITE_MISUSE_BKPT; } #endif sqlite3_mutex_enter(db->mutex); switch( op ){ case SQLITE_DBSTATUS_LOOKASIDE_USED: { *pCurrent = sqlite3LookasideUsed(db, pHighwater); if( resetFlag ){ LookasideSlot *p = db->lookaside.pFree; if( p ){ while( p->pNext ) p = p->pNext; p->pNext = db->lookaside.pInit; db->lookaside.pInit = db->lookaside.pFree; db->lookaside.pFree = 0; } } break; } case SQLITE_DBSTATUS_LOOKASIDE_HIT: case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE: case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: { |
︙ | ︙ |
Changes to src/table.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 | ** interface routines. These are just wrappers around the main ** interface routine of sqlite3_exec(). ** ** These routines are in a separate files so that they will not be linked ** if they are not used. */ #include "sqliteInt.h" | < < | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ** interface routines. These are just wrappers around the main ** interface routine of sqlite3_exec(). ** ** These routines are in a separate files so that they will not be linked ** if they are not used. */ #include "sqliteInt.h" #ifndef SQLITE_OMIT_GET_TABLE /* ** This structure is used to pass data from sqlite3_get_table() through ** to the callback function is uses to build the result. */ |
︙ | ︙ |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
157 158 159 160 161 162 163 164 165 166 167 168 169 170 | Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ int nStep, nSort, nIndex; /* Statistics for most recent operation */ int nTransaction; /* Number of nested [transaction] methods */ int openFlags; /* Flags used to open. (SQLITE_OPEN_URI) */ #ifdef SQLITE_TEST int bLegacyPrepare; /* True to use sqlite3_prepare() */ #endif }; | > | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ int nStep, nSort, nIndex; /* Statistics for most recent operation */ int nVMStep; /* Another statistic for most recent operation */ int nTransaction; /* Number of nested [transaction] methods */ int openFlags; /* Flags used to open. (SQLITE_OPEN_URI) */ #ifdef SQLITE_TEST int bLegacyPrepare; /* True to use sqlite3_prepare() */ #endif }; |
︙ | ︙ | |||
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 | ,const char *zArg5 #endif ){ const char *zCode; Tcl_DString str; int rc; const char *zReply; SqliteDb *pDb = (SqliteDb*)pArg; if( pDb->disableAuth ) return SQLITE_OK; switch( code ){ case SQLITE_COPY : zCode="SQLITE_COPY"; break; case SQLITE_CREATE_INDEX : zCode="SQLITE_CREATE_INDEX"; break; case SQLITE_CREATE_TABLE : zCode="SQLITE_CREATE_TABLE"; break; case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break; case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break; case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break; | > > > > > > > | 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 | ,const char *zArg5 #endif ){ const char *zCode; Tcl_DString str; int rc; const char *zReply; /* EVIDENCE-OF: R-38590-62769 The first parameter to the authorizer ** callback is a copy of the third parameter to the ** sqlite3_set_authorizer() interface. */ SqliteDb *pDb = (SqliteDb*)pArg; if( pDb->disableAuth ) return SQLITE_OK; /* EVIDENCE-OF: R-56518-44310 The second parameter to the callback is an ** integer action code that specifies the particular action to be ** authorized. */ switch( code ){ case SQLITE_COPY : zCode="SQLITE_COPY"; break; case SQLITE_CREATE_INDEX : zCode="SQLITE_CREATE_INDEX"; break; case SQLITE_CREATE_TABLE : zCode="SQLITE_CREATE_TABLE"; break; case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break; case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break; case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break; |
︙ | ︙ | |||
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 | */ static int dbPrepare( SqliteDb *pDb, /* Database object */ const char *zSql, /* SQL to compile */ sqlite3_stmt **ppStmt, /* OUT: Prepared statement */ const char **pzOut /* OUT: Pointer to next SQL statement */ ){ #ifdef SQLITE_TEST if( pDb->bLegacyPrepare ){ return sqlite3_prepare(pDb->db, zSql, -1, ppStmt, pzOut); } #endif | > > > > > > | | 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 | */ static int dbPrepare( SqliteDb *pDb, /* Database object */ const char *zSql, /* SQL to compile */ sqlite3_stmt **ppStmt, /* OUT: Prepared statement */ const char **pzOut /* OUT: Pointer to next SQL statement */ ){ unsigned int prepFlags = 0; #ifdef SQLITE_TEST if( pDb->bLegacyPrepare ){ return sqlite3_prepare(pDb->db, zSql, -1, ppStmt, pzOut); } #endif /* If the statement cache is large, use the SQLITE_PREPARE_PERSISTENT ** flags, which uses less lookaside memory. But if the cache is small, ** omit that flag to make full use of lookaside */ if( pDb->maxStmt>5 ) prepFlags = SQLITE_PREPARE_PERSISTENT; return sqlite3_prepare_v3(pDb->db, zSql, -1, prepFlags, ppStmt, pzOut); } /* ** Search the cache for a prepared-statement object that implements the ** first SQL statement in the buffer pointed to by parameter zIn. If ** no such prepared-statement can be found, allocate and prepare a new ** one. In either case, bind the current values of the relevant Tcl |
︙ | ︙ | |||
1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 | typedef struct DbEvalContext DbEvalContext; struct DbEvalContext { SqliteDb *pDb; /* Database handle */ Tcl_Obj *pSql; /* Object holding string zSql */ const char *zSql; /* Remaining SQL to execute */ SqlPreparedStmt *pPreStmt; /* Current statement */ int nCol; /* Number of columns returned by pStmt */ Tcl_Obj *pArray; /* Name of array variable */ Tcl_Obj **apColName; /* Array of column names */ }; /* ** Release any cache of column names currently held as part of ** the DbEvalContext structure passed as the first argument. */ static void dbReleaseColumnNames(DbEvalContext *p){ if( p->apColName ){ | > > > | 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 | typedef struct DbEvalContext DbEvalContext; struct DbEvalContext { SqliteDb *pDb; /* Database handle */ Tcl_Obj *pSql; /* Object holding string zSql */ const char *zSql; /* Remaining SQL to execute */ SqlPreparedStmt *pPreStmt; /* Current statement */ int nCol; /* Number of columns returned by pStmt */ int evalFlags; /* Flags used */ Tcl_Obj *pArray; /* Name of array variable */ Tcl_Obj **apColName; /* Array of column names */ }; #define SQLITE_EVAL_WITHOUTNULLS 0x00001 /* Unset array(*) for NULL */ /* ** Release any cache of column names currently held as part of ** the DbEvalContext structure passed as the first argument. */ static void dbReleaseColumnNames(DbEvalContext *p){ if( p->apColName ){ |
︙ | ︙ | |||
1475 1476 1477 1478 1479 1480 1481 | ** ** set ${pArray}(*) {a b c} */ static void dbEvalInit( DbEvalContext *p, /* Pointer to structure to initialize */ SqliteDb *pDb, /* Database handle */ Tcl_Obj *pSql, /* Object containing SQL script */ | | > > | 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 | ** ** set ${pArray}(*) {a b c} */ static void dbEvalInit( DbEvalContext *p, /* Pointer to structure to initialize */ SqliteDb *pDb, /* Database handle */ Tcl_Obj *pSql, /* Object containing SQL script */ Tcl_Obj *pArray, /* Name of Tcl array to set (*) element of */ int evalFlags /* Flags controlling evaluation */ ){ memset(p, 0, sizeof(DbEvalContext)); p->pDb = pDb; p->zSql = Tcl_GetString(pSql); p->pSql = pSql; Tcl_IncrRefCount(pSql); if( pArray ){ p->pArray = pArray; Tcl_IncrRefCount(pArray); } p->evalFlags = evalFlags; } /* ** Obtain information about the row that the DbEvalContext passed as the ** first argument currently points to. */ static void dbEvalRowInfo( |
︙ | ︙ | |||
1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 | dbEvalRowInfo(p, 0, 0); } rcs = sqlite3_reset(pStmt); pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); pDb->nIndex = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1); dbReleaseColumnNames(p); p->pPreStmt = 0; if( rcs!=SQLITE_OK ){ /* If a run-time error occurs, report the error and stop reading ** the SQL. */ dbReleaseStmt(pDb, pPreStmt, 1); | > | 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 | dbEvalRowInfo(p, 0, 0); } rcs = sqlite3_reset(pStmt); pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); pDb->nIndex = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1); pDb->nVMStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_VM_STEP,1); dbReleaseColumnNames(p); p->pPreStmt = 0; if( rcs!=SQLITE_OK ){ /* If a run-time error occurs, report the error and stop reading ** the SQL. */ dbReleaseStmt(pDb, pPreStmt, 1); |
︙ | ︙ | |||
1717 1718 1719 1720 1721 1722 1723 | while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){ int i; int nCol; Tcl_Obj **apColName; dbEvalRowInfo(p, &nCol, &apColName); for(i=0; i<nCol; i++){ | < | > > > > > | | 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 | while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){ int i; int nCol; Tcl_Obj **apColName; dbEvalRowInfo(p, &nCol, &apColName); for(i=0; i<nCol; i++){ if( pArray==0 ){ Tcl_ObjSetVar2(interp, apColName[i], 0, dbEvalColumnValue(p,i), 0); }else if( (p->evalFlags & SQLITE_EVAL_WITHOUTNULLS)!=0 && sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL ){ Tcl_UnsetVar2(interp, Tcl_GetString(pArray), Tcl_GetString(apColName[i]), 0); }else{ Tcl_ObjSetVar2(interp, pArray, apColName[i], dbEvalColumnValue(p,i), 0); } } /* The required interpreter variables are now populated with the data ** from the current row. If using NRE, schedule callbacks to evaluate ** script pScript, then to invoke this function again to fetch the next ** row (or clean up if there is no next row or the script throws an |
︙ | ︙ | |||
2303 2304 2305 2306 2307 2308 2309 | if( rc ){ Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), (char*)0); sqlite3_finalize(pStmt); return TCL_ERROR; } in = fopen(zFile, "rb"); if( in==0 ){ | | | 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 | if( rc ){ Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), (char*)0); sqlite3_finalize(pStmt); return TCL_ERROR; } in = fopen(zFile, "rb"); if( in==0 ){ Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, (char*)0); sqlite3_finalize(pStmt); return TCL_ERROR; } azCol = malloc( sizeof(azCol[0])*(nCol+1) ); if( azCol==0 ) { Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0); fclose(in); |
︙ | ︙ | |||
2434 2435 2436 2437 2438 2439 2440 | Tcl_Obj *pResult = 0; DbEvalContext sEval; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL"); return TCL_ERROR; } | | | > > > > > > > > > > > > > | > | | | | 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 | Tcl_Obj *pResult = 0; DbEvalContext sEval; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL"); return TCL_ERROR; } dbEvalInit(&sEval, pDb, objv[2], 0, 0); rc = dbEvalStep(&sEval); if( choice==DB_ONECOLUMN ){ if( rc==TCL_OK ){ pResult = dbEvalColumnValue(&sEval, 0); }else if( rc==TCL_BREAK ){ Tcl_ResetResult(interp); } }else if( rc==TCL_BREAK || rc==TCL_OK ){ pResult = Tcl_NewBooleanObj(rc==TCL_OK); } dbEvalFinalize(&sEval); if( pResult ) Tcl_SetObjResult(interp, pResult); if( rc==TCL_BREAK ){ rc = TCL_OK; } break; } /* ** $db eval ?options? $sql ?array? ?{ ...code... }? ** ** The SQL statement in $sql is evaluated. For each row, the values are ** placed in elements of the array named "array" and ...code... is executed. ** If "array" and "code" are omitted, then no callback is every invoked. ** If "array" is an empty string, then the values are placed in variables ** that have the same name as the fields extracted by the query. */ case DB_EVAL: { int evalFlags = 0; const char *zOpt; while( objc>3 && (zOpt = Tcl_GetString(objv[2]))!=0 && zOpt[0]=='-' ){ if( strcmp(zOpt, "-withoutnulls")==0 ){ evalFlags |= SQLITE_EVAL_WITHOUTNULLS; } else{ Tcl_AppendResult(interp, "unknown option: \"", zOpt, "\"", (void*)0); return TCL_ERROR; } objc--; objv++; } if( objc<3 || objc>5 ){ Tcl_WrongNumArgs(interp, 2, objv, "?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?"); return TCL_ERROR; } if( objc==3 ){ DbEvalContext sEval; Tcl_Obj *pRet = Tcl_NewObj(); Tcl_IncrRefCount(pRet); dbEvalInit(&sEval, pDb, objv[2], 0, 0); while( TCL_OK==(rc = dbEvalStep(&sEval)) ){ int i; int nCol; dbEvalRowInfo(&sEval, &nCol, 0); for(i=0; i<nCol; i++){ Tcl_ListObjAppendElement(interp, pRet, dbEvalColumnValue(&sEval, i)); } } dbEvalFinalize(&sEval); if( rc==TCL_BREAK ){ Tcl_SetObjResult(interp, pRet); rc = TCL_OK; } Tcl_DecrRefCount(pRet); }else{ ClientData cd2[2]; DbEvalContext *p; Tcl_Obj *pArray = 0; Tcl_Obj *pScript; if( objc>=5 && *(char *)Tcl_GetString(objv[3]) ){ pArray = objv[3]; } pScript = objv[objc-1]; Tcl_IncrRefCount(pScript); p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext)); dbEvalInit(p, pDb, objv[2], pArray, evalFlags); cd2[0] = (void *)p; cd2[1] = (void *)pScript; rc = DbEvalNextCmd(cd2, interp, TCL_OK); } break; } |
︙ | ︙ | |||
2532 2533 2534 2535 2536 2537 2538 | return TCL_ERROR; } for(i=3; i<(objc-1); i++){ const char *z = Tcl_GetString(objv[i]); int n = strlen30(z); if( n>2 && strncmp(z, "-argcount",n)==0 ){ if( i==(objc-2) ){ | | | | 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 | return TCL_ERROR; } for(i=3; i<(objc-1); i++){ const char *z = Tcl_GetString(objv[i]); int n = strlen30(z); if( n>2 && strncmp(z, "-argcount",n)==0 ){ if( i==(objc-2) ){ Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0); return TCL_ERROR; } if( Tcl_GetIntFromObj(interp, objv[i+1], &nArg) ) return TCL_ERROR; if( nArg<0 ){ Tcl_AppendResult(interp, "number of arguments must be non-negative", (char*)0); return TCL_ERROR; } i++; }else if( n>2 && strncmp(z, "-deterministic",n)==0 ){ flags |= SQLITE_DETERMINISTIC; }else{ Tcl_AppendResult(interp, "bad option \"", z, "\": must be -argcount or -deterministic", (char*)0 ); return TCL_ERROR; } } pScript = objv[objc-1]; zName = Tcl_GetStringFromObj(objv[2], 0); |
︙ | ︙ | |||
2844 2845 2846 2847 2848 2849 2850 | rc = TCL_ERROR; } sqlite3_close(pSrc); break; } /* | | > > | | 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 | rc = TCL_ERROR; } sqlite3_close(pSrc); break; } /* ** $db status (step|sort|autoindex|vmstep) ** ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or ** SQLITE_STMTSTATUS_SORT for the most recent eval. */ case DB_STATUS: { int v; const char *zOp; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "(step|sort|autoindex)"); return TCL_ERROR; } zOp = Tcl_GetString(objv[2]); if( strcmp(zOp, "step")==0 ){ v = pDb->nStep; }else if( strcmp(zOp, "sort")==0 ){ v = pDb->nSort; }else if( strcmp(zOp, "autoindex")==0 ){ v = pDb->nIndex; }else if( strcmp(zOp, "vmstep")==0 ){ v = pDb->nVMStep; }else{ Tcl_AppendResult(interp, "bad argument: should be autoindex, step, sort or vmstep", (char*)0); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(v)); break; } |
︙ | ︙ | |||
3136 3137 3138 3139 3140 3141 3142 | ** $db preupdate_hook count ** $db preupdate_hook hook ?SCRIPT? ** $db preupdate_hook new INDEX ** $db preupdate_hook old INDEX */ case DB_PREUPDATE: { #ifndef SQLITE_ENABLE_PREUPDATE_HOOK | | > | 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 | ** $db preupdate_hook count ** $db preupdate_hook hook ?SCRIPT? ** $db preupdate_hook new INDEX ** $db preupdate_hook old INDEX */ case DB_PREUPDATE: { #ifndef SQLITE_ENABLE_PREUPDATE_HOOK Tcl_AppendResult(interp, "preupdate_hook was omitted at compile-time", (char*)0); rc = TCL_ERROR; #else static const char *azSub[] = {"count", "depth", "hook", "new", "old", 0}; enum DbPreupdateSubCmd { PRE_COUNT, PRE_DEPTH, PRE_HOOK, PRE_NEW, PRE_OLD }; int iSub; |
︙ | ︙ | |||
3203 3204 3205 3206 3207 3208 3209 | } if( rc==SQLITE_OK ){ Tcl_Obj *pObj; pObj = Tcl_NewStringObj((char*)sqlite3_value_text(pValue), -1); Tcl_SetObjResult(interp, pObj); }else{ | | | 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 | } if( rc==SQLITE_OK ){ Tcl_Obj *pObj; pObj = Tcl_NewStringObj((char*)sqlite3_value_text(pValue), -1); Tcl_SetObjResult(interp, pObj); }else{ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0); return TCL_ERROR; } } } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ break; } |
︙ | ︙ | |||
3400 3401 3402 3403 3404 3405 3406 | " ?-key CODECKEY?" #endif ); return TCL_ERROR; } zErrMsg = 0; p = (SqliteDb*)Tcl_Alloc( sizeof(*p) ); | < < < < | 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 | " ?-key CODECKEY?" #endif ); return TCL_ERROR; } zErrMsg = 0; p = (SqliteDb*)Tcl_Alloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); zFile = Tcl_GetStringFromObj(objv[2], 0); zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename); rc = sqlite3_open_v2(zFile, &p->db, flags, zVfs); Tcl_DStringFree(&translatedFilename); if( p->db ){ if( SQLITE_OK!=sqlite3_errcode(p->db) ){ |
︙ | ︙ | |||
3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 | static int SQLITE_TCLAPI md5file_cmd( void*cd, Tcl_Interp *interp, int argc, const char **argv ){ FILE *in; MD5Context ctx; void (*converter)(unsigned char*, char*); unsigned char digest[16]; char zBuf[10240]; | > > | | > > > > > > > > < > | > | 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 | static int SQLITE_TCLAPI md5file_cmd( void*cd, Tcl_Interp *interp, int argc, const char **argv ){ FILE *in; int ofst; int amt; MD5Context ctx; void (*converter)(unsigned char*, char*); unsigned char digest[16]; char zBuf[10240]; if( argc!=2 && argc!=4 ){ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], " FILENAME [OFFSET AMT]\"", (char*)0); return TCL_ERROR; } if( argc==4 ){ ofst = atoi(argv[2]); amt = atoi(argv[3]); }else{ ofst = 0; amt = 2147483647; } in = fopen(argv[1],"rb"); if( in==0 ){ Tcl_AppendResult(interp,"unable to open file \"", argv[1], "\" for reading", (char*)0); return TCL_ERROR; } fseek(in, ofst, SEEK_SET); MD5Init(&ctx); while( amt>0 ){ int n; n = (int)fread(zBuf, 1, sizeof(zBuf)<=amt ? sizeof(zBuf) : amt, in); if( n<=0 ) break; MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n); amt -= n; } fclose(in); MD5Final(digest, &ctx); converter = (void(*)(unsigned char*,char*))cd; converter(digest, zBuf); Tcl_AppendResult(interp, zBuf, (char*)0); return TCL_OK; |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 | const char *zName; int op; } aOp[] = { { "SQLITE_STMTSTATUS_FULLSCAN_STEP", SQLITE_STMTSTATUS_FULLSCAN_STEP }, { "SQLITE_STMTSTATUS_SORT", SQLITE_STMTSTATUS_SORT }, { "SQLITE_STMTSTATUS_AUTOINDEX", SQLITE_STMTSTATUS_AUTOINDEX }, { "SQLITE_STMTSTATUS_VM_STEP", SQLITE_STMTSTATUS_VM_STEP }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; zOpName = Tcl_GetString(objv[2]); | > > > | 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 | const char *zName; int op; } aOp[] = { { "SQLITE_STMTSTATUS_FULLSCAN_STEP", SQLITE_STMTSTATUS_FULLSCAN_STEP }, { "SQLITE_STMTSTATUS_SORT", SQLITE_STMTSTATUS_SORT }, { "SQLITE_STMTSTATUS_AUTOINDEX", SQLITE_STMTSTATUS_AUTOINDEX }, { "SQLITE_STMTSTATUS_VM_STEP", SQLITE_STMTSTATUS_VM_STEP }, { "SQLITE_STMTSTATUS_REPREPARE", SQLITE_STMTSTATUS_REPREPARE }, { "SQLITE_STMTSTATUS_RUN", SQLITE_STMTSTATUS_RUN }, { "SQLITE_STMTSTATUS_MEMUSED", SQLITE_STMTSTATUS_MEMUSED }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; zOpName = Tcl_GetString(objv[2]); |
︙ | ︙ | |||
2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 | if( sqlite3TestMakePointerStr(interp, zBuf, pSnapshot) ) return TCL_ERROR; Tcl_SetObjResult(interp, Tcl_NewStringObj(zBuf, -1)); } return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifdef SQLITE_ENABLE_SNAPSHOT /* ** Usage: sqlite3_snapshot_open DB DBNAME SNAPSHOT */ static int SQLITE_TCLAPI test_snapshot_open( void * clientData, Tcl_Interp *interp, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( sqlite3TestMakePointerStr(interp, zBuf, pSnapshot) ) return TCL_ERROR; Tcl_SetObjResult(interp, Tcl_NewStringObj(zBuf, -1)); } return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifdef SQLITE_ENABLE_SNAPSHOT /* ** Usage: sqlite3_snapshot_recover DB DBNAME */ static int SQLITE_TCLAPI test_snapshot_recover( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; sqlite3 *db; char *zName; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; zName = Tcl_GetString(objv[2]); rc = sqlite3_snapshot_recover(db, zName); if( rc!=SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; }else{ Tcl_ResetResult(interp); } return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifdef SQLITE_ENABLE_SNAPSHOT /* ** Usage: sqlite3_snapshot_open DB DBNAME SNAPSHOT */ static int SQLITE_TCLAPI test_snapshot_open( void * clientData, Tcl_Interp *interp, |
︙ | ︙ | |||
2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 | p2 = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[2])); res = sqlite3_snapshot_cmp(p1, p2); Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ /* ** Usage: sqlite3_delete_database FILENAME */ int sqlite3_delete_database(const char*); /* in test_delete.c */ static int SQLITE_TCLAPI test_delete_database( void * clientData, Tcl_Interp *interp, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | p2 = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[2])); res = sqlite3_snapshot_cmp(p1, p2); Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifdef SQLITE_ENABLE_SNAPSHOT /* ** Usage: sqlite3_snapshot_get_blob DB DBNAME */ static int SQLITE_TCLAPI test_snapshot_get_blob( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; sqlite3 *db; char *zName; sqlite3_snapshot *pSnapshot = 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; zName = Tcl_GetString(objv[2]); rc = sqlite3_snapshot_get(db, zName, &pSnapshot); if( rc!=SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; }else{ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((unsigned char*)pSnapshot, sizeof(sqlite3_snapshot)) ); sqlite3_snapshot_free(pSnapshot); } return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifdef SQLITE_ENABLE_SNAPSHOT /* ** Usage: sqlite3_snapshot_open_blob DB DBNAME SNAPSHOT */ static int SQLITE_TCLAPI test_snapshot_open_blob( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; sqlite3 *db; char *zName; unsigned char *pBlob; int nBlob; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SNAPSHOT"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; zName = Tcl_GetString(objv[2]); pBlob = Tcl_GetByteArrayFromObj(objv[3], &nBlob); if( nBlob!=sizeof(sqlite3_snapshot) ){ Tcl_AppendResult(interp, "bad SNAPSHOT", 0); return TCL_ERROR; } rc = sqlite3_snapshot_open(db, zName, (sqlite3_snapshot*)pBlob); if( rc!=SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; } return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifdef SQLITE_ENABLE_SNAPSHOT /* ** Usage: sqlite3_snapshot_cmp_blob SNAPSHOT1 SNAPSHOT2 */ static int SQLITE_TCLAPI test_snapshot_cmp_blob( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int res; unsigned char *p1; unsigned char *p2; int n1; int n2; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "SNAPSHOT1 SNAPSHOT2"); return TCL_ERROR; } p1 = Tcl_GetByteArrayFromObj(objv[1], &n1); p2 = Tcl_GetByteArrayFromObj(objv[2], &n2); if( n1!=sizeof(sqlite3_snapshot) || n1!=n2 ){ Tcl_AppendResult(interp, "bad SNAPSHOT", 0); return TCL_ERROR; } res = sqlite3_snapshot_cmp((sqlite3_snapshot*)p1, (sqlite3_snapshot*)p2); Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ /* ** Usage: sqlite3_delete_database FILENAME */ int sqlite3_delete_database(const char*); /* in test_delete.c */ static int SQLITE_TCLAPI test_delete_database( void * clientData, Tcl_Interp *interp, |
︙ | ︙ | |||
2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 | } zFile = (const char*)Tcl_GetString(objv[1]); rc = sqlite3_delete_database(zFile); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_OK; } /* ** Usage: sqlite3_next_stmt DB STMT ** ** Return the next statment in sequence after STMT. */ static int SQLITE_TCLAPI test_next_stmt( | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } zFile = (const char*)Tcl_GetString(objv[1]); rc = sqlite3_delete_database(zFile); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_OK; } /* ** Usage: atomic_batch_write PATH */ static int SQLITE_TCLAPI test_atomic_batch_write( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ char *zFile = 0; /* Path to file to test */ sqlite3 *db = 0; /* Database handle */ sqlite3_file *pFd = 0; /* SQLite fd open on zFile */ int bRes = 0; /* Integer result of this command */ int dc = 0; /* Device-characteristics mask */ int rc; /* sqlite3_open() return code */ if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "PATH"); return TCL_ERROR; } zFile = Tcl_GetString(objv[1]); rc = sqlite3_open(zFile, &db); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, sqlite3_errmsg(db), 0); sqlite3_close(db); return TCL_ERROR; } rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void*)&pFd); dc = pFd->pMethods->xDeviceCharacteristics(pFd); if( dc & SQLITE_IOCAP_BATCH_ATOMIC ){ bRes = 1; } Tcl_SetObjResult(interp, Tcl_NewIntObj(bRes)); sqlite3_close(db); return TCL_OK; } /* ** Usage: sqlite3_next_stmt DB STMT ** ** Return the next statment in sequence after STMT. */ static int SQLITE_TCLAPI test_next_stmt( |
︙ | ︙ | |||
4806 4807 4808 4809 4810 4811 4812 | return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; sqlite3_interrupt(db); return TCL_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 | return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; sqlite3_interrupt(db); return TCL_OK; } /* ** Usage: sqlite_delete_function DB function-name ** ** Delete the user function 'function-name' from database handle DB. It ** is assumed that the user function was created as UTF8, any number of ** arguments (the way the TCL interface does it). */ |
︙ | ︙ | |||
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 | extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); 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*); extern int sqlite3_regexp_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*); static const struct { const char *zExtName; int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); } aExtension[] = { { "amatch", sqlite3_amatch_init }, { "carray", sqlite3_carray_init }, { "closure", sqlite3_closure_init }, { "csv", sqlite3_csv_init }, { "eval", sqlite3_eval_init }, { "fileio", sqlite3_fileio_init }, { "fuzzer", sqlite3_fuzzer_init }, { "ieee754", sqlite3_ieee_init }, { "nextchar", sqlite3_nextchar_init }, { "percentile", sqlite3_percentile_init }, { "regexp", sqlite3_regexp_init }, { "series", sqlite3_series_init }, { "spellfix", sqlite3_spellfix_init }, { "totype", sqlite3_totype_init }, { "wholenumber", sqlite3_wholenumber_init }, }; sqlite3 *db; const char *zName; int i, j, rc; char *zErrMsg = 0; if( objc<3 ){ | > > > > | 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 | extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); 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*); 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*); static const struct { const char *zExtName; int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); } aExtension[] = { { "amatch", sqlite3_amatch_init }, { "carray", sqlite3_carray_init }, { "closure", sqlite3_closure_init }, { "csv", sqlite3_csv_init }, { "eval", sqlite3_eval_init }, { "fileio", sqlite3_fileio_init }, { "fuzzer", sqlite3_fuzzer_init }, { "ieee754", sqlite3_ieee_init }, { "nextchar", sqlite3_nextchar_init }, { "percentile", sqlite3_percentile_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 }, }; sqlite3 *db; const char *zName; int i, j, rc; char *zErrMsg = 0; if( objc<3 ){ |
︙ | ︙ | |||
7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 | const char *zName; int eVal; } aSetting[] = { { "FKEY", SQLITE_DBCONFIG_ENABLE_FKEY }, { "TRIGGER", SQLITE_DBCONFIG_ENABLE_TRIGGER }, { "FTS3_TOKENIZER", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER }, { "LOAD_EXTENSION", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION }, }; int i; int v; const char *zSetting; sqlite3 *db; if( objc!=4 ){ | > > | 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 | const char *zName; int eVal; } aSetting[] = { { "FKEY", SQLITE_DBCONFIG_ENABLE_FKEY }, { "TRIGGER", SQLITE_DBCONFIG_ENABLE_TRIGGER }, { "FTS3_TOKENIZER", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER }, { "LOAD_EXTENSION", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION }, { "NO_CKPT_ON_CLOSE",SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE }, { "QPSG", SQLITE_DBCONFIG_ENABLE_QPSG }, }; int i; int v; const char *zSetting; sqlite3 *db; if( objc!=4 ){ |
︙ | ︙ | |||
7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 | }else{ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "icecube"); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_OK; } } /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; extern int sqlite3_found_count; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 | }else{ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "icecube"); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_OK; } } /* ** Usage: sqlite3_mmap_warm DB DBNAME */ static int SQLITE_TCLAPI test_mmap_warm( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); extern int sqlite3_mmap_warm(sqlite3 *db, const char *); if( objc!=2 && objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB ?DBNAME?"); return TCL_ERROR; }else{ int rc; sqlite3 *db; const char *zDb = 0; if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; if( objc==3 ){ zDb = Tcl_GetString(objv[2]); } rc = sqlite3_mmap_warm(db, zDb); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_OK; } } /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; extern int sqlite3_found_count; |
︙ | ︙ | |||
7327 7328 7329 7330 7331 7332 7333 | { "sqlite3_key", (Tcl_CmdProc*)test_key }, { "sqlite3_rekey", (Tcl_CmdProc*)test_rekey }, { "sqlite_set_magic", (Tcl_CmdProc*)sqlite_set_magic }, { "sqlite3_interrupt", (Tcl_CmdProc*)test_interrupt }, { "sqlite_delete_function", (Tcl_CmdProc*)delete_function }, { "sqlite_delete_collation", (Tcl_CmdProc*)delete_collation }, { "sqlite3_get_autocommit", (Tcl_CmdProc*)get_autocommit }, | < | 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 | { "sqlite3_key", (Tcl_CmdProc*)test_key }, { "sqlite3_rekey", (Tcl_CmdProc*)test_rekey }, { "sqlite_set_magic", (Tcl_CmdProc*)sqlite_set_magic }, { "sqlite3_interrupt", (Tcl_CmdProc*)test_interrupt }, { "sqlite_delete_function", (Tcl_CmdProc*)delete_function }, { "sqlite_delete_collation", (Tcl_CmdProc*)delete_collation }, { "sqlite3_get_autocommit", (Tcl_CmdProc*)get_autocommit }, { "sqlite3_busy_timeout", (Tcl_CmdProc*)test_busy_timeout }, { "printf", (Tcl_CmdProc*)test_printf }, { "sqlite3IoTrace", (Tcl_CmdProc*)test_io_trace }, { "clang_sanitize_address", (Tcl_CmdProc*)clang_sanitize_address }, }; static struct { char *zName; |
︙ | ︙ | |||
7534 7535 7536 7537 7538 7539 7540 7541 | #endif { "vfs_current_time_int64", vfsCurrentTimeInt64, 0 }, #ifdef SQLITE_ENABLE_SNAPSHOT { "sqlite3_snapshot_get", test_snapshot_get, 0 }, { "sqlite3_snapshot_open", test_snapshot_open, 0 }, { "sqlite3_snapshot_free", test_snapshot_free, 0 }, { "sqlite3_snapshot_cmp", test_snapshot_cmp, 0 }, #endif | > > > > | > > | 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 | #endif { "vfs_current_time_int64", vfsCurrentTimeInt64, 0 }, #ifdef SQLITE_ENABLE_SNAPSHOT { "sqlite3_snapshot_get", test_snapshot_get, 0 }, { "sqlite3_snapshot_open", test_snapshot_open, 0 }, { "sqlite3_snapshot_free", test_snapshot_free, 0 }, { "sqlite3_snapshot_cmp", test_snapshot_cmp, 0 }, { "sqlite3_snapshot_recover", test_snapshot_recover, 0 }, { "sqlite3_snapshot_get_blob", test_snapshot_get_blob, 0 }, { "sqlite3_snapshot_open_blob", test_snapshot_open_blob, 0 }, { "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 }, #endif { "sqlite3_delete_database", test_delete_database, 0 }, { "atomic_batch_write", test_atomic_batch_write, 0 }, { "sqlite3_mmap_warm", test_mmap_warm, 0 }, }; static int bitmask_size = sizeof(Bitmask)*8; static int longdouble_size = sizeof(LONGDOUBLE_TYPE); int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; |
︙ | ︙ |
Changes to src/test3.c.
︙ | ︙ | |||
302 303 304 305 306 307 308 | if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } pCur = sqlite3TestTextToPtr(argv[1]); sqlite3BtreeEnter(pCur->pBtree); | | > > > > | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } pCur = sqlite3TestTextToPtr(argv[1]); sqlite3BtreeEnter(pCur->pBtree); rc = sqlite3BtreeNext(pCur, 0); if( rc==SQLITE_DONE ){ res = 1; rc = SQLITE_OK; } sqlite3BtreeLeave(pCur->pBtree); if( rc ){ Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); Tcl_AppendResult(interp, zBuf, 0); |
︙ | ︙ |
Changes to src/test6.c.
︙ | ︙ | |||
157 158 159 160 161 162 163 | /* ** Set this global variable to 1 to enable crash testing. */ static int sqlite3CrashTestEnable = 0; static void *crash_malloc(int nByte){ | | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | /* ** Set this global variable to 1 to enable crash testing. */ static int sqlite3CrashTestEnable = 0; static void *crash_malloc(int nByte){ return (void *)Tcl_AttemptAlloc((size_t)nByte); } static void crash_free(void *p){ Tcl_Free(p); } static void *crash_realloc(void *p, int n){ return (void *)Tcl_AttemptRealloc(p, (size_t)n); } /* ** Wrapper around the sqlite3OsWrite() function that avoids writing to the ** 512 byte block begining at offset PENDING_BYTE. */ static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){ |
︙ | ︙ | |||
311 312 313 314 315 316 317 | u8 *zGarbage; int iFirst = (int)(pWrite->iOffset/g.iSectorSize); int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize); assert(pWrite->zBuf); #ifdef TRACE_CRASHTEST | | > | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | u8 *zGarbage; int iFirst = (int)(pWrite->iOffset/g.iSectorSize); int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize); assert(pWrite->zBuf); #ifdef TRACE_CRASHTEST printf("Trashing %d sectors (%d bytes) @ %lld (sector %d) (%s)\n", 1+iLast-iFirst, (1+iLast-iFirst)*g.iSectorSize, pWrite->iOffset, iFirst, pWrite->pFile->zName ); #endif zGarbage = crash_malloc(g.iSectorSize); if( zGarbage ){ sqlite3_int64 i; for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){ |
︙ | ︙ | |||
731 732 733 734 735 736 737 738 739 740 741 742 743 744 | { "atomic8k", SQLITE_IOCAP_ATOMIC8K }, { "atomic16k", SQLITE_IOCAP_ATOMIC16K }, { "atomic32k", SQLITE_IOCAP_ATOMIC32K }, { "atomic64k", SQLITE_IOCAP_ATOMIC64K }, { "sequential", SQLITE_IOCAP_SEQUENTIAL }, { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, { "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE }, { 0, 0 } }; int i; int iDc = 0; int iSectorSize = 0; int setSectorsize = 0; | > | 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 | { "atomic8k", SQLITE_IOCAP_ATOMIC8K }, { "atomic16k", SQLITE_IOCAP_ATOMIC16K }, { "atomic32k", SQLITE_IOCAP_ATOMIC32K }, { "atomic64k", SQLITE_IOCAP_ATOMIC64K }, { "sequential", SQLITE_IOCAP_SEQUENTIAL }, { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, { "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE }, { "batch-atomic", SQLITE_IOCAP_BATCH_ATOMIC }, { 0, 0 } }; int i; int iDc = 0; int iSectorSize = 0; int setSectorsize = 0; |
︙ | ︙ | |||
823 824 825 826 827 828 829 | } writeListSync(0, 1); assert( 0 ); return TCL_OK; } /* | | > | 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 | } writeListSync(0, 1); assert( 0 ); return TCL_OK; } /* ** tclcmd: sqlite_crash_enable ENABLE ?DEFAULT? ** ** Parameter ENABLE must be a boolean value. If true, then the "crash" ** vfs is added to the system. If false, it is removed. */ static int SQLITE_TCLAPI crashEnableCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int isEnable; int isDefault = 0; static sqlite3_vfs crashVfs = { 2, /* iVersion */ 0, /* szOsFile */ 0, /* mxPathname */ 0, /* pNext */ "crash", /* zName */ 0, /* pAppData */ |
︙ | ︙ | |||
858 859 860 861 862 863 864 | cfRandomness, /* xRandomness */ cfSleep, /* xSleep */ cfCurrentTime, /* xCurrentTime */ cfGetLastError, /* xGetLastError */ 0, /* xCurrentTimeInt64 */ }; | | | > > > | | 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 | cfRandomness, /* xRandomness */ cfSleep, /* xSleep */ cfCurrentTime, /* xCurrentTime */ cfGetLastError, /* xGetLastError */ 0, /* xCurrentTimeInt64 */ }; if( objc!=2 && objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "ENABLE ?DEFAULT?"); return TCL_ERROR; } if( Tcl_GetBooleanFromObj(interp, objv[1], &isEnable) ){ return TCL_ERROR; } if( objc==3 && Tcl_GetBooleanFromObj(interp, objv[2], &isDefault) ){ return TCL_ERROR; } if( (isEnable && crashVfs.pAppData) || (!isEnable && !crashVfs.pAppData) ){ return TCL_OK; } if( crashVfs.pAppData==0 ){ sqlite3_vfs *pOriginalVfs = sqlite3_vfs_find(0); crashVfs.mxPathname = pOriginalVfs->mxPathname; crashVfs.pAppData = (void *)pOriginalVfs; crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile; sqlite3_vfs_register(&crashVfs, isDefault); }else{ crashVfs.pAppData = 0; sqlite3_vfs_unregister(&crashVfs); } return TCL_OK; } |
︙ | ︙ | |||
967 968 969 970 971 972 973 | if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){ return TCL_ERROR; } devsym_register(iDc, iSectorSize); return TCL_OK; | | > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){ return TCL_ERROR; } devsym_register(iDc, iSectorSize); return TCL_OK; } /* ** tclcmd: sqlite3_crash_on_write N */ static int SQLITE_TCLAPI writeCrashObjCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ void devsym_crash_on_write(int); int nWrite = 0; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "NWRITE"); return TCL_ERROR; } if( Tcl_GetIntFromObj(interp, objv[1], &nWrite) ){ return TCL_ERROR; } devsym_crash_on_write(nWrite); return TCL_OK; } /* ** tclcmd: unregister_devsim */ static int SQLITE_TCLAPI dsUnregisterObjCmd( void * clientData, |
︙ | ︙ | |||
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 | */ int Sqlitetest6_Init(Tcl_Interp *interp){ #ifndef SQLITE_OMIT_DISKIO Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0); #endif return TCL_OK; } #endif /* SQLITE_TEST */ | > | 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | */ int Sqlitetest6_Init(Tcl_Interp *interp){ #ifndef SQLITE_OMIT_DISKIO Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_crash_on_write", writeCrashObjCmd,0,0); Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0); #endif return TCL_OK; } #endif /* SQLITE_TEST */ |
Changes to src/test8.c.
︙ | ︙ | |||
893 894 895 896 897 898 899 | case SQLITE_INDEX_CONSTRAINT_LIKE: zOp = "like"; break; case SQLITE_INDEX_CONSTRAINT_GLOB: zOp = "glob"; break; case SQLITE_INDEX_CONSTRAINT_REGEXP: zOp = "regexp"; break; } | > | | | | | | | < | | | > | 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 | case SQLITE_INDEX_CONSTRAINT_LIKE: zOp = "like"; break; case SQLITE_INDEX_CONSTRAINT_GLOB: zOp = "glob"; break; case SQLITE_INDEX_CONSTRAINT_REGEXP: zOp = "regexp"; break; } if( zOp ){ if( zOp[0]=='L' ){ zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')", zSep, zNewCol); } else { zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zNewCol, zOp); } string_concat(&zQuery, zNew, 1, &rc); zSep = "AND"; pUsage->argvIndex = ++nArg; pUsage->omit = 1; } } } /* If there is only one term in the ORDER BY clause, and it is ** on a column that this virtual table has an index for, then consume ** the ORDER BY clause. */ |
︙ | ︙ |
Changes to src/test_bestindex.c.
︙ | ︙ | |||
410 411 412 413 414 415 416 417 418 419 420 421 422 423 | zOp = "match"; break; case SQLITE_INDEX_CONSTRAINT_LIKE: zOp = "like"; break; case SQLITE_INDEX_CONSTRAINT_GLOB: zOp = "glob"; break; case SQLITE_INDEX_CONSTRAINT_REGEXP: zOp = "regexp"; break; } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1)); | > > > > > > > > > > | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | zOp = "match"; break; case SQLITE_INDEX_CONSTRAINT_LIKE: zOp = "like"; break; case SQLITE_INDEX_CONSTRAINT_GLOB: zOp = "glob"; break; case SQLITE_INDEX_CONSTRAINT_REGEXP: zOp = "regexp"; break; case SQLITE_INDEX_CONSTRAINT_NE: zOp = "ne"; break; case SQLITE_INDEX_CONSTRAINT_ISNOT: zOp = "isnot"; break; case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: zOp = "isnotnull"; break; case SQLITE_INDEX_CONSTRAINT_ISNULL: zOp = "isnull"; break; case SQLITE_INDEX_CONSTRAINT_IS: zOp = "is"; break; } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1)); |
︙ | ︙ |
Changes to src/test_blob.c.
︙ | ︙ | |||
110 111 112 113 114 115 116 | const char *zTable; const char *zColumn; Tcl_WideInt iRowid; int flags; const char *zVarname; int nVarname; | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | const char *zTable; const char *zColumn; Tcl_WideInt iRowid; int flags; const char *zVarname; int nVarname; sqlite3_blob *pBlob = (sqlite3_blob*)&flags; /* Non-zero initialization */ int rc; if( objc!=8 ){ const char *zUsage = "DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME"; Tcl_WrongNumArgs(interp, 1, objv, zUsage); return TCL_ERROR; } |
︙ | ︙ | |||
235 236 237 238 239 240 241 | if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte) ){ return TCL_ERROR; } if( nByte>0 ){ | | > > > > | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte) ){ return TCL_ERROR; } if( nByte>0 ){ zBuf = (unsigned char *)Tcl_AttemptAlloc(nByte); if( zBuf==0 ){ Tcl_AppendResult(interp, "out of memory in " __FILE__, 0); return TCL_ERROR; } } rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset); if( rc==SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte)); }else{ Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); } |
︙ | ︙ |
Changes to src/test_config.c.
︙ | ︙ | |||
264 265 266 267 268 269 270 | #ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "between_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "between_opt", "1", TCL_GLOBAL_ONLY); #endif | | | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | #ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "between_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "between_opt", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_UNTESTABLE Tcl_SetVar2(interp, "sqlite_options", "builtin_test", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "builtin_test", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_BLOB_LITERAL Tcl_SetVar2(interp, "sqlite_options", "bloblit", "0", TCL_GLOBAL_ONLY); |
︙ | ︙ | |||
556 557 558 559 560 561 562 563 564 565 566 567 568 569 | Tcl_SetVar2(interp, "sqlite_options", "stat4", "0", TCL_GLOBAL_ONLY); #endif #if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4) Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_ENABLE_STMT_SCANSTATUS Tcl_SetVar2(interp, "sqlite_options", "scanstatus", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "scanstatus", "0", TCL_GLOBAL_ONLY); #endif | > > > > > > | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | Tcl_SetVar2(interp, "sqlite_options", "stat4", "0", TCL_GLOBAL_ONLY); #endif #if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4) Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY); #endif #if defined(SQLITE_ENABLE_STMTVTAB) && !defined(SQLITE_OMIT_VIRTUALTABLE) Tcl_SetVar2(interp, "sqlite_options", "stmtvtab", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "stmtvtab", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_ENABLE_STMT_SCANSTATUS Tcl_SetVar2(interp, "sqlite_options", "scanstatus", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "scanstatus", "0", TCL_GLOBAL_ONLY); #endif |
︙ | ︙ | |||
709 710 711 712 713 714 715 716 717 718 719 720 721 722 | #endif #ifdef SQLITE_ENABLE_SQLLOG Tcl_SetVar2(interp, "sqlite_options", "sqllog", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "sqllog", "0", TCL_GLOBAL_ONLY); #endif #define LINKVAR(x) { \ static const int cv_ ## x = SQLITE_ ## x; \ Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \ TCL_LINK_INT | TCL_LINK_READ_ONLY); } LINKVAR( MAX_LENGTH ); | > > > > > > | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 | #endif #ifdef SQLITE_ENABLE_SQLLOG Tcl_SetVar2(interp, "sqlite_options", "sqllog", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "sqllog", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_ENABLE_URI_00_ERROR Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "0", TCL_GLOBAL_ONLY); #endif #define LINKVAR(x) { \ static const int cv_ ## x = SQLITE_ ## x; \ Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \ TCL_LINK_INT | TCL_LINK_READ_ONLY); } LINKVAR( MAX_LENGTH ); |
︙ | ︙ | |||
730 731 732 733 734 735 736 737 738 739 740 741 742 743 | LINKVAR( MAX_PAGE_SIZE ); LINKVAR( MAX_PAGE_COUNT ); LINKVAR( MAX_LIKE_PATTERN_LENGTH ); LINKVAR( MAX_TRIGGER_DEPTH ); LINKVAR( DEFAULT_CACHE_SIZE ); LINKVAR( DEFAULT_PAGE_SIZE ); LINKVAR( DEFAULT_FILE_FORMAT ); LINKVAR( MAX_ATTACHED ); LINKVAR( MAX_DEFAULT_PAGE_SIZE ); LINKVAR( MAX_WORKER_THREADS ); { static const int cv_TEMP_STORE = SQLITE_TEMP_STORE; Tcl_LinkVar(interp, "TEMP_STORE", (char *)&(cv_TEMP_STORE), | > > | 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 | LINKVAR( MAX_PAGE_SIZE ); LINKVAR( MAX_PAGE_COUNT ); LINKVAR( MAX_LIKE_PATTERN_LENGTH ); LINKVAR( MAX_TRIGGER_DEPTH ); LINKVAR( DEFAULT_CACHE_SIZE ); LINKVAR( DEFAULT_PAGE_SIZE ); LINKVAR( DEFAULT_FILE_FORMAT ); LINKVAR( DEFAULT_SYNCHRONOUS ); LINKVAR( DEFAULT_WAL_SYNCHRONOUS ); LINKVAR( MAX_ATTACHED ); LINKVAR( MAX_DEFAULT_PAGE_SIZE ); LINKVAR( MAX_WORKER_THREADS ); { static const int cv_TEMP_STORE = SQLITE_TEMP_STORE; Tcl_LinkVar(interp, "TEMP_STORE", (char *)&(cv_TEMP_STORE), |
︙ | ︙ |
Changes to src/test_delete.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 | ** * The journal file. ** * The wal file. ** * The SQLITE_ENABLE_8_3_NAMES version of the db, journal or wal files. ** * Files created by the test_multiplex.c module to extend any of the ** above. */ | | | < < | | | | < | > > > > | > > > > > > > > > > > < > < | | | > | | | | 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 | ** * The journal file. ** * The wal file. ** * The SQLITE_ENABLE_8_3_NAMES version of the db, journal or wal files. ** * Files created by the test_multiplex.c module to extend any of the ** above. */ #ifndef SQLITE_OS_WIN # include <unistd.h> # include <errno.h> #endif #include <string.h> #include <assert.h> #include "sqlite3.h" /* The following #defines are copied from test_multiplex.c */ #ifndef MX_CHUNK_NUMBER # define MX_CHUNK_NUMBER 299 #endif #ifndef SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET # define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400 #endif #ifndef SQLITE_MULTIPLEX_WAL_8_3_OFFSET # define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700 #endif /* ** This routine is a copy of (most of) the code from SQLite function ** sqlite3FileSuffix3(). It modifies the filename in buffer z in the ** same way as SQLite does when in 8.3 filenames mode. */ static void sqlite3Delete83Name(char *z){ int i, sz; sz = (int)strlen(z); for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} if( z[i]=='.' && (sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4); } /* ** zFile is a filename. Assuming no error occurs, if this file exists, ** set *pbExists to true and unlink it. Or, if the file does not exist, ** set *pbExists to false before returning. ** ** If an error occurs, non-zero is returned. Or, if no error occurs, zero. */ static int sqlite3DeleteUnlinkIfExists( sqlite3_vfs *pVfs, const char *zFile, int *pbExists ){ int rc = SQLITE_ERROR; #if SQLITE_OS_WIN if( pVfs ){ if( pbExists ) *pbExists = 1; rc = pVfs->xDelete(pVfs, zFile, 0); if( rc==SQLITE_IOERR_DELETE_NOENT ){ if( pbExists ) *pbExists = 0; rc = SQLITE_OK; } } #else assert( pVfs==0 ); rc = access(zFile, F_OK); if( rc ){ if( errno==ENOENT ){ if( pbExists ) *pbExists = 0; rc = SQLITE_OK; } }else{ if( pbExists ) *pbExists = 1; rc = unlink(zFile); } #endif return rc; } /* ** Delete the database file identified by the string argument passed to this ** function. The string must contain a filename, not an SQLite URI. */ SQLITE_API int sqlite3_delete_database( const char *zFile /* File to delete */ ){ char *zBuf; /* Buffer to sprintf() filenames to */ int nBuf; /* Size of buffer in bytes */ int rc = 0; /* System error code */ int i; /* Iterate through azFmt[] and aMFile[] */ |
︙ | ︙ | |||
98 99 100 101 102 103 104 105 106 107 | { "%s%03d", 0, 0 }, { "%s-journal%03d", 0, 0 }, { "%s-wal%03d", 0, 0 }, { "%s%03d", 0, 1 }, { "%s-journal%03d", SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET, 1 }, { "%s-wal%03d", SQLITE_MULTIPLEX_WAL_8_3_OFFSET, 1 }, }; /* Allocate a buffer large enough for any of the files that need to be ** deleted. */ | > > > > > > | | | | | 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 | { "%s%03d", 0, 0 }, { "%s-journal%03d", 0, 0 }, { "%s-wal%03d", 0, 0 }, { "%s%03d", 0, 1 }, { "%s-journal%03d", SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET, 1 }, { "%s-wal%03d", SQLITE_MULTIPLEX_WAL_8_3_OFFSET, 1 }, }; #ifdef SQLITE_OS_WIN sqlite3_vfs *pVfs = sqlite3_vfs_find("win32"); #else sqlite3_vfs *pVfs = 0; #endif /* Allocate a buffer large enough for any of the files that need to be ** deleted. */ nBuf = (int)strlen(zFile) + 100; zBuf = (char*)sqlite3_malloc(nBuf); if( zBuf==0 ) return SQLITE_NOMEM; /* Delete both the regular and 8.3 filenames versions of the database, ** journal, wal and shm files. */ for(i=0; rc==0 && i<sizeof(azFmt)/sizeof(azFmt[0]); i++){ sqlite3_snprintf(nBuf, zBuf, azFmt[i], zFile); rc = sqlite3DeleteUnlinkIfExists(pVfs, zBuf, 0); if( rc==0 && i!=0 ){ sqlite3Delete83Name(zBuf); rc = sqlite3DeleteUnlinkIfExists(pVfs, zBuf, 0); } } /* Delete any multiplexor files */ for(i=0; rc==0 && i<sizeof(aMFile)/sizeof(aMFile[0]); i++){ struct MFile *p = &aMFile[i]; int iChunk; for(iChunk=1; iChunk<=MX_CHUNK_NUMBER; iChunk++){ int bExists; sqlite3_snprintf(nBuf, zBuf, p->zFmt, zFile, iChunk+p->iOffset); if( p->b83 ) sqlite3Delete83Name(zBuf); rc = sqlite3DeleteUnlinkIfExists(pVfs, zBuf, &bExists); if( bExists==0 || rc!=0 ) break; } } sqlite3_free(zBuf); return (rc ? SQLITE_ERROR : SQLITE_OK); } |
Changes to src/test_devsym.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 | */ #define DEVSYM_MAX_PATHNAME 512 /* ** Name used to identify this VFS. */ #define DEVSYM_VFS_NAME "devsym" typedef struct devsym_file devsym_file; struct devsym_file { sqlite3_file base; sqlite3_file *pReal; }; | > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | */ #define DEVSYM_MAX_PATHNAME 512 /* ** Name used to identify this VFS. */ #define DEVSYM_VFS_NAME "devsym" #define WRITECRASH_NAME "writecrash" typedef struct devsym_file devsym_file; struct devsym_file { sqlite3_file base; sqlite3_file *pReal; }; |
︙ | ︙ | |||
68 69 70 71 72 73 74 | static void (*devsymDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); static void devsymDlClose(sqlite3_vfs*, void*); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut); static int devsymSleep(sqlite3_vfs*, int microseconds); static int devsymCurrentTime(sqlite3_vfs*, double*); | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | static void (*devsymDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); static void devsymDlClose(sqlite3_vfs*, void*); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut); static int devsymSleep(sqlite3_vfs*, int microseconds); static int devsymCurrentTime(sqlite3_vfs*, double*); struct DevsymGlobal { sqlite3_vfs *pVfs; int iDeviceChar; int iSectorSize; int nWriteCrash; }; struct DevsymGlobal g = {0, 0, 512, 0}; /* ** Close an devsym-file. */ static int devsymClose(sqlite3_file *pFile){ devsym_file *p = (devsym_file *)pFile; sqlite3OsClose(p->pReal); |
︙ | ︙ | |||
267 268 269 270 271 272 273 274 275 276 277 278 279 280 | static int devsymOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ int rc; devsym_file *p = (devsym_file *)pFile; p->pReal = (sqlite3_file *)&p[1]; rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags); if( p->pReal->pMethods ){ pFile->pMethods = &devsym_io_methods; } | > > > > > > > > > > > > > > > > > > > > | 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 | static int devsymOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ static sqlite3_io_methods devsym_io_methods = { 2, /* iVersion */ devsymClose, /* xClose */ devsymRead, /* xRead */ devsymWrite, /* xWrite */ devsymTruncate, /* xTruncate */ devsymSync, /* xSync */ devsymFileSize, /* xFileSize */ devsymLock, /* xLock */ devsymUnlock, /* xUnlock */ devsymCheckReservedLock, /* xCheckReservedLock */ devsymFileControl, /* xFileControl */ devsymSectorSize, /* xSectorSize */ devsymDeviceCharacteristics, /* xDeviceCharacteristics */ devsymShmMap, /* xShmMap */ devsymShmLock, /* xShmLock */ devsymShmBarrier, /* xShmBarrier */ devsymShmUnmap /* xShmUnmap */ }; int rc; devsym_file *p = (devsym_file *)pFile; p->pReal = (sqlite3_file *)&p[1]; rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags); if( p->pReal->pMethods ){ pFile->pMethods = &devsym_io_methods; } |
︙ | ︙ | |||
368 369 370 371 372 373 374 | /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ return g.pVfs->xCurrentTime(g.pVfs, pTimeOut); } | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ return g.pVfs->xCurrentTime(g.pVfs, pTimeOut); } /* ** Return the sector-size in bytes for an writecrash-file. */ static int writecrashSectorSize(sqlite3_file *pFile){ devsym_file *p = (devsym_file *)pFile; return sqlite3OsSectorSize(p->pReal); } /* ** Return the device characteristic flags supported by an writecrash-file. */ static int writecrashDeviceCharacteristics(sqlite3_file *pFile){ devsym_file *p = (devsym_file *)pFile; return sqlite3OsDeviceCharacteristics(p->pReal); } /* ** Write data to an writecrash-file. */ static int writecrashWrite( sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst ){ devsym_file *p = (devsym_file *)pFile; if( g.nWriteCrash>0 ){ g.nWriteCrash--; if( g.nWriteCrash==0 ) abort(); } return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); } /* ** Open an writecrash file handle. */ static int writecrashOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ static sqlite3_io_methods writecrash_io_methods = { 2, /* iVersion */ devsymClose, /* xClose */ devsymRead, /* xRead */ writecrashWrite, /* xWrite */ devsymTruncate, /* xTruncate */ devsymSync, /* xSync */ devsymFileSize, /* xFileSize */ devsymLock, /* xLock */ devsymUnlock, /* xUnlock */ devsymCheckReservedLock, /* xCheckReservedLock */ devsymFileControl, /* xFileControl */ writecrashSectorSize, /* xSectorSize */ writecrashDeviceCharacteristics, /* xDeviceCharacteristics */ devsymShmMap, /* xShmMap */ devsymShmLock, /* xShmLock */ devsymShmBarrier, /* xShmBarrier */ devsymShmUnmap /* xShmUnmap */ }; int rc; devsym_file *p = (devsym_file *)pFile; p->pReal = (sqlite3_file *)&p[1]; rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags); if( p->pReal->pMethods ){ pFile->pMethods = &writecrash_io_methods; } return rc; } static sqlite3_vfs devsym_vfs = { 2, /* iVersion */ sizeof(devsym_file), /* szOsFile */ DEVSYM_MAX_PATHNAME, /* mxPathname */ 0, /* pNext */ DEVSYM_VFS_NAME, /* zName */ 0, /* pAppData */ devsymOpen, /* xOpen */ devsymDelete, /* xDelete */ devsymAccess, /* xAccess */ devsymFullPathname, /* xFullPathname */ #ifndef SQLITE_OMIT_LOAD_EXTENSION devsymDlOpen, /* xDlOpen */ devsymDlError, /* xDlError */ devsymDlSym, /* xDlSym */ devsymDlClose, /* xDlClose */ #else 0, /* xDlOpen */ 0, /* xDlError */ 0, /* xDlSym */ 0, /* xDlClose */ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ devsymRandomness, /* xRandomness */ devsymSleep, /* xSleep */ devsymCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ 0 /* xCurrentTimeInt64 */ }; static sqlite3_vfs writecrash_vfs = { 2, /* iVersion */ sizeof(devsym_file), /* szOsFile */ DEVSYM_MAX_PATHNAME, /* mxPathname */ 0, /* pNext */ WRITECRASH_NAME, /* zName */ 0, /* pAppData */ writecrashOpen, /* xOpen */ devsymDelete, /* xDelete */ devsymAccess, /* xAccess */ devsymFullPathname, /* xFullPathname */ #ifndef SQLITE_OMIT_LOAD_EXTENSION devsymDlOpen, /* xDlOpen */ devsymDlError, /* xDlError */ devsymDlSym, /* xDlSym */ devsymDlClose, /* xDlClose */ #else 0, /* xDlOpen */ 0, /* xDlError */ 0, /* xDlSym */ 0, /* xDlClose */ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ devsymRandomness, /* xRandomness */ devsymSleep, /* xSleep */ devsymCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ 0 /* xCurrentTimeInt64 */ }; /* ** This procedure registers the devsym vfs with SQLite. If the argument is ** true, the devsym vfs becomes the new default vfs. It is the only publicly ** available function in this file. */ void devsym_register(int iDeviceChar, int iSectorSize){ if( g.pVfs==0 ){ g.pVfs = sqlite3_vfs_find(0); devsym_vfs.szOsFile += g.pVfs->szOsFile; writecrash_vfs.szOsFile += g.pVfs->szOsFile; sqlite3_vfs_register(&devsym_vfs, 0); sqlite3_vfs_register(&writecrash_vfs, 0); } if( iDeviceChar>=0 ){ g.iDeviceChar = iDeviceChar; }else{ g.iDeviceChar = 0; } if( iSectorSize>=0 ){ g.iSectorSize = iSectorSize; }else{ g.iSectorSize = 512; } } void devsym_unregister(){ sqlite3_vfs_unregister(&devsym_vfs); g.pVfs = 0; g.iDeviceChar = 0; g.iSectorSize = 0; } void devsym_crash_on_write(int nWrite){ if( g.pVfs==0 ){ g.pVfs = sqlite3_vfs_find(0); devsym_vfs.szOsFile += g.pVfs->szOsFile; writecrash_vfs.szOsFile += g.pVfs->szOsFile; sqlite3_vfs_register(&devsym_vfs, 0); sqlite3_vfs_register(&writecrash_vfs, 0); } g.nWriteCrash = nWrite; } #endif |
Changes to src/test_fs.c.
︙ | ︙ | |||
541 542 543 544 545 546 547 548 549 550 551 552 553 554 | for(i=nPrefix; zQuery[i]; i++){ if( zQuery[i]==aWild[0] || zQuery[i]==aWild[1] ) break; if( zQuery[i]=='/' ) nDir = i; } zDir = zQuery; } } sqlite3_bind_text(pCsr->pStmt, 1, zDir, nDir, SQLITE_TRANSIENT); sqlite3_bind_text(pCsr->pStmt, 2, zRoot, nRoot, SQLITE_TRANSIENT); sqlite3_bind_text(pCsr->pStmt, 3, zPrefix, nPrefix, SQLITE_TRANSIENT); #if SQLITE_OS_WIN sqlite3_free(zPrefix); | > | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 | for(i=nPrefix; zQuery[i]; i++){ if( zQuery[i]==aWild[0] || zQuery[i]==aWild[1] ) break; if( zQuery[i]=='/' ) nDir = i; } zDir = zQuery; } } if( nDir==0 ) nDir = 1; sqlite3_bind_text(pCsr->pStmt, 1, zDir, nDir, SQLITE_TRANSIENT); sqlite3_bind_text(pCsr->pStmt, 2, zRoot, nRoot, SQLITE_TRANSIENT); sqlite3_bind_text(pCsr->pStmt, 3, zPrefix, nPrefix, SQLITE_TRANSIENT); #if SQLITE_OS_WIN sqlite3_free(zPrefix); |
︙ | ︙ |
Changes to src/test_func.c.
︙ | ︙ | |||
151 152 153 154 155 156 157 | } /* ** The following aggregate function, test_agg_errmsg16(), takes zero ** arguments. It returns the text value returned by the sqlite3_errmsg16() ** API function. */ | | | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | } /* ** The following aggregate function, test_agg_errmsg16(), takes zero ** arguments. It returns the text value returned by the sqlite3_errmsg16() ** API function. */ #ifndef SQLITE_UNTESTABLE void sqlite3BeginBenignMalloc(void); void sqlite3EndBenignMalloc(void); #else #define sqlite3BeginBenignMalloc() #define sqlite3EndBenignMalloc() #endif static void test_agg_errmsg16_step(sqlite3_context *a, int b,sqlite3_value **c){ |
︙ | ︙ |
Changes to src/test_journal.c.
︙ | ︙ | |||
553 554 555 556 557 558 559 560 561 562 563 564 565 566 | ** it needs to fill in the non-locking-region part of the original ** pending-byte page. */ }else{ u32 pgno = (u32)(iOfst/p->nPagesize + 1); assert( (iAmt==1||iAmt==(int)p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 ); assert( pgno<=p->nPage || p->nSync>0 ); assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) ); } } rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); if( (p->flags&SQLITE_OPEN_MAIN_JOURNAL) && iAmt==12 ){ | > > > | 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 | ** it needs to fill in the non-locking-region part of the original ** pending-byte page. */ }else{ u32 pgno = (u32)(iOfst/p->nPagesize + 1); assert( (iAmt==1||iAmt==(int)p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 ); /* The following assert() statements may fail if this layer is used ** with a connection in "PRAGMA synchronous=off" mode. If they ** fail with sync=normal or sync=full, this may indicate problem. */ assert( pgno<=p->nPage || p->nSync>0 ); assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) ); } } rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); if( (p->flags&SQLITE_OPEN_MAIN_JOURNAL) && iAmt==12 ){ |
︙ | ︙ |
Changes to src/test_malloc.c.
︙ | ︙ | |||
883 884 885 886 887 888 889 | break; } } return TCL_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 883 884 885 886 887 888 889 890 891 892 893 894 895 896 | break; } } return TCL_OK; } /* ** Usage: sqlite3_config_pagecache SIZE N ** ** Set the page-cache memory buffer using SQLITE_CONFIG_PAGECACHE. ** The buffer is static and is of limited size. N might be ** adjusted downward as needed to accommodate the requested size. ** The revised value of N is returned. |
︙ | ︙ | |||
1534 1535 1536 1537 1538 1539 1540 | { "sqlite3_memdebug_backtrace", test_memdebug_backtrace ,0 }, { "sqlite3_memdebug_dump", test_memdebug_dump ,0 }, { "sqlite3_memdebug_fail", test_memdebug_fail ,0 }, { "sqlite3_memdebug_pending", test_memdebug_pending ,0 }, { "sqlite3_memdebug_settitle", test_memdebug_settitle ,0 }, { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 }, { "sqlite3_memdebug_log", test_memdebug_log ,0 }, | < | 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 | { "sqlite3_memdebug_backtrace", test_memdebug_backtrace ,0 }, { "sqlite3_memdebug_dump", test_memdebug_dump ,0 }, { "sqlite3_memdebug_fail", test_memdebug_fail ,0 }, { "sqlite3_memdebug_pending", test_memdebug_pending ,0 }, { "sqlite3_memdebug_settitle", test_memdebug_settitle ,0 }, { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 }, { "sqlite3_memdebug_log", test_memdebug_log ,0 }, { "sqlite3_config_pagecache", test_config_pagecache ,0 }, { "sqlite3_config_alt_pcache", test_alt_pcache ,0 }, { "sqlite3_status", test_status ,0 }, { "sqlite3_db_status", test_db_status ,0 }, { "install_malloc_faultsim", test_install_malloc_faultsim ,0 }, { "sqlite3_config_heap", test_config_heap ,0 }, { "sqlite3_config_heap_size", test_config_heap_size ,0 }, |
︙ | ︙ |
Changes to src/test_multiplex.c.
︙ | ︙ | |||
64 65 66 67 68 69 70 | ** the multiplex VFS shim can be built as a loadable ** module. */ #define UNUSED_PARAMETER(x) (void)(x) #define MAX_PAGE_SIZE 0x10000 #define DEFAULT_SECTOR_SIZE 0x1000 | < < < < < < < < < < < < < | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | ** the multiplex VFS shim can be built as a loadable ** module. */ #define UNUSED_PARAMETER(x) (void)(x) #define MAX_PAGE_SIZE 0x10000 #define DEFAULT_SECTOR_SIZE 0x1000 /* Maximum chunk number */ #define MX_CHUNK_NUMBER 299 /* First chunk for rollback journal files */ #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400 #define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700 |
︙ | ︙ | |||
135 136 137 138 139 140 141 | int nReal; /* Number of chunks */ char *zName; /* Base filename of this group */ int nName; /* Length of base filename */ int flags; /* Flags used for original opening */ unsigned int szChunk; /* Chunk size used for this group */ unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */ unsigned char bTruncate; /* TRUE to enable truncation of databases */ | < | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | int nReal; /* Number of chunks */ char *zName; /* Base filename of this group */ int nName; /* Length of base filename */ int flags; /* Flags used for original opening */ unsigned int szChunk; /* Chunk size used for this group */ unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */ unsigned char bTruncate; /* TRUE to enable truncation of databases */ }; /* ** An instance of the following object represents each open connection ** to a file that is multiplex'ed. This object is a ** subclass of sqlite3_file. The sqlite3_file object for the underlying ** VFS is appended to this structure. |
︙ | ︙ | |||
183 184 185 186 187 188 189 | */ sqlite3_io_methods sIoMethodsV1; sqlite3_io_methods sIoMethodsV2; /* True when this shim has been initialized. */ int isInitialized; | < < < < < < < < < < < < < < < < < < < | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | */ sqlite3_io_methods sIoMethodsV1; sqlite3_io_methods sIoMethodsV2; /* True when this shim has been initialized. */ int isInitialized; } gMultiplex; /************************* Utility Routines *********************************/ /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. ** ** The value returned will never be negative. Nor will it ever be greater ** than the actual length of the string. For very long strings (greater ** than 1GiB) the value returned might be less than the true string length. |
︙ | ︙ | |||
515 516 517 518 519 520 521 | UNUSED_PARAMETER(pVfs); memset(pConn, 0, pVfs->szOsFile); assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) ); /* We need to create a group structure and manage ** access to this group of files. */ | < | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 | UNUSED_PARAMETER(pVfs); memset(pConn, 0, pVfs->szOsFile); assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) ); /* We need to create a group structure and manage ** access to this group of files. */ pMultiplexOpen = (multiplexConn*)pConn; if( rc==SQLITE_OK ){ /* allocate space for group */ nName = zName ? multiplexStrlen30(zName) : 0; sz = sizeof(multiplexGroup) /* multiplexGroup */ + nName + 1; /* zName */ |
︙ | ︙ | |||
622 623 624 625 626 627 628 | if( rc==SQLITE_OK ){ if( pSubOpen->pMethods->iVersion==1 ){ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; }else{ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; } | < < < < < | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | if( rc==SQLITE_OK ){ if( pSubOpen->pMethods->iVersion==1 ){ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; }else{ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; } }else{ multiplexFreeComponents(pGroup); sqlite3_free(pGroup); } } sqlite3_free(zToFree); return rc; } /* ** This is the xDelete method used for the "multiplex" VFS. ** It attempts to delete the filename specified. |
︙ | ︙ | |||
734 735 736 737 738 739 740 | ** The group structure for this file is unlinked from ** our list of groups and freed. */ static int multiplexClose(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; | < < < < < < < < < | 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 | ** The group structure for this file is unlinked from ** our list of groups and freed. */ static int multiplexClose(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexFreeComponents(pGroup); sqlite3_free(pGroup); return rc; } /* Pass xRead requests thru to the original VFS after ** determining the correct chunk to operate on. ** Break up reads across chunk boundaries. */ |
︙ | ︙ | |||
841 842 843 844 845 846 847 | ** determining the correct chunk to operate on. Delete any ** chunks above the truncate mark. */ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; | < | 793 794 795 796 797 798 799 800 801 802 803 804 805 806 | ** determining the correct chunk to operate on. Delete any ** chunks above the truncate mark. */ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0); if( pSubOpen==0 ){ rc = SQLITE_IOERR_TRUNCATE; }else{ rc = pSubOpen->pMethods->xTruncate(pSubOpen, size); } |
︙ | ︙ | |||
873 874 875 876 877 878 879 | pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0); if( pSubOpen ){ rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk); } } if( rc ) rc = SQLITE_IOERR_TRUNCATE; } | < < < < < | 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 | pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0); if( pSubOpen ){ rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk); } } if( rc ) rc = SQLITE_IOERR_TRUNCATE; } return rc; } /* Pass xSync requests through to the original VFS without change */ static int multiplexSync(sqlite3_file *pConn, int flags){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int i; for(i=0; i<pGroup->nReal; i++){ sqlite3_file *pSubOpen = pGroup->aReal[i].p; if( pSubOpen ){ int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); if( rc2!=SQLITE_OK ) rc = rc2; } } return rc; } /* Pass xFileSize requests through to the original VFS. ** Aggregate the size of all the chunks before returning. */ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int i; if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0); if( pSubOpen==0 ){ rc = SQLITE_IOERR_FSTAT; }else{ rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize); } }else{ *pSize = 0; for(i=0; rc==SQLITE_OK; i++){ sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc); if( sz==0 ) break; *pSize = i*(sqlite3_int64)pGroup->szChunk + sz; } } return rc; } /* Pass xLock requests through to the original VFS unchanged. */ static int multiplexLock(sqlite3_file *pConn, int lock){ multiplexConn *p = (multiplexConn*)pConn; |
︙ | ︙ | |||
1147 1148 1149 1150 1151 1152 1153 | */ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ sqlite3_vfs *pOrigVfs; if( gMultiplex.isInitialized ) return SQLITE_MISUSE; pOrigVfs = sqlite3_vfs_find(zOrigVfsName); if( pOrigVfs==0 ) return SQLITE_ERROR; assert( pOrigVfs!=&gMultiplex.sThisVfs ); | < < < < < | 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 | */ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ sqlite3_vfs *pOrigVfs; if( gMultiplex.isInitialized ) return SQLITE_MISUSE; pOrigVfs = sqlite3_vfs_find(zOrigVfsName); if( pOrigVfs==0 ) return SQLITE_ERROR; assert( pOrigVfs!=&gMultiplex.sThisVfs ); gMultiplex.isInitialized = 1; gMultiplex.pOrigVfs = pOrigVfs; gMultiplex.sThisVfs = *pOrigVfs; gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME; gMultiplex.sThisVfs.xOpen = multiplexOpen; gMultiplex.sThisVfs.xDelete = multiplexDelete; |
︙ | ︙ | |||
1210 1211 1212 1213 1214 1215 1216 | ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while ** shutting down in order to free all remaining multiplex groups. */ int sqlite3_multiplex_shutdown(int eForce){ int rc = SQLITE_OK; if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; | < < < < < < < | 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 | ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while ** shutting down in order to free all remaining multiplex groups. */ int sqlite3_multiplex_shutdown(int eForce){ int rc = SQLITE_OK; if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; gMultiplex.isInitialized = 0; sqlite3_vfs_unregister(&gMultiplex.sThisVfs); memset(&gMultiplex, 0, sizeof(gMultiplex)); return rc; } /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST |
︙ | ︙ | |||
1295 1296 1297 1298 1299 1300 1301 | /* Call sqlite3_multiplex_shutdown() */ rc = sqlite3_multiplex_shutdown(objc==2); Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 | /* Call sqlite3_multiplex_shutdown() */ rc = sqlite3_multiplex_shutdown(objc==2); Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } /* ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE? */ static int SQLITE_TCLAPI test_multiplex_control( ClientData cd, Tcl_Interp *interp, int objc, |
︙ | ︙ | |||
1424 1425 1426 1427 1428 1429 1430 | int Sqlitemultiplex_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; } aCmd[] = { { "sqlite3_multiplex_initialize", test_multiplex_initialize }, { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, | < | 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 | int Sqlitemultiplex_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; } aCmd[] = { { "sqlite3_multiplex_initialize", test_multiplex_initialize }, { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, { "sqlite3_multiplex_control", test_multiplex_control }, }; int i; for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); } return TCL_OK; } #endif |
Changes to src/test_sqllog.c.
︙ | ︙ | |||
309 310 311 312 313 314 315 | if( zInit==0 ){ int rc; sqlite3 *copy = 0; int iDb; /* Generate a file-name to use for the copy of this database */ iDb = sqllogglobal.iNextDb++; | | | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | if( zInit==0 ){ int rc; sqlite3 *copy = 0; int iDb; /* Generate a file-name to use for the copy of this database */ iDb = sqllogglobal.iNextDb++; zInit = sqlite3_mprintf("%s_%02d.db", sqllogglobal.zPrefix, iDb); /* Create the backup */ assert( sqllogglobal.bRec==0 ); sqllogglobal.bRec = 1; rc = sqlite3_open(zInit, ©); if( rc==SQLITE_OK ){ sqlite3_backup *pBak; |
︙ | ︙ | |||
372 373 374 375 376 377 378 | /* If it is still NULL, have global.zPrefix point to a copy of ** environment variable $ENVIRONMENT_VARIABLE1_NAME. */ if( sqllogglobal.zPrefix[0]==0 ){ FILE *fd; char *zVar = getenv(ENVIRONMENT_VARIABLE1_NAME); if( zVar==0 || strlen(zVar)+10>=(sizeof(sqllogglobal.zPrefix)) ) return; sqlite3_snprintf(sizeof(sqllogglobal.zPrefix), sqllogglobal.zPrefix, | | | | 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 | /* If it is still NULL, have global.zPrefix point to a copy of ** environment variable $ENVIRONMENT_VARIABLE1_NAME. */ if( sqllogglobal.zPrefix[0]==0 ){ FILE *fd; char *zVar = getenv(ENVIRONMENT_VARIABLE1_NAME); if( zVar==0 || strlen(zVar)+10>=(sizeof(sqllogglobal.zPrefix)) ) return; sqlite3_snprintf(sizeof(sqllogglobal.zPrefix), sqllogglobal.zPrefix, "%s/sqllog_%05d", zVar, getProcessId()); sqlite3_snprintf(sizeof(sqllogglobal.zIdx), sqllogglobal.zIdx, "%s.idx", sqllogglobal.zPrefix); if( getenv(ENVIRONMENT_VARIABLE2_NAME) ){ sqllogglobal.bReuse = atoi(getenv(ENVIRONMENT_VARIABLE2_NAME)); } fd = fopen(sqllogglobal.zIdx, "w"); if( fd ) fclose(fd); } /* Open the log file */ zLog = sqlite3_mprintf("%s_%05d.sql", sqllogglobal.zPrefix, p->iLog); p->fd = fopen(zLog, "w"); sqlite3_free(zLog); if( p->fd==0 ){ sqlite3_log(SQLITE_IOERR, "sqllogOpenlog(): Failed to open log file"); } } } |
︙ | ︙ |
Changes to src/test_tclvar.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ************************************************************************* ** Code for testing the virtual table interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** The emphasis of this file is a virtual table that provides ** access to TCL variables. */ #include "sqliteInt.h" #if defined(INCLUDE_SQLITE_TCL_H) # include "sqlite_tcl.h" #else # include "tcl.h" #endif | > > > > > > > > > > > > > > > > > > > | 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 | ************************************************************************* ** Code for testing the virtual table interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** The emphasis of this file is a virtual table that provides ** access to TCL variables. ** ** The TCLVAR eponymous virtual table has a schema like this: ** ** CREATE TABLE tclvar( ** name TEXT, -- base name of the variable: "x" in "$x(y)" ** arrayname TEXT, -- array index name: "y" in "$x(y)" ** value TEXT, -- the value of the variable ** fullname TEXT, -- the full name of the variable ** PRIMARY KEY(fullname) ** ) WITHOUT ROWID; ** ** DELETE, INSERT, and UPDATE operations use the "fullname" field to ** determine the variable to be modified. Changing "value" to NULL ** deletes the variable. ** ** For SELECT operations, the "name" and "arrayname" fields will always ** match the "fullname" field. For DELETE, INSERT, and UPDATE, the ** "name" and "arrayname" fields are ignored and the variable is modified ** according to "fullname" and "value" only. */ #include "sqliteInt.h" #if defined(INCLUDE_SQLITE_TCL_H) # include "sqlite_tcl.h" #else # include "tcl.h" #endif |
︙ | ︙ | |||
63 64 65 66 67 68 69 | void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ tclvar_vtab *pVtab; static const char zSchema[] = | | > > > > > | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ tclvar_vtab *pVtab; static const char zSchema[] = "CREATE TABLE x(" " name TEXT," /* Base name */ " arrayname TEXT," /* Array index */ " value TEXT," /* Value */ " fullname TEXT PRIMARY KEY" /* base(index) name */ ") WITHOUT ROWID"; pVtab = sqlite3MallocZero( sizeof(*pVtab) ); if( pVtab==0 ) return SQLITE_NOMEM; *ppVtab = &pVtab->base; pVtab->interp = (Tcl_Interp *)pAux; sqlite3_declare_vtab(db, zSchema); return SQLITE_OK; } |
︙ | ︙ | |||
247 248 249 250 251 252 253 254 255 256 257 258 259 260 | break; } case 2: { Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY); sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT); break; } } return SQLITE_OK; } static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ *pRowid = 0; return SQLITE_OK; | > > > > > > > > > > | 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | break; } case 2: { Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY); sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT); break; } case 3: { char *z3; if( p2 ){ z3 = sqlite3_mprintf("%s(%s)", z1, z2); sqlite3_result_text(ctx, z3, -1, sqlite3_free); }else{ sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT); } break; } } return SQLITE_OK; } static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ *pRowid = 0; return SQLITE_OK; |
︙ | ︙ | |||
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 | } } pIdxInfo->idxStr = zStr; pIdxInfo->needToFreeIdxStr = 1; return SQLITE_OK; } /* ** A virtual table module that provides read-only access to a ** Tcl global variable namespace. */ static sqlite3_module tclvarModule = { 0, /* iVersion */ tclvarConnect, tclvarConnect, tclvarBestIndex, tclvarDisconnect, tclvarDisconnect, tclvarOpen, /* xOpen - open a cursor */ tclvarClose, /* xClose - close a cursor */ tclvarFilter, /* xFilter - configure scan constraints */ tclvarNext, /* xNext - advance a cursor */ tclvarEof, /* xEof - check for end of scan */ tclvarColumn, /* xColumn - read data */ tclvarRowid, /* xRowid - read data */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | } } pIdxInfo->idxStr = zStr; pIdxInfo->needToFreeIdxStr = 1; return SQLITE_OK; } /* ** Invoked for any UPDATE, INSERT, or DELETE against a tclvar table */ static int tclvarUpdate( sqlite3_vtab *tab, int argc, sqlite3_value **argv, sqlite_int64 *pRowid ){ tclvar_vtab *pTab = (tclvar_vtab*)tab; if( argc==1 ){ /* A DELETE operation. The variable to be deleted is stored in argv[0] */ const char *zVar = (const char*)sqlite3_value_text(argv[0]); Tcl_UnsetVar(pTab->interp, zVar, TCL_GLOBAL_ONLY); return SQLITE_OK; } if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ /* An INSERT operation */ const char *zValue = (const char*)sqlite3_value_text(argv[4]); const char *zName; if( sqlite3_value_type(argv[5])!=SQLITE_TEXT ){ tab->zErrMsg = sqlite3_mprintf("the 'fullname' column must be TEXT"); return SQLITE_ERROR; } zName = (const char*)sqlite3_value_text(argv[5]); if( zValue ){ Tcl_SetVar(pTab->interp, zName, zValue, TCL_GLOBAL_ONLY); }else{ Tcl_UnsetVar(pTab->interp, zName, TCL_GLOBAL_ONLY); } return SQLITE_OK; } if( sqlite3_value_type(argv[0])==SQLITE_TEXT && sqlite3_value_type(argv[1])==SQLITE_TEXT ){ /* An UPDATE operation */ const char *zOldName = (const char*)sqlite3_value_text(argv[0]); const char *zNewName = (const char*)sqlite3_value_text(argv[1]); const char *zValue = (const char*)sqlite3_value_text(argv[4]); if( strcmp(zOldName, zNewName)!=0 || zValue==0 ){ Tcl_UnsetVar(pTab->interp, zOldName, TCL_GLOBAL_ONLY); } if( zValue!=0 ){ Tcl_SetVar(pTab->interp, zNewName, zValue, TCL_GLOBAL_ONLY); } return SQLITE_OK; } tab->zErrMsg = sqlite3_mprintf("prohibited TCL variable change"); return SQLITE_ERROR; } /* ** A virtual table module that provides read-only access to a ** Tcl global variable namespace. */ static sqlite3_module tclvarModule = { 0, /* iVersion */ tclvarConnect, tclvarConnect, tclvarBestIndex, tclvarDisconnect, tclvarDisconnect, tclvarOpen, /* xOpen - open a cursor */ tclvarClose, /* xClose - close a cursor */ tclvarFilter, /* xFilter - configure scan constraints */ tclvarNext, /* xNext - advance a cursor */ tclvarEof, /* xEof - check for end of scan */ tclvarColumn, /* xColumn - read data */ tclvarRowid, /* xRowid - read data */ tclvarUpdate, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; |
︙ | ︙ |
Changes to src/test_windirent.c.
︙ | ︙ | |||
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | memset(dirp, 0, sizeof(DIR)); /* TODO: Remove this if Unix-style root paths are not used. */ if( sqlite3_stricmp(dirname, "/")==0 ){ dirname = windirent_getenv("SystemDrive"); } _snprintf(data.name, namesize, "%s\\*", dirname); dirp->d_handle = _findfirst(data.name, &data); if( dirp->d_handle==BAD_INTPTR_T ){ closedir(dirp); return NULL; } | > | > > | > > > > | 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 | memset(dirp, 0, sizeof(DIR)); /* TODO: Remove this if Unix-style root paths are not used. */ if( sqlite3_stricmp(dirname, "/")==0 ){ dirname = windirent_getenv("SystemDrive"); } memset(&data, 0, sizeof(struct _finddata_t)); _snprintf(data.name, namesize, "%s\\*", dirname); dirp->d_handle = _findfirst(data.name, &data); if( dirp->d_handle==BAD_INTPTR_T ){ closedir(dirp); return NULL; } /* TODO: Remove this block to allow hidden and/or system files. */ if( is_filtered(data) ){ next: memset(&data, 0, sizeof(struct _finddata_t)); if( _findnext(dirp->d_handle, &data)==-1 ){ closedir(dirp); return NULL; } /* TODO: Remove this block to allow hidden and/or system files. */ if( is_filtered(data) ) goto next; } dirp->d_first.d_attributes = data.attrib; strncpy(dirp->d_first.d_name, data.name, NAME_MAX); dirp->d_first.d_name[NAME_MAX] = '\0'; return dirp; |
︙ | ︙ | |||
101 102 103 104 105 106 107 108 109 | dirp->d_next.d_ino++; return &dirp->d_first; } next: if( _findnext(dirp->d_handle, &data)==-1 ) return NULL; | > | | < | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | dirp->d_next.d_ino++; return &dirp->d_first; } next: memset(&data, 0, sizeof(struct _finddata_t)); if( _findnext(dirp->d_handle, &data)==-1 ) return NULL; /* TODO: Remove this block to allow hidden and/or system files. */ if( is_filtered(data) ) goto next; dirp->d_next.d_ino++; dirp->d_next.d_attributes = data.attrib; strncpy(dirp->d_next.d_name, data.name, NAME_MAX); dirp->d_next.d_name[NAME_MAX] = '\0'; return &dirp->d_next; |
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 | *result = entry; return 0; } next: if( _findnext(dirp->d_handle, &data)==-1 ){ *result = NULL; return ENOENT; } | > | | < | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | *result = entry; return 0; } next: memset(&data, 0, sizeof(struct _finddata_t)); if( _findnext(dirp->d_handle, &data)==-1 ){ *result = NULL; return ENOENT; } /* TODO: Remove this block to allow hidden and/or system files. */ if( is_filtered(data) ) goto next; entry->d_ino = (ino_t)-1; /* not available */ entry->d_attributes = data.attrib; strncpy(entry->d_name, data.name, NAME_MAX); entry->d_name[NAME_MAX] = '\0'; *result = entry; |
︙ | ︙ |
Changes to src/test_windirent.h.
︙ | ︙ | |||
88 89 90 91 92 93 94 95 96 97 98 99 100 101 | struct DIR { intptr_t d_handle; /* Value returned by "_findfirst". */ DIRENT d_first; /* DIRENT constructed based on "_findfirst". */ DIRENT d_next; /* DIRENT constructed based on "_findnext". */ }; /* ** Provide the function prototype for the POSIX compatiable getenv() ** function. This function is not thread-safe. */ extern const char *windirent_getenv(const char *name); | > > > > > > > > > > > | 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 | struct DIR { intptr_t d_handle; /* Value returned by "_findfirst". */ DIRENT d_first; /* DIRENT constructed based on "_findfirst". */ DIRENT d_next; /* DIRENT constructed based on "_findnext". */ }; /* ** Provide a macro, for use by the implementation, to determine if a ** particular directory entry should be skipped over when searching for ** the next directory entry that should be returned by the readdir() or ** readdir_r() functions. */ #ifndef is_filtered # define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM)) #endif /* ** Provide the function prototype for the POSIX compatiable getenv() ** function. This function is not thread-safe. */ extern const char *windirent_getenv(const char *name); |
︙ | ︙ |
Changes to src/tokenize.c.
︙ | ︙ | |||
77 78 79 80 81 82 83 | #endif #ifdef SQLITE_EBCDIC /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ /* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27, /* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, /* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, /* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, | | | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | #endif #ifdef SQLITE_EBCDIC /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ /* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27, /* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, /* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, /* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, /* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 12, 17, 20, 10, /* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27, /* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 6, /* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8, /* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, /* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, /* Ax */ 27, 25, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27, /* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27, /* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, /* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, /* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27, /* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27, #endif }; |
︙ | ︙ | |||
471 472 473 474 475 476 477 | ** passed in. An SQLITE_ status code is returned. If an error occurs ** then an and attempt is made to write an error message into ** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that ** error message. */ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ int nErr = 0; /* Number of errors encountered */ | < > > > > < > > > > > | < < | < | < | > > | < > > > > | > > > > | 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 | ** passed in. An SQLITE_ status code is returned. If an error occurs ** then an and attempt is made to write an error message into ** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that ** error message. */ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ int nErr = 0; /* Number of errors encountered */ void *pEngine; /* The LEMON-generated LALR(1) parser */ int n = 0; /* Length of the next token token */ int tokenType; /* type of the next token */ int lastTokenParsed = -1; /* type of the previous token */ sqlite3 *db = pParse->db; /* The database connection */ int mxSqlLen; /* Max length of an SQL string */ #ifdef sqlite3Parser_ENGINEALWAYSONSTACK yyParser sEngine; /* Space to hold the Lemon-generated Parser object */ #endif assert( zSql!=0 ); mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; if( db->nVdbeActive==0 ){ db->u1.isInterrupted = 0; } pParse->rc = SQLITE_OK; pParse->zTail = zSql; assert( pzErrMsg!=0 ); /* sqlite3ParserTrace(stdout, "parser: "); */ #ifdef sqlite3Parser_ENGINEALWAYSONSTACK pEngine = &sEngine; sqlite3ParserInit(pEngine); #else pEngine = sqlite3ParserAlloc(sqlite3Malloc); if( pEngine==0 ){ sqlite3OomFault(db); return SQLITE_NOMEM_BKPT; } #endif assert( pParse->pNewTable==0 ); assert( pParse->pNewTrigger==0 ); assert( pParse->nVar==0 ); assert( pParse->pVList==0 ); while( 1 ){ if( zSql[0]!=0 ){ n = sqlite3GetToken((u8*)zSql, &tokenType); mxSqlLen -= n; if( mxSqlLen<0 ){ pParse->rc = SQLITE_TOOBIG; break; } }else{ /* Upon reaching the end of input, call the parser two more times ** with tokens TK_SEMI and 0, in that order. */ if( lastTokenParsed==TK_SEMI ){ tokenType = 0; }else if( lastTokenParsed==0 ){ break; }else{ tokenType = TK_SEMI; } zSql -= n; } if( tokenType>=TK_SPACE ){ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); if( db->u1.isInterrupted ){ pParse->rc = SQLITE_INTERRUPT; break; } if( tokenType==TK_ILLEGAL ){ sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql); break; } zSql += n; }else{ pParse->sLastToken.z = zSql; pParse->sLastToken.n = n; sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse); lastTokenParsed = tokenType; zSql += n; if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break; } } assert( nErr==0 ); pParse->zTail = zSql; #ifdef YYTRACKMAXSTACKDEPTH sqlite3_mutex_enter(sqlite3MallocMutex()); sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK, sqlite3ParserStackPeak(pEngine) ); sqlite3_mutex_leave(sqlite3MallocMutex()); #endif /* YYDEBUG */ #ifdef sqlite3Parser_ENGINEALWAYSONSTACK sqlite3ParserFinalize(pEngine); #else sqlite3ParserFree(pEngine, sqlite3_free); #endif if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM_BKPT; } if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); } assert( pzErrMsg!=0 ); |
︙ | ︙ | |||
584 585 586 587 588 589 590 | ** will take responsibility for freeing the Table structure. */ sqlite3DeleteTable(db, pParse->pNewTable); } if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree); sqlite3DeleteTrigger(db, pParse->pNewTrigger); | < | | | 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 | ** will take responsibility for freeing the Table structure. */ sqlite3DeleteTable(db, pParse->pNewTable); } if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree); sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->pVList); while( pParse->pAinc ){ AutoincInfo *p = pParse->pAinc; pParse->pAinc = p->pNext; sqlite3DbFreeNN(db, p); } while( pParse->pZombieTab ){ Table *p = pParse->pZombieTab; pParse->pZombieTab = p->pNextZombie; sqlite3DeleteTable(db, p); } assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; } |
Changes to src/treeview.c.
︙ | ︙ | |||
61 62 63 64 65 66 67 68 69 70 71 72 73 74 | sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4); } sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4); } va_start(ap, zFormat); sqlite3VXPrintf(&acc, zFormat, ap); va_end(ap); if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1); sqlite3StrAccumFinish(&acc); fprintf(stdout,"%s", zBuf); fflush(stdout); } /* | > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4); } sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4); } va_start(ap, zFormat); sqlite3VXPrintf(&acc, zFormat, ap); va_end(ap); assert( acc.nChar>0 ); if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1); sqlite3StrAccumFinish(&acc); fprintf(stdout,"%s", zBuf); fflush(stdout); } /* |
︙ | ︙ | |||
121 122 123 124 125 126 127 128 129 130 131 132 133 134 | /* ** Generate a human-readable description of a Select object. */ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){ int n = 0; int cnt = 0; pView = sqlite3TreeViewPush(pView, moreToFollow); if( p->pWith ){ sqlite3TreeViewWith(pView, p->pWith, 1); cnt = 1; sqlite3TreeViewPush(pView, 1); } do{ | > > > > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | /* ** Generate a human-readable description of a Select object. */ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){ int n = 0; int cnt = 0; if( p==0 ){ sqlite3TreeViewLine(pView, "nil-SELECT"); return; } pView = sqlite3TreeViewPush(pView, moreToFollow); if( p->pWith ){ sqlite3TreeViewWith(pView, p->pWith, 1); cnt = 1; sqlite3TreeViewPush(pView, 1); } do{ |
︙ | ︙ | |||
229 230 231 232 233 234 235 | /* ** Generate a human-readable explanation of an expression tree. */ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ const char *zBinOp = 0; /* Binary operator */ const char *zUniOp = 0; /* Unary operator */ | | > > > > | > | 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 | /* ** Generate a human-readable explanation of an expression tree. */ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ const char *zBinOp = 0; /* Binary operator */ const char *zUniOp = 0; /* Unary operator */ char zFlgs[60]; pView = sqlite3TreeViewPush(pView, moreToFollow); if( pExpr==0 ){ sqlite3TreeViewLine(pView, "nil"); sqlite3TreeViewPop(pView); return; } if( pExpr->flags ){ if( ExprHasProperty(pExpr, EP_FromJoin) ){ sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x iRJT=%d", pExpr->flags, pExpr->iRightJoinTable); }else{ sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x",pExpr->flags); } }else{ zFlgs[0] = 0; } switch( pExpr->op ){ case TK_AGG_COLUMN: { sqlite3TreeViewLine(pView, "AGG{%d:%d}%s", pExpr->iTable, pExpr->iColumn, zFlgs); |
︙ | ︙ | |||
368 369 370 371 372 373 374 | if( pFarg ){ sqlite3TreeViewExprList(pView, pFarg, 0, 0); } break; } #ifndef SQLITE_OMIT_SUBQUERY case TK_EXISTS: { | | | | | 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 | if( pFarg ){ sqlite3TreeViewExprList(pView, pFarg, 0, 0); } break; } #ifndef SQLITE_OMIT_SUBQUERY case TK_EXISTS: { sqlite3TreeViewLine(pView, "EXISTS-expr flags=0x%x", pExpr->flags); sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); break; } case TK_SELECT: { sqlite3TreeViewLine(pView, "SELECT-expr flags=0x%x", pExpr->flags); sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); break; } case TK_IN: { sqlite3TreeViewLine(pView, "IN flags=0x%x", pExpr->flags); sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); if( ExprHasProperty(pExpr, EP_xIsSelect) ){ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); }else{ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); } break; |
︙ | ︙ | |||
455 456 457 458 459 460 461 462 463 464 465 466 467 468 | sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR"); break; } case TK_SELECT_COLUMN: { sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); break; } default: { sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; } } if( zBinOp ){ | > > > > > | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR"); break; } case TK_SELECT_COLUMN: { sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); break; } case TK_IF_NULL_ROW: { sqlite3TreeViewLine(pView, "IF-NULL-ROW %d", pExpr->iTable); sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); break; } default: { sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; } } if( zBinOp ){ |
︙ | ︙ |
Changes to src/trigger.c.
︙ | ︙ | |||
302 303 304 305 306 307 308 309 310 | char *z; /* Make an entry in the sqlite_master table */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; sqlite3BeginWriteOperation(pParse, 0, iDb); z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); sqlite3NestedParse(pParse, "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", | > | | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | char *z; /* Make an entry in the sqlite_master table */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; sqlite3BeginWriteOperation(pParse, 0, iDb); z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); testcase( z==0 ); sqlite3NestedParse(pParse, "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", db->aDb[iDb].zDbSName, MASTER_NAME, zName, pTrig->table, z); sqlite3DbFree(db, z); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddParseSchemaOp(v, iDb, sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName)); } |
︙ | ︙ | |||
555 556 557 558 559 560 561 | /* Generate code to destroy the database record of the trigger. */ assert( pTable!=0 ); if( (v = sqlite3GetVdbe(pParse))!=0 ){ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'", | | | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | /* Generate code to destroy the database record of the trigger. */ assert( pTable!=0 ); if( (v = sqlite3GetVdbe(pParse))!=0 ){ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'", db->aDb[iDb].zDbSName, MASTER_NAME, pTrigger->zName ); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0); } } /* |
︙ | ︙ | |||
580 581 582 583 584 585 586 | if( pTrigger->pSchema==pTrigger->pTabSchema ){ Table *pTab = tableOfTrigger(pTrigger); Trigger **pp; for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext)); *pp = (*pp)->pNext; } sqlite3DeleteTrigger(db, pTrigger); | | | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | if( pTrigger->pSchema==pTrigger->pTabSchema ){ Table *pTab = tableOfTrigger(pTrigger); Trigger **pp; for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext)); *pp = (*pp)->pNext; } sqlite3DeleteTrigger(db, pTrigger); db->mDbFlags |= DBFLAG_SchemaChange; } } /* ** pEList is the SET clause of an UPDATE statement. Each entry ** in pEList is of the format <id>=<expr>. If any of the entries ** in pEList have an <id> which matches an identifier in pIdList, |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
65 66 67 68 69 70 71 | u8 enc = ENC(sqlite3VdbeDb(v)); Column *pCol = &pTab->aCol[i]; VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); assert( i<pTab->nCol ); sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc, pCol->affinity, &pValue); if( pValue ){ | | > | | | < | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | u8 enc = ENC(sqlite3VdbeDb(v)); Column *pCol = &pTab->aCol[i]; VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); assert( i<pTab->nCol ); sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc, pCol->affinity, &pValue); if( pValue ){ sqlite3VdbeAppendP4(v, pValue, P4_MEM); } } #ifndef SQLITE_OMIT_FLOATING_POINT if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); } #endif } /* ** Process an UPDATE statement. ** ** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL; ** \_______/ \________/ \______/ \________________/ |
︙ | ︙ | |||
101 102 103 104 105 106 107 | Index *pIdx; /* For looping over indices */ Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */ int nIdx; /* Number of indices that need updating */ int iBaseCur; /* Base cursor number */ int iDataCur; /* Cursor for the canonical data btree */ int iIdxCur; /* Cursor for the first index */ sqlite3 *db; /* The database structure */ | | | > > > > > | 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 | Index *pIdx; /* For looping over indices */ Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */ int nIdx; /* Number of indices that need updating */ int iBaseCur; /* Base cursor number */ int iDataCur; /* Cursor for the canonical data btree */ int iIdxCur; /* Cursor for the first index */ sqlite3 *db; /* The database structure */ int *aRegIdx = 0; /* First register in array assigned to each index */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ u8 *aToOpen; /* 1 for tables and indices to be opened */ u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */ u8 chngRowid; /* Rowid changed in a normal table */ u8 chngKey; /* Either chngPk or chngRowid */ Expr *pRowidExpr = 0; /* Expression defining the new record number */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ int eOnePass; /* ONEPASS_XXX value from where.c */ int hasFK; /* True if foreign key processing is required */ int labelBreak; /* Jump here to break out of UPDATE loop */ int labelContinue; /* Jump here to continue next step of UPDATE loop */ int flags; /* Flags for sqlite3WhereBegin() */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* True when updating a view (INSTEAD OF trigger) */ Trigger *pTrigger; /* List of triggers on pTab, if required */ int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ #endif int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ int iEph = 0; /* Ephemeral table holding all primary key values */ int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */ int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */ int addrOpen = 0; /* Address of OP_OpenEphemeral */ int iPk = 0; /* First of nPk cells holding PRIMARY KEY value */ i16 nPk = 0; /* Number of components of the PRIMARY KEY */ int bReplace = 0; /* True if REPLACE conflict resolution might happen */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid = 0; /* The old rowid */ int regNewRowid = 0; /* The new rowid */ int regNew = 0; /* Content of the NEW.* table in triggers */ int regOld = 0; /* Content of OLD.* table in triggers */ |
︙ | ︙ | |||
276 277 278 279 280 281 282 | ** being updated. Fill in aRegIdx[] with a register number that will hold ** the key for accessing each index. ** ** FIXME: Be smarter about omitting indexes that use expressions. */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; | | > > > > > > > > > > > > | 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 | ** being updated. Fill in aRegIdx[] with a register number that will hold ** the key for accessing each index. ** ** FIXME: Be smarter about omitting indexes that use expressions. */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; if( chngKey || hasFK>1 || pIdx->pPartIdxWhere || pIdx==pPk ){ reg = ++pParse->nMem; pParse->nMem += pIdx->nColumn; }else{ reg = 0; for(i=0; i<pIdx->nKeyCol; i++){ i16 iIdxCol = pIdx->aiColumn[i]; if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){ reg = ++pParse->nMem; pParse->nMem += pIdx->nColumn; if( (onError==OE_Replace) || (onError==OE_Default && pIdx->onError==OE_Replace) ){ bReplace = 1; } break; } } } if( reg==0 ) aToOpen[j+1] = 0; aRegIdx[j] = reg; } if( bReplace ){ /* If REPLACE conflict resolution might be invoked, open cursors on all ** indexes in case they are needed to delete records. */ memset(aToOpen, 1, nIdx+1); } /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto update_cleanup; if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); sqlite3BeginWriteOperation(pParse, 1, iDb); |
︙ | ︙ | |||
343 344 345 346 347 348 349 | if( IsVirtual(pTab) ){ updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, pWhere, onError); goto update_cleanup; } #endif | | < < < < | < < < < | < < | < < | | < < > | < < < < > > > > > > > > > > > > > > | < | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | < | | < < < < < > | > | | < < < < | | < < < | < | | < < < | > > | | > | > > > | | 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 | if( IsVirtual(pTab) ){ updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, pWhere, onError); goto update_cleanup; } #endif /* Initialize the count of updated rows */ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){ regRowCount = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); } if( HasRowid(pTab) ){ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); }else{ assert( pPk!=0 ); nPk = pPk->nKeyCol; iPk = pParse->nMem+1; pParse->nMem += nPk; regKey = ++pParse->nMem; iEph = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_Null, 0, iPk); addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); sqlite3VdbeSetP4KeyInfo(pParse, pPk); } /* Begin the database scan. ** ** Do not consider a single-pass strategy for a multi-row update if ** there are any triggers or foreign keys to process, or rows may ** be deleted as a result of REPLACE conflict handling. Any of these ** things might disturb a cursor being used to scan through the table ** or index, causing a single-pass approach to malfunction. */ flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE; if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ flags |= WHERE_ONEPASS_MULTIROW; } pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur); if( pWInfo==0 ) goto update_cleanup; /* A one-pass strategy that might update more than one row may not ** be used if any column of the index used for the scan is being ** updated. Otherwise, if there is an index on "b", statements like ** the following could create an infinite loop: ** ** UPDATE t1 SET b=b+1 WHERE b>? ** ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI ** strategy that uses an index for which one or more columns are being ** updated. */ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); if( eOnePass==ONEPASS_MULTI ){ int iCur = aiCurOnePass[1]; if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){ eOnePass = ONEPASS_OFF; } assert( iCur!=iDataCur || !HasRowid(pTab) ); } if( HasRowid(pTab) ){ /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF ** mode, write the rowid into the FIFO. In either of the one-pass modes, ** leave it in register regOldRowid. */ sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); if( eOnePass==ONEPASS_OFF ){ sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); } }else{ /* Read the PK of the current row into an array of registers. In ** ONEPASS_OFF mode, serialize the array into a record and store it in ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change ** the OP_OpenEphemeral instruction to a Noop (the ephemeral table ** is not required) and leave the PK fields in the array of registers. */ for(i=0; i<nPk; i++){ assert( pPk->aiColumn[i]>=0 ); sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,pPk->aiColumn[i],iPk+i); } if( eOnePass ){ sqlite3VdbeChangeToNoop(v, addrOpen); nKey = nPk; regKey = iPk; }else{ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey, sqlite3IndexAffinityStr(db, pPk), nPk); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk); } } if( eOnePass!=ONEPASS_MULTI ){ sqlite3WhereEnd(pWInfo); } labelBreak = sqlite3VdbeMakeLabel(v); if( !isView ){ int addrOnce = 0; /* Open every index that needs updating. */ if( eOnePass!=ONEPASS_OFF ){ if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0; if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0; } if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen, 0, 0); if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce); } /* Top of the update loop */ if( eOnePass!=ONEPASS_OFF ){ if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){ assert( pPk ); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey); VdbeCoverageNeverTaken(v); } if( eOnePass==ONEPASS_SINGLE ){ labelContinue = labelBreak; }else{ labelContinue = sqlite3VdbeMakeLabel(v); } sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); VdbeCoverageIf(v, pPk==0); VdbeCoverageIf(v, pPk!=0); }else if( pPk ){ labelContinue = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0); VdbeCoverage(v); }else{ labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak, regOldRowid); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); |
︙ | ︙ | |||
564 565 566 567 568 569 570 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); } } } if( !isView ){ int addr1 = 0; /* Address of jump instruction */ | < | 601 602 603 604 605 606 607 608 609 610 611 612 613 614 | sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); } } } if( !isView ){ int addr1 = 0; /* Address of jump instruction */ /* Do constraint checks. */ assert( regOldRowid>0 ); sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace, aXRef); |
︙ | ︙ | |||
600 601 602 603 604 605 606 | ** pre-update hook. If the caller invokes preupdate_new(), the returned ** value is copied from memory cell (regNewRowid+1+iCol), where iCol ** is the column index supplied by the user. */ assert( regNew==regNewRowid+1 ); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK sqlite3VdbeAddOp3(v, OP_Delete, iDataCur, | | > > > > | | | | > > > | 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 | ** pre-update hook. If the caller invokes preupdate_new(), the returned ** value is copied from memory cell (regNewRowid+1+iCol), where iCol ** is the column index supplied by the user. */ assert( regNew==regNewRowid+1 ); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK sqlite3VdbeAddOp3(v, OP_Delete, iDataCur, OPFLAG_ISUPDATE | ((hasFK>1 || chngKey) ? 0 : OPFLAG_ISNOOP), regNewRowid ); if( eOnePass==ONEPASS_MULTI ){ assert( hasFK==0 && chngKey==0 ); sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); } if( !pParse->nested ){ sqlite3VdbeAppendP4(v, pTab, P4_TABLE); } #else if( hasFK>1 || chngKey ){ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0); } #endif if( bReplace || chngKey ){ sqlite3VdbeJumpHere(v, addr1); } if( hasFK ){ sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey); } /* Insert the new index entries and the new record. */ sqlite3CompleteInsertion( pParse, pTab, iDataCur, iIdxCur, regNewRowid, aRegIdx, OPFLAG_ISUPDATE | (eOnePass==ONEPASS_MULTI ? OPFLAG_SAVEPOSITION : 0), 0, 0 ); /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key ** to the row just updated. */ if( hasFK ){ sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey); } |
︙ | ︙ | |||
643 644 645 646 647 648 649 | sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ | | > > > < < < < < < < < < | 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 | sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ if( eOnePass==ONEPASS_SINGLE ){ /* Nothing to do at end-of-loop for a single-pass */ }else if( eOnePass==ONEPASS_MULTI ){ sqlite3VdbeResolveLabel(v, labelContinue); sqlite3WhereEnd(pWInfo); }else if( pPk ){ sqlite3VdbeResolveLabel(v, labelContinue); sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); }else{ sqlite3VdbeGoto(v, labelContinue); } sqlite3VdbeResolveLabel(v, labelBreak); /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite3AutoincrementEnd(pParse); } |
︙ | ︙ | |||
762 763 764 765 766 767 768 | regRowid = ++pParse->nMem; /* Start scanning the virtual table */ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0); if( pWInfo==0 ) return; /* Populate the argument registers. */ | < < < < < < > > > > > > > > > > > > > > > > > | 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 | regRowid = ++pParse->nMem; /* Start scanning the virtual table */ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0); if( pWInfo==0 ) return; /* Populate the argument registers. */ for(i=0; i<pTab->nCol; i++){ if( aXRef[i]>=0 ){ sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); }else{ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); } } if( HasRowid(pTab) ){ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg); if( pRowid ){ sqlite3ExprCode(pParse, pRowid, regArg+1); }else{ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1); } }else{ Index *pPk; /* PRIMARY KEY index */ i16 iPk; /* PRIMARY KEY column */ pPk = sqlite3PrimaryKeyIndex(pTab); assert( pPk!=0 ); assert( pPk->nKeyCol==1 ); iPk = pPk->aiColumn[0]; sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg); sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1); } bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy); if( bOnePass ){ /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded ** above. Also, if this is a top-level parse (not a trigger), clear the ** multi-write flag so that the VM does not open a statement journal */ |
︙ | ︙ |
Changes to src/utf.c.
︙ | ︙ | |||
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | char zBuf[100]; sqlite3VdbeMemPrettyPrint(pMem, zBuf); fprintf(stderr, "OUTPUT: %s\n", zBuf); } #endif return SQLITE_OK; } /* ** This routine checks for a byte-order mark at the beginning of the ** UTF-16 string stored in *pMem. If one is present, it is removed and ** the encoding of the Mem adjusted. This routine does not do any ** byte-swapping, it just sets Mem.enc appropriately. ** ** The allocation (static, dynamic etc.) and encoding of the Mem may be | > > | 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | char zBuf[100]; sqlite3VdbeMemPrettyPrint(pMem, zBuf); fprintf(stderr, "OUTPUT: %s\n", zBuf); } #endif return SQLITE_OK; } #endif /* SQLITE_OMIT_UTF16 */ #ifndef SQLITE_OMIT_UTF16 /* ** This routine checks for a byte-order mark at the beginning of the ** UTF-16 string stored in *pMem. If one is present, it is removed and ** the encoding of the Mem adjusted. This routine does not do any ** byte-swapping, it just sets Mem.enc appropriately. ** ** The allocation (static, dynamic etc.) and encoding of the Mem may be |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
38 39 40 41 42 43 44 | ** ** The intent of the integer argument is to let the fault simulator know ** which of multiple sqlite3FaultSim() calls has been hit. ** ** Return whatever integer value the test callback returns, or return ** SQLITE_OK if no test callback is installed. */ | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | ** ** The intent of the integer argument is to let the fault simulator know ** which of multiple sqlite3FaultSim() calls has been hit. ** ** Return whatever integer value the test callback returns, or return ** SQLITE_OK if no test callback is installed. */ #ifndef SQLITE_UNTESTABLE int sqlite3FaultSim(int iTest){ int (*xCallback)(int) = sqlite3GlobalConfig.xTestCallback; return xCallback ? xCallback(iTest) : SQLITE_OK; } #endif #ifndef SQLITE_OMIT_FLOATING_POINT |
︙ | ︙ | |||
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | result = s * scale; result *= 1.0e+308; } }else{ assert( e>=342 ); if( esign<0 ){ result = 0.0*s; }else{ result = 1e308*1e308*s; /* Infinity */ } } }else{ /* 1.0e+22 is the largest power of 10 than can be ** represented exactly. */ while( e%22 ) { scale *= 1.0e+1; e -= 1; } while( e>0 ) { scale *= 1.0e+22; e -= 22; } | > > > > | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | result = s * scale; result *= 1.0e+308; } }else{ assert( e>=342 ); if( esign<0 ){ result = 0.0*s; }else{ #ifdef INFINITY result = INFINITY*s; #else result = 1e308*1e308*s; /* Infinity */ #endif } } }else{ /* 1.0e+22 is the largest power of 10 than can be ** represented exactly. */ while( e%22 ) { scale *= 1.0e+1; e -= 1; } while( e>0 ) { scale *= 1.0e+22; e -= 22; } |
︙ | ︙ | |||
549 550 551 552 553 554 555 | return c; } /* ** Convert zNum to a 64-bit signed integer. zNum must be decimal. This ** routine does *not* accept hexadecimal notation. ** | < < | < < < > > | | < > | 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 | return c; } /* ** Convert zNum to a 64-bit signed integer. zNum must be decimal. This ** routine does *not* accept hexadecimal notation. ** ** Returns: ** ** 0 Successful transformation. Fits in a 64-bit signed integer. ** 1 Excess text after the integer value ** 2 Integer too large for a 64-bit signed integer or is malformed ** 3 Special case of 9223372036854775808 ** ** length is the number of bytes in the string (bytes, not characters). ** The string is not necessarily zero-terminated. The encoding is ** given by enc. */ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ int incr; u64 u = 0; int neg = 0; /* assume positive */ int i; int c = 0; int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */ int rc; /* Baseline return code */ const char *zStart; const char *zEnd = zNum + length; assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); if( enc==SQLITE_UTF8 ){ incr = 1; }else{ incr = 2; |
︙ | ︙ | |||
610 611 612 613 614 615 616 | *pNum = (i64)u; } testcase( i==18 ); testcase( i==19 ); testcase( i==20 ); if( &zNum[i]<zEnd /* Extra bytes at the end */ || (i==0 && zStart==zNum) /* No digits */ | < > > > > > | | | | | > | | | | 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 | *pNum = (i64)u; } testcase( i==18 ); testcase( i==19 ); testcase( i==20 ); if( &zNum[i]<zEnd /* Extra bytes at the end */ || (i==0 && zStart==zNum) /* No digits */ || nonNum /* UTF16 with high-order bytes non-zero */ ){ rc = 1; }else{ rc = 0; } if( i>19*incr ){ /* Too many digits */ /* zNum is empty or contains non-numeric text or is longer ** than 19 digits (thus guaranteeing that it is too large) */ return 2; }else if( i<19*incr ){ /* Less than 19 digits, so we know that it fits in 64 bits */ assert( u<=LARGEST_INT64 ); return rc; }else{ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */ c = compare2pow63(zNum, incr); if( c<0 ){ /* zNum is less than 9223372036854775808 so it fits */ assert( u<=LARGEST_INT64 ); return rc; }else if( c>0 ){ /* zNum is greater than 9223372036854775808 so it overflows */ return 2; }else{ /* zNum is exactly 9223372036854775808. Fits if negative. The ** special case 2 overflow if positive */ assert( u-1==LARGEST_INT64 ); return neg ? rc : 3; } } } /* ** Transform a UTF-8 integer literal, in either decimal or hexadecimal, ** into a 64-bit signed integer. This routine accepts hexadecimal literals, ** whereas sqlite3Atoi64() does not. ** ** Returns: ** ** 0 Successful transformation. Fits in a 64-bit signed integer. ** 1 Excess text after the integer value ** 2 Integer too large for a 64-bit signed integer or is malformed ** 3 Special case of 9223372036854775808 */ int sqlite3DecOrHexToI64(const char *z, i64 *pOut){ #ifndef SQLITE_OMIT_HEX_INTEGER if( z[0]=='0' && (z[1]=='x' || z[1]=='X') ){ u64 u = 0; int i, k; for(i=2; z[i]=='0'; i++){} for(k=i; sqlite3Isxdigit(z[k]); k++){ u = u*16 + sqlite3HexToInt(z[k]); } memcpy(pOut, &u, 8); return (z[k]==0 && k-i<=16) ? 0 : 2; }else #endif /* SQLITE_OMIT_HEX_INTEGER */ { return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); } } |
︙ | ︙ | |||
709 710 711 712 713 714 715 716 717 718 719 720 721 722 | memcpy(pValue, &u, 4); return 1; }else{ return 0; } } #endif while( zNum[0]=='0' ) zNum++; for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){ v = v*10 + c; } /* The longest decimal representation of a 32 bit integer is 10 digits: ** | > | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 | memcpy(pValue, &u, 4); return 1; }else{ return 0; } } #endif if( !sqlite3Isdigit(zNum[0]) ) return 0; while( zNum[0]=='0' ) zNum++; for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){ v = v*10 + c; } /* The longest decimal representation of a 32 bit integer is 10 digits: ** |
︙ | ︙ | |||
1136 1137 1138 1139 1140 1141 1142 | ** Read or write a four-byte big-endian integer value. */ u32 sqlite3Get4byte(const u8 *p){ #if SQLITE_BYTEORDER==4321 u32 x; memcpy(&x,p,4); return x; | | < | < | < | < | 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 | ** Read or write a four-byte big-endian integer value. */ u32 sqlite3Get4byte(const u8 *p){ #if SQLITE_BYTEORDER==4321 u32 x; memcpy(&x,p,4); return x; #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 u32 x; memcpy(&x,p,4); return __builtin_bswap32(x); #elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 u32 x; memcpy(&x,p,4); return _byteswap_ulong(x); #else testcase( p[0]&0x80 ); return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; #endif } void sqlite3Put4byte(unsigned char *p, u32 v){ #if SQLITE_BYTEORDER==4321 memcpy(p,&v,4); #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 u32 x = __builtin_bswap32(v); memcpy(p,&x,4); #elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 u32 x = _byteswap_ulong(v); memcpy(p,&x,4); #else p[0] = (u8)(v>>24); p[1] = (u8)(v>>16); p[2] = (u8)(v>>8); p[3] = (u8)v; |
︙ | ︙ | |||
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 | /* ** Attempt to add, substract, or multiply the 64-bit signed value iB against ** the other 64-bit signed integer at *pA and store the result in *pA. ** Return 0 on success. Or if the operation would have resulted in an ** overflow, leave *pA unchanged and return 1. */ int sqlite3AddInt64(i64 *pA, i64 iB){ i64 iA = *pA; testcase( iA==0 ); testcase( iA==1 ); testcase( iB==-1 ); testcase( iB==0 ); if( iB>=0 ){ testcase( iA>0 && LARGEST_INT64 - iA == iB ); testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; }else{ testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; } *pA += iB; return 0; } int sqlite3SubInt64(i64 *pA, i64 iB){ testcase( iB==SMALLEST_INT64+1 ); if( iB==SMALLEST_INT64 ){ testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); if( (*pA)>=0 ) return 1; *pA -= iB; return 0; }else{ return sqlite3AddInt64(pA, -iB); } } int sqlite3MulInt64(i64 *pA, i64 iB){ i64 iA = *pA; if( iB>0 ){ if( iA>LARGEST_INT64/iB ) return 1; if( iA<SMALLEST_INT64/iB ) return 1; }else if( iB<0 ){ if( iA>0 ){ if( iB<SMALLEST_INT64/iA ) return 1; }else if( iA<0 ){ if( iB==SMALLEST_INT64 ) return 1; if( iA==SMALLEST_INT64 ) return 1; if( -iA>LARGEST_INT64/-iB ) return 1; } } *pA = iA*iB; return 0; } /* ** Compute the absolute value of a 32-bit signed integer, of possible. Or ** if the integer has a value of -2147483648, return +2147483647 */ int sqlite3AbsInt32(int x){ | > > > > > > > > > > > > | 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 | /* ** Attempt to add, substract, or multiply the 64-bit signed value iB against ** the other 64-bit signed integer at *pA and store the result in *pA. ** Return 0 on success. Or if the operation would have resulted in an ** overflow, leave *pA unchanged and return 1. */ int sqlite3AddInt64(i64 *pA, i64 iB){ #if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) return __builtin_add_overflow(*pA, iB, pA); #else i64 iA = *pA; testcase( iA==0 ); testcase( iA==1 ); testcase( iB==-1 ); testcase( iB==0 ); if( iB>=0 ){ testcase( iA>0 && LARGEST_INT64 - iA == iB ); testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; }else{ testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; } *pA += iB; return 0; #endif } int sqlite3SubInt64(i64 *pA, i64 iB){ #if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) return __builtin_sub_overflow(*pA, iB, pA); #else testcase( iB==SMALLEST_INT64+1 ); if( iB==SMALLEST_INT64 ){ testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); if( (*pA)>=0 ) return 1; *pA -= iB; return 0; }else{ return sqlite3AddInt64(pA, -iB); } #endif } int sqlite3MulInt64(i64 *pA, i64 iB){ #if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) return __builtin_mul_overflow(*pA, iB, pA); #else i64 iA = *pA; if( iB>0 ){ if( iA>LARGEST_INT64/iB ) return 1; if( iA<SMALLEST_INT64/iB ) return 1; }else if( iB<0 ){ if( iA>0 ){ if( iB<SMALLEST_INT64/iA ) return 1; }else if( iA<0 ){ if( iB==SMALLEST_INT64 ) return 1; if( iA==SMALLEST_INT64 ) return 1; if( -iA>LARGEST_INT64/-iB ) return 1; } } *pA = iA*iB; return 0; #endif } /* ** Compute the absolute value of a 32-bit signed integer, of possible. Or ** if the integer has a value of -2147483648, return +2147483647 */ int sqlite3AbsInt32(int x){ |
︙ | ︙ | |||
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 | LogEst sqlite3LogEst(u64 x){ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 }; LogEst y = 40; if( x<8 ){ if( x<2 ) return 0; while( x<8 ){ y -= 10; x <<= 1; } }else{ while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/ while( x>15 ){ y += 10; x >>= 1; } } return a[x&7] + y - 10; } #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Convert a double into a LogEst | > > > > > > | 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 | LogEst sqlite3LogEst(u64 x){ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 }; LogEst y = 40; if( x<8 ){ if( x<2 ) return 0; while( x<8 ){ y -= 10; x <<= 1; } }else{ #if GCC_VERSION>=5004000 int i = 60 - __builtin_clzll(x); y += i*10; x >>= i; #else while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/ while( x>15 ){ y += 10; x >>= 1; } #endif } return a[x&7] + y - 10; } #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Convert a double into a LogEst |
︙ | ︙ | |||
1449 1450 1451 1452 1453 1454 1455 | /* If only SQLITE_ENABLE_STAT3_OR_STAT4 is on, then the largest input ** possible to this routine is 310, resulting in a maximum x of 31 */ assert( x<=60 ); #endif return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x); } #endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* If only SQLITE_ENABLE_STAT3_OR_STAT4 is on, then the largest input ** possible to this routine is 310, resulting in a maximum x of 31 */ assert( x<=60 ); #endif return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x); } #endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */ /* ** Add a new name/number pair to a VList. This might require that the ** VList object be reallocated, so return the new VList. If an OOM ** error occurs, the original VList returned and the ** db->mallocFailed flag is set. ** ** A VList is really just an array of integers. To destroy a VList, ** simply pass it to sqlite3DbFree(). ** ** The first integer is the number of integers allocated for the whole ** VList. The second integer is the number of integers actually used. ** Each name/number pair is encoded by subsequent groups of 3 or more ** integers. ** ** Each name/number pair starts with two integers which are the numeric ** value for the pair and the size of the name/number pair, respectively. ** The text name overlays one or more following integers. The text name ** is always zero-terminated. ** ** Conceptually: ** ** struct VList { ** int nAlloc; // Number of allocated slots ** int nUsed; // Number of used slots ** struct VListEntry { ** int iValue; // Value for this entry ** int nSlot; // Slots used by this entry ** // ... variable name goes here ** } a[0]; ** } ** ** During code generation, pointers to the variable names within the ** VList are taken. When that happens, nAlloc is set to zero as an ** indication that the VList may never again be enlarged, since the ** accompanying realloc() would invalidate the pointers. */ VList *sqlite3VListAdd( sqlite3 *db, /* The database connection used for malloc() */ VList *pIn, /* The input VList. Might be NULL */ const char *zName, /* Name of symbol to add */ int nName, /* Bytes of text in zName */ int iVal /* Value to associate with zName */ ){ int nInt; /* number of sizeof(int) objects needed for zName */ char *z; /* Pointer to where zName will be stored */ int i; /* Index in pIn[] where zName is stored */ nInt = nName/4 + 3; assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */ if( pIn==0 || pIn[1]+nInt > pIn[0] ){ /* Enlarge the allocation */ int nAlloc = (pIn ? pIn[0]*2 : 10) + nInt; VList *pOut = sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int)); if( pOut==0 ) return pIn; if( pIn==0 ) pOut[1] = 2; pIn = pOut; pIn[0] = nAlloc; } i = pIn[1]; pIn[i] = iVal; pIn[i+1] = nInt; z = (char*)&pIn[i+2]; pIn[1] = i+nInt; assert( pIn[1]<=pIn[0] ); memcpy(z, zName, nName); z[nName] = 0; return pIn; } /* ** Return a pointer to the name of a variable in the given VList that ** has the value iVal. Or return a NULL if there is no such variable in ** the list */ const char *sqlite3VListNumToName(VList *pIn, int iVal){ int i, mx; if( pIn==0 ) return 0; mx = pIn[1]; i = 2; do{ if( pIn[i]==iVal ) return (char*)&pIn[i+2]; i += pIn[i+1]; }while( i<mx ); return 0; } /* ** Return the number of the variable named zName, if it is in VList. ** or return 0 if there is no such variable. */ int sqlite3VListNameToNum(VList *pIn, const char *zName, int nName){ int i, mx; if( pIn==0 ) return 0; mx = pIn[1]; i = 2; do{ const char *z = (const char*)&pIn[i+2]; if( strncmp(z,zName,nName)==0 && z[nName]==0 ) return pIn[i]; i += pIn[i+1]; }while( i<mx ); return 0; } |
Changes to src/vacuum.c.
︙ | ︙ | |||
94 95 96 97 98 99 100 | ** not work if other processes are attached to the original database. ** And a power loss in between deleting the original and renaming the ** transient would cause the database file to appear to be deleted ** following reboot. */ void sqlite3Vacuum(Parse *pParse, Token *pNm){ Vdbe *v = sqlite3GetVdbe(pParse); | > > > > > > | > > > > > > > > > > > | > | | 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 | ** not work if other processes are attached to the original database. ** And a power loss in between deleting the original and renaming the ** transient would cause the database file to appear to be deleted ** following reboot. */ void sqlite3Vacuum(Parse *pParse, Token *pNm){ Vdbe *v = sqlite3GetVdbe(pParse); int iDb = 0; if( v==0 ) return; if( pNm ){ #ifndef SQLITE_BUG_COMPATIBLE_20160819 /* Default behavior: Report an error if the argument to VACUUM is ** not recognized */ iDb = sqlite3TwoPartName(pParse, pNm, pNm, &pNm); if( iDb<0 ) return; #else /* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments ** to VACUUM are silently ignored. This is a back-out of a bug fix that ** occurred on 2016-08-19 (https://www.sqlite.org/src/info/083f9e6270). ** The buggy behavior is required for binary compatibility with some ** legacy applications. */ iDb = sqlite3FindDb(pParse->db, pNm); if( iDb<0 ) iDb = 0; #endif } if( iDb!=1 ){ sqlite3VdbeAddOp1(v, OP_Vacuum, iDb); sqlite3VdbeUsesBtree(v, iDb); } return; } /* ** This routine implements the OP_Vacuum opcode of the VDBE. */ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ int rc = SQLITE_OK; /* Return code from service routines */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; /* The temporary database we vacuum into */ u16 saved_mDbFlags; /* Saved value of db->mDbFlags */ u32 saved_flags; /* Saved value of db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ |
︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 | return SQLITE_ERROR; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; saved_mTrace = db->mTrace; | > | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | return SQLITE_ERROR; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_mDbFlags = db->mDbFlags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; saved_mTrace = db->mTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows); db->mTrace = 0; zDbMain = db->aDb[iDb].zDbSName; pMain = db->aDb[iDb].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); |
︙ | ︙ | |||
180 181 182 183 184 185 186 | /* A VACUUM cannot change the pagesize of an encrypted database. */ #ifdef SQLITE_HAS_CODEC if( db->nextPagesize ){ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); int nKey; char *zKey; | | | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | /* A VACUUM cannot change the pagesize of an encrypted database. */ #ifdef SQLITE_HAS_CODEC if( db->nextPagesize ){ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); int nKey; char *zKey; sqlite3CodecGetKey(db, iDb, (void**)&zKey, &nKey); if( nKey ) db->nextPagesize = 0; } #endif sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); /* Begin a transaction and take an exclusive lock on the main database ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below, ** to ensure that we do not try to change the page-size on a WAL database. */ rc = execSql(db, pzErrMsg, "BEGIN"); if( rc!=SQLITE_OK ) goto end_of_vacuum; |
︙ | ︙ | |||
247 248 249 250 251 252 253 | rc = execSqlF(db, pzErrMsg, "SELECT'INSERT INTO vacuum_db.'||quote(name)" "||' SELECT*FROM\"%w\".'||quote(name)" "FROM vacuum_db.sqlite_master " "WHERE type='table'AND coalesce(rootpage,1)>0", zDbMain ); | | | | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | rc = execSqlF(db, pzErrMsg, "SELECT'INSERT INTO vacuum_db.'||quote(name)" "||' SELECT*FROM\"%w\".'||quote(name)" "FROM vacuum_db.sqlite_master " "WHERE type='table'AND coalesce(rootpage,1)>0", zDbMain ); assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 ); db->mDbFlags &= ~DBFLAG_Vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy the triggers, views, and virtual tables from the main database ** over to the temporary database. None of these objects has any ** associated storage, so all we have to do is copy their entries ** from the SQLITE_MASTER table. */ |
︙ | ︙ | |||
316 317 318 319 320 321 322 323 324 325 326 327 328 329 | assert( rc==SQLITE_OK ); rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1); end_of_vacuum: /* Restore the original value of db->flags */ db->init.iDb = 0; db->flags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; db->mTrace = saved_mTrace; sqlite3BtreeSetPageSize(pMain, -1, -1, 1); /* Currently there is an SQL level transaction open on the vacuum | > | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | assert( rc==SQLITE_OK ); rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1); end_of_vacuum: /* Restore the original value of db->flags */ db->init.iDb = 0; db->mDbFlags = saved_mDbFlags; db->flags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; db->mTrace = saved_mTrace; sqlite3BtreeSetPageSize(pMain, -1, -1, 1); /* Currently there is an SQL level transaction open on the vacuum |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
107 108 109 110 111 112 113 | int sqlite3_found_count = 0; #endif /* ** Test a register to see if it exceeds the current maximum blob size. ** If it does, record the new maximum blob size. */ | | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | int sqlite3_found_count = 0; #endif /* ** Test a register to see if it exceeds the current maximum blob size. ** If it does, record the new maximum blob size. */ #if defined(SQLITE_TEST) && !defined(SQLITE_UNTESTABLE) # define UPDATE_MAX_BLOBSIZE(P) updateMaxBlobsize(P) #else # define UPDATE_MAX_BLOBSIZE(P) #endif /* ** Invoke the VDBE coverage callback, if that callback is defined. This |
︙ | ︙ | |||
217 218 219 220 221 222 223 | assert( iCur>=0 && iCur<p->nCursor ); if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); p->apCsr[iCur] = 0; } if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; | | | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | assert( iCur>=0 && iCur<p->nCursor ); if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); p->apCsr[iCur] = 0; } if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); pCx->eCurType = eCurType; pCx->iDb = iDb; pCx->nField = nField; pCx->aOffset = &pCx->aType[nField]; if( eCurType==CURTYPE_BTREE ){ pCx->uc.pCursor = (BtCursor*) &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; |
︙ | ︙ | |||
350 351 352 353 354 355 356 | */ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ assert( (pMem->flags & (MEM_Int|MEM_Real))==0 ); assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){ return 0; } | | | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | */ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ assert( (pMem->flags & (MEM_Int|MEM_Real))==0 ); assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){ return 0; } if( sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc)==0 ){ return MEM_Int; } return MEM_Real; } /* ** Return the numeric type for pMem, either MEM_Int or MEM_Real or both or |
︙ | ︙ | |||
399 400 401 402 403 404 405 | assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); }else if( f & MEM_Ephem ){ c = 'e'; assert( (f & (MEM_Static|MEM_Dyn))==0 ); }else{ c = 's'; } | | < < | < < | 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 | assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); }else if( f & MEM_Ephem ){ c = 'e'; assert( (f & (MEM_Static|MEM_Dyn))==0 ); }else{ c = 's'; } *(zCsr++) = c; sqlite3_snprintf(100, zCsr, "%d[", pMem->n); zCsr += sqlite3Strlen30(zCsr); for(i=0; i<16 && i<pMem->n; i++){ sqlite3_snprintf(100, zCsr, "%02X", ((int)pMem->z[i] & 0xFF)); zCsr += sqlite3Strlen30(zCsr); } for(i=0; i<16 && i<pMem->n; i++){ char z = pMem->z[i]; if( z<32 || z>126 ) *zCsr++ = '.'; else *zCsr++ = z; } *(zCsr++) = ']'; if( f & MEM_Zero ){ sqlite3_snprintf(100, zCsr,"+%dz",pMem->u.nZero); zCsr += sqlite3Strlen30(zCsr); } *zCsr = '\0'; }else if( f & MEM_Str ){ int j, k; |
︙ | ︙ | |||
486 487 488 489 490 491 492 493 494 495 496 497 498 499 | } if( p->flags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype); } static void registerTrace(int iReg, Mem *p){ printf("REG[%d] = ", iReg); memTracePrint(p); printf("\n"); } #endif #ifdef SQLITE_DEBUG # define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M) #else # define REGISTER_TRACE(R,M) | > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 | } if( p->flags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype); } static void registerTrace(int iReg, Mem *p){ printf("REG[%d] = ", iReg); memTracePrint(p); printf("\n"); sqlite3VdbeCheckMemInvariants(p); } #endif #ifdef SQLITE_DEBUG # define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M) #else # define REGISTER_TRACE(R,M) |
︙ | ︙ | |||
572 573 574 575 576 577 578 | int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ int iCompare = 0; /* Result of last comparison */ unsigned nVmStep = 0; /* Number of virtual machine steps */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK | | < < < > > | 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 | int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ int iCompare = 0; /* Result of last comparison */ unsigned nVmStep = 0; /* Number of virtual machine steps */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK unsigned nProgressLimit; /* Invoke xProgress() when nVmStep reaches this */ #endif Mem *aMem = p->aMem; /* Copy of p->aMem */ Mem *pIn1 = 0; /* 1st input operand */ Mem *pIn2 = 0; /* 2nd input operand */ Mem *pIn3 = 0; /* 3rd input operand */ Mem *pOut = 0; /* Output operand */ #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ #endif /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ sqlite3VdbeEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ goto no_mem; } assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY ); assert( p->bIsReader || p->readOnly!=0 ); p->iCurrentTime = 0; assert( p->explain==0 ); p->pResultSet = 0; db->busyHandler.nBusy = 0; if( db->u1.isInterrupted ) goto abort_due_to_interrupt; sqlite3VdbeIOTraceSql(p); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( db->xProgress ){ u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; assert( 0 < db->nProgressOps ); nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); }else{ nProgressLimit = 0xffffffff; } #endif #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); if( p->pc==0 && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0 ){ |
︙ | ︙ | |||
766 767 768 769 770 771 772 | ** to the current line should be indented for EXPLAIN output. */ case OP_Goto: { /* jump */ jump_to_p2_and_check_for_interrupt: pOp = &aOp[pOp->p2 - 1]; /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev, | | | | 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 | ** to the current line should be indented for EXPLAIN output. */ case OP_Goto: { /* jump */ jump_to_p2_and_check_for_interrupt: pOp = &aOp[pOp->p2 - 1]; /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev, ** OP_VNext, or OP_SorterNext) all jump here upon ** completion. Check to see if sqlite3_interrupt() has been called ** or if the progress callback needs to be invoked. ** ** This code uses unstructured "goto" statements and does not look clean. ** But that is not due to sloppy coding habits. The code is written this ** way for performance, to avoid having to run the interrupt and progress ** checks on every opcode. This helps sqlite3_step() to run about 1.5% ** faster according to "valgrind --tool=cachegrind" */ check_for_interrupt: if( db->u1.isInterrupted ) goto abort_due_to_interrupt; #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* Call the progress callback if it is configured and the required number ** of VDBE ops have been executed (either since this invocation of ** sqlite3VdbeExec() or since last time the progress callback was called). ** If the progress callback returns non-zero, exit the virtual machine with ** a return code SQLITE_ABORT. */ if( nVmStep>=nProgressLimit && db->xProgress!=0 ){ assert( db->nProgressOps!=0 ); nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps); if( db->xProgress(db->pProgressArg) ){ rc = SQLITE_INTERRUPT; goto abort_due_to_error; } } |
︙ | ︙ | |||
956 957 958 959 960 961 962 | if( pOp->p1==SQLITE_OK && p->pFrame ){ /* Halt the sub-program. Return control to the parent frame. */ pFrame = p->pFrame; p->pFrame = pFrame->pParent; p->nFrame--; sqlite3VdbeSetChanges(db, p->nChange); pcx = sqlite3VdbeFrameRestore(pFrame); | < | | 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 | if( pOp->p1==SQLITE_OK && p->pFrame ){ /* Halt the sub-program. Return control to the parent frame. */ pFrame = p->pFrame; p->pFrame = pFrame->pParent; p->nFrame--; sqlite3VdbeSetChanges(db, p->nChange); pcx = sqlite3VdbeFrameRestore(pFrame); if( pOp->p2==OE_Ignore ){ /* Instruction pcx is the OP_Program that invoked the sub-program ** currently being halted. If the p2 instruction of this OP_Halt ** instruction is set to OE_Ignore, then the sub-program is throwing ** an IGNORE exception. In this case jump to the address specified ** as the p2 of the calling OP_Program. */ pcx = p->aOp[pcx].p2-1; } aOp = p->aOp; aMem = p->aMem; pOp = &aOp[pcx]; break; } p->rc = pOp->p1; p->errorAction = (u8)pOp->p2; p->pc = pcx; assert( pOp->p5<=4 ); if( p->rc ){ if( pOp->p5 ){ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", "FOREIGN KEY" }; testcase( pOp->p5==1 ); testcase( pOp->p5==2 ); testcase( pOp->p5==3 ); |
︙ | ︙ | |||
1155 1156 1157 1158 1159 1160 1161 | ** instruction, but do not free any string or blob memory associated with ** the register, so that if the value was a string or blob that was ** previously copied using OP_SCopy, the copies will continue to be valid. */ case OP_SoftNull: { assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); pOut = &aMem[pOp->p1]; | | | 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 | ** instruction, but do not free any string or blob memory associated with ** the register, so that if the value was a string or blob that was ** previously copied using OP_SCopy, the copies will continue to be valid. */ case OP_SoftNull: { assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); pOut = &aMem[pOp->p1]; pOut->flags = (pOut->flags&~(MEM_Undefined|MEM_AffMask))|MEM_Null; break; } /* Opcode: Blob P1 P2 * P4 * ** Synopsis: r[P2]=P4 (len=P1) ** ** P4 points to a blob of data P1 bytes long. Store this |
︙ | ︙ | |||
1186 1187 1188 1189 1190 1191 1192 | ** If the parameter is named, then its name appears in P4. ** The P4 value is used by sqlite3_bind_parameter_name(). */ case OP_Variable: { /* out2 */ Mem *pVar; /* Value being transferred */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); | | | | 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 | ** If the parameter is named, then its name appears in P4. ** The P4 value is used by sqlite3_bind_parameter_name(). */ case OP_Variable: { /* out2 */ Mem *pVar; /* Value being transferred */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); assert( pOp->p4.z==0 || pOp->p4.z==sqlite3VListNumToName(p->pVList,pOp->p1) ); pVar = &p->aVar[pOp->p1 - 1]; if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; } pOut = &aMem[pOp->p2]; sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Move P1 P2 P3 * * ** Synopsis: r[P2@P3]=r[P1@P3] |
︙ | ︙ | |||
1327 1328 1329 1330 1331 1332 1333 | assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* Run the progress counter just before returning. */ if( db->xProgress!=0 | | | 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 | assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* Run the progress counter just before returning. */ if( db->xProgress!=0 && nVmStep>=nProgressLimit && db->xProgress(db->pProgressArg)!=0 ){ rc = SQLITE_INTERRUPT; goto abort_due_to_error; } #endif |
︙ | ︙ | |||
1498 1499 1500 1501 1502 1503 1504 | pIn1 = &aMem[pOp->p1]; type1 = numericType(pIn1); pIn2 = &aMem[pOp->p2]; type2 = numericType(pIn2); pOut = &aMem[pOp->p3]; flags = pIn1->flags | pIn2->flags; | < | 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 | pIn1 = &aMem[pOp->p1]; type1 = numericType(pIn1); pIn2 = &aMem[pOp->p2]; type2 = numericType(pIn2); pOut = &aMem[pOp->p3]; flags = pIn1->flags | pIn2->flags; if( (type1 & type2 & MEM_Int)!=0 ){ iA = pIn1->u.i; iB = pIn2->u.i; bIntint = 1; switch( pOp->opcode ){ case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break; case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break; |
︙ | ︙ | |||
1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 | if( iA==-1 ) iA = 1; iB %= iA; break; } } pOut->u.i = iB; MemSetTypeFlag(pOut, MEM_Int); }else{ bIntint = 0; fp_math: rA = sqlite3VdbeRealValue(pIn1); rB = sqlite3VdbeRealValue(pIn2); switch( pOp->opcode ){ case OP_Add: rB += rA; break; | > > | 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 | if( iA==-1 ) iA = 1; iB %= iA; break; } } pOut->u.i = iB; MemSetTypeFlag(pOut, MEM_Int); }else if( (flags & MEM_Null)!=0 ){ goto arithmetic_result_is_null; }else{ bIntint = 0; fp_math: rA = sqlite3VdbeRealValue(pIn1); rB = sqlite3VdbeRealValue(pIn2); switch( pOp->opcode ){ case OP_Add: rB += rA; break; |
︙ | ︙ | |||
1569 1570 1571 1572 1573 1574 1575 | arithmetic_result_is_null: sqlite3VdbeMemSetNull(pOut); break; } /* Opcode: CollSeq P1 * * P4 ** | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | arithmetic_result_is_null: sqlite3VdbeMemSetNull(pOut); break; } /* Opcode: CollSeq P1 * * P4 ** ** P4 is a pointer to a CollSeq object. If the next call to a user function ** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will ** be returned. This is used by the built-in min(), max() and nullif() ** functions. ** ** If P1 is not zero, then it is a register that a subsequent min() or ** max() aggregate will set to 1 if the current row is not the minimum or ** maximum. The P1 register is initialized to 0 by this instruction. ** ** The interface used by the implementation of the aforementioned functions ** to retrieve the collation sequence set by this opcode is not available ** publicly. Only built-in functions have access to this feature. */ case OP_CollSeq: { assert( pOp->p4type==P4_COLLSEQ ); if( pOp->p1 ){ sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0); } break; } /* Opcode: BitAnd P1 P2 P3 * * ** Synopsis: r[P3]=r[P1]&r[P2] ** ** Take the bit-wise AND of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ |
︙ | ︙ | |||
1852 1853 1854 1855 1856 1857 1858 | #ifndef SQLITE_OMIT_CAST /* Opcode: Cast P1 P2 * * * ** Synopsis: affinity(r[P1]) ** ** Force the value in register P1 to be the type defined by P2. ** ** <ul> | | | | | | | 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 | #ifndef SQLITE_OMIT_CAST /* Opcode: Cast P1 P2 * * * ** Synopsis: affinity(r[P1]) ** ** Force the value in register P1 to be the type defined by P2. ** ** <ul> ** <li> P2=='A' → BLOB ** <li> P2=='B' → TEXT ** <li> P2=='C' → NUMERIC ** <li> P2=='D' → INTEGER ** <li> P2=='E' → REAL ** </ul> ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_Cast: { /* in1 */ assert( pOp->p2>=SQLITE_AFF_BLOB && pOp->p2<=SQLITE_AFF_REAL ); testcase( pOp->p2==SQLITE_AFF_TEXT ); |
︙ | ︙ | |||
2068 2069 2070 2071 2072 2073 2074 | flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask); } } assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } compare_op: | > > > > > > > > > > | | > | > > | < < < < | 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 | flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask); } } assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } compare_op: /* At this point, res is negative, zero, or positive if reg[P1] is ** less than, equal to, or greater than reg[P3], respectively. Compute ** the answer to this operator in res2, depending on what the comparison ** operator actually is. The next block of code depends on the fact ** that the 6 comparison operators are consecutive integers in this ** order: NE, EQ, GT, LE, LT, GE */ assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 ); assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 ); if( res<0 ){ /* ne, eq, gt, le, lt, ge */ static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 }; res2 = aLTb[pOp->opcode - OP_Ne]; }else if( res==0 ){ static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 }; res2 = aEQb[pOp->opcode - OP_Ne]; }else{ static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 }; res2 = aGTb[pOp->opcode - OP_Ne]; } /* Undo any changes made by applyAffinity() to the input registers. */ assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); pIn1->flags = flags1; assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); pIn3->flags = flags3; if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; iCompare = res; if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){ /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1 ** and prevents OP_Ne from overwriting NULL with 0. This flag ** is only used in contexts where either: ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0) ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1) ** Therefore it is not necessary to check the content of r[P2] for |
︙ | ︙ | |||
2136 2137 2138 2139 2140 2141 2142 | if( iCompare!=0 ) goto jump_to_p2; break; } /* Opcode: Permutation * * * P4 * ** | | | | > | 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 | if( iCompare!=0 ) goto jump_to_p2; break; } /* Opcode: Permutation * * * P4 * ** ** Set the permutation used by the OP_Compare operator in the next ** instruction. The permutation is stored in the P4 operand. ** ** The permutation is only valid until the next OP_Compare that has ** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should ** occur immediately prior to the OP_Compare. ** ** The first integer in the P4 integer array is the length of the array ** and does not become part of the permutation. */ case OP_Permutation: { assert( pOp->p4type==P4_INTARRAY ); assert( pOp->p4.ai ); assert( pOp[1].opcode==OP_Compare ); assert( pOp[1].p5 & OPFLAG_PERMUTE ); break; } /* Opcode: Compare P1 P2 P3 P4 P5 ** Synopsis: r[P1@P3] <-> r[P2@P3] ** ** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this |
︙ | ︙ | |||
2182 2183 2184 2185 2186 2187 2188 2189 | int i; int p1; int p2; const KeyInfo *pKeyInfo; int idx; CollSeq *pColl; /* Collating sequence to use on this term */ int bRev; /* True for DESCENDING sort order */ | > | > > > > > > > > | | < | 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 | int i; int p1; int p2; const KeyInfo *pKeyInfo; int idx; CollSeq *pColl; /* Collating sequence to use on this term */ int bRev; /* True for DESCENDING sort order */ int *aPermute; /* The permutation */ if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){ aPermute = 0; }else{ assert( pOp>aOp ); assert( pOp[-1].opcode==OP_Permutation ); assert( pOp[-1].p4type==P4_INTARRAY ); aPermute = pOp[-1].p4.ai + 1; assert( aPermute!=0 ); } n = pOp->p3; pKeyInfo = pOp->p4.pKeyInfo; assert( n>0 ); assert( pKeyInfo!=0 ); p1 = pOp->p1; p2 = pOp->p2; #ifdef SQLITE_DEBUG if( aPermute ){ int k, mx = 0; for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k]; assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); }else{ assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 ); assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 ); } #endif /* SQLITE_DEBUG */ for(i=0; i<n; i++){ idx = aPermute ? aPermute[i] : i; assert( memIsValid(&aMem[p1+idx]) ); assert( memIsValid(&aMem[p2+idx]) ); REGISTER_TRACE(p1+idx, &aMem[p1+idx]); REGISTER_TRACE(p2+idx, &aMem[p2+idx]); assert( i<pKeyInfo->nKeyField ); pColl = pKeyInfo->aColl[i]; bRev = pKeyInfo->aSortOrder[i]; iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); if( iCompare ){ if( bRev ) iCompare = -iCompare; break; } } break; } /* Opcode: Jump P1 P2 P3 * * ** ** Jump to the instruction at address P1, P2, or P3 depending on whether ** in the most recent OP_Compare instruction the P1 vector was less than |
︙ | ︙ | |||
2329 2330 2331 2332 2333 2334 2335 | pOut->u.i = ~sqlite3VdbeIntValue(pIn1); } break; } /* Opcode: Once P1 P2 * * * ** | > > > > > | | | > | > > > > > > > > | < | > > | > > | > > > | 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 | pOut->u.i = ~sqlite3VdbeIntValue(pIn1); } break; } /* Opcode: Once P1 P2 * * * ** ** Fall through to the next instruction the first time this opcode is ** encountered on each invocation of the byte-code program. Jump to P2 ** on the second and all subsequent encounters during the same invocation. ** ** Top-level programs determine first invocation by comparing the P1 ** operand against the P1 operand on the OP_Init opcode at the beginning ** of the program. If the P1 values differ, then fall through and make ** the P1 of this opcode equal to the P1 of OP_Init. If P1 values are ** the same then take the jump. ** ** For subprograms, there is a bitmask in the VdbeFrame that determines ** whether or not the jump should be taken. The bitmask is necessary ** because the self-altering code trick does not work for recursive ** triggers. */ case OP_Once: { /* jump */ u32 iAddr; /* Address of this instruction */ assert( p->aOp[0].opcode==OP_Init ); if( p->pFrame ){ iAddr = (int)(pOp - p->aOp); if( (p->pFrame->aOnce[iAddr/8] & (1<<(iAddr & 7)))!=0 ){ VdbeBranchTaken(1, 2); goto jump_to_p2; } p->pFrame->aOnce[iAddr/8] |= 1<<(iAddr & 7); }else{ if( p->aOp[0].p1==pOp->p1 ){ VdbeBranchTaken(1, 2); goto jump_to_p2; } } VdbeBranchTaken(0, 2); pOp->p1 = p->aOp[0].p1; break; } /* Opcode: If P1 P2 P3 * * ** ** Jump to P2 if the value in register P1 is true. The value ** is considered true if it is numeric and non-zero. If the value |
︙ | ︙ | |||
2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 | pIn1 = &aMem[pOp->p1]; VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); if( (pIn1->flags & MEM_Null)==0 ){ goto jump_to_p2; } break; } /* Opcode: Column P1 P2 P3 P4 P5 ** Synopsis: r[P3]=PX ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional ** information about the format of the data.) Extract the P2-th column ** from this record. If there are less that (P2+1) ** values in the record, extract a NULL. ** ** The value extracted is stored in register P3. ** | > > > > > > > > > > > > > > > > > > | | < < | > > < > > | | | > | > | < < | < < | < | | < | | | > | | > > > > > > > > > | > > > > > | < | > | 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 | pIn1 = &aMem[pOp->p1]; VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); if( (pIn1->flags & MEM_Null)==0 ){ goto jump_to_p2; } break; } /* Opcode: IfNullRow P1 P2 P3 * * ** Synopsis: if P1.nullRow then r[P3]=NULL, goto P2 ** ** Check the cursor P1 to see if it is currently pointing at a NULL row. ** If it is, then set register P3 to NULL and jump immediately to P2. ** If P1 is not on a NULL row, then fall through without making any ** changes. */ case OP_IfNullRow: { /* jump */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( p->apCsr[pOp->p1]!=0 ); if( p->apCsr[pOp->p1]->nullRow ){ sqlite3VdbeMemSetNull(aMem + pOp->p3); goto jump_to_p2; } break; } /* Opcode: Column P1 P2 P3 P4 P5 ** Synopsis: r[P3]=PX ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional ** information about the format of the data.) Extract the P2-th column ** from this record. If there are less that (P2+1) ** values in the record, extract a NULL. ** ** The value extracted is stored in register P3. ** ** If the record contains fewer than P2 fields, then extract a NULL. Or, ** if the P4 argument is a P4_MEM use the value of the P4 argument as ** the result. ** ** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor, ** then the cache of the cursor is reset prior to extracting the column. ** The first OP_Column against a pseudo-table after the value of the content ** register has changed should have this bit set. ** ** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then ** the result is guaranteed to only be used as the argument of a length() ** or typeof() function, respectively. The loading of large blobs can be ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { int p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ BtCursor *pCrsr; /* The BTree cursor */ u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ int len; /* The length of the serialized data for the column */ int i; /* Loop counter */ Mem *pDest; /* Where to write the extracted value */ Mem sMem; /* For storing the record being decoded */ const u8 *zData; /* Part of the record being decoded */ const u8 *zHdr; /* Next unparsed byte of the header */ const u8 *zEndHdr; /* Pointer to first byte after the header */ u64 offset64; /* 64-bit offset */ u32 t; /* A type code from the record header */ Mem *pReg; /* PseudoTable input register */ pC = p->apCsr[pOp->p1]; p2 = pOp->p2; /* If the cursor cache is stale (meaning it is not currently point at ** the correct row) then bring it up-to-date by doing the necessary ** B-Tree seek. */ rc = sqlite3VdbeCursorMoveto(&pC, &p2); if( rc ) goto abort_due_to_error; assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pC!=0 ); assert( p2<pC->nField ); aOffset = pC->aOffset; assert( pC->eCurType!=CURTYPE_VTAB ); assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); assert( pC->eCurType!=CURTYPE_SORTER ); if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/ if( pC->nullRow ){ if( pC->eCurType==CURTYPE_PSEUDO ){ /* For the special case of as pseudo-cursor, the seekResult field ** identifies the register that holds the record */ assert( pC->seekResult>0 ); pReg = &aMem[pC->seekResult]; assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); pC->payloadSize = pC->szRow = pReg->n; pC->aRow = (u8*)pReg->z; }else{ sqlite3VdbeMemSetNull(pDest); goto op_column_out; } }else{ pCrsr = pC->uc.pCursor; assert( pC->eCurType==CURTYPE_BTREE ); assert( pCrsr ); assert( sqlite3BtreeCursorIsValid(pCrsr) ); pC->payloadSize = sqlite3BtreePayloadSize(pCrsr); pC->aRow = sqlite3BtreePayloadFetch(pCrsr, &pC->szRow); assert( pC->szRow<=pC->payloadSize ); assert( pC->szRow<=65536 ); /* Maximum page size is 64KiB */ if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } } pC->cacheStatus = p->cacheCtr; pC->iHdrOffset = getVarint32(pC->aRow, aOffset[0]); pC->nHdrParsed = 0; if( pC->szRow<aOffset[0] ){ /*OPTIMIZATION-IF-FALSE*/ /* pC->aRow does not have to hold the entire row, but it does at least ** need to cover the header of the record. If pC->aRow does not contain ** the complete header, then set it to zero, forcing the header to be ** dynamically allocated. */ pC->aRow = 0; pC->szRow = 0; /* Make sure a corrupt database has not given us an oversize header. ** Do this now to avoid an oversize memory allocation. ** ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte ** types use so much data space that there can only be 4096 and 32 of ** them, respectively. So the maximum header length results from a ** 3-byte type for each of the maximum of 32768 columns plus three ** extra bytes for the header length itself. 32768*3 + 3 = 98307. */ if( aOffset[0] > 98307 || aOffset[0] > pC->payloadSize ){ goto op_column_corrupt; } }else{ /* This is an optimization. By skipping over the first few tests ** (ex: pC->nHdrParsed<=p2) in the next section, we achieve a ** measurable performance gain. ** ** This branch is taken even if aOffset[0]==0. Such a record is never ** generated by SQLite, and could be considered corruption, but we ** accept it for historical reasons. When aOffset[0]==0, the code this ** branch jumps to reads past the end of the record, but never more ** than a few bytes. Even if the record occurs at the end of the page ** content area, the "page header" comes after the page content and so ** this overread is harmless. Similar overreads can occur for a corrupt ** database file. */ zData = pC->aRow; assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */ testcase( aOffset[0]==0 ); goto op_column_read_header; } } /* Make sure at least the first p2+1 entries of the header have been ** parsed and valid information is in aOffset[] and pC->aType[]. */ if( pC->nHdrParsed<=p2 ){ /* If there is more header available for parsing in the record, try ** to extract additional fields up through the p2+1-th field */ if( pC->iHdrOffset<aOffset[0] ){ /* Make sure zData points to enough of the record to cover the header. */ if( pC->aRow==0 ){ memset(&sMem, 0, sizeof(sMem)); rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, 0, aOffset[0], &sMem); if( rc!=SQLITE_OK ) goto abort_due_to_error; zData = (u8*)sMem.z; }else{ zData = pC->aRow; } /* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */ op_column_read_header: i = pC->nHdrParsed; offset64 = aOffset[i]; zHdr = zData + pC->iHdrOffset; zEndHdr = zData + aOffset[0]; testcase( zHdr>=zEndHdr ); do{ if( (t = zHdr[0])<0x80 ){ zHdr++; offset64 += sqlite3VdbeOneByteSerialTypeLen(t); }else{ zHdr += sqlite3GetVarint32(zHdr, &t); offset64 += sqlite3VdbeSerialTypeLen(t); } pC->aType[i++] = t; aOffset[i] = (u32)(offset64 & 0xffffffff); }while( i<=p2 && zHdr<zEndHdr ); /* The record is corrupt if any of the following are true: ** (1) the bytes of the header extend past the declared header size ** (2) the entire header was used but not all data was used ** (3) the end of the data extends beyond the end of the record. */ if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize)) || (offset64 > pC->payloadSize) ){ if( aOffset[0]==0 ){ i = 0; zHdr = zEndHdr; }else{ if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem); goto op_column_corrupt; } } pC->nHdrParsed = i; pC->iHdrOffset = (u32)(zHdr - zData); if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem); }else{ t = 0; |
︙ | ︙ | |||
2654 2655 2656 2657 2658 2659 2660 | || (len = sqlite3VdbeSerialTypeLen(t))==0 ){ /* Content is irrelevant for ** 1. the typeof() function, ** 2. the length(X) function if X is a blob, and ** 3. if the content length is zero. ** So we might as well use bogus content rather than reading | | > > > > > | | < > > > > > > > > > | | < > < > | < > | | | 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 | || (len = sqlite3VdbeSerialTypeLen(t))==0 ){ /* Content is irrelevant for ** 1. the typeof() function, ** 2. the length(X) function if X is a blob, and ** 3. if the content length is zero. ** So we might as well use bogus content rather than reading ** content from disk. ** ** Although sqlite3VdbeSerialGet() may read at most 8 bytes from the ** buffer passed to it, debugging function VdbeMemPrettyPrint() may ** read up to 16. So 16 bytes of bogus content is supplied. */ static u8 aZero[16]; /* This is the bogus content */ sqlite3VdbeSerialGet(aZero, t, pDest); }else{ rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, aOffset[p2], len, pDest); if( rc!=SQLITE_OK ) goto abort_due_to_error; sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); pDest->flags &= ~MEM_Ephem; } } op_column_out: UPDATE_MAX_BLOBSIZE(pDest); REGISTER_TRACE(pOp->p3, pDest); break; op_column_corrupt: if( aOp[0].p3>0 ){ pOp = &aOp[aOp[0].p3-1]; break; }else{ rc = SQLITE_CORRUPT_BKPT; goto abort_due_to_error; } } /* Opcode: Affinity P1 P2 * P4 * ** Synopsis: affinity(r[P1@P2]) ** ** Apply affinities to a range of P2 registers starting with P1. ** ** P4 is a string that is P2 characters long. The N-th character of the ** string indicates the column affinity that should be used for the N-th ** memory cell in the range. */ case OP_Affinity: { const char *zAffinity; /* The affinity to be applied */ zAffinity = pOp->p4.z; assert( zAffinity!=0 ); assert( pOp->p2>0 ); assert( zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; do{ assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] ); assert( memIsValid(pIn1) ); applyAffinity(pIn1, *(zAffinity++), encoding); pIn1++; }while( zAffinity[0] ); break; } /* Opcode: MakeRecord P1 P2 P3 P4 * ** Synopsis: r[P3]=mkrec(r[P1@P2]) ** ** Convert P2 registers beginning with P1 into the [record format] ** use as a data record in a database table or as a key ** in an index. The OP_Column opcode can decode the record later. ** ** P4 may be a string that is P2 characters long. The N-th character of the ** string indicates the column affinity that should be used for the N-th ** field of the index key. ** ** The mapping from character to affinity is given by the SQLITE_AFF_ ** macros defined in sqliteInt.h. ** ** If P4 is NULL then all index fields have the affinity BLOB. */ |
︙ | ︙ | |||
2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 | if( zAffinity ){ pRec = pData0; do{ applyAffinity(pRec++, *(zAffinity++), encoding); assert( zAffinity[0]==0 || pRec<=pLast ); }while( zAffinity[0] ); } /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ pRec = pLast; do{ assert( memIsValid(pRec) ); | > > > > > > > > > > > > > > | 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 | if( zAffinity ){ pRec = pData0; do{ applyAffinity(pRec++, *(zAffinity++), encoding); assert( zAffinity[0]==0 || pRec<=pLast ); }while( zAffinity[0] ); } #ifdef SQLITE_ENABLE_NULL_TRIM /* NULLs can be safely trimmed from the end of the record, as long as ** as the schema format is 2 or more and none of the omitted columns ** have a non-NULL default value. Also, the record must be left with ** at least one field. If P5>0 then it will be one more than the ** index of the right-most column with a non-NULL default value */ if( pOp->p5 ){ while( (pLast->flags & MEM_Null)!=0 && nField>pOp->p5 ){ pLast--; nField--; } } #endif /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ pRec = pLast; do{ assert( memIsValid(pRec) ); |
︙ | ︙ | |||
2851 2852 2853 2854 2855 2856 2857 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pOut->n = (int)nByte; pOut->flags = MEM_Blob; if( nZero ){ pOut->u.nZero = nZero; pOut->flags |= MEM_Zero; } | < | 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pOut->n = (int)nByte; pOut->flags = MEM_Blob; if( nZero ){ pOut->u.nZero = nZero; pOut->flags |= MEM_Zero; } REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Count P1 P2 * * * ** Synopsis: r[P2]=count() |
︙ | ︙ | |||
2997 2998 2999 3000 3001 3002 3003 | } db->isTransactionSavepoint = 0; rc = p->rc; }else{ int isSchemaChange; iSavepoint = db->nSavepoint - iSavepoint - 1; if( p1==SAVEPOINT_ROLLBACK ){ | | | | 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 | } db->isTransactionSavepoint = 0; rc = p->rc; }else{ int isSchemaChange; iSavepoint = db->nSavepoint - iSavepoint - 1; if( p1==SAVEPOINT_ROLLBACK ){ isSchemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0; for(ii=0; ii<db->nDb; ii++){ rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT_ROLLBACK, isSchemaChange==0); if( rc!=SQLITE_OK ) goto abort_due_to_error; } }else{ isSchemaChange = 0; } for(ii=0; ii<db->nDb; ii++){ rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } if( isSchemaChange ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetAllSchemasOfConnection(db); db->mDbFlags |= DBFLAG_SchemaChange; } } /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ while( db->pSavepoint!=pSavepoint ){ pTmp = db->pSavepoint; |
︙ | ︙ | |||
3296 3297 3298 3299 3300 3301 3302 | assert( pDb->pBt!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); /* See note about index shifting on OP_ReadCookie */ rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3); if( pOp->p2==BTREE_SCHEMA_VERSION ){ /* When the schema cookie changes, record the new cookie internally */ pDb->pSchema->schema_cookie = pOp->p3; | | | 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 | assert( pDb->pBt!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); /* See note about index shifting on OP_ReadCookie */ rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3); if( pOp->p2==BTREE_SCHEMA_VERSION ){ /* When the schema cookie changes, record the new cookie internally */ pDb->pSchema->schema_cookie = pOp->p3; db->mDbFlags |= DBFLAG_SchemaChange; }else if( pOp->p2==BTREE_FILE_FORMAT ){ /* Record changes in the file format */ pDb->pSchema->file_format = pOp->p3; } if( pOp->p1==1 ){ /* Invalidate all prepared statements whenever the TEMP database ** schema is changed. Ticket #1644 */ |
︙ | ︙ | |||
3435 3436 3437 3438 3439 3440 3441 | assert( p2>0 ); assert( p2<=(p->nMem+1 - p->nCursor) ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); p2 = (int)pIn2->u.i; | | | | 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 | assert( p2>0 ); assert( p2<=(p->nMem+1 - p->nCursor) ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); p2 = (int)pIn2->u.i; /* The p2 value always comes from a prior OP_CreateBtree opcode and ** that opcode will always set the p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ assert( p2>=2 ); } if( pOp->p4type==P4_KEYINFO ){ pKeyInfo = pOp->p4.pKeyInfo; assert( pKeyInfo->enc==ENC(db) ); assert( pKeyInfo->db==db ); nField = pKeyInfo->nAllField; }else if( pOp->p4type==P4_INT32 ){ nField = pOp->p4.i; } assert( pOp->p1>=0 ); assert( nField>=0 ); testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE); |
︙ | ︙ | |||
3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 | testcase( pOp->p2 & OPFLAG_SEEKEQ ); #endif sqlite3BtreeCursorHintFlags(pCur->uc.pCursor, (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); if( rc ) goto abort_due_to_error; break; } /* Opcode: OpenEphemeral P1 P2 * P4 P5 ** Synopsis: nColumn=P2 ** ** Open a new cursor P1 to a transient table. ** The cursor is always opened read/write even if ** the main database is read-only. The ephemeral | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | testcase( pOp->p2 & OPFLAG_SEEKEQ ); #endif sqlite3BtreeCursorHintFlags(pCur->uc.pCursor, (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); if( rc ) goto abort_due_to_error; break; } /* Opcode: OpenDup P1 P2 * * * ** ** Open a new cursor P1 that points to the same ephemeral table as ** cursor P2. The P2 cursor must have been opened by a prior OP_OpenEphemeral ** opcode. Only ephemeral cursors may be duplicated. ** ** Duplicate ephemeral cursors are used for self-joins of materialized views. */ case OP_OpenDup: { VdbeCursor *pOrig; /* The original cursor to be duplicated */ VdbeCursor *pCx; /* The new cursor */ pOrig = p->apCsr[pOp->p2]; assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */ pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->isEphemeral = 1; pCx->pKeyInfo = pOrig->pKeyInfo; pCx->isTable = pOrig->isTable; rc = sqlite3BtreeCursor(pOrig->pBtx, MASTER_ROOT, BTREE_WRCSR, pCx->pKeyInfo, pCx->uc.pCursor); /* The sqlite3BtreeCursor() routine can only fail for the first cursor ** opened for a database. Since there is already an open cursor when this ** opcode is run, the sqlite3BtreeCursor() cannot fail */ assert( rc==SQLITE_OK ); break; } /* Opcode: OpenEphemeral P1 P2 * P4 P5 ** Synopsis: nColumn=P2 ** ** Open a new cursor P1 to a transient table. ** The cursor is always opened read/write even if ** the main database is read-only. The ephemeral |
︙ | ︙ | |||
3524 3525 3526 3527 3528 3529 3530 | SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); assert( pOp->p2>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->isEphemeral = 1; | | | | | < | | | 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 | SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); assert( pOp->p2>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->isEphemeral = 1; rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1); } if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ** opening it. If a transient table is required, just use the ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ int pgno; assert( pOp->p4type==P4_KEYINFO ); rc = sqlite3BtreeCreateTable(pCx->pBtx, &pgno, BTREE_BLOBKEY | pOp->p5); if( rc==SQLITE_OK ){ assert( pgno==MASTER_ROOT+1 ); assert( pKeyInfo->db==db ); assert( pKeyInfo->enc==ENC(db) ); rc = sqlite3BtreeCursor(pCx->pBtx, pgno, BTREE_WRCSR, pKeyInfo, pCx->uc.pCursor); } pCx->isTable = 0; }else{ rc = sqlite3BtreeCursor(pCx->pBtx, MASTER_ROOT, BTREE_WRCSR, 0, pCx->uc.pCursor); pCx->isTable = 1; } } if( rc ) goto abort_due_to_error; pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); break; |
︙ | ︙ | |||
3626 3627 3628 3629 3630 3631 3632 | VdbeCursor *pCx; assert( pOp->p1>=0 ); assert( pOp->p3>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; | | > > > > > | 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 | VdbeCursor *pCx; assert( pOp->p1>=0 ); assert( pOp->p3>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->seekResult = pOp->p2; pCx->isTable = 1; /* Give this pseudo-cursor a fake BtCursor pointer so that pCx ** can be safely passed to sqlite3VdbeCursorMoveto(). This avoids a test ** for pCx->eCurType==CURTYPE_BTREE inside of sqlite3VdbeCursorMoveto() ** which is a performance optimization */ pCx->uc.pCursor = sqlite3BtreeFakeValidCursor(); assert( pOp->p5==0 ); break; } /* Opcode: Close P1 * * * * ** ** Close a cursor previously opened as P1. If P1 is not |
︙ | ︙ | |||
3781 3782 3783 3784 3785 3786 3787 | pC->nullRow = 0; #ifdef SQLITE_DEBUG pC->seekOp = pOp->opcode; #endif if( pC->isTable ){ /* The BTREE_SEEK_EQ flag is only set on index cursors */ | | > | 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 | pC->nullRow = 0; #ifdef SQLITE_DEBUG pC->seekOp = pOp->opcode; #endif if( pC->isTable ){ /* The BTREE_SEEK_EQ flag is only set on index cursors */ assert( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0 || CORRUPT_DB ); /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do ** the seek, so convert it. */ pIn3 = &aMem[pOp->p3]; if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn3, 0); |
︙ | ︙ | |||
3886 3887 3888 3889 3890 3891 3892 | pC->cacheStatus = CACHE_STALE; #ifdef SQLITE_TEST sqlite3_search_count++; #endif if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT ); if( res<0 || (res==0 && oc==OP_SeekGT) ){ res = 0; | | | > > > > > > > | | > > > > > > > | 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 | pC->cacheStatus = CACHE_STALE; #ifdef SQLITE_TEST sqlite3_search_count++; #endif if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT ); if( res<0 || (res==0 && oc==OP_SeekGT) ){ res = 0; rc = sqlite3BtreeNext(pC->uc.pCursor, 0); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; res = 1; }else{ goto abort_due_to_error; } } }else{ res = 0; } }else{ assert( oc==OP_SeekLT || oc==OP_SeekLE ); if( res>0 || (res==0 && oc==OP_SeekLT) ){ res = 0; rc = sqlite3BtreePrevious(pC->uc.pCursor, 0); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; res = 1; }else{ goto abort_due_to_error; } } }else{ /* res might be negative because the table is empty. Check to ** see if this is the case. */ res = sqlite3BtreeEof(pC->uc.pCursor); } } |
︙ | ︙ | |||
3983 3984 3985 3986 3987 3988 3989 | case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ int alreadyExists; int takeJump; int ii; VdbeCursor *pC; int res; | | < < > > > > > | < < < < | | 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 | case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ int alreadyExists; int takeJump; int ii; VdbeCursor *pC; int res; UnpackedRecord *pFree; UnpackedRecord *pIdxKey; UnpackedRecord r; #ifdef SQLITE_TEST if( pOp->opcode!=OP_NoConflict ) sqlite3_found_count++; #endif assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pOp->p4type==P4_INT32 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); #ifdef SQLITE_DEBUG pC->seekOp = pOp->opcode; #endif pIn3 = &aMem[pOp->p3]; assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->uc.pCursor!=0 ); assert( pC->isTable==0 ); if( pOp->p4.i>0 ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; r.aMem = pIn3; #ifdef SQLITE_DEBUG for(ii=0; ii<r.nField; ii++){ assert( memIsValid(&r.aMem[ii]) ); assert( (r.aMem[ii].flags & MEM_Zero)==0 || r.aMem[ii].n==0 ); if( ii ) REGISTER_TRACE(pOp->p3+ii, &r.aMem[ii]); } #endif pIdxKey = &r; pFree = 0; }else{ assert( pIn3->flags & MEM_Blob ); rc = ExpandBlob(pIn3); assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); if( rc ) goto no_mem; pFree = pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); if( pIdxKey==0 ) goto no_mem; sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); } pIdxKey->default_rc = 0; takeJump = 0; if( pOp->opcode==OP_NoConflict ){ /* For the OP_NoConflict opcode, take the jump if any of the ** input fields are NULL, since any key with a NULL will not ** conflict */ for(ii=0; ii<pIdxKey->nField; ii++){ if( pIdxKey->aMem[ii].flags & MEM_Null ){ takeJump = 1; break; } } } rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res); if( pFree ) sqlite3DbFreeNN(db, pFree); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } pC->seekResult = res; alreadyExists = (res==0); pC->nullRow = 1-alreadyExists; pC->deferredMoveto = 0; |
︙ | ︙ | |||
4266 4267 4268 4269 4270 4271 4272 | } assert( memIsValid(pMem) ); REGISTER_TRACE(pOp->p3, pMem); sqlite3VdbeMemIntegerify(pMem); assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ | | | 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 | } assert( memIsValid(pMem) ); REGISTER_TRACE(pOp->p3, pMem); sqlite3VdbeMemIntegerify(pMem); assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ rc = SQLITE_FULL; /* IMP: R-17817-00630 */ goto abort_due_to_error; } if( v<pMem->u.i+1 ){ v = pMem->u.i + 1; } pMem->u.i = v; } |
︙ | ︙ | |||
4318 4319 4320 4321 4322 4323 4324 | ** be a MEM_Int. ** ** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is ** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set, ** then rowid is stored for subsequent return by the ** sqlite3_last_insert_rowid() function (otherwise it is unmodified). ** | | | | < | < < < < | 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 | ** be a MEM_Int. ** ** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is ** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set, ** then rowid is stored for subsequent return by the ** sqlite3_last_insert_rowid() function (otherwise it is unmodified). ** ** If the OPFLAG_USESEEKRESULT flag of P5 is set, the implementation might ** run faster by avoiding an unnecessary seek on cursor P1. However, ** the OPFLAG_USESEEKRESULT flag must only be set if there have been no prior ** seeks on the cursor or if the most recent seek used a key equal to P3. ** ** If the OPFLAG_ISUPDATE flag is set, then this opcode is part of an ** UPDATE operation. Otherwise (if the flag is clear) then this opcode ** is part of an INSERT operation. The difference is only important to ** the update hook. ** ** Parameter P4 may point to a Table structure, or may be NULL. If it is |
︙ | ︙ | |||
4371 4372 4373 4374 4375 4376 4377 | pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( memIsValid(pData) ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->uc.pCursor!=0 ); | | < | | > | | | 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 | pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( memIsValid(pData) ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->uc.pCursor!=0 ); assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable ); assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC ); REGISTER_TRACE(pOp->p2, pData); if( pOp->opcode==OP_Insert ){ pKey = &aMem[pOp->p3]; assert( pKey->flags & MEM_Int ); assert( memIsValid(pKey) ); REGISTER_TRACE(pOp->p3, pKey); x.nKey = pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); x.nKey = pOp->p3; } if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ assert( pC->iDb>=0 ); zDb = db->aDb[pC->iDb].zDbSName; pTab = pOp->p4.pTab; assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) ); op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); }else{ pTab = 0; /* Not needed. Silence a compiler warning. */ zDb = 0; /* Not needed. Silence a compiler warning. */ } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* Invoke the pre-update hook, if any */ if( db->xPreUpdateCallback && pOp->p4type==P4_TABLE && !(pOp->p5 & OPFLAG_ISUPDATE) ){ sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey, pOp->p2); } if( pOp->p5 & OPFLAG_ISNOOP ) break; #endif if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; if( pData->flags & MEM_Null ){ x.pData = 0; x.nData = 0; }else{ assert( pData->flags & (MEM_Blob|MEM_Str) ); x.pData = pData->z; x.nData = pData->n; } seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); if( pData->flags & MEM_Zero ){ x.nZero = pData->u.nZero; }else{ x.nZero = 0; } x.pKey = 0; rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult ); pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc ) goto abort_due_to_error; if( db->xUpdateCallback && op ){ |
︙ | ︙ | |||
4518 4519 4520 4521 4522 4523 4524 | }else{ zDb = 0; /* Not needed. Silence a compiler warning. */ pTab = 0; /* Not needed. Silence a compiler warning. */ } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* Invoke the pre-update-hook if required. */ | | | > > > | 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 | }else{ zDb = 0; /* Not needed. Silence a compiler warning. */ pTab = 0; /* Not needed. Silence a compiler warning. */ } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* Invoke the pre-update-hook if required. */ if( db->xPreUpdateCallback && pOp->p4.pTab ){ assert( !(opflags & OPFLAG_ISUPDATE) || HasRowid(pTab)==0 || (aMem[pOp->p3].flags & MEM_Int) ); sqlite3VdbePreUpdateHook(p, pC, (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, zDb, pTab, pC->movetoTarget, pOp->p3 ); } if( opflags & OPFLAG_ISNOOP ) break; |
︙ | ︙ | |||
4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 | nExtraDelete--; } } #endif rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); pC->cacheStatus = CACHE_STALE; if( rc ) goto abort_due_to_error; /* Invoke the update-hook if required. */ if( opflags & OPFLAG_NCHANGE ){ p->nChange++; if( db->xUpdateCallback && HasRowid(pTab) ){ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName, | > | 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 | nExtraDelete--; } } #endif rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); pC->cacheStatus = CACHE_STALE; pC->seekResult = 0; if( rc ) goto abort_due_to_error; /* Invoke the update-hook if required. */ if( opflags & OPFLAG_NCHANGE ){ p->nChange++; if( db->xUpdateCallback && HasRowid(pTab) ){ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName, |
︙ | ︙ | |||
4636 4637 4638 4639 4640 4641 4642 | assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) ); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); if( rc ) goto abort_due_to_error; p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE; break; } | | | > > > > < < < | | | > > > | > > > | < < | < < < < | < < < < < < | < < < < > | 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 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 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 | assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) ); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); if( rc ) goto abort_due_to_error; p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE; break; } /* Opcode: RowData P1 P2 P3 * * ** Synopsis: r[P2]=data ** ** Write into register P2 the complete row content for the row at ** which cursor P1 is currently pointing. ** There is no interpretation of the data. ** It is just copied onto the P2 register exactly as ** it is found in the database file. ** ** If cursor P1 is an index, then the content is the key of the row. ** If cursor P2 is a table, then the content extracted is the data. ** ** If the P1 cursor must be pointing to a valid row (not a NULL row) ** of a real table, not a pseudo-table. ** ** If P3!=0 then this opcode is allowed to make an ephermeral pointer ** into the database page. That means that the content of the output ** register will be invalidated as soon as the cursor moves - including ** moves caused by other cursors that "save" the the current cursors ** position in order that they can write to the same table. If P3==0 ** then a copy of the data is made into memory. P3!=0 is faster, but ** P3==0 is safer. ** ** If P3!=0 then the content of the P2 register is unsuitable for use ** in OP_Result and any OP_Result will invalidate the P2 register content. ** The P2 register content is invalidated by opcodes like OP_Function or ** by any use of another cursor pointing to the same table. */ case OP_RowData: { VdbeCursor *pC; BtCursor *pCrsr; u32 n; pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); assert( isSorter(pC)==0 ); assert( pC->nullRow==0 ); assert( pC->uc.pCursor!=0 ); pCrsr = pC->uc.pCursor; /* The OP_RowData opcodes always follow OP_NotExists or ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions ** that might invalidate the cursor. ** If this where not the case, on of the following assert()s ** would fail. Should this ever change (because of changes in the code ** generator) then the fix would be to insert a call to ** sqlite3VdbeCursorMoveto(). */ assert( pC->deferredMoveto==0 ); assert( sqlite3BtreeCursorIsValid(pCrsr) ); #if 0 /* Not required due to the previous to assert() statements */ rc = sqlite3VdbeCursorMoveto(pC); if( rc!=SQLITE_OK ) goto abort_due_to_error; #endif n = sqlite3BtreePayloadSize(pCrsr); if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } testcase( n==0 ); rc = sqlite3VdbeMemFromBtree(pCrsr, 0, n, pOut); if( rc ) goto abort_due_to_error; if( !pOp->p3 ) Deephemeralize(pOut); UPDATE_MAX_BLOBSIZE(pOut); REGISTER_TRACE(pOp->p2, pOut); break; } /* Opcode: Rowid P1 P2 * * * ** Synopsis: r[P2]=rowid |
︙ | ︙ | |||
4788 4789 4790 4791 4792 4793 4794 | if( pC->eCurType==CURTYPE_BTREE ){ assert( pC->uc.pCursor!=0 ); sqlite3BtreeClearCursor(pC->uc.pCursor); } break; } | > > > > > > > > > > | > > > > > > > > > > > < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 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 | if( pC->eCurType==CURTYPE_BTREE ){ assert( pC->uc.pCursor!=0 ); sqlite3BtreeClearCursor(pC->uc.pCursor); } break; } /* Opcode: SeekEnd P1 * * * * ** ** Position cursor P1 at the end of the btree for the purpose of ** appending a new entry onto the btree. ** ** It is assumed that the cursor is used only for appending and so ** if the cursor is valid, then the cursor must already be pointing ** at the end of the btree and so no changes are made to ** the cursor. */ /* Opcode: Last P1 P2 * * * ** ** The next use of the Rowid or Column or Prev instruction for P1 ** will refer to the last entry in the database table or index. ** If the table or index is empty and P2>0, then jump immediately to P2. ** If P2 is 0 or if the table or index is not empty, fall through ** 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: case OP_Last: { /* jump */ VdbeCursor *pC; BtCursor *pCrsr; int res; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); pCrsr = pC->uc.pCursor; res = 0; assert( pCrsr!=0 ); #ifdef SQLITE_DEBUG pC->seekOp = pOp->opcode; #endif if( pOp->opcode==OP_SeekEnd ){ assert( pOp->p2==0 ); pC->seekResult = -1; if( sqlite3BtreeCursorIsValidNN(pCrsr) ){ break; } } rc = sqlite3BtreeLast(pCrsr, &res); pC->nullRow = (u8)res; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; if( rc ) goto abort_due_to_error; if( pOp->p2>0 ){ VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; } break; } /* Opcode: IfSmaller P1 P2 P3 * * ** ** Estimate the number of rows in the table P1. Jump to P2 if that ** estimate is less than approximately 2**(0.1*P3). */ case OP_IfSmaller: { /* jump */ VdbeCursor *pC; BtCursor *pCrsr; int res; i64 sz; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); 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 = sqlite3BtreeRowCountEst(pCrsr); if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)<pOp->p3 ) res = 1; } VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; break; } /* Opcode: SorterSort P1 P2 * * * ** ** After all records have been inserted into the Sorter object ** identified by P1, invoke this opcode to actually do the sorting. ** Jump to P2 if there are no records to be sorted. ** ** This opcode is an alias for OP_Sort and OP_Rewind that is used ** for Sorter objects. */ /* Opcode: Sort P1 P2 * * * ** ** This opcode does exactly the same thing as OP_Rewind except that ** it increments an undocumented global variable used for testing. ** ** Sorting is accomplished by writing records into a sorting index, ** then rewinding that index and playing it back from beginning to |
︙ | ︙ | |||
4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 | if( rc ) goto abort_due_to_error; pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2<p->nOp ); VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; break; } /* Opcode: Next P1 P2 P3 P4 P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its ** table or index. If there are no more key/value pairs then fall through ** to the following instruction. But if the cursor advance was successful, ** jump immediately to P2. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( rc ) goto abort_due_to_error; pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2<p->nOp ); VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; break; } /* ** Opcode: EstRowCnt P1 P2 P3 * * * ** ** Estimate the number of entries in btree for cursor P1 do a proportional ** seek to of P3. Store the result as a floating point value in P2. */ case OP_EstRowCnt: { /* out2 */ VdbeCursor *pC; BtCursor *pCrsr; int rc; sqlite3_uint64 n = 0; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC->eCurType==CURTYPE_BTREE ); pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeMovetoProportional(pCrsr, pOp->p3, &n); if( rc ) goto abort_due_to_error; pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Real; pOut->u.r = n; pC->nullRow = 0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; break; } /* Opcode: Next P1 P2 P3 P4 P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its ** table or index. If there are no more key/value pairs then fall through ** to the following instruction. But if the cursor advance was successful, ** jump immediately to P2. |
︙ | ︙ | |||
4956 4957 4958 4959 4960 4961 4962 4963 4964 | ** number P5-1 in the prepared statement is incremented. */ /* Opcode: PrevIfOpen P1 P2 P3 P4 P5 ** ** This opcode works just like Prev except that if cursor P1 is not ** open it behaves a no-op. */ case OP_SorterNext: { /* jump */ VdbeCursor *pC; | > > > > > > > < < | < < < | | < | < < > > > | > > > > > | | | | > > | | 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 | ** number P5-1 in the prepared statement is incremented. */ /* Opcode: PrevIfOpen P1 P2 P3 P4 P5 ** ** This opcode works just like Prev except that if cursor P1 is not ** open it behaves a no-op. */ /* Opcode: SorterNext P1 P2 * * P5 ** ** This opcode works just like OP_Next except that P1 must be a ** sorter object for which the OP_SorterSort opcode has been ** invoked. This opcode advances the cursor to the next sorted ** record, or jumps to P2 if there are no more sorted records. */ case OP_SorterNext: { /* jump */ VdbeCursor *pC; pC = p->apCsr[pOp->p1]; assert( isSorter(pC) ); rc = sqlite3VdbeSorterNext(db, pC); goto next_tail; case OP_PrevIfOpen: /* jump */ case OP_NextIfOpen: /* jump */ if( p->apCsr[pOp->p1]==0 ) break; /* Fall through */ case OP_Prev: /* jump */ case OP_Next: /* jump */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pOp->p5<ArraySize(p->aCounter) ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->deferredMoveto==0 ); assert( pC->eCurType==CURTYPE_BTREE ); assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext ); assert( pOp->opcode!=OP_PrevIfOpen || pOp->p4.xAdvance==sqlite3BtreePrevious); /* The Next opcode is only used after SeekGT, SeekGE, and Rewind. ** The Prev opcode is only used after SeekLT, SeekLE, and Last. */ assert( pOp->opcode!=OP_Next || pOp->opcode!=OP_NextIfOpen || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found); assert( pOp->opcode!=OP_Prev || pOp->opcode!=OP_PrevIfOpen || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE || pC->seekOp==OP_Last ); rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3); next_tail: pC->cacheStatus = CACHE_STALE; VdbeBranchTaken(rc==SQLITE_OK,2); if( rc==SQLITE_OK ){ pC->nullRow = 0; p->aCounter[pOp->p5]++; #ifdef SQLITE_TEST sqlite3_search_count++; #endif goto jump_to_p2_and_check_for_interrupt; } if( rc!=SQLITE_DONE ) goto abort_due_to_error; rc = SQLITE_OK; pC->nullRow = 1; goto check_for_interrupt; } /* Opcode: IdxInsert P1 P2 P3 P4 P5 ** Synopsis: key=r[P2] ** ** Register P2 holds an SQL index key made using the ** MakeRecord instructions. This opcode writes that key ** into the index P1. Data for the entry is nil. ** ** If P4 is not zero, then it is the number of values in the unpacked ** key of reg(P2). In that case, P3 is the index of the first register ** for the unpacked key. The availability of the unpacked key can sometimes ** be an optimization. ** ** If P5 has the OPFLAG_APPEND bit set, that is a hint to the b-tree layer ** that this insert is likely to be an append. ** ** If P5 has the OPFLAG_NCHANGE bit set, then the change counter is ** incremented by this instruction. If the OPFLAG_NCHANGE bit is clear, ** then the change counter is unchanged. ** ** If the OPFLAG_USESEEKRESULT flag of P5 is set, the implementation might ** run faster by avoiding an unnecessary seek on cursor P1. However, ** the OPFLAG_USESEEKRESULT flag must only be set if there have been no prior ** seeks on the cursor or if the most recent seek used a key equivalent ** to P2. ** ** This instruction only works for indices. The equivalent instruction ** for tables is OP_Insert. */ /* Opcode: SorterInsert P1 P2 * * * ** Synopsis: key=r[P2] ** |
︙ | ︙ | |||
5061 5062 5063 5064 5065 5066 5067 | rc = ExpandBlob(pIn2); if( rc ) goto abort_due_to_error; if( pOp->opcode==OP_SorterInsert ){ rc = sqlite3VdbeSorterWrite(pC, pIn2); }else{ x.nKey = pIn2->n; x.pKey = pIn2->z; | > > | > | 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 | rc = ExpandBlob(pIn2); if( rc ) goto abort_due_to_error; if( pOp->opcode==OP_SorterInsert ){ rc = sqlite3VdbeSorterWrite(pC, pIn2); }else{ x.nKey = pIn2->n; x.pKey = pIn2->z; x.aMem = aMem + pOp->p3; x.nMem = (u16)pOp->p4.i; rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) ); assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; } if( rc) goto abort_due_to_error; break; |
︙ | ︙ | |||
5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 | if( rc ) goto abort_due_to_error; if( res==0 ){ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); if( rc ) goto abort_due_to_error; } assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; break; } | > | | | 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 | if( rc ) goto abort_due_to_error; if( res==0 ){ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); if( rc ) goto abort_due_to_error; } assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; pC->seekResult = 0; break; } /* Opcode: DeferredSeek P1 * P3 P4 * ** Synopsis: Move P3 to P1.rowid if needed ** ** P1 is an open index cursor and P3 is a cursor on the corresponding ** table. This opcode does a deferred seek of the P3 table cursor ** to the row that corresponds to the current row of P1. ** ** This is a deferred seek. Nothing actually happens until ** the cursor is used to read a record. That way, if no reads |
︙ | ︙ | |||
5136 5137 5138 5139 5140 5141 5142 | ** ** Write into register P2 an integer which is the last entry in the record at ** the end of the index key pointed to by cursor P1. This integer should be ** the rowid of the table entry to which this index entry points. ** ** See also: Rowid, MakeRecord. */ | | | | | | | 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 | ** ** Write into register P2 an integer which is the last entry in the record at ** the end of the index key pointed to by cursor P1. This integer should be ** the rowid of the table entry to which this index entry points. ** ** See also: Rowid, MakeRecord. */ case OP_DeferredSeek: case OP_IdxRowid: { /* out2 */ VdbeCursor *pC; /* The P1 index cursor */ VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */ i64 rowid; /* Rowid that P1 current points to */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->uc.pCursor!=0 ); assert( pC->isTable==0 ); |
︙ | ︙ | |||
5166 5167 5168 5169 5170 5171 5172 | if( !pC->nullRow ){ rowid = 0; /* Not needed. Only used to silence a warning. */ rc = sqlite3VdbeIdxRowid(db, pC->uc.pCursor, &rowid); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } | | < | 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 | if( !pC->nullRow ){ rowid = 0; /* Not needed. Only used to silence a warning. */ rc = sqlite3VdbeIdxRowid(db, pC->uc.pCursor, &rowid); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } if( pOp->opcode==OP_DeferredSeek ){ assert( pOp->p3>=0 && pOp->p3<p->nCursor ); pTabCur = p->apCsr[pOp->p3]; assert( pTabCur!=0 ); assert( pTabCur->eCurType==CURTYPE_BTREE ); assert( pTabCur->uc.pCursor!=0 ); assert( pTabCur->isTable ); pTabCur->nullRow = 0; pTabCur->movetoTarget = rowid; pTabCur->deferredMoveto = 1; assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); pTabCur->aAltMap = pOp->p4.ai; pTabCur->pAltCursor = pC; }else{ pOut = out2Prerelease(p, pOp); pOut->u.i = rowid; } }else{ assert( pOp->opcode==OP_IdxRowid ); sqlite3VdbeMemSetNull(&aMem[pOp->p2]); } break; } |
︙ | ︙ | |||
5294 5295 5296 5297 5298 5299 5300 | ** P3==1 then the table to be clear is in the auxiliary database file ** that is used to store tables create using CREATE TEMPORARY TABLE. ** ** If AUTOVACUUM is enabled then it is possible that another root page ** might be moved into the newly deleted root page in order to keep all ** root pages contiguous at the beginning of the database. The former ** value of the root page that moved - its value before the move occurred - | | | | | > > > > > > > | 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 | ** P3==1 then the table to be clear is in the auxiliary database file ** that is used to store tables create using CREATE TEMPORARY TABLE. ** ** If AUTOVACUUM is enabled then it is possible that another root page ** might be moved into the newly deleted root page in order to keep all ** root pages contiguous at the beginning of the database. The former ** value of the root page that moved - its value before the move occurred - ** is stored in register P2. If no page movement was required (because the ** table being dropped was already the last one in the database) then a ** zero is stored in register P2. If AUTOVACUUM is disabled then a zero ** is stored in register P2. ** ** This opcode throws an error if there are any active reader VMs when ** it is invoked. This is done to avoid the difficulty associated with ** updating existing cursors when a root page is moved in an AUTOVACUUM ** database. This error is thrown even if the database is not an AUTOVACUUM ** db in order to avoid introducing an incompatibility between autovacuum ** and non-autovacuum modes. ** ** See also: Clear */ case OP_Destroy: { /* out2 */ int iMoved; int iDb; |
︙ | ︙ | |||
5397 5398 5399 5400 5401 5402 5403 | assert( pC->isEphemeral ); rc = sqlite3BtreeClearTableOfCursor(pC->uc.pCursor); if( rc ) goto abort_due_to_error; } break; } | | | | | | < < < < < < < < | < < < < | < < < < | < > < < < < < < | > > > > > > > > > > > > | 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 | assert( pC->isEphemeral ); rc = sqlite3BtreeClearTableOfCursor(pC->uc.pCursor); if( rc ) goto abort_due_to_error; } break; } /* Opcode: CreateBtree P1 P2 P3 * * ** Synopsis: r[P2]=root iDb=P1 flags=P3 ** ** Allocate a new b-tree in the main database file if P1==0 or in the ** TEMP database file if P1==1 or in an attached database if ** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table ** it must be 2 (BTREE_BLOBKEY) for a index or WITHOUT ROWID table. ** The root page number of the new b-tree is stored in register P2. */ case OP_CreateBtree: { /* out2 */ int pgno; Db *pDb; pOut = out2Prerelease(p, pOp); pgno = 0; assert( pOp->p3==BTREE_INTKEY || pOp->p3==BTREE_BLOBKEY ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( DbMaskTest(p->btreeMask, pOp->p1) ); assert( p->readOnly==0 ); pDb = &db->aDb[pOp->p1]; 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 * * * P4 * ** ** Run the SQL statement or statements specified in the P4 string. */ case OP_SqlExec: { db->nSqlExec++; rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0); db->nSqlExec--; if( rc ) goto abort_due_to_error; break; } /* Opcode: ParseSchema P1 * * P4 * ** ** Read and parse all entries from the SQLITE_MASTER table of database P1 ** that match the WHERE clause P4. ** ** This opcode invokes the parser to create a new virtual machine, |
︙ | ︙ | |||
5474 5475 5476 5477 5478 5479 5480 | } #endif iDb = pOp->p1; assert( iDb>=0 && iDb<db->nDb ); assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); /* Used to be a conditional */ { | | | | 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 | } #endif iDb = pOp->p1; assert( iDb>=0 && iDb<db->nDb ); assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); /* Used to be a conditional */ { zMaster = MASTER_NAME; initData.db = db; initData.iDb = pOp->p1; initData.pzErrMsg = &p->zErrMsg; zSql = sqlite3MPrintf(db, "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid", db->aDb[iDb].zDbSName, zMaster, pOp->p4.z); if( zSql==0 ){ rc = SQLITE_NOMEM_BKPT; }else{ assert( db->init.busy==0 ); db->init.busy = 1; initData.rc = SQLITE_OK; assert( !db->mallocFailed ); rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); if( rc==SQLITE_OK ) rc = initData.rc; sqlite3DbFreeNN(db, zSql); db->init.busy = 0; } } if( rc ){ sqlite3ResetAllSchemasOfConnection(db); if( rc==SQLITE_NOMEM ){ goto no_mem; |
︙ | ︙ | |||
5566 5567 5568 5569 5570 5571 5572 | #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* Opcode: IntegrityCk P1 P2 P3 P4 P5 ** ** Do an analysis of the currently open database. Store in ** register P1 the text of an error message describing any problems. ** If no problems are found, store a NULL in register P1. ** | | | 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 | #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* Opcode: IntegrityCk P1 P2 P3 P4 P5 ** ** Do an analysis of the currently open database. Store in ** register P1 the text of an error message describing any problems. ** If no problems are found, store a NULL in register P1. ** ** The register P3 contains one less than the maximum number of allowed errors. ** At most reg(P3) errors will be reported. ** In other words, the analysis stops as soon as reg(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. ** |
︙ | ︙ | |||
5590 5591 5592 5593 5594 5595 5596 | char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ assert( p->bIsReader ); nRoot = pOp->p2; aRoot = pOp->p4.ai; assert( nRoot>0 ); | | | | < > | | > | | 5697 5698 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 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 | char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ assert( p->bIsReader ); nRoot = pOp->p2; aRoot = pOp->p4.ai; assert( nRoot>0 ); assert( aRoot[0]==nRoot ); assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pnErr = &aMem[pOp->p3]; assert( (pnErr->flags & MEM_Int)!=0 ); assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); pIn1 = &aMem[pOp->p1]; assert( pOp->p5<db->nDb ); assert( DbMaskTest(p->btreeMask, pOp->p5) ); z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, (int)pnErr->u.i+1, &nErr); sqlite3VdbeMemSetNull(pIn1); if( nErr==0 ){ assert( z==0 ); }else if( z==0 ){ goto no_mem; }else{ pnErr->u.i -= nErr-1; sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite3_free); } UPDATE_MAX_BLOBSIZE(pIn1); sqlite3VdbeChangeEncoding(pIn1, encoding); break; } #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ /* Opcode: RowSetAdd P1 P2 * * * ** Synopsis: rowset(P1)=r[P2] ** ** Insert the integer value held by register P2 into a RowSet object ** held in register P1. ** ** An assertion fails if P2 is not an integer. */ case OP_RowSetAdd: { /* in1, in2 */ pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; assert( (pIn2->flags & MEM_Int)!=0 ); if( (pIn1->flags & MEM_RowSet)==0 ){ sqlite3VdbeMemSetRowSet(pIn1); if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem; } sqlite3RowSetInsert(pIn1->u.pRowSet, pIn2->u.i); break; } /* Opcode: RowSetRead P1 P2 P3 * * ** Synopsis: r[P3]=rowset(P1) ** ** Extract the smallest value from the RowSet object in P1 ** and put that value into register P3. ** Or, if RowSet object P1 is initially empty, leave P3 ** unchanged and jump to instruction P2. */ case OP_RowSetRead: { /* jump, in1, out3 */ i64 val; pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_RowSet)==0 |
︙ | ︙ | |||
5670 5671 5672 5673 5674 5675 5676 | ** ** Register P3 is assumed to hold a 64-bit integer value. If register P1 ** contains a RowSet object and that RowSet object contains ** the value held in P3, jump to register P2. Otherwise, insert the ** integer in P3 into the RowSet and continue on to the ** next opcode. ** | | | | | < | | | 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 | ** ** Register P3 is assumed to hold a 64-bit integer value. If register P1 ** contains a RowSet object and that RowSet object contains ** the value held in P3, jump to register P2. Otherwise, insert the ** integer in P3 into the RowSet and continue on to the ** next opcode. ** ** The RowSet object is optimized for the case where sets of integers ** are inserted in distinct phases, which each set contains no duplicates. ** Each set is identified by a unique P4 value. The first set ** must have P4==0, the final set must have P4==-1, and for all other sets ** must have P4>0. ** ** This allows optimizations: (a) when P4==0 there is no need to test ** the RowSet object for P3, as it is guaranteed not to contain it, ** (b) when P4==-1 there is no need to insert the value, as it will ** never be tested for, and (c) when a value that is part of set X is ** inserted, there is no need to search to see if the same value was ** previously inserted as part of set X (only if it was previously ** inserted as part of some other set). */ case OP_RowSetTest: { /* jump, in1, in3 */ |
︙ | ︙ | |||
5785 5786 5787 5788 5789 5790 5791 | ** variable nMem (and later, VdbeFrame.nChildMem) to this value. */ nMem = pProgram->nMem + pProgram->nCsr; assert( nMem>0 ); if( pProgram->nCsr==0 ) nMem++; nByte = ROUND8(sizeof(VdbeFrame)) + nMem * sizeof(Mem) | | > | 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 | ** variable nMem (and later, VdbeFrame.nChildMem) to this value. */ nMem = pProgram->nMem + pProgram->nCsr; assert( nMem>0 ); if( pProgram->nCsr==0 ) nMem++; nByte = ROUND8(sizeof(VdbeFrame)) + nMem * sizeof(Mem) + pProgram->nCsr * sizeof(VdbeCursor*) + (pProgram->nOp + 7)/8; pFrame = sqlite3DbMallocZero(db, nByte); if( !pFrame ){ goto no_mem; } sqlite3VdbeMemRelease(pRt); pRt->flags = MEM_Frame; pRt->u.pFrame = pFrame; |
︙ | ︙ | |||
5824 5825 5826 5827 5828 5829 5830 | || (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) ); assert( pProgram->nCsr==pFrame->nChildCsr ); assert( (int)(pOp - aOp)==pFrame->pc ); } p->nFrame++; pFrame->pParent = p->pFrame; | | > > | 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 | || (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) ); assert( pProgram->nCsr==pFrame->nChildCsr ); assert( (int)(pOp - aOp)==pFrame->pc ); } p->nFrame++; pFrame->pParent = p->pFrame; pFrame->lastRowid = db->lastRowid; pFrame->nChange = p->nChange; pFrame->nDbChange = p->db->nChange; assert( pFrame->pAuxData==0 ); pFrame->pAuxData = p->pAuxData; p->pAuxData = 0; p->nChange = 0; p->pFrame = pFrame; p->aMem = aMem = VdbeFrameMem(pFrame); p->nMem = pFrame->nChildMem; p->nCursor = (u16)pFrame->nChildCsr; p->apCsr = (VdbeCursor **)&aMem[p->nMem]; pFrame->aOnce = (u8*)&p->apCsr[pProgram->nCsr]; memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8); p->aOp = aOp = pProgram->aOp; p->nOp = pProgram->nOp; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS p->anExec = 0; #endif pOp = &aOp[-1]; |
︙ | ︙ | |||
5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 | ** ** if r[P1] is zero or negative, that means there is no LIMIT ** and r[P2] is set to -1. ** ** Otherwise, r[P2] is set to the sum of r[P1] and r[P3]. */ case OP_OffsetLimit: { /* in1, out2, in3 */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; pOut = out2Prerelease(p, pOp); assert( pIn1->flags & MEM_Int ); assert( pIn3->flags & MEM_Int ); | > > > > > > > > > > | > > > | | | > | < | | | | | 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 | ** ** if r[P1] is zero or negative, that means there is no LIMIT ** and r[P2] is set to -1. ** ** Otherwise, r[P2] is set to the sum of r[P1] and r[P3]. */ case OP_OffsetLimit: { /* in1, out2, in3 */ i64 x; pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; pOut = out2Prerelease(p, pOp); assert( pIn1->flags & MEM_Int ); assert( pIn3->flags & MEM_Int ); x = pIn1->u.i; if( x<=0 || sqlite3AddInt64(&x, pIn3->u.i>0?pIn3->u.i:0) ){ /* If the LIMIT is less than or equal to zero, loop forever. This ** is documented. But also, if the LIMIT+OFFSET exceeds 2^63 then ** also loop forever. This is undocumented. In fact, one could argue ** that the loop should terminate. But assuming 1 billion iterations ** per second (far exceeding the capabilities of any current hardware) ** it would take nearly 300 years to actually reach the limit. So ** looping forever is a reasonable approximation. */ pOut->u.i = -1; }else{ pOut->u.i = x; } break; } /* Opcode: IfNotZero P1 P2 * * * ** Synopsis: if r[P1]!=0 then r[P1]--, goto P2 ** ** Register P1 must contain an integer. If the content of register P1 is ** initially greater than zero, then decrement the value in register P1. ** If it is non-zero (negative or positive) and then also jump to P2. ** If register P1 is initially zero, leave it unchanged and fall through. */ case OP_IfNotZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); VdbeBranchTaken(pIn1->u.i<0, 2); if( pIn1->u.i ){ if( pIn1->u.i>0 ) pIn1->u.i--; goto jump_to_p2; } break; } /* Opcode: DecrJumpZero P1 P2 * * * ** Synopsis: if (--r[P1])==0 goto P2 ** ** Register P1 must hold an integer. Decrement the value in P1 ** and jump to P2 if the new value is exactly zero. */ case OP_DecrJumpZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); if( pIn1->u.i>SMALLEST_INT64 ) pIn1->u.i--; VdbeBranchTaken(pIn1->u.i==0, 2); if( pIn1->u.i==0 ) goto jump_to_p2; break; } /* Opcode: AggStep0 * P2 P3 P4 P5 |
︙ | ︙ | |||
6378 6379 6380 6381 6382 6383 6384 | ** P2 contains the root-page of the table to lock. ** ** P4 contains a pointer to the name of the table being locked. This is only ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { u8 isWriteLock = (u8)pOp->p3; | | | 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 | ** P2 contains the root-page of the table to lock. ** ** P4 contains a pointer to the name of the table being locked. This is only ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { u8 isWriteLock = (u8)pOp->p3; if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommit) ){ int p1 = pOp->p1; assert( p1>=0 && p1<db->nDb ); assert( DbMaskTest(p->btreeMask, p1) ); assert( isWriteLock==0 || isWriteLock==1 ); rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); if( rc ){ if( (rc&0xFF)==SQLITE_LOCKED ){ |
︙ | ︙ | |||
6752 6753 6754 6755 6756 6757 6758 | } db->vtabOnConflict = pOp->p5; rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); db->vtabOnConflict = vtabOnConflict; sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK && pOp->p1 ){ assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); | | | 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 | } db->vtabOnConflict = pOp->p5; rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); db->vtabOnConflict = vtabOnConflict; sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK && pOp->p1 ){ assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); db->lastRowid = rowid; } if( (rc&0xff)==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ if( pOp->p5==OE_Ignore ){ rc = SQLITE_OK; }else{ p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5); } |
︙ | ︙ | |||
6807 6808 6809 6810 6811 6812 6813 6814 | if( newMax < (unsigned)pOp->p3 ) newMax = (unsigned)pOp->p3; } pOut->u.i = sqlite3BtreeMaxPageCount(pBt, newMax); break; } #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | 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 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 | if( newMax < (unsigned)pOp->p3 ) newMax = (unsigned)pOp->p3; } pOut->u.i = sqlite3BtreeMaxPageCount(pBt, newMax); break; } #endif /* Opcode: Function0 P1 P2 P3 P4 P5 ** Synopsis: r[P3]=func(r[P2@P5]) ** ** Invoke a user function (P4 is a pointer to a FuncDef object that ** defines the function) with P5 arguments taken from register P2 and ** successors. The result of the function is stored in register P3. ** Register P3 must not be one of the function inputs. ** ** P1 is a 32-bit bitmask indicating whether or not each argument to the ** function was determined to be constant at compile time. If the first ** argument was constant then bit 0 of P1 is set. This is used to determine ** whether meta data associated with a user function argument using the ** sqlite3_set_auxdata() API may be safely retained until the next ** invocation of this opcode. ** ** See also: Function, AggStep, AggFinal */ /* Opcode: Function P1 P2 P3 P4 P5 ** Synopsis: r[P3]=func(r[P2@P5]) ** ** Invoke a user function (P4 is a pointer to an sqlite3_context object that ** contains a pointer to the function to be run) with P5 arguments taken ** from register P2 and successors. The result of the function is stored ** in register P3. Register P3 must not be one of the function inputs. ** ** P1 is a 32-bit bitmask indicating whether or not each argument to the ** function was determined to be constant at compile time. If the first ** argument was constant then bit 0 of P1 is set. This is used to determine ** whether meta data associated with a user function argument using the ** sqlite3_set_auxdata() API may be safely retained until the next ** invocation of this opcode. ** ** SQL functions are initially coded as OP_Function0 with P4 pointing ** to a FuncDef object. But on first evaluation, the P4 operand is ** automatically converted into an sqlite3_context object and the operation ** changed to this OP_Function opcode. In this way, the initialization of ** the sqlite3_context object occurs only once, rather than once for each ** evaluation of the function. ** ** See also: Function0, AggStep, AggFinal */ case OP_PureFunc0: case OP_Function0: { int n; sqlite3_context *pCtx; assert( pOp->p4type==P4_FUNCDEF ); n = pOp->p5; assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n ); pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*)); if( pCtx==0 ) goto no_mem; pCtx->pOut = 0; pCtx->pFunc = pOp->p4.pFunc; pCtx->iOp = (int)(pOp - aOp); pCtx->pVdbe = p; pCtx->argc = n; pOp->p4type = P4_FUNCCTX; pOp->p4.pCtx = pCtx; assert( OP_PureFunc == OP_PureFunc0+2 ); assert( OP_Function == OP_Function0+2 ); pOp->opcode += 2; /* Fall through into OP_Function */ } case OP_PureFunc: case OP_Function: { int i; sqlite3_context *pCtx; assert( pOp->p4type==P4_FUNCCTX ); pCtx = pOp->p4.pCtx; /* If this function is inside of a trigger, the register array in aMem[] ** might change from one evaluation to the next. The next block of code ** checks to see if the register array has changed, and if so it ** reinitializes the relavant parts of the sqlite3_context object */ pOut = &aMem[pOp->p3]; if( pCtx->pOut != pOut ){ pCtx->pOut = pOut; for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; } memAboutToChange(p, pOut); #ifdef SQLITE_DEBUG for(i=0; i<pCtx->argc; i++){ assert( memIsValid(pCtx->argv[i]) ); REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]); } #endif MemSetTypeFlag(pOut, MEM_Null); pCtx->fErrorOrAux = 0; (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */ /* If the function returned an error, throw an exception */ if( pCtx->fErrorOrAux ){ if( pCtx->isError ){ sqlite3VdbeError(p, "%s", sqlite3_value_text(pOut)); rc = pCtx->isError; } sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1); if( rc ) goto abort_due_to_error; } /* Copy the result of the function into register P3 */ if( pOut->flags & (MEM_Str|MEM_Blob) ){ sqlite3VdbeChangeEncoding(pOut, encoding); if( sqlite3VdbeMemTooBig(pOut) ) goto too_big; } REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Init P1 P2 P3 P4 * ** Synopsis: Start at P2 ** ** Programs contain a single instance of this opcode as the very first ** opcode. ** ** If tracing is enabled (by the sqlite3_trace()) interface, then ** the UTF-8 string contained in P4 is emitted on the trace callback. ** Or if P4 is blank, use the string returned by sqlite3_sql(). ** ** If P2 is not zero, jump to instruction P2. ** ** 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_Init: { /* jump */ char *zTrace; int i; /* 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. |
︙ | ︙ | |||
6852 6853 6854 6855 6856 6857 6858 | if( db->mTrace & SQLITE_TRACE_LEGACY ){ void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace; char *z = sqlite3VdbeExpandSql(p, zTrace); x(db->pTraceArg, z); sqlite3_free(z); }else #endif | < > > > > > | 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 | if( db->mTrace & SQLITE_TRACE_LEGACY ){ void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace; char *z = sqlite3VdbeExpandSql(p, zTrace); x(db->pTraceArg, z); sqlite3_free(z); }else #endif if( db->nVdbeExec>1 ){ char *z = sqlite3MPrintf(db, "-- %s", zTrace); (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, z); sqlite3DbFree(db, z); }else{ (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); } } #ifdef SQLITE_USE_FCNTL_TRACE zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); if( zTrace ){ int j; |
︙ | ︙ | |||
6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 | if( pOp->p1>=sqlite3GlobalConfig.iOnceResetThreshold ){ for(i=1; i<p->nOp; i++){ if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0; } pOp->p1 = 0; } pOp->p1++; goto jump_to_p2; } #ifdef SQLITE_ENABLE_CURSOR_HINTS /* Opcode: CursorHint P1 * * P4 * ** ** Provide a hint to cursor P1 that it only needs to return rows that | > | 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 | if( pOp->p1>=sqlite3GlobalConfig.iOnceResetThreshold ){ for(i=1; i<p->nOp; i++){ if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0; } pOp->p1 = 0; } pOp->p1++; p->aCounter[SQLITE_STMTSTATUS_RUN]++; goto jump_to_p2; } #ifdef SQLITE_ENABLE_CURSOR_HINTS /* Opcode: CursorHint P1 * * P4 * ** ** Provide a hint to cursor P1 that it only needs to return rows that |
︙ | ︙ | |||
6988 6989 6990 6991 6992 6993 6994 | sqlite3ResetOneSchema(db, resetSchemaOnFault-1); } /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: | < | 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 | sqlite3ResetOneSchema(db, resetSchemaOnFault-1); } /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: testcase( nVmStep>0 ); p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; sqlite3VdbeLeave(p); assert( rc!=SQLITE_OK || nExtraDelete==0 || sqlite3_strlike("DELETE%",p->zSql,0)!=0 ); return rc; |
︙ | ︙ |
Changes to src/vdbe.h.
︙ | ︙ | |||
26 27 28 29 30 31 32 | */ typedef struct Vdbe Vdbe; /* ** The names of the following types declared in vdbeInt.h are required ** for the VdbeOp definition. */ | | < | | 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 | */ typedef struct Vdbe Vdbe; /* ** The names of the following types declared in vdbeInt.h are required ** for the VdbeOp definition. */ typedef struct sqlite3_value Mem; typedef struct SubProgram SubProgram; /* ** A single instruction of the virtual machine has an opcode ** and as many as three operands. The instruction is recorded ** as an instance of the following structure: */ struct VdbeOp { u8 opcode; /* What operation to perform */ signed char p4type; /* One of the P4_xxx constants for p4 */ u16 p5; /* Fifth parameter is an unsigned 16-bit integer */ int p1; /* First operand */ int p2; /* Second parameter (often the jump destination) */ int p3; /* The third parameter */ union p4union { /* fourth parameter */ int i; /* Integer value if p4type==P4_INT32 */ void *p; /* Generic pointer */ char *z; /* Pointer to data for string (char array) types */ |
︙ | ︙ | |||
60 61 62 63 64 65 66 | KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Table *pTab; /* Used when p4type is P4_TABLE */ #ifdef SQLITE_ENABLE_CURSOR_HINTS Expr *pExpr; /* Used when p4type is P4_EXPR */ #endif | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Table *pTab; /* Used when p4type is P4_TABLE */ #ifdef SQLITE_ENABLE_CURSOR_HINTS Expr *pExpr; /* Used when p4type is P4_EXPR */ #endif int (*xAdvance)(BtCursor *, int); } p4; #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE u32 cnt; /* Number of times this instruction was executed */ u64 cycles; /* Total time spent executing this instruction */ |
︙ | ︙ | |||
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 | ** A sub-routine used to implement a trigger program. */ struct SubProgram { VdbeOp *aOp; /* Array of opcodes for sub-program */ int nOp; /* Elements in aOp[] */ int nMem; /* Number of memory cells required */ int nCsr; /* Number of cursors required */ void *token; /* id that may be used to recursive triggers */ SubProgram *pNext; /* Next sub-program already visited */ }; /* ** A smaller version of VdbeOp used for the VdbeAddOpList() function because ** it takes up less space. */ struct VdbeOpList { u8 opcode; /* What operation to perform */ signed char p1; /* First operand */ signed char p2; /* Second parameter (often the jump destination) */ signed char p3; /* Third parameter */ }; typedef struct VdbeOpList VdbeOpList; /* ** Allowed values of VdbeOp.p4type */ | > | | | | > > > > > > > | | | | < | < | | < | < < < | | 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 | ** A sub-routine used to implement a trigger program. */ struct SubProgram { VdbeOp *aOp; /* Array of opcodes for sub-program */ int nOp; /* Elements in aOp[] */ int nMem; /* Number of memory cells required */ int nCsr; /* Number of cursors required */ u8 *aOnce; /* Array of OP_Once flags */ void *token; /* id that may be used to recursive triggers */ SubProgram *pNext; /* Next sub-program already visited */ }; /* ** A smaller version of VdbeOp used for the VdbeAddOpList() function because ** it takes up less space. */ struct VdbeOpList { u8 opcode; /* What operation to perform */ signed char p1; /* First operand */ signed char p2; /* Second parameter (often the jump destination) */ signed char p3; /* Third parameter */ }; typedef struct VdbeOpList VdbeOpList; /* ** Allowed values of VdbeOp.p4type */ #define P4_NOTUSED 0 /* The P4 parameter is not used */ #define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */ #define P4_STATIC (-1) /* Pointer to a static string */ #define P4_COLLSEQ (-2) /* P4 is a pointer to a CollSeq structure */ #define P4_INT32 (-3) /* P4 is a 32-bit signed integer */ #define P4_SUBPROGRAM (-4) /* P4 is a pointer to a SubProgram structure */ #define P4_ADVANCE (-5) /* P4 is a pointer to BtreeNext() or BtreePrev() */ #define P4_TABLE (-6) /* P4 is a pointer to a Table structure */ /* Above do not own any resources. Must free those below */ #define P4_FREE_IF_LE (-7) #define P4_DYNAMIC (-7) /* Pointer to memory from sqliteMalloc() */ #define P4_FUNCDEF (-8) /* P4 is a pointer to a FuncDef structure */ #define P4_KEYINFO (-9) /* P4 is a pointer to a KeyInfo structure */ #define P4_EXPR (-10) /* P4 is a pointer to an Expr tree */ #define P4_MEM (-11) /* P4 is a pointer to a Mem* structure */ #define P4_VTAB (-12) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_REAL (-13) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-14) /* P4 is a 64-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-16) /* 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 |
︙ | ︙ | |||
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 | /* ** The makefile scans the vdbe.c source file and creates the "opcodes.h" ** header file that defines a number for each opcode used by the VDBE. */ #include "opcodes.h" /* ** Prototypes for the VDBE interface. See comments on the implementation ** for a description of what each of these routines does. */ Vdbe *sqlite3VdbeCreate(Parse*); int sqlite3VdbeAddOp0(Vdbe*,int); int sqlite3VdbeAddOp1(Vdbe*,int,int); int sqlite3VdbeAddOp2(Vdbe*,int,int,int); int sqlite3VdbeGoto(Vdbe*,int); int sqlite3VdbeLoadString(Vdbe*,int,const char*); void sqlite3VdbeMultiLoad(Vdbe*,int,const char*,...); int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); int sqlite3VdbeAddOp4Dup8(Vdbe*,int,int,int,int,const u8*,int); int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); void sqlite3VdbeEndCoroutine(Vdbe*,int); #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N); #else # define sqlite3VdbeVerifyNoMallocRequired(A,B) #endif VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno); void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8); void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1); void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2); void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); | > > > > > > > > | > | 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 | /* ** The makefile scans the vdbe.c source file and creates the "opcodes.h" ** header file that defines a number for each opcode used by the VDBE. */ #include "opcodes.h" /* ** Additional non-public SQLITE_PREPARE_* flags */ #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ #define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */ /* ** Prototypes for the VDBE interface. See comments on the implementation ** for a description of what each of these routines does. */ Vdbe *sqlite3VdbeCreate(Parse*); int sqlite3VdbeAddOp0(Vdbe*,int); int sqlite3VdbeAddOp1(Vdbe*,int,int); int sqlite3VdbeAddOp2(Vdbe*,int,int,int); int sqlite3VdbeGoto(Vdbe*,int); int sqlite3VdbeLoadString(Vdbe*,int,const char*); void sqlite3VdbeMultiLoad(Vdbe*,int,const char*,...); int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); int sqlite3VdbeAddOp4Dup8(Vdbe*,int,int,int,int,const u8*,int); int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); void sqlite3VdbeEndCoroutine(Vdbe*,int); #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N); void sqlite3VdbeVerifyNoResultRow(Vdbe *p); #else # define sqlite3VdbeVerifyNoMallocRequired(A,B) # define sqlite3VdbeVerifyNoResultRow(A) #endif VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno); void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8); void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1); void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2); void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); void sqlite3VdbeChangeP5(Vdbe*, u16 P5); void sqlite3VdbeJumpHere(Vdbe*, int addr); int sqlite3VdbeChangeToNoop(Vdbe*, int addr); int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op); void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type); void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); void sqlite3VdbeUsesBtree(Vdbe*, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); void sqlite3VdbeRunOnlyOnce(Vdbe*); void sqlite3VdbeReusable(Vdbe*); void sqlite3VdbeDelete(Vdbe*); |
︙ | ︙ | |||
216 217 218 219 220 221 222 | void sqlite3VdbeResetStepResult(Vdbe*); void sqlite3VdbeRewind(Vdbe*); int sqlite3VdbeReset(Vdbe*); void sqlite3VdbeSetNumCols(Vdbe*,int); int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); void sqlite3VdbeCountChanges(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*); | > | | > > | 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 | void sqlite3VdbeResetStepResult(Vdbe*); void sqlite3VdbeRewind(Vdbe*); int sqlite3VdbeReset(Vdbe*); void sqlite3VdbeSetNumCols(Vdbe*,int); int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); void sqlite3VdbeCountChanges(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*); u8 sqlite3VdbePrepareFlags(Vdbe*); void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, u8); void sqlite3VdbeSwap(Vdbe*,Vdbe*); VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8); void sqlite3VdbeSetVarmask(Vdbe*, int); #ifndef SQLITE_OMIT_TRACE char *sqlite3VdbeExpandSql(Vdbe*, const char*); #endif int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int); UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*); typedef int (*RecordCompare)(int,const void*,UnpackedRecord*); RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*); #ifndef SQLITE_OMIT_TRIGGER void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); #endif int sqlite3NotPureFunc(sqlite3_context*); /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on ** each VDBE opcode. ** ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op ** comments in VDBE programs that show key decision points in the code ** generator. |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
69 70 71 72 73 74 75 | ** - On either an index or a table ** * A sorter ** * A virtual table ** * A one-row "pseudotable" stored in a single register */ typedef struct VdbeCursor VdbeCursor; struct VdbeCursor { | | | | | | | | | | | | | | | | | | > | < < < | > > | > | > > > > > > > > > > > > > > | > > > | < < < < < < < < < < < < < < < < > | 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 | ** - On either an index or a table ** * A sorter ** * A virtual table ** * A one-row "pseudotable" stored in a single register */ typedef struct VdbeCursor VdbeCursor; struct VdbeCursor { u8 eCurType; /* One of the CURTYPE_* values above */ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ u8 nullRow; /* True if pointing to a row with no data */ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ u8 isTable; /* True for rowid tables. False for indexes */ #ifdef SQLITE_DEBUG u8 seekOp; /* Most recent seek operation on this cursor */ u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ #endif Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ Btree *pBtx; /* Separate file holding temporary table */ i64 seqCount; /* Sequence counter */ int *aAltMap; /* Mapping from table to index column numbers */ /* Cached OP_Column parse information is only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that ** the cache is out of date. */ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ int seekResult; /* Result of previous sqlite3BtreeMoveto() or 0 ** if there have been no prior seeks on the cursor. */ /* seekResult does not distinguish between "no seeks have ever occurred ** on this cursor" and "the most recent seek was an exact match". ** For CURTYPE_PSEUDO, seekResult is the register holding the record */ /* When a new VdbeCursor is allocated, only the fields above are zeroed. ** The fields that follow are uninitialized, and must be individually ** initialized prior to first use. */ VdbeCursor *pAltCursor; /* Associated index cursor from which to read */ union { BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */ sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ } uc; KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ u32 iHdrOffset; /* Offset to next unparsed byte of the header */ Pgno pgnoRoot; /* Root page of the open btree cursor */ i16 nField; /* Number of fields in the header */ u16 nHdrParsed; /* Number of header fields parsed so far */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ u32 *aOffset; /* Pointer to aType[nField] */ const u8 *aRow; /* Data for the current row, if all on one page */ u32 payloadSize; /* Total number of bytes in the record */ u32 szRow; /* Byte available in aRow */ #ifdef SQLITE_ENABLE_COLUMN_USED_MASK u64 maskUsed; /* Mask of columns used by this cursor */ #endif /* 2*nField extra array elements allocated for aType[], beyond the one ** static element declared in the structure. nField total array slots for ** aType[] and nField+1 array slots for aOffset[] */ u32 aType[1]; /* Type values record decode. MUST BE LAST */ }; /* ** A value for VdbeCursor.cacheStatus that means the cache is always invalid. */ #define CACHE_STALE 0 |
︙ | ︙ | |||
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 | struct VdbeFrame { Vdbe *v; /* VM this frame belongs to */ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ Op *aOp; /* Program instructions for parent frame */ i64 *anExec; /* Event counters from parent frame */ Mem *aMem; /* Array of memory cells for parent frame */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ void *token; /* Copy of SubProgram.token */ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ AuxData *pAuxData; /* Linked list of auxdata allocations */ int nCursor; /* Number of entries in apCsr */ int pc; /* Program Counter in parent (calling) frame */ int nOp; /* Size of aOp array */ int nMem; /* Number of entries in aMem */ int nChildMem; /* Number of memory cells for child frame */ int nChildCsr; /* Number of cursors for child frame */ int nChange; /* Statement changes (Vdbe.nChange) */ int nDbChange; /* Value of db->nChange */ }; #define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) /* ** Internally, the vdbe manipulates nearly all SQL values as Mem ** structures. Each Mem struct may cache multiple representations (string, ** integer etc.) of the same value. */ | > | | > | 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 | struct VdbeFrame { Vdbe *v; /* VM this frame belongs to */ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ Op *aOp; /* Program instructions for parent frame */ i64 *anExec; /* Event counters from parent frame */ Mem *aMem; /* Array of memory cells for parent frame */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ u8 *aOnce; /* Bitmask used by OP_Once */ void *token; /* Copy of SubProgram.token */ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ AuxData *pAuxData; /* Linked list of auxdata allocations */ int nCursor; /* Number of entries in apCsr */ int pc; /* Program Counter in parent (calling) frame */ int nOp; /* Size of aOp array */ int nMem; /* Number of entries in aMem */ int nChildMem; /* Number of memory cells for child frame */ int nChildCsr; /* Number of cursors for child frame */ int nChange; /* Statement changes (Vdbe.nChange) */ int nDbChange; /* Value of db->nChange */ }; #define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) /* ** Internally, the vdbe manipulates nearly all SQL values as Mem ** structures. Each Mem struct may cache multiple representations (string, ** integer etc.) of the same value. */ struct sqlite3_value { union MemValue { double r; /* Real value used when MEM_Real is set in flags */ i64 i; /* Integer value used when MEM_Int is set in flags */ int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */ const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ RowSet *pRowSet; /* Used only when flags==MEM_RowSet */ VdbeFrame *pFrame; /* Used when flags==MEM_Frame */ } u; u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ u8 eSubtype; /* Subtype for this value */ |
︙ | ︙ | |||
213 214 215 216 217 218 219 | */ #define MEMCELLSIZE offsetof(Mem,zMalloc) /* One or more of the following flags are set to indicate the validOK ** representations of the value stored in the Mem struct. ** ** If the MEM_Null flag is set, then the value is an SQL NULL value. | > | | | | | 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 | */ #define MEMCELLSIZE offsetof(Mem,zMalloc) /* One or more of the following flags are set to indicate the validOK ** representations of the value stored in the Mem struct. ** ** If the MEM_Null flag is set, then the value is an SQL NULL value. ** For a pointer type created using sqlite3_bind_pointer() or ** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. ** ** If the MEM_Str flag is set then Mem.z points at a string representation. ** Usually this is encoded in the same unicode encoding as the main ** database (see below for exceptions). If the MEM_Term flag is also ** set, then the string is nul terminated. The MEM_Int and MEM_Real ** flags may coexist with the MEM_Str flag. */ #define MEM_Null 0x0001 /* Value is NULL (or a pointer) */ #define MEM_Str 0x0002 /* Value is a string */ #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_AffMask 0x001f /* Mask of affinity bits */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ #define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ #define MEM_Undefined 0x0080 /* Value is undefined */ #define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ #define MEM_TypeMask 0xc1ff /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the ** string is \000 or \u0000 terminated */ #define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ #define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ #define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ #define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */ #ifdef SQLITE_OMIT_INCRBLOB |
︙ | ︙ | |||
279 280 281 282 283 284 285 | ** Each auxiliary data pointer stored by a user defined function ** implementation calling sqlite3_set_auxdata() is stored in an instance ** of this structure. All such structures associated with a single VM ** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed ** when the VM is halted (if not before). */ struct AuxData { | | | | | | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | ** Each auxiliary data pointer stored by a user defined function ** implementation calling sqlite3_set_auxdata() is stored in an instance ** of this structure. All such structures associated with a single VM ** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed ** when the VM is halted (if not before). */ struct AuxData { int iAuxOp; /* Instruction number of OP_Function opcode */ int iAuxArg; /* Index of function argument. */ void *pAux; /* Aux data pointer */ void (*xDeleteAux)(void*); /* Destructor for the aux data */ AuxData *pNextAux; /* Next element in list */ }; /* ** The "context" argument for an installable function. A pointer to an ** instance of this structure is the first argument to the routines used ** implement the SQL functions. ** |
︙ | ︙ | |||
339 340 341 342 343 344 345 | ** is really a pointer to an instance of this structure. */ struct Vdbe { sqlite3 *db; /* The database connection that owns this statement */ Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ Parse *pParse; /* Parsing context used to create this Vdbe */ ynVar nVar; /* Number of entries in aVar[] */ | < | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | ** is really a pointer to an instance of this structure. */ struct Vdbe { sqlite3 *db; /* The database connection that owns this statement */ Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ Parse *pParse; /* Parsing context used to create this Vdbe */ ynVar nVar; /* Number of entries in aVar[] */ u32 magic; /* Magic number for sanity checking */ int nMem; /* Number of memory locations currently allocated */ int nCursor; /* Number of slots in apCsr[] */ u32 cacheCtr; /* VdbeCursor row cache generation counter */ int pc; /* The program counter */ int rc; /* Value to return */ int nChange; /* Number of db changes made since last reset */ |
︙ | ︙ | |||
364 365 366 367 368 369 370 | Mem *aMem; /* The memory locations */ Mem **apArg; /* Arguments to currently executing user function */ Mem *aColName; /* Column names to return */ Mem *pResultSet; /* Pointer to an array of results */ char *zErrMsg; /* Error message written here */ VdbeCursor **apCsr; /* One element of this array for each open cursor */ Mem *aVar; /* Values for the OP_Variable opcode. */ | | > < | | 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 | Mem *aMem; /* The memory locations */ Mem **apArg; /* Arguments to currently executing user function */ Mem *aColName; /* Column names to return */ Mem *pResultSet; /* Pointer to an array of results */ char *zErrMsg; /* Error message written here */ VdbeCursor **apCsr; /* One element of this array for each open cursor */ Mem *aVar; /* Values for the OP_Variable opcode. */ VList *pVList; /* Name of variables */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif int nOp; /* Number of instructions in the program */ #ifdef SQLITE_DEBUG int rcApp; /* errcode set by sqlite3_result_error_code() */ #endif u16 nResColumn; /* Number of columns in one row of the result set */ u8 errorAction; /* Recovery action to do in case of an error */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ bft expired:1; /* True if the VM needs to be recompiled */ bft doingRerun:1; /* True if rerunning after an auto-reprepare */ bft explain:2; /* True if EXPLAIN present on SQL command */ bft changeCntOn:1; /* True to update the change-counter */ bft runOnlyOnce:1; /* Automatically expire on reset */ bft usesStmtJournal:1; /* True if uses a statement journal */ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ VdbeFrame *pFrame; /* Parent frame */ VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */ int nFrame; /* Number of frames in pFrame list */ u32 expmask; /* Binding to these vars invalidates VM */ SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ |
︙ | ︙ | |||
426 427 428 429 430 431 432 | u8 *aRecord; /* old.* database record */ KeyInfo keyinfo; UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ int iNewReg; /* Register for new.* values */ i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ | < > > | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | u8 *aRecord; /* old.* database record */ KeyInfo keyinfo; UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ int iNewReg; /* Register for new.* values */ i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem *aNew; /* Array of new.* values */ Table *pTab; /* Schema object being upated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ }; /* ** Function prototypes */ void sqlite3VdbeError(Vdbe*, const char *, ...); void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); |
︙ | ︙ | |||
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); void sqlite3VdbeMemSetInt64(Mem*, i64); #ifdef SQLITE_OMIT_FLOATING_POINT # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 #else void sqlite3VdbeMemSetDouble(Mem*, double); #endif void sqlite3VdbeMemInit(Mem*,sqlite3*,u16); void sqlite3VdbeMemSetNull(Mem*); void sqlite3VdbeMemSetZeroBlob(Mem*,int); void sqlite3VdbeMemSetRowSet(Mem*); int sqlite3VdbeMemMakeWriteable(Mem*); int sqlite3VdbeMemStringify(Mem*, u8, u8); i64 sqlite3VdbeIntValue(Mem*); int sqlite3VdbeMemIntegerify(Mem*); double sqlite3VdbeRealValue(Mem*); void sqlite3VdbeIntegerAffinity(Mem*); int sqlite3VdbeMemRealify(Mem*); int sqlite3VdbeMemNumerify(Mem*); void sqlite3VdbeMemCast(Mem*,u8,u8); | > | | | 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 | int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); void sqlite3VdbeMemSetInt64(Mem*, i64); #ifdef SQLITE_OMIT_FLOATING_POINT # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 #else void sqlite3VdbeMemSetDouble(Mem*, double); #endif void sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*)); void sqlite3VdbeMemInit(Mem*,sqlite3*,u16); void sqlite3VdbeMemSetNull(Mem*); void sqlite3VdbeMemSetZeroBlob(Mem*,int); void sqlite3VdbeMemSetRowSet(Mem*); int sqlite3VdbeMemMakeWriteable(Mem*); int sqlite3VdbeMemStringify(Mem*, u8, u8); i64 sqlite3VdbeIntValue(Mem*); int sqlite3VdbeMemIntegerify(Mem*); double sqlite3VdbeRealValue(Mem*); void sqlite3VdbeIntegerAffinity(Mem*); int sqlite3VdbeMemRealify(Mem*); int sqlite3VdbeMemNumerify(Mem*); void sqlite3VdbeMemCast(Mem*,u8,u8); int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*); void sqlite3VdbeMemRelease(Mem *p); int sqlite3VdbeMemFinalize(Mem*, FuncDef*); const char *sqlite3OpcodeName(int); int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); int sqlite3VdbeMemClearAndResize(Mem *pMem, int n); int sqlite3VdbeCloseStatement(Vdbe *, int); void sqlite3VdbeFrameDelete(VdbeFrame*); int sqlite3VdbeFrameRestore(VdbeFrame *); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int); #endif int sqlite3VdbeTransferError(Vdbe *p); int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *); void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *); void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *); int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *); int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *); int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); #if !defined(SQLITE_OMIT_SHARED_CACHE) void sqlite3VdbeEnter(Vdbe*); #else |
︙ | ︙ | |||
526 527 528 529 530 531 532 | #ifndef SQLITE_OMIT_FOREIGN_KEY int sqlite3VdbeCheckFk(Vdbe *, int); #else # define sqlite3VdbeCheckFk(p,i) 0 #endif | < > > | > | 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 | #ifndef SQLITE_OMIT_FOREIGN_KEY int sqlite3VdbeCheckFk(Vdbe *, int); #else # define sqlite3VdbeCheckFk(p,i) 0 #endif #ifdef SQLITE_DEBUG void sqlite3VdbePrintSql(Vdbe*); void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf); #endif #ifndef SQLITE_OMIT_UTF16 int sqlite3VdbeMemTranslate(Mem*, u8); int sqlite3VdbeMemHandleBom(Mem *pMem); #endif #ifndef SQLITE_OMIT_INCRBLOB int sqlite3VdbeMemExpandBlob(Mem *); #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0) #else #define sqlite3VdbeMemExpandBlob(x) SQLITE_OK #define ExpandBlob(P) SQLITE_OK #endif #endif /* !defined(SQLITE_VDBEINT_H) */ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
150 151 152 153 154 155 156 | sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex; #endif sqlite3_mutex_enter(mutex); for(i=0; i<p->nVar; i++){ sqlite3VdbeMemRelease(&p->aVar[i]); p->aVar[i].flags = MEM_Null; } | > | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | sqlite3_mutex *mutex = ((Vdbe*)pStmt)->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 ); if( p->expmask ){ p->expired = 1; } sqlite3_mutex_leave(mutex); return rc; } |
︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 205 206 | } sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){ return sqlite3VdbeIntValue((Mem*)pVal); } unsigned int sqlite3_value_subtype(sqlite3_value *pVal){ Mem *pMem = (Mem*)pVal; return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0); } const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8); } #ifndef SQLITE_OMIT_UTF16 const void *sqlite3_value_text16(sqlite3_value* pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); | > > > > > > > > > > > > > | 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 | } sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){ return sqlite3VdbeIntValue((Mem*)pVal); } unsigned int sqlite3_value_subtype(sqlite3_value *pVal){ Mem *pMem = (Mem*)pVal; return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0); } void *sqlite3_value_pointer(sqlite3_value *pVal, const char *zPType){ Mem *p = (Mem*)pVal; if( (p->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) == (MEM_Null|MEM_Term|MEM_Subtype) && zPType!=0 && p->eSubtype=='p' && strcmp(p->u.zPType, zPType)==0 ){ return (void*)p->z; }else{ return 0; } } const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8); } #ifndef SQLITE_OMIT_UTF16 const void *sqlite3_value_text16(sqlite3_value* pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); |
︙ | ︙ | |||
372 373 374 375 376 377 378 379 380 381 382 383 384 385 | assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); } void sqlite3_result_null(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); } void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){ Mem *pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); pOut->eSubtype = eSubtype & 0xff; pOut->flags |= MEM_Subtype; } void sqlite3_result_text( | > > > > > > > > > > > > | 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 | assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); } void sqlite3_result_null(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); } void sqlite3_result_pointer( sqlite3_context *pCtx, void *pPtr, const char *zPType, void (*xDestructor)(void*) ){ Mem *pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); sqlite3VdbeMemRelease(pOut); pOut->flags = MEM_Null; sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor); } void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){ Mem *pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); pOut->eSubtype = eSubtype & 0xff; pOut->flags |= MEM_Subtype; } void sqlite3_result_text( |
︙ | ︙ | |||
494 495 496 497 498 499 500 | for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ int nEntry; sqlite3BtreeEnter(pBt); nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); sqlite3BtreeLeave(pBt); | | | 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ int nEntry; sqlite3BtreeEnter(pBt); nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); sqlite3BtreeLeave(pBt); if( nEntry>0 && db->xWalCallback && rc==SQLITE_OK ){ rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry); } } } #endif return rc; } |
︙ | ︙ | |||
604 605 606 607 608 609 610 | } #ifndef SQLITE_OMIT_TRACE /* If the statement completed successfully, invoke the profile callback */ if( rc!=SQLITE_ROW ) checkProfileCallback(db, p); #endif | | | 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 | } #ifndef SQLITE_OMIT_TRACE /* If the statement completed successfully, invoke the profile callback */ if( rc!=SQLITE_ROW ) checkProfileCallback(db, p); #endif if( rc==SQLITE_DONE && db->autoCommit ){ assert( p->rc==SQLITE_OK ); p->rc = doWalCallbacks(db); if( p->rc!=SQLITE_OK ){ rc = SQLITE_ERROR; } } |
︙ | ︙ | |||
628 629 630 631 632 633 634 | ** contains the value that would be returned if sqlite3_finalize() ** were called on statement p. */ assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE ); assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp ); | > > | > | < | | < < < < < | | | | | | | | | | | | | | | | | > | | > > > | 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 | ** contains the value that would be returned if sqlite3_finalize() ** were called on statement p. */ assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE ); assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp ); if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ /* If this statement was prepared using saved SQL and an ** error has occurred, then return the error code in p->rc to the ** caller. Set the error code in the database handle to the same value. */ rc = sqlite3VdbeTransferError(p); } return (rc&db->errMask); } /* ** This is the top-level implementation of sqlite3_step(). Call ** sqlite3Step() to do most of the work. If a schema error occurs, ** call sqlite3Reprepare() and try again. */ int sqlite3_step(sqlite3_stmt *pStmt){ int rc = SQLITE_OK; /* Result from sqlite3Step() */ Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */ int cnt = 0; /* Counter to prevent infinite loop of reprepares */ sqlite3 *db; /* The database connection */ if( vdbeSafetyNotNull(v) ){ return SQLITE_MISUSE_BKPT; } db = v->db; sqlite3_mutex_enter(db->mutex); v->doingRerun = 0; while( (rc = sqlite3Step(v))==SQLITE_SCHEMA && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){ int savedPc = v->pc; rc = sqlite3Reprepare(v); if( rc!=SQLITE_OK ){ /* This case occurs after failing to recompile an sql statement. ** The error message from the SQL compiler has already been loaded ** into the database handle. This block copies the error message ** from the database handle into the statement and sets the statement ** program counter to 0 to ensure that when the statement is ** finalized or reset the parser error message is available via ** sqlite3_errmsg() and sqlite3_errcode(). */ const char *zErr = (const char *)sqlite3_value_text(db->pErr); sqlite3DbFree(db, v->zErrMsg); if( !db->mallocFailed ){ v->zErrMsg = sqlite3DbStrDup(db, zErr); v->rc = rc = sqlite3ApiExit(db, rc); } else { v->zErrMsg = 0; v->rc = rc = SQLITE_NOMEM_BKPT; } break; } sqlite3_reset(pStmt); if( savedPc>=0 ) v->doingRerun = 1; assert( v->expired==0 ); } sqlite3_mutex_leave(db->mutex); return rc; } /* ** Extract the user data from a sqlite3_context structure and return a |
︙ | ︙ | |||
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 | return (void*)p->pMem->z; } } /* ** Return the auxiliary data pointer, if any, for the iArg'th argument to ** the user-function defined by pCtx. */ void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ AuxData *pAuxData; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); #if SQLITE_ENABLE_STAT3_OR_STAT4 if( pCtx->pVdbe==0 ) return 0; #else assert( pCtx->pVdbe!=0 ); #endif | > > > > > > | > | | | | > > > > > > < | | > > | | | | | | | 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 | return (void*)p->pMem->z; } } /* ** Return the auxiliary data pointer, if any, for the iArg'th argument to ** the user-function defined by pCtx. ** ** The left-most argument is 0. ** ** Undocumented behavior: If iArg is negative then access a cache of ** auxiliary data pointers that is available to all functions within a ** single prepared statement. The iArg values must match. */ void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ AuxData *pAuxData; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); #if SQLITE_ENABLE_STAT3_OR_STAT4 if( pCtx->pVdbe==0 ) return 0; #else assert( pCtx->pVdbe!=0 ); #endif for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNextAux){ if( pAuxData->iAuxArg==iArg && (pAuxData->iAuxOp==pCtx->iOp || iArg<0) ){ return pAuxData->pAux; } } return 0; } /* ** Set the auxiliary data pointer and delete function, for the iArg'th ** argument to the user-function defined by pCtx. Any previous value is ** deleted by calling the delete function specified when it was set. ** ** The left-most argument is 0. ** ** Undocumented behavior: If iArg is negative then make the data available ** to all functions within the current prepared statement using iArg as an ** access code. */ void sqlite3_set_auxdata( sqlite3_context *pCtx, int iArg, void *pAux, void (*xDelete)(void*) ){ AuxData *pAuxData; Vdbe *pVdbe = pCtx->pVdbe; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( pVdbe==0 ) goto failed; #else assert( pVdbe!=0 ); #endif for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNextAux){ if( pAuxData->iAuxArg==iArg && (pAuxData->iAuxOp==pCtx->iOp || iArg<0) ){ break; } } if( pAuxData==0 ){ pAuxData = sqlite3DbMallocZero(pVdbe->db, sizeof(AuxData)); if( !pAuxData ) goto failed; pAuxData->iAuxOp = pCtx->iOp; pAuxData->iAuxArg = iArg; pAuxData->pNextAux = pVdbe->pAuxData; pVdbe->pAuxData = pAuxData; if( pCtx->fErrorOrAux==0 ){ pCtx->isError = 0; pCtx->fErrorOrAux = 1; } }else if( pAuxData->xDeleteAux ){ pAuxData->xDeleteAux(pAuxData->pAux); } pAuxData->pAux = pAux; pAuxData->xDeleteAux = xDelete; return; failed: if( xDelete ){ xDelete(pAux); } } |
︙ | ︙ | |||
1254 1255 1256 1257 1258 1259 1260 | ** ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host ** parameter in the WHERE clause might influence the choice of query plan ** for a statement, then the statement will be automatically recompiled, ** as if there had been a schema change, on the first sqlite3_step() call ** following any change to the bindings of that parameter. */ | < | < > | 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 | ** ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host ** parameter in the WHERE clause might influence the choice of query plan ** for a statement, then the statement will be automatically recompiled, ** as if there had been a schema change, on the first sqlite3_step() call ** following any change to the bindings of that parameter. */ assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 ); if( p->expmask!=0 && (p->expmask & (i>=31 ? 0x80000000 : (u32)1<<i))!=0 ){ p->expired = 1; } return SQLITE_OK; } /* ** Bind a text or BLOB value. |
︙ | ︙ | |||
1285 1286 1287 1288 1289 1290 1291 | if( rc==SQLITE_OK ){ if( zData!=0 ){ pVar = &p->aVar[i-1]; rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); if( rc==SQLITE_OK && encoding!=0 ){ rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); } | > | | > | 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 | if( rc==SQLITE_OK ){ if( zData!=0 ){ pVar = &p->aVar[i-1]; rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); if( rc==SQLITE_OK && encoding!=0 ){ rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); } if( rc ){ sqlite3Error(p->db, rc); rc = sqlite3ApiExit(p->db, rc); } } sqlite3_mutex_leave(p->db->mutex); }else if( xDel!=SQLITE_STATIC && xDel!=SQLITE_TRANSIENT ){ xDel((void*)zData); } return rc; } |
︙ | ︙ | |||
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 | int rc; Vdbe *p = (Vdbe*)pStmt; rc = vdbeUnbind(p, i); if( rc==SQLITE_OK ){ sqlite3_mutex_leave(p->db->mutex); } return rc; } int sqlite3_bind_text( sqlite3_stmt *pStmt, int i, const char *zData, int nData, void (*xDel)(void*) | > > > > > > > > > > > > > > > > > > | 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 | int rc; Vdbe *p = (Vdbe*)pStmt; rc = vdbeUnbind(p, i); if( rc==SQLITE_OK ){ sqlite3_mutex_leave(p->db->mutex); } return rc; } int sqlite3_bind_pointer( sqlite3_stmt *pStmt, int i, void *pPtr, const char *zPTtype, void (*xDestructor)(void*) ){ int rc; Vdbe *p = (Vdbe*)pStmt; rc = vdbeUnbind(p, i); if( rc==SQLITE_OK ){ sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); sqlite3_mutex_leave(p->db->mutex); }else if( xDestructor ){ xDestructor(pPtr); } return rc; } int sqlite3_bind_text( sqlite3_stmt *pStmt, int i, const char *zData, int nData, void (*xDel)(void*) |
︙ | ︙ | |||
1466 1467 1468 1469 1470 1471 1472 | ** Return the name of a wildcard parameter. Return NULL if the index ** is out of range or if the wildcard is unnamed. ** ** The result is always UTF-8. */ const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ Vdbe *p = (Vdbe*)pStmt; | < | < | < | | < < < < < < < < < < | 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 | ** Return the name of a wildcard parameter. Return NULL if the index ** is out of range or if the wildcard is unnamed. ** ** The result is always UTF-8. */ const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ Vdbe *p = (Vdbe*)pStmt; if( p==0 ) return 0; return sqlite3VListNumToName(p->pVList, i); } /* ** Given a wildcard parameter name, return the index of the variable ** with that name. If there is no variable with the given name, ** return 0. */ int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){ if( p==0 || zName==0 ) return 0; return sqlite3VListNameToNum(p->pVList, zName, nName); } int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ return sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlite3Strlen30(zName)); } /* ** Transfer all bindings from the first statement over to the second. |
︙ | ︙ | |||
1532 1533 1534 1535 1536 1537 1538 | */ int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ Vdbe *pFrom = (Vdbe*)pFromStmt; Vdbe *pTo = (Vdbe*)pToStmt; if( pFrom->nVar!=pTo->nVar ){ return SQLITE_ERROR; } | > | > | | 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 | */ int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ Vdbe *pFrom = (Vdbe*)pFromStmt; Vdbe *pTo = (Vdbe*)pToStmt; if( pFrom->nVar!=pTo->nVar ){ return SQLITE_ERROR; } assert( (pTo->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || pTo->expmask==0 ); if( pTo->expmask ){ pTo->expired = 1; } assert( (pFrom->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || pFrom->expmask==0 ); if( pFrom->expmask ){ pFrom->expired = 1; } return sqlite3TransferBindings(pFromStmt, pToStmt); } #endif /* |
︙ | ︙ | |||
1604 1605 1606 1607 1608 1609 1610 | u32 v; #ifdef SQLITE_ENABLE_API_ARMOR if( !pStmt ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif | > > > > > > > > > > | | > | 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 | u32 v; #ifdef SQLITE_ENABLE_API_ARMOR if( !pStmt ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif if( op==SQLITE_STMTSTATUS_MEMUSED ){ sqlite3 *db = pVdbe->db; sqlite3_mutex_enter(db->mutex); v = 0; db->pnBytesFreed = (int*)&v; sqlite3VdbeClearObject(db, pVdbe); sqlite3DbFree(db, pVdbe); db->pnBytesFreed = 0; sqlite3_mutex_leave(db->mutex); }else{ v = pVdbe->aCounter[op]; if( resetFlag ) pVdbe->aCounter[op] = 0; } return (int)v; } /* ** Return the SQL associated with a prepared statement */ const char *sqlite3_sql(sqlite3_stmt *pStmt){ |
︙ | ︙ | |||
1653 1654 1655 1656 1657 1658 1659 | ** if successful, or a NULL pointer if an OOM error is encountered. */ static UnpackedRecord *vdbeUnpackRecord( KeyInfo *pKeyInfo, int nKey, const void *pKey ){ | < | | > > > > | > > > | | < | | | | 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 | ** if successful, or a NULL pointer if an OOM error is encountered. */ static UnpackedRecord *vdbeUnpackRecord( KeyInfo *pKeyInfo, int nKey, const void *pKey ){ UnpackedRecord *pRet; /* Return value */ pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo); if( pRet ){ memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1)); sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet); } return pRet; } /* ** This function is called from within a pre-update callback to retrieve ** a field of the row currently being updated or deleted. */ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p = db->pPreUpdate; Mem *pMem; int rc = SQLITE_OK; /* Test that this call is being made from within an SQLITE_DELETE or ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ if( !p || p->op==SQLITE_INSERT ){ rc = SQLITE_MISUSE_BKPT; goto preupdate_old_out; } if( p->pPk ){ iIdx = sqlite3ColumnOfIndex(p->pPk, iIdx); } if( iIdx>=p->pCsr->nField || iIdx<0 ){ rc = SQLITE_RANGE; goto preupdate_old_out; } /* If the old.* record has not yet been loaded into memory, do so now. */ if( p->pUnpacked==0 ){ u32 nRec; u8 *aRec; nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); aRec = sqlite3DbMallocRaw(db, nRec); if( !aRec ) goto preupdate_old_out; rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); if( rc==SQLITE_OK ){ p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); if( !p->pUnpacked ) rc = SQLITE_NOMEM; } if( rc!=SQLITE_OK ){ sqlite3DbFree(db, aRec); goto preupdate_old_out; } p->aRecord = aRec; } pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; if( iIdx==p->pTab->iPKey ){ sqlite3VdbeMemSetInt64(pMem, p->iKey1); }else if( iIdx>=p->pUnpacked->nField ){ *ppValue = (sqlite3_value *)columnNullValue(); }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ if( pMem->flags & MEM_Int ){ sqlite3VdbeMemRealify(pMem); } } preupdate_old_out: sqlite3Error(db, rc); return sqlite3ApiExit(db, rc); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is called from within a pre-update callback to retrieve ** the number of columns in the row being updated, deleted or inserted. */ int sqlite3_preupdate_count(sqlite3 *db){ PreUpdate *p = db->pPreUpdate; return (p ? p->keyinfo.nKeyField : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is designed to be called from within a pre-update callback ** only. It returns zero if the change that caused the callback was made |
︙ | ︙ | |||
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 | int rc = SQLITE_OK; Mem *pMem; if( !p || p->op==SQLITE_DELETE ){ rc = SQLITE_MISUSE_BKPT; goto preupdate_new_out; } if( iIdx>=p->pCsr->nField || iIdx<0 ){ rc = SQLITE_RANGE; goto preupdate_new_out; } if( p->op==SQLITE_INSERT ){ /* For an INSERT, memory cell p->iNewReg contains the serialized record ** that is being inserted. Deserialize it. */ UnpackedRecord *pUnpack = p->pNewUnpacked; if( !pUnpack ){ Mem *pData = &p->v->aMem[p->iNewReg]; rc = ExpandBlob(pData); if( rc!=SQLITE_OK ) goto preupdate_new_out; pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); if( !pUnpack ){ rc = SQLITE_NOMEM; goto preupdate_new_out; } p->pNewUnpacked = pUnpack; } | > > > < < < | | | < > > | | 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 | int rc = SQLITE_OK; Mem *pMem; if( !p || p->op==SQLITE_DELETE ){ rc = SQLITE_MISUSE_BKPT; goto preupdate_new_out; } if( p->pPk && p->op!=SQLITE_UPDATE ){ iIdx = sqlite3ColumnOfIndex(p->pPk, iIdx); } if( iIdx>=p->pCsr->nField || iIdx<0 ){ rc = SQLITE_RANGE; goto preupdate_new_out; } if( p->op==SQLITE_INSERT ){ /* For an INSERT, memory cell p->iNewReg contains the serialized record ** that is being inserted. Deserialize it. */ UnpackedRecord *pUnpack = p->pNewUnpacked; if( !pUnpack ){ Mem *pData = &p->v->aMem[p->iNewReg]; rc = ExpandBlob(pData); if( rc!=SQLITE_OK ) goto preupdate_new_out; pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); if( !pUnpack ){ rc = SQLITE_NOMEM; goto preupdate_new_out; } p->pNewUnpacked = pUnpack; } pMem = &pUnpack->aMem[iIdx]; if( iIdx==p->pTab->iPKey ){ sqlite3VdbeMemSetInt64(pMem, p->iKey2); }else if( iIdx>=pUnpack->nField ){ pMem = (sqlite3_value *)columnNullValue(); } }else{ /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required ** value. Make a copy of the cell contents and return a pointer to it. ** It is not safe to return a pointer to the memory cell itself as the ** caller may modify the value text encoding. */ assert( p->op==SQLITE_UPDATE ); if( !p->aNew ){ p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField); if( !p->aNew ){ rc = SQLITE_NOMEM; goto preupdate_new_out; } } assert( iIdx>=0 && iIdx<p->pCsr->nField ); pMem = &p->aNew[iIdx]; if( pMem->flags==0 ){ if( iIdx==p->pTab->iPKey ){ sqlite3VdbeMemSetInt64(pMem, p->iKey2); }else{ rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); if( rc!=SQLITE_OK ) goto preupdate_new_out; } } } |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
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 | db->pVdbe->pPrev = p; } p->pNext = db->pVdbe; p->pPrev = 0; db->pVdbe = p; p->magic = VDBE_MAGIC_INIT; p->pParse = pParse; assert( pParse->aLabel==0 ); assert( pParse->nLabel==0 ); assert( pParse->nOpAlloc==0 ); assert( pParse->szOpAlloc==0 ); return p; } /* ** Change the error string stored in Vdbe.zErrMsg */ void sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){ va_list ap; sqlite3DbFree(p->db, p->zErrMsg); va_start(ap, zFormat); p->zErrMsg = sqlite3VMPrintf(p->db, zFormat, ap); va_end(ap); } /* ** Remember the SQL string for a prepared statement. */ | > > | < > | < | > < | 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 | db->pVdbe->pPrev = p; } p->pNext = db->pVdbe; p->pPrev = 0; db->pVdbe = p; p->magic = VDBE_MAGIC_INIT; p->pParse = pParse; pParse->pVdbe = p; assert( pParse->aLabel==0 ); assert( pParse->nLabel==0 ); assert( pParse->nOpAlloc==0 ); assert( pParse->szOpAlloc==0 ); sqlite3VdbeAddOp2(p, OP_Init, 0, 1); return p; } /* ** Change the error string stored in Vdbe.zErrMsg */ void sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){ va_list ap; sqlite3DbFree(p->db, p->zErrMsg); va_start(ap, zFormat); p->zErrMsg = sqlite3VMPrintf(p->db, zFormat, ap); va_end(ap); } /* ** Remember the SQL string for a prepared statement. */ void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlags){ if( p==0 ) return; p->prepFlags = prepFlags; if( (prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){ p->expmask = 0; } assert( p->zSql==0 ); p->zSql = sqlite3DbStrNDup(p->db, z, n); } /* ** Swap all content between two VDBE structures. */ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ Vdbe tmp, *pTmp; |
︙ | ︙ | |||
80 81 82 83 84 85 86 | pB->pNext = pTmp; pTmp = pA->pPrev; pA->pPrev = pB->pPrev; pB->pPrev = pTmp; zTmp = pA->zSql; pA->zSql = pB->zSql; pB->zSql = zTmp; | | > > > | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | pB->pNext = pTmp; pTmp = pA->pPrev; pA->pPrev = pB->pPrev; pB->pPrev = pTmp; zTmp = pA->zSql; pA->zSql = pB->zSql; pB->zSql = zTmp; pB->expmask = pA->expmask; pB->prepFlags = pA->prepFlags; memcpy(pB->aCounter, pA->aCounter, sizeof(pB->aCounter)); pB->aCounter[SQLITE_STMTSTATUS_REPREPARE]++; } /* ** Resize the Vdbe.aOp array so that it is at least nOp elements larger ** than its current size. nOp is guaranteed to be less than or equal ** to 1024/sizeof(Op). ** |
︙ | ︙ | |||
110 111 112 113 114 115 116 117 118 119 120 121 122 123 | ** size of the op array or add 1KB of space, whichever is smaller. */ #ifdef SQLITE_TEST_REALLOC_STRESS int nNew = (p->nOpAlloc>=512 ? p->nOpAlloc*2 : p->nOpAlloc+nOp); #else int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op))); UNUSED_PARAMETER(nOp); #endif assert( nOp<=(1024/sizeof(Op)) ); assert( nNew>=(p->nOpAlloc+nOp) ); pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); if( pNew ){ p->szOpAlloc = sqlite3DbMallocSize(p->db, pNew); p->nOpAlloc = p->szOpAlloc/sizeof(Op); | > > > > > > | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | ** size of the op array or add 1KB of space, whichever is smaller. */ #ifdef SQLITE_TEST_REALLOC_STRESS int nNew = (p->nOpAlloc>=512 ? p->nOpAlloc*2 : p->nOpAlloc+nOp); #else int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op))); UNUSED_PARAMETER(nOp); #endif /* Ensure that the size of a VDBE does not grow too large */ if( nNew > p->db->aLimit[SQLITE_LIMIT_VDBE_OP] ){ sqlite3OomFault(p->db); return SQLITE_NOMEM; } assert( nOp<=(1024/sizeof(Op)) ); assert( nNew>=(p->nOpAlloc+nOp) ); pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); if( pNew ){ p->szOpAlloc = sqlite3DbMallocSize(p->db, pNew); p->nOpAlloc = p->szOpAlloc/sizeof(Op); |
︙ | ︙ | |||
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | /* ** Generate code that initializes multiple registers to string or integer ** constants. The registers begin with iDest and increase consecutively. ** One register is initialized for each characgter in zTypes[]. For each ** "s" character in zTypes[], the register is a string if the argument is ** not NULL, or OP_Null if the value is a null pointer. For each "i" character ** in zTypes[], the register is initialized to an integer. */ void sqlite3VdbeMultiLoad(Vdbe *p, int iDest, const char *zTypes, ...){ va_list ap; int i; char c; va_start(ap, zTypes); for(i=0; (c = zTypes[i])!=0; i++){ if( c=='s' ){ const char *z = va_arg(ap, const char*); | > > > | < | | > > > > | 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 | /* ** Generate code that initializes multiple registers to string or integer ** constants. The registers begin with iDest and increase consecutively. ** One register is initialized for each characgter in zTypes[]. For each ** "s" character in zTypes[], the register is a string if the argument is ** not NULL, or OP_Null if the value is a null pointer. For each "i" character ** in zTypes[], the register is initialized to an integer. ** ** If the input string does not end with "X" then an OP_ResultRow instruction ** is generated for the values inserted. */ void sqlite3VdbeMultiLoad(Vdbe *p, int iDest, const char *zTypes, ...){ va_list ap; int i; char c; va_start(ap, zTypes); for(i=0; (c = zTypes[i])!=0; i++){ if( c=='s' ){ const char *z = va_arg(ap, const char*); sqlite3VdbeAddOp4(p, z==0 ? OP_Null : OP_String8, 0, iDest+i, 0, z, 0); }else if( c=='i' ){ sqlite3VdbeAddOp2(p, OP_Integer, va_arg(ap, int), iDest+i); }else{ goto skip_op_resultrow; } } sqlite3VdbeAddOp2(p, OP_ResultRow, iDest, i); skip_op_resultrow: va_end(ap); } /* ** Add an opcode that includes the p4 value as a pointer. */ int sqlite3VdbeAddOp4( |
︙ | ︙ | |||
309 310 311 312 313 314 315 | int op, /* The new opcode */ int p1, /* The P1 operand */ int p2, /* The P2 operand */ int p3, /* The P3 operand */ int p4 /* The P4 operand as an integer */ ){ int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); | > > | > > | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | int op, /* The new opcode */ int p1, /* The P1 operand */ int p2, /* The P2 operand */ int p3, /* The P3 operand */ int p4 /* The P4 operand as an integer */ ){ int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); if( p->db->mallocFailed==0 ){ VdbeOp *pOp = &p->aOp[addr]; pOp->p4type = P4_INT32; pOp->p4.i = p4; } return addr; } /* Insert the end of a co-routine */ void sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){ sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield); |
︙ | ︙ | |||
468 469 470 471 472 473 474 | ** ** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. ** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort. ** * OP_Destroy ** * OP_VUpdate ** * OP_VRename ** * OP_FkCounter with P2==0 (immediate foreign key constraint) | > | | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | ** ** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. ** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort. ** * OP_Destroy ** * OP_VUpdate ** * OP_VRename ** * OP_FkCounter with P2==0 (immediate foreign key constraint) ** * OP_CreateBtree/BTREE_INTKEY and OP_InitCoroutine ** (for CREATE TABLE AS SELECT ...) ** ** Then check that the value of Parse.mayAbort is true if an ** ABORT may be thrown, or false otherwise. Return true if it does ** match, or false otherwise. This function is intended to be used as ** part of an assert statement in the compiler. Similar to: ** ** assert( sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); |
︙ | ︙ | |||
496 497 498 499 500 501 502 | if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename || ((opcode==OP_Halt || opcode==OP_HaltIfNull) && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) ){ hasAbort = 1; break; } | | | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 | if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename || ((opcode==OP_Halt || opcode==OP_HaltIfNull) && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) ){ hasAbort = 1; break; } if( opcode==OP_CreateBtree && pOp->p3==BTREE_INTKEY ) hasCreateTable = 1; if( opcode==OP_InitCoroutine ) hasInitCoroutine = 1; #ifndef SQLITE_OMIT_FOREIGN_KEY if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){ hasFkCounter = 1; } #endif } |
︙ | ︙ | |||
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | case OP_Checkpoint: #endif case OP_Vacuum: case OP_JournalMode: { p->readOnly = 0; p->bIsReader = 1; break; } #ifndef SQLITE_OMIT_VIRTUALTABLE case OP_VUpdate: { if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; break; } case OP_VFilter: { int n; assert( (pOp - p->aOp) >= 3 ); assert( pOp[-1].opcode==OP_Integer ); n = pOp[-1].p1; if( n>nMaxArgs ) nMaxArgs = n; | > > > > > > > > > > > > > > > > > > > > > | | | | > > > | | < | < < < < > > > | < < < | 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 | case OP_Checkpoint: #endif case OP_Vacuum: case OP_JournalMode: { p->readOnly = 0; p->bIsReader = 1; break; } case OP_Next: case OP_NextIfOpen: case OP_SorterNext: { pOp->p4.xAdvance = sqlite3BtreeNext; pOp->p4type = P4_ADVANCE; /* The code generator never codes any of these opcodes as a jump ** to a label. They are always coded as a jump backwards to a ** known address */ assert( pOp->p2>=0 ); break; } case OP_Prev: case OP_PrevIfOpen: { pOp->p4.xAdvance = sqlite3BtreePrevious; pOp->p4type = P4_ADVANCE; /* The code generator never codes any of these opcodes as a jump ** to a label. They are always coded as a jump backwards to a ** known address */ assert( pOp->p2>=0 ); break; } #ifndef SQLITE_OMIT_VIRTUALTABLE case OP_VUpdate: { if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; break; } case OP_VFilter: { int n; assert( (pOp - p->aOp) >= 3 ); assert( pOp[-1].opcode==OP_Integer ); n = pOp[-1].p1; if( n>nMaxArgs ) nMaxArgs = n; /* Fall through into the default case */ } #endif default: { if( pOp->p2<0 ){ /* 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 ); assert( ADDR(pOp->p2)<pParse->nLabel ); 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); } if( pOp==p->aOp ) break; pOp--; } sqlite3DbFree(p->db, pParse->aLabel); pParse->aLabel = 0; pParse->nLabel = 0; |
︙ | ︙ | |||
637 638 639 640 641 642 643 644 645 646 647 648 649 650 | ** to verify that certain calls to sqlite3VdbeAddOpList() can never ** fail due to a OOM fault and hence that the return value from ** sqlite3VdbeAddOpList() will always be non-NULL. */ #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){ assert( p->nOp + N <= p->pParse->nOpAlloc ); } #endif /* ** This function returns a pointer to the array of opcodes associated with ** the Vdbe passed as the first argument. It is the callers responsibility ** to arrange for the returned array to be eventually freed using the | > > > > > > > > > > > > > > > > | 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 | ** to verify that certain calls to sqlite3VdbeAddOpList() can never ** fail due to a OOM fault and hence that the return value from ** sqlite3VdbeAddOpList() will always be non-NULL. */ #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){ assert( p->nOp + N <= p->pParse->nOpAlloc ); } #endif /* ** Verify that the VM passed as the only argument does not contain ** an OP_ResultRow opcode. Fail an assert() if it does. This is used ** by code in pragma.c to ensure that the implementation of certain ** pragmas comports with the flags specified in the mkpragmatab.tcl ** script. */ #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) void sqlite3VdbeVerifyNoResultRow(Vdbe *p){ int i; for(i=0; i<p->nOp; i++){ assert( p->aOp[i].opcode!=OP_ResultRow ); } } #endif /* ** This function returns a pointer to the array of opcodes associated with ** the Vdbe passed as the first argument. It is the callers responsibility ** to arrange for the returned array to be eventually freed using the |
︙ | ︙ | |||
759 760 761 762 763 764 765 | } void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){ sqlite3VdbeGetOp(p,addr)->p2 = val; } void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){ sqlite3VdbeGetOp(p,addr)->p3 = val; } | | | | | | 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 | } void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){ sqlite3VdbeGetOp(p,addr)->p2 = val; } void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){ sqlite3VdbeGetOp(p,addr)->p3 = val; } void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ assert( p->nOp>0 || p->db->mallocFailed ); if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5; } /* ** Change the P2 operand of instruction addr so that it points to ** the address of the next instruction to be coded. */ void sqlite3VdbeJumpHere(Vdbe *p, int addr){ sqlite3VdbeChangeP2(p, addr, p->nOp); } /* ** If the input FuncDef structure is ephemeral, then free it. If ** the FuncDef is not ephermal, then do nothing. */ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){ sqlite3DbFreeNN(db, pDef); } } static void vdbeFreeOpArray(sqlite3 *, Op *, int); /* ** Delete a P4 value if necessary. */ static SQLITE_NOINLINE void freeP4Mem(sqlite3 *db, Mem *p){ if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc); sqlite3DbFreeNN(db, p); } static SQLITE_NOINLINE void freeP4FuncCtx(sqlite3 *db, sqlite3_context *p){ freeEphemeralFunction(db, p->pFunc); sqlite3DbFreeNN(db, p); } static void freeP4(sqlite3 *db, int p4type, void *p4){ assert( db ); switch( p4type ){ case P4_FUNCCTX: { freeP4FuncCtx(db, (sqlite3_context*)p4); break; |
︙ | ︙ | |||
820 821 822 823 824 825 826 | } #ifdef SQLITE_ENABLE_CURSOR_HINTS case P4_EXPR: { sqlite3ExprDelete(db, (Expr*)p4); break; } #endif | < < < < | 876 877 878 879 880 881 882 883 884 885 886 887 888 889 | } #ifdef SQLITE_ENABLE_CURSOR_HINTS case P4_EXPR: { sqlite3ExprDelete(db, (Expr*)p4); break; } #endif case P4_FUNCDEF: { freeEphemeralFunction(db, (FuncDef*)p4); break; } case P4_MEM: { if( db->pnBytesFreed==0 ){ sqlite3ValueFree((sqlite3_value*)p4); |
︙ | ︙ | |||
851 852 853 854 855 856 857 | ** 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. */ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ if( aOp ){ Op *pOp; | | | > < | 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 | ** 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. */ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ if( aOp ){ Op *pOp; for(pOp=&aOp[nOp-1]; pOp>=aOp; pOp--){ if( pOp->p4type <= P4_FREE_IF_LE ) freeP4(db, pOp->p4type, pOp->p4.p); #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS sqlite3DbFree(db, pOp->zComment); #endif } sqlite3DbFreeNN(db, aOp); } } /* ** Link the SubProgram object passed as the second argument into the linked ** list at Vdbe.pSubProgram. This list is used to delete all sub-program ** objects when the VM is no longer required. */ |
︙ | ︙ | |||
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 | }else if( zP4!=0 ){ assert( n<0 ); pOp->p4.p = (void*)zP4; pOp->p4type = (signed char)n; if( n==P4_VTAB ) sqlite3VtabLock((VTable*)zP4); } } /* ** Set the P4 on the most recently added opcode to the KeyInfo for the ** index given. */ void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ Vdbe *v = pParse->pVdbe; assert( v!=0 ); assert( pIdx!=0 ); | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 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 | }else if( zP4!=0 ){ assert( n<0 ); pOp->p4.p = (void*)zP4; pOp->p4type = (signed char)n; if( n==P4_VTAB ) sqlite3VtabLock((VTable*)zP4); } } /* ** Change the P4 operand of the most recently coded instruction ** to the value defined by the arguments. This is a high-speed ** version of sqlite3VdbeChangeP4(). ** ** The P4 operand must not have been previously defined. And the new ** P4 must not be P4_INT32. Use sqlite3VdbeChangeP4() in either of ** those cases. */ void sqlite3VdbeAppendP4(Vdbe *p, void *pP4, int n){ VdbeOp *pOp; assert( n!=P4_INT32 && n!=P4_VTAB ); assert( n<=0 ); if( p->db->mallocFailed ){ freeP4(p->db, n, pP4); }else{ assert( pP4!=0 ); assert( p->nOp>0 ); pOp = &p->aOp[p->nOp-1]; assert( pOp->p4type==P4_NOTUSED ); pOp->p4type = n; pOp->p4.p = pP4; } } /* ** Set the P4 on the most recently added opcode to the KeyInfo for the ** index given. */ void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ Vdbe *v = pParse->pVdbe; KeyInfo *pKeyInfo; assert( v!=0 ); assert( pIdx!=0 ); pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pIdx); if( pKeyInfo ) sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO); } #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS /* ** Change the comment on the most recently coded instruction. Or ** insert a No-op and add the comment to that new instruction. This ** makes the code easier to read during debugging. None of this happens |
︙ | ︙ | |||
1241 1242 1243 1244 1245 1246 1247 | assert( nTemp>=20 ); sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0); switch( pOp->p4type ){ case P4_KEYINFO: { int j; KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; assert( pKeyInfo->aSortOrder!=0 ); | | | | 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 | assert( nTemp>=20 ); sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0); switch( pOp->p4type ){ case P4_KEYINFO: { int j; KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; assert( pKeyInfo->aSortOrder!=0 ); sqlite3XPrintf(&x, "k(%d", pKeyInfo->nKeyField); for(j=0; j<pKeyInfo->nKeyField; j++){ CollSeq *pColl = pKeyInfo->aColl[j]; const char *zColl = pColl ? pColl->zName : ""; if( strcmp(zColl, "BINARY")==0 ) zColl = "B"; sqlite3XPrintf(&x, ",%s%s", pKeyInfo->aSortOrder[j] ? "-" : "", zColl); } sqlite3StrAccumAppend(&x, ")", 1); break; |
︙ | ︙ | |||
1267 1268 1269 1270 1271 1272 1273 | break; } case P4_FUNCDEF: { FuncDef *pDef = pOp->p4.pFunc; sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg); break; } | | | 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 | break; } case P4_FUNCDEF: { FuncDef *pDef = pOp->p4.pFunc; sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg); break; } #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) case P4_FUNCCTX: { FuncDef *pDef = pOp->p4.pCtx->pFunc; sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg); break; } #endif case P4_INT64: { |
︙ | ︙ | |||
1314 1315 1316 1317 1318 1319 1320 | } #endif case P4_INTARRAY: { int i; int *ai = pOp->p4.ai; int n = ai[0]; /* The first element of an INTARRAY is always the ** count of the number of elements to follow */ | | | 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 | } #endif case P4_INTARRAY: { int i; int *ai = pOp->p4.ai; int n = ai[0]; /* The first element of an INTARRAY is always the ** count of the number of elements to follow */ for(i=1; i<=n; i++){ sqlite3XPrintf(&x, ",%d", ai[i]); } zTemp[0] = '['; sqlite3StrAccumAppend(&x, "]", 1); break; } case P4_SUBPROGRAM: { |
︙ | ︙ | |||
1505 1506 1507 1508 1509 1510 1511 | testcase( p->flags & MEM_Agg ); testcase( p->flags & MEM_Dyn ); testcase( p->flags & MEM_Frame ); testcase( p->flags & MEM_RowSet ); if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){ sqlite3VdbeMemRelease(p); }else if( p->szMalloc ){ | | | 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 | testcase( p->flags & MEM_Agg ); testcase( p->flags & MEM_Dyn ); testcase( p->flags & MEM_Frame ); testcase( p->flags & MEM_RowSet ); if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){ sqlite3VdbeMemRelease(p); }else if( p->szMalloc ){ sqlite3DbFreeNN(db, p->zMalloc); p->szMalloc = 0; } p->flags = MEM_Undefined; }while( (++p)<pEnd ); } } |
︙ | ︙ | |||
1945 1946 1947 1948 1949 1950 1951 | p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); #endif if( x.nNeeded==0 ) break; x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded); x.nFree = x.nNeeded; }while( !db->mallocFailed ); | | < | < | 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 | p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); #endif if( x.nNeeded==0 ) break; x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded); x.nFree = x.nNeeded; }while( !db->mallocFailed ); p->pVList = pParse->pVList; pParse->pVList = 0; p->explain = pParse->explain; if( db->mallocFailed ){ p->nVar = 0; p->nCursor = 0; p->nMem = 0; }else{ p->nCursor = nCursor; |
︙ | ︙ | |||
1976 1977 1978 1979 1980 1981 1982 | ** Close a VDBE cursor and release all the resources that cursor ** happens to hold. */ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ if( pCx==0 ){ return; } | | | | | 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 | ** Close a VDBE cursor and release all the resources that cursor ** happens to hold. */ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ if( pCx==0 ){ return; } assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE ); switch( pCx->eCurType ){ case CURTYPE_SORTER: { sqlite3VdbeSorterClose(p->db, pCx); break; } case CURTYPE_BTREE: { if( pCx->isEphemeral ){ if( pCx->pBtx ) sqlite3BtreeClose(pCx->pBtx); /* The pCx->pCursor will be close automatically, if it exists, by ** the call above. */ }else{ assert( pCx->uc.pCursor!=0 ); sqlite3BtreeCloseCursor(pCx->uc.pCursor); } break; |
︙ | ︙ | |||
2080 2081 2082 2083 2084 2085 2086 | } /* Delete any auxdata allocations made by the VM */ if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p->db, &p->pAuxData, -1, 0); assert( p->pAuxData==0 ); } | < < < < < < < < < < < < < < < < < < < < < < > | | > | | | 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 | } /* Delete any auxdata allocations made by the VM */ if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p->db, &p->pAuxData, -1, 0); assert( p->pAuxData==0 ); } /* ** Set the number of result columns that will be returned by this SQL ** statement. This is now set at compile time, rather than during ** execution of the vdbe program so that sqlite3_column_count() can ** be called on an SQL statement before sqlite3_step(). */ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ int n; sqlite3 *db = p->db; if( p->nResColumn ){ releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); sqlite3DbFree(db, p->aColName); } n = nResColumn*COLNAME_N; p->nResColumn = (u16)nResColumn; p->aColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n ); if( p->aColName==0 ) return; initMemArray(p->aColName, n, db, MEM_Null); } /* ** Set the name of the idx'th column to be returned by the SQL statement. ** zName must be a pointer to a nul terminated string. ** ** This call must be made after a call to sqlite3VdbeSetNumCols(). |
︙ | ︙ | |||
2453 2454 2455 2456 2457 2458 2459 | ** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement ** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the ** statement transaction is committed. ** ** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned. ** Otherwise SQLITE_OK. */ | | < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < > > > > > > > | 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 | ** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement ** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the ** statement transaction is committed. ** ** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned. ** Otherwise SQLITE_OK. */ static SQLITE_NOINLINE int vdbeCloseStatement(Vdbe *p, int eOp){ sqlite3 *const db = p->db; int rc = SQLITE_OK; int i; const int iSavepoint = p->iStatement-1; assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); assert( db->nStatement>0 ); assert( p->iStatement==(db->nStatement+db->nSavepoint) ); for(i=0; i<db->nDb; i++){ int rc2 = SQLITE_OK; Btree *pBt = db->aDb[i].pBt; if( pBt ){ if( eOp==SAVEPOINT_ROLLBACK ){ rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint); } if( rc2==SQLITE_OK ){ rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint); } if( rc==SQLITE_OK ){ rc = rc2; } } } db->nStatement--; p->iStatement = 0; if( rc==SQLITE_OK ){ if( eOp==SAVEPOINT_ROLLBACK ){ rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint); } if( rc==SQLITE_OK ){ rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint); } } /* If the statement transaction is being rolled back, also restore the ** database handles deferred constraint counter to the value it had when ** the statement transaction was opened. */ if( eOp==SAVEPOINT_ROLLBACK ){ db->nDeferredCons = p->nStmtDefCons; db->nDeferredImmCons = p->nStmtDefImmCons; } return rc; } int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ if( p->db->nStatement && p->iStatement ){ return vdbeCloseStatement(p, eOp); } return SQLITE_OK; } /* ** This function is called when a transaction opened by the database ** handle associated with the VM passed as an argument is about to be ** committed. If there are outstanding deferred foreign key constraint ** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK. ** |
︙ | ︙ | |||
2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 | ** SQLITE_INTERRUPT ** ** Then the internal cache might have been left in an inconsistent ** state. We need to rollback the statement transaction, if there is ** one, or the complete transaction if there is no statement transaction. */ if( db->mallocFailed ){ p->rc = SQLITE_NOMEM_BKPT; } closeAllCursors(p); | > > > < < < | 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 | ** SQLITE_INTERRUPT ** ** Then the internal cache might have been left in an inconsistent ** state. We need to rollback the statement transaction, if there is ** one, or the complete transaction if there is no statement transaction. */ if( p->magic!=VDBE_MAGIC_RUN ){ return SQLITE_OK; } if( db->mallocFailed ){ p->rc = SQLITE_NOMEM_BKPT; } closeAllCursors(p); checkActiveVdbeCnt(db); /* No commit or rollback needed if the program never started or if the ** SQL statement does not read or write a database file. */ if( p->pc>=0 && p->bIsReader ){ int mrc; /* Primary error code from p->rc */ int eStatementOp = 0; |
︙ | ︙ | |||
2769 2770 2771 2772 2773 2774 2775 | if( p->zErrMsg ){ db->bBenignMalloc++; sqlite3BeginBenignMalloc(); if( db->pErr==0 ) db->pErr = sqlite3ValueNew(db); sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); sqlite3EndBenignMalloc(); db->bBenignMalloc--; | < | | > | 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 | if( p->zErrMsg ){ db->bBenignMalloc++; sqlite3BeginBenignMalloc(); if( db->pErr==0 ) db->pErr = sqlite3ValueNew(db); sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); sqlite3EndBenignMalloc(); db->bBenignMalloc--; }else if( db->pErr ){ sqlite3ValueSetNull(db->pErr); } db->errCode = rc; return rc; } #ifdef SQLITE_ENABLE_SQLLOG /* ** If an SQLITE_CONFIG_SQLLOG hook is registered and the VM has been run, ** invoke it. |
︙ | ︙ | |||
2826 2827 2828 2829 2830 2831 2832 | ** and error message from the VDBE into the main database structure. But ** if the VDBE has just been set to run but has not actually executed any ** instructions yet, leave the main database error information unchanged. */ if( p->pc>=0 ){ vdbeInvokeSqllog(p); sqlite3VdbeTransferError(p); | < < < < | > > | > > > > > > > > > | 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 | ** and error message from the VDBE into the main database structure. But ** if the VDBE has just been set to run but has not actually executed any ** instructions yet, leave the main database error information unchanged. */ if( p->pc>=0 ){ vdbeInvokeSqllog(p); sqlite3VdbeTransferError(p); if( p->runOnlyOnce ) p->expired = 1; }else if( p->rc && p->expired ){ /* The expired flag was set on the VDBE before the first call ** to sqlite3_step(). For consistency (since sqlite3_step() was ** called), set the database error in this case as well. */ sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg); } /* Reset register contents and reclaim error message memory. */ #ifdef SQLITE_DEBUG /* Execute assert() statements to ensure that the Vdbe.apCsr[] and ** Vdbe.aMem[] arrays have already been cleaned up. */ int i; if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 ); if( p->aMem ){ for(i=0; i<p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined ); } #endif sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; p->pResultSet = 0; /* Save profiling information from this VDBE run. */ #ifdef VDBE_PROFILE { FILE *out = fopen("vdbe_profile.out", "a"); if( out ){ |
︙ | ︙ | |||
2879 2880 2881 2882 2883 2884 2885 | fprintf(out, "%s", zHdr); sqlite3VdbePrintOp(out, i, &p->aOp[i]); } fclose(out); } } #endif | < | 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 | fprintf(out, "%s", zHdr); sqlite3VdbePrintOp(out, i, &p->aOp[i]); } fclose(out); } } #endif p->magic = VDBE_MAGIC_RESET; return p->rc & db->errMask; } /* ** Clean up and delete a VDBE after execution. Return an integer which is ** the result code. Write any error message text into *pzErrMsg. |
︙ | ︙ | |||
2918 2919 2920 2921 2922 2923 2924 | ** * the corresponding bit in argument mask is clear (where the first ** function parameter corresponds to bit 0 etc.). */ void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp, int mask){ while( *pp ){ AuxData *pAux = *pp; if( (iOp<0) | > > | | | | | | < < | > > | | | | > | 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 | ** * the corresponding bit in argument mask is clear (where the first ** function parameter corresponds to bit 0 etc.). */ void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp, int mask){ while( *pp ){ AuxData *pAux = *pp; if( (iOp<0) || (pAux->iAuxOp==iOp && pAux->iAuxArg>=0 && (pAux->iAuxArg>31 || !(mask & MASKBIT32(pAux->iAuxArg)))) ){ testcase( pAux->iAuxArg==31 ); if( pAux->xDeleteAux ){ pAux->xDeleteAux(pAux->pAux); } *pp = pAux->pNextAux; sqlite3DbFree(db, pAux); }else{ pp= &pAux->pNextAux; } } } /* ** Free all memory associated with the Vdbe passed as the second argument, ** except for object itself, which is preserved. ** ** The difference between this function and sqlite3VdbeDelete() is that ** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with ** the database connection and frees the object itself. */ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ SubProgram *pSub, *pNext; assert( p->db==0 || p->db==db ); releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); for(pSub=p->pProgram; pSub; pSub=pNext){ pNext = pSub->pNext; vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); sqlite3DbFree(db, pSub); } if( p->magic!=VDBE_MAGIC_INIT ){ releaseMemArray(p->aVar, p->nVar); sqlite3DbFree(db, p->pVList); sqlite3DbFree(db, p->pFree); } vdbeFreeOpArray(db, p->aOp, p->nOp); sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->zSql); #ifdef SQLITE_ENABLE_STMT_SCANSTATUS { int i; for(i=0; i<p->nScan; i++){ sqlite3DbFree(db, p->aScan[i].zName); } sqlite3DbFree(db, p->aScan); } #endif } /* ** Delete an entire VDBE. */ void sqlite3VdbeDelete(Vdbe *p){ |
︙ | ︙ | |||
2988 2989 2990 2991 2992 2993 2994 | db->pVdbe = p->pNext; } if( p->pNext ){ p->pNext->pPrev = p->pPrev; } p->magic = VDBE_MAGIC_DEAD; p->db = 0; | | | 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 | db->pVdbe = p->pNext; } if( p->pNext ){ p->pNext->pPrev = p->pPrev; } p->magic = VDBE_MAGIC_DEAD; p->db = 0; sqlite3DbFreeNN(db, p); } /* ** The cursor "p" has a pending seek operation that has not yet been ** carried out. Seek the cursor now. If an error occurs, return ** the appropriate error code. */ |
︙ | ︙ | |||
3060 3061 3062 3063 3064 3065 3066 | ** a NULL row. ** ** If the cursor is already pointing to the correct row and that row has ** not been deleted out from under the cursor, then this routine is a no-op. */ int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){ VdbeCursor *p = *pp; | | | | | | | | | | | | | < | 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 | ** a NULL row. ** ** If the cursor is already pointing to the correct row and that row has ** not been deleted out from under the cursor, then this routine is a no-op. */ int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){ VdbeCursor *p = *pp; assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); if( p->deferredMoveto ){ int iMap; if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){ *pp = p->pAltCursor; *piCol = iMap - 1; return SQLITE_OK; } return handleDeferredMoveto(p); } if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ return handleMovedCursor(p); } return SQLITE_OK; } /* ** The following functions: ** |
︙ | ︙ | |||
3464 3465 3466 3467 3468 3469 3470 | ** be eventually freed by the caller using sqlite3DbFree(). Or, if the ** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL ** before returning. ** ** If an OOM error occurs, NULL is returned. */ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord( | | < < < < < < < < < < | < | < | < < < < < | | 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 | ** be eventually freed by the caller using sqlite3DbFree(). Or, if the ** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL ** before returning. ** ** If an OOM error occurs, NULL is returned. */ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord( KeyInfo *pKeyInfo /* Description of the record */ ){ UnpackedRecord *p; /* Unpacked record to return */ int nByte; /* Number of bytes required for *p */ nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte); if( !p ) return 0; p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))]; assert( pKeyInfo->aSortOrder!=0 ); p->pKeyInfo = pKeyInfo; p->nField = pKeyInfo->nKeyField + 1; return p; } /* ** Given the nKey-byte encoding of a record in pKey[], populate the ** UnpackedRecord structure indicated by the fourth argument with the ** contents of the decoded record. |
︙ | ︙ | |||
3531 3532 3533 3534 3535 3536 3537 | /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */ pMem->szMalloc = 0; pMem->z = 0; d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); pMem++; if( (++u)>=p->nField ) break; } | | | | 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 | /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */ pMem->szMalloc = 0; pMem->z = 0; d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); pMem++; if( (++u)>=p->nField ) break; } assert( u<=pKeyInfo->nKeyField + 1 ); p->nField = u; } #ifdef SQLITE_DEBUG /* ** This function compares two index or table record keys in the same way ** as the sqlite3VdbeRecordCompare() routine. Unlike VdbeRecordCompare(), ** this function deserializes and compares values using the ** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used ** in assert() statements to ensure that the optimized code in ** sqlite3VdbeRecordCompare() returns results with these two primitives. |
︙ | ︙ | |||
3580 3581 3582 3583 3584 3585 3586 | ** to ignore the compiler warnings and leave this variable uninitialized. */ /* mem1.u.i = 0; // not needed, here to silence compiler warning */ idx1 = getVarint32(aKey1, szHdr1); if( szHdr1>98307 ) return SQLITE_CORRUPT; d1 = szHdr1; | | | | 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 | ** to ignore the compiler warnings and leave this variable uninitialized. */ /* mem1.u.i = 0; // not needed, here to silence compiler warning */ idx1 = getVarint32(aKey1, szHdr1); if( szHdr1>98307 ) return SQLITE_CORRUPT; d1 = szHdr1; assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB ); assert( pKeyInfo->aSortOrder!=0 ); assert( pKeyInfo->nKeyField>0 ); assert( idx1<=szHdr1 || CORRUPT_DB ); do{ u32 serial_type1; /* Read the serial types for the next element in each key. */ idx1 += getVarint32( aKey1+idx1, serial_type1 ); |
︙ | ︙ | |||
3640 3641 3642 3643 3644 3645 3646 | if( desiredResult>0 && rc>0 ) return 1; if( CORRUPT_DB ) return 1; if( pKeyInfo->db->mallocFailed ) return 1; return 0; } #endif | | | | | | 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 | if( desiredResult>0 && rc>0 ) return 1; if( CORRUPT_DB ) return 1; if( pKeyInfo->db->mallocFailed ) return 1; return 0; } #endif #ifdef SQLITE_DEBUG /* ** Count the number of fields (a.k.a. columns) in the record given by ** pKey,nKey. The verify that this count is less than or equal to the ** limit given by pKeyInfo->nAllField. ** ** If this constraint is not satisfied, it means that the high-speed ** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will ** not work correctly. If this assert() ever fires, it probably means ** that the KeyInfo.nKeyField or KeyInfo.nAllField values were computed ** incorrectly. */ static void vdbeAssertFieldCountWithinLimits( int nKey, const void *pKey, /* The record to verify */ const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */ ){ int nField = 0; u32 szHdr; u32 idx; u32 notUsed; const unsigned char *aKey = (const unsigned char*)pKey; if( CORRUPT_DB ) return; idx = getVarint32(aKey, szHdr); assert( nKey>=0 ); assert( szHdr<=(u32)nKey ); while( idx<szHdr ){ idx += getVarint32(aKey+idx, notUsed); nField++; } assert( nField <= pKeyInfo->nAllField ); } #else # define vdbeAssertFieldCountWithinLimits(A,B,C) #endif /* ** Both *pMem1 and *pMem2 contain string values. Compare the two values |
︙ | ︙ | |||
3695 3696 3697 3698 3699 3700 3701 | if( pMem1->enc==pColl->enc ){ /* The strings are already in the correct encoding. Call the ** comparison function directly */ return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); }else{ int rc; const void *v1, *v2; | < < | > > > | < > | 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 | if( pMem1->enc==pColl->enc ){ /* The strings are already in the correct encoding. Call the ** comparison function directly */ return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); }else{ int rc; const void *v1, *v2; Mem c1; Mem c2; sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null); sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null); sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem); sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem); v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc); v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc); if( (v1==0 || v2==0) ){ if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT; rc = 0; }else{ rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2); } sqlite3VdbeMemRelease(&c1); sqlite3VdbeMemRelease(&c2); return rc; } } /* |
︙ | ︙ | |||
3974 3975 3976 3977 3978 3979 3980 | pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; return 0; /* Corruption */ } i = 0; } VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */ | | | | 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 | pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; return 0; /* Corruption */ } i = 0; } VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */ assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB ); assert( pPKey2->pKeyInfo->aSortOrder!=0 ); assert( pPKey2->pKeyInfo->nKeyField>0 ); assert( idx1<=szHdr1 || CORRUPT_DB ); do{ u32 serial_type; /* RHS is an integer */ if( pRhs->flags & MEM_Int ){ serial_type = aKey1[idx1]; |
︙ | ︙ | |||
4310 4311 4312 4313 4314 4315 4316 | ** buffer passed to varintRecordCompareInt() this makes it convenient to ** limit the size of the header to 64 bytes in cases where the first field ** is an integer. ** ** The easiest way to enforce this limit is to consider only records with ** 13 fields or less. If the first field is an integer, the maximum legal ** header size is (12*5 + 1 + 1) bytes. */ | | | 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 | ** buffer passed to varintRecordCompareInt() this makes it convenient to ** limit the size of the header to 64 bytes in cases where the first field ** is an integer. ** ** The easiest way to enforce this limit is to consider only records with ** 13 fields or less. If the first field is an integer, the maximum legal ** header size is (12*5 + 1 + 1) bytes. */ if( p->pKeyInfo->nAllField<=13 ){ int flags = p->aMem[0].flags; if( p->pKeyInfo->aSortOrder[0] ){ p->r1 = 1; p->r2 = -1; }else{ p->r1 = -1; p->r2 = 1; |
︙ | ︙ | |||
4361 4362 4363 4364 4365 4366 4367 | */ assert( sqlite3BtreeCursorIsValid(pCur) ); nCellKey = sqlite3BtreePayloadSize(pCur); assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); /* Read in the complete content of the index entry */ sqlite3VdbeMemInit(&m, db, 0); | | | 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 | */ assert( sqlite3BtreeCursorIsValid(pCur) ); nCellKey = sqlite3BtreePayloadSize(pCur); assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); /* Read in the complete content of the index entry */ sqlite3VdbeMemInit(&m, db, 0); rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, &m); if( rc ){ return rc; } /* The index entry must begin with a header size */ (void)getVarint32((u8*)m.z, szHdr); testcase( szHdr==3 ); |
︙ | ︙ | |||
4441 4442 4443 4444 4445 4446 4447 | /* nCellKey will always be between 0 and 0xffffffff because of the way ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ if( nCellKey<=0 || nCellKey>0x7fffffff ){ *res = 0; return SQLITE_CORRUPT_BKPT; } sqlite3VdbeMemInit(&m, db, 0); | | | 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 | /* nCellKey will always be between 0 and 0xffffffff because of the way ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ if( nCellKey<=0 || nCellKey>0x7fffffff ){ *res = 0; return SQLITE_CORRUPT_BKPT; } sqlite3VdbeMemInit(&m, db, 0); rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, &m); if( rc ){ return rc; } *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked); sqlite3VdbeMemRelease(&m); return SQLITE_OK; } |
︙ | ︙ | |||
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 | /* ** Return the database associated with the Vdbe. */ sqlite3 *sqlite3VdbeDb(Vdbe *v){ return v->db; } /* ** Return a pointer to an sqlite3_value structure containing the value bound ** parameter iVar of VM v. Except, if the value is an SQL NULL, return ** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_* ** constants) to the value before returning it. ** ** The returned value must be freed by the caller using sqlite3ValueFree(). */ sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff){ assert( iVar>0 ); if( v ){ Mem *pMem = &v->aVar[iVar-1]; if( 0==(pMem->flags & MEM_Null) ){ sqlite3_value *pRet = sqlite3ValueNew(v->db); if( pRet ){ sqlite3VdbeMemCopy((Mem *)pRet, pMem); sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); } return pRet; } } return 0; } /* ** Configure SQL variable iVar so that binding a new value to it signals ** to sqlite3_reoptimize() that re-preparing the statement may result ** in a better query plan. */ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ assert( iVar>0 ); | > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Return the database associated with the Vdbe. */ sqlite3 *sqlite3VdbeDb(Vdbe *v){ return v->db; } /* ** Return the SQLITE_PREPARE flags for a Vdbe. */ u8 sqlite3VdbePrepareFlags(Vdbe *v){ return v->prepFlags; } /* ** Return a pointer to an sqlite3_value structure containing the value bound ** parameter iVar of VM v. Except, if the value is an SQL NULL, return ** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_* ** constants) to the value before returning it. ** ** The returned value must be freed by the caller using sqlite3ValueFree(). */ sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff){ assert( iVar>0 ); if( v ){ Mem *pMem = &v->aVar[iVar-1]; assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); if( 0==(pMem->flags & MEM_Null) ){ sqlite3_value *pRet = sqlite3ValueNew(v->db); if( pRet ){ sqlite3VdbeMemCopy((Mem *)pRet, pMem); sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); } return pRet; } } return 0; } /* ** Configure SQL variable iVar so that binding a new value to it signals ** to sqlite3_reoptimize() that re-preparing the statement may result ** in a better query plan. */ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ assert( iVar>0 ); assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); if( iVar>=32 ){ v->expmask |= 0x80000000; }else{ v->expmask |= ((u32)1 << (iVar-1)); } } /* ** Cause a function to throw an error if it was call from OP_PureFunc ** rather than OP_Function. ** ** OP_PureFunc means that the function must be deterministic, and should ** throw an error if it is given inputs that would make it non-deterministic. ** This routine is invoked by date/time functions that use non-deterministic ** features such as 'now'. */ int sqlite3NotPureFunc(sqlite3_context *pCtx){ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( pCtx->pVdbe==0 ) return 1; #endif if( pCtx->pVdbe->aOp[pCtx->iOp].opcode==OP_PureFunc ){ sqlite3_result_error(pCtx, "non-deterministic function in index expression or CHECK constraint", -1); return 0; } return 1; } #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored ** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored ** in memory obtained from sqlite3DbMalloc). */ |
︙ | ︙ | |||
4557 4558 4559 4560 4561 4562 4563 | ** If the second argument is not NULL, release any allocations associated ** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord ** structure itself, using sqlite3DbFree(). ** ** This function is used to free UnpackedRecord structures allocated by ** the vdbeUnpackRecord() function found in vdbeapi.c. */ | | | | | 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 | ** If the second argument is not NULL, release any allocations associated ** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord ** structure itself, using sqlite3DbFree(). ** ** This function is used to free UnpackedRecord structures allocated by ** the vdbeUnpackRecord() function found in vdbeapi.c. */ static void vdbeFreeUnpacked(sqlite3 *db, int nField, UnpackedRecord *p){ if( p ){ int i; for(i=0; i<nField; i++){ Mem *pMem = &p->aMem[i]; if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem); } sqlite3DbFreeNN(db, p); } } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call, |
︙ | ︙ | |||
4593 4594 4595 4596 4597 4598 4599 | i64 iKey2; PreUpdate preupdate; const char *zTbl = pTab->zName; static const u8 fakeSortOrder = 0; assert( db->pPreUpdate==0 ); memset(&preupdate, 0, sizeof(PreUpdate)); | > > > > | | | | > | | | | | | 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 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 | i64 iKey2; PreUpdate preupdate; const char *zTbl = pTab->zName; static const u8 fakeSortOrder = 0; assert( db->pPreUpdate==0 ); memset(&preupdate, 0, sizeof(PreUpdate)); if( HasRowid(pTab)==0 ){ iKey1 = iKey2 = 0; preupdate.pPk = sqlite3PrimaryKeyIndex(pTab); }else{ if( op==SQLITE_UPDATE ){ iKey2 = v->aMem[iReg].u.i; }else{ iKey2 = iKey1; } } assert( pCsr->nField==pTab->nCol || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) ); preupdate.v = v; preupdate.pCsr = pCsr; preupdate.op = op; preupdate.iNewReg = iReg; preupdate.keyinfo.db = db; preupdate.keyinfo.enc = ENC(db); preupdate.keyinfo.nKeyField = pTab->nCol; preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder; preupdate.iKey1 = iKey1; preupdate.iKey2 = iKey2; preupdate.pTab = pTab; db->pPreUpdate = &preupdate; db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); db->pPreUpdate = 0; sqlite3DbFree(db, preupdate.aRecord); vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); if( preupdate.aNew ){ int i; for(i=0; i<pCsr->nField; i++){ sqlite3VdbeMemRelease(&preupdate.aNew[i]); } sqlite3DbFreeNN(db, preupdate.aNew); } } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
Changes to src/vdbeblob.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | #ifndef SQLITE_OMIT_INCRBLOB /* ** Valid sqlite3_blob* handles point to Incrblob structures. */ typedef struct Incrblob Incrblob; struct Incrblob { | < | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #ifndef SQLITE_OMIT_INCRBLOB /* ** Valid sqlite3_blob* handles point to Incrblob structures. */ typedef struct Incrblob Incrblob; struct Incrblob { int nByte; /* Size of open blob, in bytes */ int iOffset; /* Byte offset of blob in cursor data */ u16 iCol; /* Table column this handle is open on */ BtCursor *pCsr; /* Cursor pointing at blob row */ sqlite3_stmt *pStmt; /* Statement holding cursor open */ sqlite3 *db; /* The associated database */ char *zDb; /* Database name */ Table *pTab; /* Table object */ }; |
︙ | ︙ | |||
53 54 55 56 57 58 59 | ** immediately return SQLITE_ABORT. */ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ int rc; /* Error code */ char *zErr = 0; /* Error message */ Vdbe *v = (Vdbe *)p->pStmt; | | | < | | > > > > > > > > | > | > > | 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 | ** immediately return SQLITE_ABORT. */ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ int rc; /* Error code */ char *zErr = 0; /* Error message */ Vdbe *v = (Vdbe *)p->pStmt; /* Set the value of register r[1] in the SQL statement to integer iRow. ** This is done directly as a performance optimization */ v->aMem[1].flags = MEM_Int; v->aMem[1].u.i = iRow; /* If the statement has been run before (and is paused at the OP_ResultRow) ** then back it up to the point where it does the OP_SeekRowid. This could ** have been down with an extra OP_Goto, but simply setting the program ** counter is faster. */ if( v->pc>3 ){ v->pc = 3; rc = sqlite3VdbeExec(v); }else{ rc = sqlite3_step(p->pStmt); } if( rc==SQLITE_ROW ){ VdbeCursor *pC = v->apCsr[0]; u32 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; testcase( pC->nHdrParsed==p->iCol ); testcase( pC->nHdrParsed==p->iCol+1 ); if( type<12 ){ zErr = sqlite3MPrintf(p->db, "cannot open value of type %s", type==0?"null": type==7?"real": "integer" ); rc = SQLITE_ERROR; sqlite3_finalize(p->pStmt); p->pStmt = 0; |
︙ | ︙ | |||
108 109 110 111 112 113 114 | */ int sqlite3_blob_open( sqlite3* db, /* The database connection */ const char *zDb, /* The attached database containing the blob */ const char *zTable, /* The table containing the blob */ const char *zColumn, /* The column containing the blob */ sqlite_int64 iRow, /* The row containing the glob */ | | < > | < < < < | > | | | | | | | | | 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 | */ int sqlite3_blob_open( sqlite3* db, /* The database connection */ const char *zDb, /* The attached database containing the blob */ const char *zTable, /* The table containing the blob */ const char *zColumn, /* The column containing the blob */ sqlite_int64 iRow, /* The row containing the glob */ int wrFlag, /* True -> read/write access, false -> read-only */ sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */ ){ int nAttempt = 0; int iCol; /* Index of zColumn in row-record */ int rc = SQLITE_OK; char *zErr = 0; Table *pTab; Incrblob *pBlob = 0; Parse sParse; #ifdef SQLITE_ENABLE_API_ARMOR if( ppBlob==0 ){ return SQLITE_MISUSE_BKPT; } #endif *ppBlob = 0; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || zTable==0 ){ return SQLITE_MISUSE_BKPT; } #endif wrFlag = !!wrFlag; /* wrFlag = (wrFlag ? 1 : 0); */ sqlite3_mutex_enter(db->mutex); pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); do { memset(&sParse, 0, sizeof(Parse)); if( !pBlob ) goto blob_open_out; sParse.db = db; sqlite3DbFree(db, zErr); zErr = 0; sqlite3BtreeEnterAll(db); pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb); if( pTab && IsVirtual(pTab) ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable); } if( pTab && !HasRowid(pTab) ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); } #ifndef SQLITE_OMIT_VIEW if( pTab && pTab->pSelect ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); } #endif if( !pTab ){ if( sParse.zErrMsg ){ sqlite3DbFree(db, zErr); zErr = sParse.zErrMsg; sParse.zErrMsg = 0; } rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } pBlob->pTab = pTab; pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; |
︙ | ︙ | |||
190 191 192 193 194 195 196 | rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } /* If the value is being opened for writing, check that the ** column is not indexed, and that it is not part of a foreign key. | | < | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } /* If the value is being opened for writing, check that the ** column is not indexed, and that it is not part of a foreign key. */ if( wrFlag ){ const char *zFault = 0; Index *pIdx; #ifndef SQLITE_OMIT_FOREIGN_KEY if( db->flags&SQLITE_ForeignKeys ){ /* Check that the column is not part of an FK child key definition. It ** is not necessary to check if it is part of a parent key, as parent ** key columns must be indexed. The check below will pick up this |
︙ | ︙ | |||
230 231 232 233 234 235 236 | zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } } | | | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } } pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse); assert( pBlob->pStmt || db->mallocFailed ); if( pBlob->pStmt ){ /* This VDBE program seeks a btree cursor to the identified ** db/table/row entry. The reason for using a vdbe program instead ** of writing code to use the b-tree layer directly is that the ** vdbe program will take advantage of the various transaction, |
︙ | ︙ | |||
253 254 255 256 257 258 259 | ** which closes the b-tree cursor and (possibly) commits the ** transaction. */ static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList openBlob[] = { {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */ {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */ | | | | | < < | | | > | | | | | | | | < < | < | 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 | ** which closes the b-tree cursor and (possibly) commits the ** transaction. */ static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList openBlob[] = { {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */ {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */ /* blobSeekToRow() will initialize r[1] to the desired rowid */ {OP_NotExists, 0, 5, 1}, /* 2: Seek the cursor to rowid=r[1] */ {OP_Column, 0, 0, 1}, /* 3 */ {OP_ResultRow, 1, 0, 0}, /* 4 */ {OP_Halt, 0, 0, 0}, /* 5 */ }; Vdbe *v = (Vdbe *)pBlob->pStmt; int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); VdbeOp *aOp; sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, pTab->pSchema->schema_cookie, pTab->pSchema->iGeneration); sqlite3VdbeChangeP5(v, 1); assert( sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed ); aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); if( db->mallocFailed==0 ){ assert( aOp!=0 ); /* Configure the OP_TableLock instruction */ #ifdef SQLITE_OMIT_SHARED_CACHE aOp[0].opcode = OP_Noop; #else aOp[0].p1 = iDb; aOp[0].p2 = pTab->tnum; aOp[0].p3 = wrFlag; sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); } if( db->mallocFailed==0 ){ #endif /* Remove either the OP_OpenWrite or OpenRead. Set the P2 ** parameter of the other to pTab->tnum. */ if( wrFlag ) aOp[1].opcode = OP_OpenWrite; aOp[1].p2 = pTab->tnum; aOp[1].p3 = iDb; /* Configure the number of columns. Configure the cursor to ** think that the table has one more column than it really ** does. An OP_Column to retrieve this imaginary column will ** always return an SQL NULL. This is useful because it means ** we can invoke OP_Column to fill in the vdbe cursors type ** and offset cache without causing any IO. */ aOp[1].p4type = P4_INT32; aOp[1].p4.i = pTab->nCol+1; aOp[3].p2 = pTab->nCol; sParse.nVar = 0; sParse.nMem = 1; sParse.nTab = 1; sqlite3VdbeMakeReady(v, &sParse); } } pBlob->iCol = iCol; pBlob->db = db; sqlite3BtreeLeaveAll(db); if( db->mallocFailed ){ goto blob_open_out; } rc = blobSeekToRow(pBlob, iRow, &zErr); } while( (++nAttempt)<SQLITE_MAX_SCHEMA_RETRY && rc==SQLITE_SCHEMA ); blob_open_out: if( rc==SQLITE_OK && db->mallocFailed==0 ){ *ppBlob = (sqlite3_blob *)pBlob; }else{ if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); sqlite3DbFree(db, pBlob); } sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); sqlite3ParserReset(&sParse); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } /* ** Close a blob handle that was previously created using |
︙ | ︙ | |||
437 438 439 440 441 442 443 | return rc; } /* ** Read data from a blob handle. */ int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ | | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | return rc; } /* ** Read data from a blob handle. */ int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreePayloadChecked); } /* ** Write data to a blob handle. */ int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData); |
︙ | ︙ |
Changes to src/vdbemem.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 | ** Check invariants on a Mem object. ** ** This routine is intended for use inside of assert() statements, like ** this: assert( sqlite3VdbeCheckMemInvariants(pMem) ); */ int sqlite3VdbeCheckMemInvariants(Mem *p){ /* If MEM_Dyn is set then Mem.xDel!=0. | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** Check invariants on a Mem object. ** ** This routine is intended for use inside of assert() statements, like ** this: assert( sqlite3VdbeCheckMemInvariants(pMem) ); */ int sqlite3VdbeCheckMemInvariants(Mem *p){ /* If MEM_Dyn is set then Mem.xDel!=0. ** Mem.xDel might not be initialized if MEM_Dyn is clear. */ assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 ); /* MEM_Dyn may only be set if Mem.szMalloc==0. In this way we ** ensure that if Mem.szMalloc>0 then it is safe to do ** Mem.z = Mem.zMalloc without having to check Mem.flags&MEM_Dyn. ** That saves a few cycles in inner loops. */ assert( (p->flags & MEM_Dyn)==0 || p->szMalloc==0 ); /* Cannot be both MEM_Int and MEM_Real at the same time */ assert( (p->flags & (MEM_Int|MEM_Real))!=(MEM_Int|MEM_Real) ); if( p->flags & MEM_Null ){ /* Cannot be both MEM_Null and some other type */ assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob |MEM_RowSet|MEM_Frame|MEM_Agg|MEM_Zero))==0 ); /* If MEM_Null is set, then either the value is a pure NULL (the usual ** case) or it is a pointer set using sqlite3_bind_pointer() or ** sqlite3_result_pointer(). If a pointer, then MEM_Term must also be ** set. */ if( (p->flags & (MEM_Term|MEM_Subtype))==(MEM_Term|MEM_Subtype) ){ /* This is a pointer type. There may be a flag to indicate what to ** do with the pointer. */ assert( ((p->flags&MEM_Dyn)!=0 ? 1 : 0) + ((p->flags&MEM_Ephem)!=0 ? 1 : 0) + ((p->flags&MEM_Static)!=0 ? 1 : 0) <= 1 ); /* No other bits set */ assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype |MEM_Dyn|MEM_Ephem|MEM_Static))==0 ); }else{ /* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn, ** MEM_Ephem, MEM_Cleared, or MEM_Subtype */ } }else{ /* The MEM_Cleared bit is only allowed on NULLs */ assert( (p->flags & MEM_Cleared)==0 ); } /* The szMalloc field holds the correct memory allocation size */ assert( p->szMalloc==0 || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc) ); /* If p holds a string or blob, the Mem.z must point to exactly ** one of the following: |
︙ | ︙ | |||
121 122 123 124 125 126 127 | /* If the bPreserve flag is set to true, then the memory cell must already ** contain a valid string or blob value. */ assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) ); testcase( bPreserve && pMem->z==0 ); assert( pMem->szMalloc==0 || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) ); | < | | | | | | | | | | | | | | | | | < | > | 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 | /* If the bPreserve flag is set to true, then the memory cell must already ** contain a valid string or blob value. */ assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) ); testcase( bPreserve && pMem->z==0 ); assert( pMem->szMalloc==0 || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) ); if( n<32 ) n = 32; if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){ pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n); bPreserve = 0; }else{ if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc); pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n); } if( pMem->zMalloc==0 ){ sqlite3VdbeMemSetNull(pMem); pMem->z = 0; pMem->szMalloc = 0; return SQLITE_NOMEM_BKPT; }else{ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); } if( bPreserve && pMem->z ){ assert( pMem->z!=pMem->zMalloc ); memcpy(pMem->zMalloc, pMem->z, pMem->n); } if( (pMem->flags&MEM_Dyn)!=0 ){ assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC ); pMem->xDel((void *)(pMem->z)); } |
︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | return sqlite3VdbeMemGrow(pMem, szNew, 0); } assert( (pMem->flags & MEM_Dyn)==0 ); pMem->z = pMem->zMalloc; pMem->flags &= (MEM_Null|MEM_Int|MEM_Real); return SQLITE_OK; } /* ** Change pMem so that its MEM_Str or MEM_Blob value is stored in ** MEM.zMalloc, where it can be safely written. ** ** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. */ int sqlite3VdbeMemMakeWriteable(Mem *pMem){ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( (pMem->flags&MEM_RowSet)==0 ); if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){ if( ExpandBlob(pMem) ) return SQLITE_NOMEM; if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){ | > > > > > > > > > > > > > > | | < < < < | 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 | return sqlite3VdbeMemGrow(pMem, szNew, 0); } assert( (pMem->flags & MEM_Dyn)==0 ); pMem->z = pMem->zMalloc; pMem->flags &= (MEM_Null|MEM_Int|MEM_Real); return SQLITE_OK; } /* ** It is already known that pMem contains an unterminated string. ** Add the zero terminator. */ static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){ if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){ return SQLITE_NOMEM_BKPT; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; pMem->flags |= MEM_Term; return SQLITE_OK; } /* ** Change pMem so that its MEM_Str or MEM_Blob value is stored in ** MEM.zMalloc, where it can be safely written. ** ** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. */ int sqlite3VdbeMemMakeWriteable(Mem *pMem){ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( (pMem->flags&MEM_RowSet)==0 ); if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){ if( ExpandBlob(pMem) ) return SQLITE_NOMEM; if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){ int rc = vdbeMemAddTerminator(pMem); if( rc ) return rc; } } pMem->flags &= ~MEM_Ephem; #ifdef SQLITE_DEBUG pMem->pScopyFrom = 0; #endif |
︙ | ︙ | |||
234 235 236 237 238 239 240 | memset(&pMem->z[pMem->n], 0, pMem->u.nZero); pMem->n += pMem->u.nZero; pMem->flags &= ~(MEM_Zero|MEM_Term); return SQLITE_OK; } #endif | < < < < < < < < < < < < < < | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | memset(&pMem->z[pMem->n], 0, pMem->u.nZero); pMem->n += pMem->u.nZero; pMem->flags &= ~(MEM_Zero|MEM_Term); return SQLITE_OK; } #endif /* ** Make sure the given Mem is \u0000 terminated. */ int sqlite3VdbeMemNulTerminate(Mem *pMem){ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) ); testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 ); |
︙ | ︙ | |||
337 338 339 340 341 342 343 | t.flags = MEM_Null; t.db = pMem->db; ctx.pOut = &t; ctx.pMem = pMem; ctx.pFunc = pFunc; pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ assert( (pMem->flags & MEM_Dyn)==0 ); | | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | t.flags = MEM_Null; t.db = pMem->db; ctx.pOut = &t; ctx.pMem = pMem; ctx.pFunc = pFunc; pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ assert( (pMem->flags & MEM_Dyn)==0 ); if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc); memcpy(pMem, &t, sizeof(t)); rc = ctx.isError; } return rc; } /* |
︙ | ︙ | |||
388 389 390 391 392 393 394 | ** to be freed. */ static SQLITE_NOINLINE void vdbeMemClear(Mem *p){ if( VdbeMemDynamic(p) ){ vdbeMemClearExternAndSetNull(p); } if( p->szMalloc ){ | | | 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 | ** to be freed. */ static SQLITE_NOINLINE void vdbeMemClear(Mem *p){ if( VdbeMemDynamic(p) ){ vdbeMemClearExternAndSetNull(p); } if( p->szMalloc ){ sqlite3DbFreeNN(p->db, p->zMalloc); p->szMalloc = 0; } p->z = 0; } /* ** Release any memory resources held by the Mem. Both the memory that is |
︙ | ︙ | |||
416 417 418 419 420 421 422 | } /* ** Convert a 64-bit IEEE double into a 64-bit signed integer. ** If the double is out of range of a 64-bit signed integer then ** return the closest available 64-bit signed integer. */ | | | 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | } /* ** Convert a 64-bit IEEE double into a 64-bit signed integer. ** If the double is out of range of a 64-bit signed integer then ** return the closest available 64-bit signed integer. */ static SQLITE_NOINLINE i64 doubleToInt64(double r){ #ifdef SQLITE_OMIT_FLOATING_POINT /* When floating-point is omitted, double and int64 are the same thing */ return r; #else /* ** Many compilers we encounter do not define constants for the ** minimum and maximum 64-bit integers, or they define them |
︙ | ︙ | |||
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | ** a floating-point then the value returned is the integer part. ** If pMem is a string or blob, then we make an attempt to convert ** it into an integer and return that. If pMem represents an ** an SQL-NULL value, return 0. ** ** If pMem represents a string value, its encoding might be changed. */ i64 sqlite3VdbeIntValue(Mem *pMem){ int flags; assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); flags = pMem->flags; if( flags & MEM_Int ){ return pMem->u.i; }else if( flags & MEM_Real ){ return doubleToInt64(pMem->u.r); }else if( flags & (MEM_Str|MEM_Blob) ){ | > > > > > < < | > > > > > > < < < | | 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 | ** a floating-point then the value returned is the integer part. ** If pMem is a string or blob, then we make an attempt to convert ** it into an integer and return that. If pMem represents an ** an SQL-NULL value, return 0. ** ** If pMem represents a string value, its encoding might be changed. */ static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){ i64 value = 0; sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); return value; } i64 sqlite3VdbeIntValue(Mem *pMem){ int flags; assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); flags = pMem->flags; if( flags & MEM_Int ){ return pMem->u.i; }else if( flags & MEM_Real ){ return doubleToInt64(pMem->u.r); }else if( flags & (MEM_Str|MEM_Blob) ){ assert( pMem->z || pMem->n==0 ); return memIntValue(pMem); }else{ return 0; } } /* ** Return the best representation of pMem that we can get into a ** double. If pMem is already a double or an integer, return its ** value. If it is a string or blob, try to convert it to a double. ** If it is a NULL, return 0.0. */ static SQLITE_NOINLINE double memRealValue(Mem *pMem){ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ double val = (double)0; sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); return val; } double sqlite3VdbeRealValue(Mem *pMem){ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); if( pMem->flags & MEM_Real ){ return pMem->u.r; }else if( pMem->flags & MEM_Int ){ return (double)pMem->u.i; }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ return memRealValue(pMem); }else{ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ return (double)0; } } /* |
︙ | ︙ | |||
560 561 562 563 564 565 566 567 568 | ** ** Every effort is made to force the conversion, even if the input ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ int sqlite3VdbeMemNumerify(Mem *pMem){ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); | > | > > > > | | > | > | 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 | ** ** Every effort is made to force the conversion, even if the input ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ int sqlite3VdbeMemNumerify(Mem *pMem){ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ int rc; assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); rc = sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc); if( rc==0 ){ MemSetTypeFlag(pMem, MEM_Int); }else{ i64 i = pMem->u.i; sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); if( rc==1 && pMem->u.r==(double)i ){ pMem->u.i = i; MemSetTypeFlag(pMem, MEM_Int); }else{ MemSetTypeFlag(pMem, MEM_Real); } } } assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 ); pMem->flags &= ~(MEM_Str|MEM_Blob|MEM_Zero); return SQLITE_OK; } |
︙ | ︙ | |||
692 693 694 695 696 697 698 699 700 701 702 703 704 705 | if( VdbeMemDynamic(pMem) ){ vdbeReleaseAndSetInt64(pMem, val); }else{ pMem->u.i = val; pMem->flags = MEM_Int; } } #ifndef SQLITE_OMIT_FLOATING_POINT /* ** Delete any previous value and set the value stored in *pMem to val, ** manifest type REAL. */ void sqlite3VdbeMemSetDouble(Mem *pMem, double val){ | > > > > > > > > > > > > > > > > > > > > > | 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | if( VdbeMemDynamic(pMem) ){ vdbeReleaseAndSetInt64(pMem, val); }else{ pMem->u.i = val; pMem->flags = MEM_Int; } } /* A no-op destructor */ static 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. */ void sqlite3VdbeMemSetPointer( Mem *pMem, void *pPtr, const char *zPType, void (*xDestructor)(void*) ){ assert( pMem->flags==MEM_Null ); pMem->u.zPType = zPType ? zPType : ""; pMem->z = pPtr; pMem->flags = MEM_Null|MEM_Dyn|MEM_Subtype|MEM_Term; pMem->eSubtype = 'p'; pMem->xDel = xDestructor ? xDestructor : sqlite3NoopDestructor; } #ifndef SQLITE_OMIT_FLOATING_POINT /* ** Delete any previous value and set the value stored in *pMem to val, ** manifest type REAL. */ void sqlite3VdbeMemSetDouble(Mem *pMem, double val){ |
︙ | ︙ | |||
873 874 875 876 877 878 879 | }else{ iLimit = SQLITE_MAX_LENGTH; } flags = (enc==0?MEM_Blob:MEM_Str); if( nByte<0 ){ assert( enc!=0 ); if( enc==SQLITE_UTF8 ){ | | | 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 | }else{ iLimit = SQLITE_MAX_LENGTH; } flags = (enc==0?MEM_Blob:MEM_Str); if( nByte<0 ){ assert( enc!=0 ); if( enc==SQLITE_UTF8 ){ nByte = 0x7fffffff & (int)strlen(z); if( nByte>iLimit ) nByte = iLimit+1; }else{ for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} } flags |= MEM_Term; } |
︙ | ︙ | |||
930 931 932 933 934 935 936 | } return SQLITE_OK; } /* ** Move data out of a btree key or data field and into a Mem structure. | | < | < | < | < < < | < | < | > | 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 | } return SQLITE_OK; } /* ** Move data out of a btree key or data field and into a Mem structure. ** The data is payload from the entry that pCur is currently pointing ** to. offset and amt determine what portion of the data or key to retrieve. ** The result is written into the pMem element. ** ** The pMem object must have been initialized. This routine will use ** pMem->zMalloc to hold the content from the btree, if possible. New ** pMem->zMalloc space will be allocated if necessary. The calling routine ** is responsible for making sure that the pMem object is eventually ** destroyed. ** ** If this routine fails for any reason (malloc returns NULL or unable ** to read from the disk) then the pMem is left in an inconsistent state. */ static SQLITE_NOINLINE int vdbeMemFromBtreeResize( BtCursor *pCur, /* Cursor pointing at record to retrieve. */ u32 offset, /* Offset from the start of data to return bytes from. */ u32 amt, /* Number of bytes to return. */ Mem *pMem /* OUT: Return data in this Mem structure. */ ){ int rc; pMem->flags = MEM_Null; if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){ rc = sqlite3BtreePayload(pCur, offset, amt, pMem->z); if( rc==SQLITE_OK ){ pMem->z[amt] = 0; /* Overrun area used when reading malformed records */ pMem->flags = MEM_Blob; pMem->n = (int)amt; }else{ sqlite3VdbeMemRelease(pMem); } } return rc; } int sqlite3VdbeMemFromBtree( BtCursor *pCur, /* Cursor pointing at record to retrieve. */ u32 offset, /* Offset from the start of data to return bytes from. */ u32 amt, /* Number of bytes to return. */ Mem *pMem /* OUT: Return data in this Mem structure. */ ){ char *zData; /* Data from the btree layer */ u32 available = 0; /* Number of bytes available on the local btree page */ int rc = SQLITE_OK; /* Return code */ assert( sqlite3BtreeCursorIsValid(pCur) ); assert( !VdbeMemDynamic(pMem) ); /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() ** that both the BtShared and database handle mutexes are held. */ assert( (pMem->flags & MEM_RowSet)==0 ); zData = (char *)sqlite3BtreePayloadFetch(pCur, &available); assert( zData!=0 ); if( offset+amt<=available ){ pMem->z = &zData[offset]; pMem->flags = MEM_Blob|MEM_Ephem; pMem->n = (int)amt; }else{ rc = vdbeMemFromBtreeResize(pCur, offset, amt, pMem); } return rc; } /* ** The pVal argument is known to be a value other than NULL. ** Convert it into a string with encoding enc and return a pointer ** to a zero-terminated version of that string. */ static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){ assert( pVal!=0 ); assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) ); assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); assert( (pVal->flags & MEM_RowSet)==0 ); assert( (pVal->flags & (MEM_Null))==0 ); if( pVal->flags & (MEM_Blob|MEM_Str) ){ if( ExpandBlob(pVal) ) return 0; pVal->flags |= MEM_Str; if( pVal->enc != (enc & ~SQLITE_UTF16_ALIGNED) ){ sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); } if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ |
︙ | ︙ | |||
1111 1112 1113 1114 1115 1116 1117 | int nCol = pIdx->nColumn; /* Number of index columns including rowid */ nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord)); pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte); if( pRec ){ pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx); if( pRec->pKeyInfo ){ | | | | 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 | int nCol = pIdx->nColumn; /* Number of index columns including rowid */ nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord)); pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte); if( pRec ){ pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx); if( pRec->pKeyInfo ){ assert( pRec->pKeyInfo->nAllField==nCol ); assert( pRec->pKeyInfo->enc==ENC(db) ); pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord))); for(i=0; i<nCol; i++){ pRec->aMem[i].flags = MEM_Null; pRec->aMem[i].db = db; } }else{ sqlite3DbFreeNN(db, pRec); pRec = 0; } } if( pRec==0 ) return 0; p->ppRec[0] = pRec; } |
︙ | ︙ | |||
1231 1232 1233 1234 1235 1236 1237 | if( rc!=SQLITE_OK ){ pVal = 0; } if( apVal ){ for(i=0; i<nVal; i++){ sqlite3ValueFree(apVal[i]); } | | | 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 | if( rc!=SQLITE_OK ){ pVal = 0; } if( apVal ){ for(i=0; i<nVal; i++){ sqlite3ValueFree(apVal[i]); } sqlite3DbFreeNN(db, apVal); } *ppVal = pVal; return rc; } #else # define valueFromFunction(a,b,c,d,e,f) SQLITE_OK |
︙ | ︙ | |||
1319 1320 1321 1322 1323 1324 1325 | } if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str; if( enc!=SQLITE_UTF8 ){ rc = sqlite3VdbeChangeEncoding(pVal, enc); } }else if( op==TK_UMINUS ) { /* This branch happens for multiple negative signs. Ex: -(-5) */ | | > | 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 | } if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str; if( enc!=SQLITE_UTF8 ){ rc = sqlite3VdbeChangeEncoding(pVal, enc); } }else if( op==TK_UMINUS ) { /* This branch happens for multiple negative signs. Ex: -(-5) */ if( SQLITE_OK==valueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal,pCtx) && pVal!=0 ){ sqlite3VdbeMemNumerify(pVal); if( pVal->flags & MEM_Real ){ pVal->u.r = -pVal->u.r; }else if( pVal->u.i==SMALLEST_INT64 ){ pVal->u.r = -(double)SMALLEST_INT64; MemSetTypeFlag(pVal, MEM_Real); }else{ pVal->u.i = -pVal->u.i; } sqlite3ValueApplyAffinity(pVal, affinity, enc); } }else if( op==TK_NULL ){ pVal = valueNew(db, pCtx); if( pVal==0 ) goto no_mem; sqlite3VdbeMemNumerify(pVal); } #ifndef SQLITE_OMIT_BLOB_LITERAL else if( op==TK_BLOB ){ int nVal; assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); assert( pExpr->u.zToken[1]=='\'' ); pVal = valueNew(db, pCtx); |
︙ | ︙ | |||
1429 1430 1431 1432 1433 1434 1435 | if( aRet==0 ){ sqlite3_result_error_nomem(context); }else{ aRet[0] = nSerial+1; putVarint32(&aRet[1], iSerial); sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial); sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT); | | | 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 | if( aRet==0 ){ sqlite3_result_error_nomem(context); }else{ aRet[0] = nSerial+1; putVarint32(&aRet[1], iSerial); sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial); sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT); sqlite3DbFreeNN(db, aRet); } } /* ** Register built-in functions used to help read ANALYZE data. */ void sqlite3AnalyzeFunctions(void){ |
︙ | ︙ | |||
1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 | int rc = SQLITE_OK; sqlite3_value *pVal = 0; sqlite3 *db = pParse->db; /* Skip over any TK_COLLATE nodes */ pExpr = sqlite3ExprSkipCollate(pExpr); if( !pExpr ){ pVal = valueNew(db, pAlloc); if( pVal ){ sqlite3VdbeMemSetNull((Mem*)pVal); } | > | < < < | < | 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 | int rc = SQLITE_OK; sqlite3_value *pVal = 0; sqlite3 *db = pParse->db; /* Skip over any TK_COLLATE nodes */ pExpr = sqlite3ExprSkipCollate(pExpr); assert( pExpr==0 || pExpr->op!=TK_REGISTER || pExpr->op2!=TK_VARIABLE ); if( !pExpr ){ pVal = valueNew(db, pAlloc); if( pVal ){ sqlite3VdbeMemSetNull((Mem*)pVal); } }else if( pExpr->op==TK_VARIABLE && (db->flags & SQLITE_EnableQPSG)==0 ){ Vdbe *v; int iBindVar = pExpr->iColumn; sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar); if( (v = pParse->pReprepare)!=0 ){ pVal = valueNew(db, pAlloc); if( pVal ){ rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]); sqlite3ValueApplyAffinity(pVal, affinity, ENC(db)); pVal->db = pParse->db; } } }else{ rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, pAlloc); } |
︙ | ︙ | |||
1649 1650 1651 1652 1653 1654 1655 | ** Unless it is NULL, the argument must be an UnpackedRecord object returned ** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes ** the object. */ void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ if( pRec ){ int i; | | | | 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 | ** Unless it is NULL, the argument must be an UnpackedRecord object returned ** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes ** the object. */ void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ if( pRec ){ int i; int nCol = pRec->pKeyInfo->nAllField; Mem *aMem = pRec->aMem; sqlite3 *db = aMem[0].db; for(i=0; i<nCol; i++){ sqlite3VdbeMemRelease(&aMem[i]); } sqlite3KeyInfoUnref(pRec->pKeyInfo); sqlite3DbFreeNN(db, pRec); } } #endif /* ifdef SQLITE_ENABLE_STAT4 */ /* ** Change the string value of an sqlite3_value object */ |
︙ | ︙ | |||
1680 1681 1682 1683 1684 1685 1686 | /* ** Free an sqlite3_value object */ void sqlite3ValueFree(sqlite3_value *v){ if( !v ) return; sqlite3VdbeMemRelease((Mem *)v); | | | 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 | /* ** Free an sqlite3_value object */ void sqlite3ValueFree(sqlite3_value *v){ if( !v ) return; sqlite3VdbeMemRelease((Mem *)v); sqlite3DbFreeNN(((Mem*)v)->db, v); } /* ** The sqlite3ValueBytes() routine returns the number of bytes in the ** sqlite3_value object assuming that it uses the encoding "enc". ** The valueBytes() routine is a helper function. */ |
︙ | ︙ |
Changes to src/vdbesort.c.
︙ | ︙ | |||
811 812 813 814 815 816 817 | const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ int n1; int n2; int res; | | | | | | 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ int n1; int n2; int res; getVarint32(&p1[1], n1); getVarint32(&p2[1], n2); res = memcmp(v1, v2, (MIN(n1, n2) - 13)/2); if( res==0 ){ res = n1 - n2; } if( res==0 ){ if( pTask->pSorter->pKeyInfo->nKeyField>1 ){ res = vdbeSorterCompareTail( pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 ); } }else{ if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){ res = res * -1; |
︙ | ︙ | |||
854 855 856 857 858 859 860 | const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ int res; /* Return value */ assert( (s1>0 && s1<7) || s1==8 || s1==9 ); assert( (s2>0 && s2<7) || s2==8 || s2==9 ); | < < < | < < < < | | > | | | | > > > > > > | | | | | | | | | | | | | < | | | 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 | const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ int res; /* Return value */ assert( (s1>0 && s1<7) || s1==8 || s1==9 ); assert( (s2>0 && s2<7) || s2==8 || s2==9 ); if( s1==s2 ){ /* The two values have the same sign. Compare using memcmp(). */ static const u8 aLen[] = {0, 1, 2, 3, 4, 6, 8, 0, 0, 0 }; const u8 n = aLen[s1]; int i; res = 0; for(i=0; i<n; i++){ if( (res = v1[i] - v2[i])!=0 ){ if( ((v1[0] ^ v2[0]) & 0x80)!=0 ){ res = v1[0] & 0x80 ? -1 : +1; } break; } } }else if( s1>7 && s2>7 ){ res = s1 - s2; }else{ if( s2>7 ){ res = +1; }else if( s1>7 ){ res = -1; }else{ res = s1 - s2; } assert( res!=0 ); if( res>0 ){ if( *v1 & 0x80 ) res = -1; }else{ if( *v2 & 0x80 ) res = +1; } } if( res==0 ){ if( pTask->pSorter->pKeyInfo->nKeyField>1 ){ res = vdbeSorterCompareTail( pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 ); } }else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){ res = res * -1; } return res; } /* ** Initialize the temporary index cursor just opened as a sorter cursor. ** ** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nKeyField) ** to determine the number of fields that should be compared from the ** records being sorted. However, if the value passed as argument nField ** is non-zero and the sorter is able to guarantee a stable sort, nField ** is used instead. This is used when sorting records for a CREATE INDEX ** statement. In this case, keys are always delivered to the sorter in ** order of the primary key, which happens to be make up the final part ** of the records being sorted. So if the sort is stable, there is never |
︙ | ︙ | |||
955 956 957 958 959 960 961 | ** to exceed the maximum merge count */ #if SQLITE_MAX_WORKER_THREADS>=SORTER_MAX_MERGE_COUNT if( nWorker>=SORTER_MAX_MERGE_COUNT ){ nWorker = SORTER_MAX_MERGE_COUNT-1; } #endif | | | < | | 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 | ** to exceed the maximum merge count */ #if SQLITE_MAX_WORKER_THREADS>=SORTER_MAX_MERGE_COUNT if( nWorker>=SORTER_MAX_MERGE_COUNT ){ nWorker = SORTER_MAX_MERGE_COUNT-1; } #endif assert( pCsr->pKeyInfo && pCsr->pBtx==0 ); assert( pCsr->eCurType==CURTYPE_SORTER ); szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); pCsr->uc.pSorter = pSorter; if( pSorter==0 ){ rc = SQLITE_NOMEM_BKPT; }else{ pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz); memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo); pKeyInfo->db = 0; if( nField && nWorker==0 ){ pKeyInfo->nKeyField = nField; } pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt); pSorter->nTask = nWorker + 1; pSorter->iPrev = (u8)(nWorker - 1); pSorter->bUseThreads = (pSorter->nTask>1); pSorter->db = db; for(i=0; i<pSorter->nTask; i++){ |
︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 | mxCache = mxCache * -1024; }else{ mxCache = mxCache * pgsz; } mxCache = MIN(mxCache, SQLITE_MAX_PMASZ); pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache); | | | < < | | | 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 | mxCache = mxCache * -1024; }else{ mxCache = mxCache * pgsz; } mxCache = MIN(mxCache, SQLITE_MAX_PMASZ); pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache); /* Avoid large memory allocations if the application has requested ** SQLITE_CONFIG_SMALL_MALLOC. */ if( sqlite3GlobalConfig.bSmallMalloc==0 ){ assert( pSorter->iMemory==0 ); pSorter->nMemory = pgsz; pSorter->list.aMemory = (u8*)sqlite3Malloc(pgsz); if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM_BKPT; } } if( pKeyInfo->nAllField<13 && (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl) ){ pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT; } } return rc; |
︙ | ︙ | |||
1323 1324 1325 1326 1327 1328 1329 | /* ** If it has not already been allocated, allocate the UnpackedRecord ** structure at pTask->pUnpacked. Return SQLITE_OK if successful (or ** if no allocation was required), or SQLITE_NOMEM otherwise. */ static int vdbeSortAllocUnpacked(SortSubtask *pTask){ if( pTask->pUnpacked==0 ){ | < | < < < | | | 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 | /* ** If it has not already been allocated, allocate the UnpackedRecord ** structure at pTask->pUnpacked. Return SQLITE_OK if successful (or ** if no allocation was required), or SQLITE_NOMEM otherwise. */ static int vdbeSortAllocUnpacked(SortSubtask *pTask){ if( pTask->pUnpacked==0 ){ pTask->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pTask->pSorter->pKeyInfo); if( pTask->pUnpacked==0 ) return SQLITE_NOMEM_BKPT; pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nKeyField; pTask->pUnpacked->errCode = 0; } return SQLITE_OK; } /* |
︙ | ︙ | |||
2613 2614 2615 2616 2617 2618 2619 | } vdbeSorterRewindDebug("rewinddone"); return rc; } /* | | > > > > | | > | > | < | 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 | } vdbeSorterRewindDebug("rewinddone"); return rc; } /* ** Advance to the next element in the sorter. Return value: ** ** SQLITE_OK success ** SQLITE_DONE end of data ** otherwise some kind of error. */ int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr){ VdbeSorter *pSorter; int rc; /* Return code */ assert( pCsr->eCurType==CURTYPE_SORTER ); pSorter = pCsr->uc.pSorter; assert( pSorter->bUsePMA || (pSorter->pReader==0 && pSorter->pMerger==0) ); if( pSorter->bUsePMA ){ assert( pSorter->pReader==0 || pSorter->pMerger==0 ); assert( pSorter->bUseThreads==0 || pSorter->pReader ); assert( pSorter->bUseThreads==1 || pSorter->pMerger ); #if SQLITE_MAX_WORKER_THREADS>0 if( pSorter->bUseThreads ){ rc = vdbePmaReaderNext(pSorter->pReader); if( rc==SQLITE_OK && pSorter->pReader->pFd==0 ) rc = SQLITE_DONE; }else #endif /*if( !pSorter->bUseThreads )*/ { int res = 0; assert( pSorter->pMerger!=0 ); assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) ); rc = vdbeMergeEngineStep(pSorter->pMerger, &res); if( rc==SQLITE_OK && res ) rc = SQLITE_DONE; } }else{ SorterRecord *pFree = pSorter->list.pList; pSorter->list.pList = pFree->u.pNext; pFree->u.pNext = 0; if( pSorter->list.aMemory==0 ) vdbeSorterRecordFree(db, pFree); rc = pSorter->list.pList ? SQLITE_OK : SQLITE_DONE; } return rc; } /* ** Return a pointer to a buffer owned by the sorter that contains the ** current key. |
︙ | ︙ | |||
2729 2730 2731 2732 2733 2734 2735 | void *pKey; int nKey; /* Sorter key to compare pVal with */ assert( pCsr->eCurType==CURTYPE_SORTER ); pSorter = pCsr->uc.pSorter; r2 = pSorter->pUnpacked; pKeyInfo = pCsr->pKeyInfo; if( r2==0 ){ | < | < | 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 | void *pKey; int nKey; /* Sorter key to compare pVal with */ assert( pCsr->eCurType==CURTYPE_SORTER ); pSorter = pCsr->uc.pSorter; r2 = pSorter->pUnpacked; pKeyInfo = pCsr->pKeyInfo; if( r2==0 ){ r2 = pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pKeyInfo); if( r2==0 ) return SQLITE_NOMEM_BKPT; r2->nField = nKeyCol; } assert( r2->nField==nKeyCol ); pKey = vdbeSorterRowkey(pSorter, &nKey); sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, r2); |
︙ | ︙ |
Changes to src/vdbetrace.c.
︙ | ︙ | |||
78 79 80 81 82 83 84 | int nextIndex = 1; /* Index of next ? host parameter */ int n; /* Length of a token prefix */ int nToken; /* Length of the parameter token */ int i; /* Loop counter */ Mem *pVar; /* Value of a host parameter */ StrAccum out; /* Accumulate the output here */ #ifndef SQLITE_OMIT_UTF16 | | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | int nextIndex = 1; /* Index of next ? host parameter */ int n; /* Length of a token prefix */ int nToken; /* Length of the parameter token */ int i; /* Loop counter */ Mem *pVar; /* Value of a host parameter */ StrAccum out; /* Accumulate the output here */ #ifndef SQLITE_OMIT_UTF16 Mem utf8; /* Used to convert UTF16 into UTF8 for display */ #endif char zBase[100]; /* Initial working space */ db = p->db; sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); if( db->nVdbeExec>1 ){ |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | */ struct VtabCtx { VTable *pVTable; /* The virtual table being constructed */ Table *pTab; /* The Table object to which the virtual table belongs */ VtabCtx *pPrior; /* Parent context (if any) */ int bDeclared; /* True after sqlite3_declare_vtab() is called */ }; /* ** The actual function that does the work of creating a new module. ** This function implements the sqlite3_create_module() and ** sqlite3_create_module_v2() interfaces. */ static int createModule( sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ const sqlite3_module *pModule, /* The definition of the module */ void *pAux, /* Context pointer for xCreate/xConnect */ void (*xDestroy)(void *) /* Module destructor function */ ){ int rc = SQLITE_OK; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < | < < < < < < < < < < < < < | 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 | */ struct VtabCtx { VTable *pVTable; /* The virtual table being constructed */ Table *pTab; /* The Table object to which the virtual table belongs */ VtabCtx *pPrior; /* Parent context (if any) */ int bDeclared; /* True after sqlite3_declare_vtab() is called */ }; /* ** Construct and install a Module object for a virtual table. When this ** routine is called, it is guaranteed that all appropriate locks are held ** and the module is not already part of the connection. */ Module *sqlite3VtabCreateModule( sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ const sqlite3_module *pModule, /* The definition of the module */ void *pAux, /* Context pointer for xCreate/xConnect */ void (*xDestroy)(void *) /* Module destructor function */ ){ Module *pMod; int nName = sqlite3Strlen30(zName); pMod = (Module *)sqlite3DbMallocRawNN(db, sizeof(Module) + nName + 1); if( pMod ){ Module *pDel; char *zCopy = (char *)(&pMod[1]); memcpy(zCopy, zName, nName+1); pMod->zName = zCopy; pMod->pModule = pModule; pMod->pAux = pAux; pMod->xDestroy = xDestroy; pMod->pEpoTab = 0; pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod); assert( pDel==0 || pDel==pMod ); if( pDel ){ sqlite3OomFault(db); sqlite3DbFree(db, pDel); pMod = 0; } } return pMod; } /* ** The actual function that does the work of creating a new module. ** This function implements the sqlite3_create_module() and ** sqlite3_create_module_v2() interfaces. */ static int createModule( sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ const sqlite3_module *pModule, /* The definition of the module */ void *pAux, /* Context pointer for xCreate/xConnect */ void (*xDestroy)(void *) /* Module destructor function */ ){ int rc = SQLITE_OK; sqlite3_mutex_enter(db->mutex); if( sqlite3HashFind(&db->aModule, zName) ){ rc = SQLITE_MISUSE_BKPT; }else{ (void)sqlite3VtabCreateModule(db, zName, pModule, pAux, xDestroy); } rc = sqlite3ApiExit(db, rc); if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux); sqlite3_mutex_leave(db->mutex); return rc; } /* ** External API function used to create a new virtual-table module. |
︙ | ︙ | |||
320 321 322 323 324 325 326 | if( pTable==0 ) return; assert( 0==pTable->pIndex ); db = pParse->db; iDb = sqlite3SchemaToIndex(db, pTable->pSchema); assert( iDb>=0 ); | < | | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | if( pTable==0 ) return; assert( 0==pTable->pIndex ); db = pParse->db; iDb = sqlite3SchemaToIndex(db, pTable->pSchema); assert( iDb>=0 ); assert( pTable->nModuleArg==0 ); addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); addModuleArgument(db, pTable, 0); addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName)); assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0) || (pParse->sNameToken.z==pName1->z && pName2->z==0) ); pParse->sNameToken.n = (int)( |
︙ | ︙ | |||
404 405 406 407 408 409 410 | ** by sqlite3StartTable(). */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3NestedParse(pParse, "UPDATE %Q.%s " "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " "WHERE rowid=#%d", | | | 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 | ** by sqlite3StartTable(). */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3NestedParse(pParse, "UPDATE %Q.%s " "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " "WHERE rowid=#%d", db->aDb[iDb].zDbSName, MASTER_NAME, pTab->zName, pTab->zName, zStmt, pParse->regRowid ); sqlite3DbFree(db, zStmt); v = sqlite3GetVdbe(pParse); |
︙ | ︙ | |||
609 610 611 612 613 614 615 | int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ sqlite3 *db = pParse->db; const char *zMod; Module *pMod; int rc; assert( pTab ); | | > | 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 | int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ sqlite3 *db = pParse->db; const char *zMod; Module *pMod; int rc; assert( pTab ); if( !IsVirtual(pTab) || sqlite3GetVTable(db, pTab) ){ return SQLITE_OK; } /* Locate the required virtual table module */ zMod = pTab->azModuleArg[0]; pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); if( !pMod ){ const char *zModule = pTab->azModuleArg[0]; sqlite3ErrorMsg(pParse, "no such module: %s", zModule); rc = SQLITE_ERROR; }else{ char *zErr = 0; rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr); if( rc!=SQLITE_OK ){ sqlite3ErrorMsg(pParse, "%s", zErr); pParse->rc = rc; } sqlite3DbFree(db, zErr); } return rc; } /* |
︙ | ︙ | |||
679 680 681 682 683 684 685 | int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ int rc = SQLITE_OK; Table *pTab; Module *pMod; const char *zMod; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); | | | 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 | int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ int rc = SQLITE_OK; Table *pTab; Module *pMod; const char *zMod; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); assert( pTab && IsVirtual(pTab) && !pTab->pVTable ); /* Locate the required virtual table module */ zMod = pTab->azModuleArg[0]; pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); /* If the module has been registered and includes a Create method, ** invoke it now. If the module has not been registered, return an |
︙ | ︙ | |||
715 716 717 718 719 720 721 | /* ** This function is used to set the schema of a virtual table. It is only ** valid to call this function from within the xCreate() or xConnect() of a ** virtual table module. */ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ VtabCtx *pCtx; | < > | < | < < | | | < | | | | | | | | | | | | | | | > > | > > > > | | | | | | | | | | | | | | | | | | | | | | | < < | 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 | /* ** This function is used to set the schema of a virtual table. It is only ** valid to call this function from within the xCreate() or xConnect() of a ** virtual table module. */ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ VtabCtx *pCtx; int rc = SQLITE_OK; Table *pTab; char *zErr = 0; Parse sParse; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ return SQLITE_MISUSE_BKPT; } #endif sqlite3_mutex_enter(db->mutex); pCtx = db->pVtabCtx; if( !pCtx || pCtx->bDeclared ){ sqlite3Error(db, SQLITE_MISUSE); sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; } pTab = pCtx->pTab; assert( IsVirtual(pTab) ); memset(&sParse, 0, sizeof(sParse)); sParse.declareVtab = 1; sParse.db = db; sParse.nQueryLoop = 1; if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr) && sParse.pNewTable && !db->mallocFailed && !sParse.pNewTable->pSelect && !IsVirtual(sParse.pNewTable) ){ if( !pTab->aCol ){ Table *pNew = sParse.pNewTable; Index *pIdx; pTab->aCol = pNew->aCol; pTab->nCol = pNew->nCol; pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); pNew->nCol = 0; pNew->aCol = 0; assert( pTab->pIndex==0 ); assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 ); if( !HasRowid(pNew) && pCtx->pVTable->pMod->pModule->xUpdate!=0 && sqlite3PrimaryKeyIndex(pNew)->nKeyCol!=1 ){ /* WITHOUT ROWID virtual tables must either be read-only (xUpdate==0) ** or else must have a single-column PRIMARY KEY */ rc = SQLITE_ERROR; } pIdx = pNew->pIndex; if( pIdx ){ assert( pIdx->pNext==0 ); pTab->pIndex = pIdx; pNew->pIndex = 0; pIdx->pTable = pTab; } } pCtx->bDeclared = 1; }else{ sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); rc = SQLITE_ERROR; } sParse.declareVtab = 0; if( sParse.pVdbe ){ sqlite3VdbeFinalize(sParse.pVdbe); } sqlite3DeleteTable(db, sParse.pNewTable); sqlite3ParserReset(&sParse); assert( (rc&0xff)==rc ); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } |
︙ | ︙ | |||
1035 1036 1037 1038 1039 1040 1041 | unsigned char *z; /* Check to see the left operand is a column in a virtual table */ if( NEVER(pExpr==0) ) return pDef; if( pExpr->op!=TK_COLUMN ) return pDef; pTab = pExpr->pTab; | | | | 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 | unsigned char *z; /* Check to see the left operand is a column in a virtual table */ if( NEVER(pExpr==0) ) return pDef; if( pExpr->op!=TK_COLUMN ) return pDef; pTab = pExpr->pTab; if( pTab==0 ) return pDef; if( !IsVirtual(pTab) ) return pDef; pVtab = sqlite3GetVTable(db, pTab)->pVtab; assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); pMod = (sqlite3_module *)pVtab->pModule; if( pMod->xFindFunction==0 ) return pDef; /* Call the xFindFunction method on the virtual table implementation |
︙ | ︙ | |||
1129 1130 1131 1132 1133 1134 1135 | if( pTab==0 ) return 0; pTab->zName = sqlite3DbStrDup(db, pMod->zName); if( pTab->zName==0 ){ sqlite3DbFree(db, pTab); return 0; } pMod->pEpoTab = pTab; | | < | | 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 | if( pTab==0 ) return 0; pTab->zName = sqlite3DbStrDup(db, pMod->zName); if( pTab->zName==0 ){ sqlite3DbFree(db, pTab); return 0; } pMod->pEpoTab = pTab; pTab->nTabRef = 1; pTab->pSchema = db->aDb[0].pSchema; assert( pTab->nModuleArg==0 ); pTab->iPKey = -1; addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); addModuleArgument(db, pTab, 0); addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr); if( rc ){ sqlite3ErrorMsg(pParse, "%s", zErr); |
︙ | ︙ | |||
1203 1204 1205 1206 1207 1208 1209 | va_start(ap, op); switch( op ){ case SQLITE_VTAB_CONSTRAINT_SUPPORT: { VtabCtx *p = db->pVtabCtx; if( !p ){ rc = SQLITE_MISUSE_BKPT; }else{ | | | 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 | va_start(ap, op); switch( op ){ case SQLITE_VTAB_CONSTRAINT_SUPPORT: { VtabCtx *p = db->pVtabCtx; if( !p ){ rc = SQLITE_MISUSE_BKPT; }else{ assert( p->pTab==0 || IsVirtual(p->pTab) ); p->pVTable->bConstraint = (u8)va_arg(ap, int); } break; } default: rc = SQLITE_MISUSE_BKPT; break; |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
1794 1795 1796 1797 1798 1799 1800 | ){ i64 nSize; /* Current size of database file */ u32 nBackfill = pInfo->nBackfill; pInfo->nBackfillAttempted = mxSafeFrame; /* Sync the WAL to disk */ | < | < | 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 | ){ i64 nSize; /* Current size of database file */ u32 nBackfill = pInfo->nBackfill; pInfo->nBackfillAttempted = mxSafeFrame; /* Sync the WAL to disk */ rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); /* If the database may grow as a result of this checkpoint, hint ** about the eventual size of the db file to the VFS layer. */ if( rc==SQLITE_OK ){ i64 nReq = ((i64)mxPage * szPage); rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
︙ | ︙ | |||
1837 1838 1839 1840 1841 1842 1843 | /* If work was actually accomplished... */ if( rc==SQLITE_OK ){ if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ i64 szDb = pWal->hdr.nPage*(i64)szPage; testcase( IS_BIG_INT(szDb) ); rc = sqlite3OsTruncate(pWal->pDbFd, szDb); | | | | 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 | /* If work was actually accomplished... */ if( rc==SQLITE_OK ){ if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ i64 szDb = pWal->hdr.nPage*(i64)szPage; testcase( IS_BIG_INT(szDb) ); rc = sqlite3OsTruncate(pWal->pDbFd, szDb); if( rc==SQLITE_OK ){ rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); } } if( rc==SQLITE_OK ){ pInfo->nBackfill = mxSafeFrame; } } |
︙ | ︙ | |||
1939 1940 1941 1942 1943 1944 1945 | ** ordinary, rollback-mode locking methods, this guarantees that the ** connection associated with this log file is the only connection to ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ | > | | | 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 | ** ordinary, rollback-mode locking methods, this guarantees that the ** connection associated with this log file is the only connection to ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ if( zBuf!=0 && SQLITE_OK==(rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE)) ){ if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } rc = sqlite3WalCheckpoint(pWal, db, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 ); if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 | }else{ assert( mxReadMark<=pWal->hdr.mxFrame ); pWal->readLock = (i16)mxI; } return rc; } /* ** Begin a read transaction on the database. ** ** This routine used to be called sqlite3OpenSnapshot() and with good reason: ** it takes a snapshot of the state of the WAL and wal-index for the current ** instant in time. The current thread will continue to use this snapshot. ** Other threads might append new content to the WAL and wal-index but | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | }else{ assert( mxReadMark<=pWal->hdr.mxFrame ); pWal->readLock = (i16)mxI; } return rc; } #ifdef SQLITE_ENABLE_SNAPSHOT /* ** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted ** variable so that older snapshots can be accessed. To do this, loop ** through all wal frames from nBackfillAttempted to (nBackfill+1), ** comparing their content to the corresponding page with the database ** file, if any. Set nBackfillAttempted to the frame number of the ** first frame for which the wal file content matches the db file. ** ** This is only really safe if the file-system is such that any page ** writes made by earlier checkpointers were atomic operations, which ** is not always true. It is also possible that nBackfillAttempted ** may be left set to a value larger than expected, if a wal frame ** contains content that duplicate of an earlier version of the same ** page. ** ** SQLITE_OK is returned if successful, or an SQLite error code if an ** error occurs. It is not an error if nBackfillAttempted cannot be ** decreased at all. */ int sqlite3WalSnapshotRecover(Wal *pWal){ int rc; assert( pWal->readLock>=0 ); rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); if( rc==SQLITE_OK ){ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); int szPage = (int)pWal->szPage; i64 szDb; /* Size of db file in bytes */ rc = sqlite3OsFileSize(pWal->pDbFd, &szDb); if( rc==SQLITE_OK ){ void *pBuf1 = sqlite3_malloc(szPage); void *pBuf2 = sqlite3_malloc(szPage); if( pBuf1==0 || pBuf2==0 ){ rc = SQLITE_NOMEM; }else{ u32 i = pInfo->nBackfillAttempted; for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){ volatile ht_slot *dummy; volatile u32 *aPgno; /* Array of page numbers */ u32 iZero; /* Frame corresponding to aPgno[0] */ u32 pgno; /* Page number in db file */ i64 iDbOff; /* Offset of db file entry */ i64 iWalOff; /* Offset of wal file entry */ rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero); if( rc!=SQLITE_OK ) break; pgno = aPgno[i-iZero]; iDbOff = (i64)(pgno-1) * szPage; if( iDbOff+szPage<=szDb ){ iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); if( rc==SQLITE_OK ){ rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); } if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ break; } } pInfo->nBackfillAttempted = i-1; } } sqlite3_free(pBuf1); sqlite3_free(pBuf2); } walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); } return rc; } #endif /* SQLITE_ENABLE_SNAPSHOT */ /* ** Begin a read transaction on the database. ** ** This routine used to be called sqlite3OpenSnapshot() and with good reason: ** it takes a snapshot of the state of the WAL and wal-index for the current ** instant in time. The current thread will continue to use this snapshot. ** Other threads might append new content to the WAL and wal-index but |
︙ | ︙ | |||
2436 2437 2438 2439 2440 2441 2442 | /* It is possible that there is a checkpointer thread running ** concurrent with this code. If this is the case, it may be that the ** checkpointer has already determined that it will checkpoint ** snapshot X, where X is later in the wal file than pSnapshot, but ** has not yet set the pInfo->nBackfillAttempted variable to indicate ** its intent. To avoid the race condition this leads to, ensure that ** there is no checkpointer process by taking a shared CKPT lock | | > > > > | 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 | /* It is possible that there is a checkpointer thread running ** concurrent with this code. If this is the case, it may be that the ** checkpointer has already determined that it will checkpoint ** snapshot X, where X is later in the wal file than pSnapshot, but ** has not yet set the pInfo->nBackfillAttempted variable to indicate ** its intent. To avoid the race condition this leads to, ensure that ** there is no checkpointer process by taking a shared CKPT lock ** before checking pInfo->nBackfillAttempted. ** ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing ** this already? */ rc = walLockShared(pWal, WAL_CKPT_LOCK); if( rc==SQLITE_OK ){ /* Check that the wal file has not been wrapped. Assuming that it has ** not, also check that no checkpointer has attempted to checkpoint any ** frames beyond pSnapshot->mxFrame. If either of these conditions are ** true, return SQLITE_BUSY_SNAPSHOT. Otherwise, overwrite pWal->hdr |
︙ | ︙ | |||
2861 2862 2863 2864 2865 2866 2867 | if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){ int iFirstAmt = (int)(p->iSyncPoint - iOffset); rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset); if( rc ) return rc; iOffset += iFirstAmt; iAmt -= iFirstAmt; pContent = (void*)(iFirstAmt + (char*)pContent); | | | | 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 | if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){ int iFirstAmt = (int)(p->iSyncPoint - iOffset); rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset); if( rc ) return rc; iOffset += iFirstAmt; iAmt -= iFirstAmt; pContent = (void*)(iFirstAmt + (char*)pContent); assert( WAL_SYNC_FLAGS(p->syncFlags)!=0 ); rc = sqlite3OsSync(p->pFd, WAL_SYNC_FLAGS(p->syncFlags)); if( iAmt==0 || rc ) return rc; } rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset); return rc; } /* |
︙ | ︙ | |||
3032 3033 3034 3035 3036 3037 3038 | } /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise ** an out-of-order write following a WAL restart could result in ** database corruption. See the ticket: ** | | | | | 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 | } /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise ** an out-of-order write following a WAL restart could result in ** database corruption. See the ticket: ** ** https://sqlite.org/src/info/ff5be73dee */ if( pWal->syncHeader ){ rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); if( rc ) return rc; } } assert( (int)pWal->szPage==szPage ); /* Setup information needed to write frames into the WAL */ w.pWal = pWal; |
︙ | ︙ | |||
3110 3111 3112 3113 3114 3115 3116 | ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not ** needed and only the sync is done. If padding is needed, then the ** final frame is repeated (with its commit mark) until the next sector ** boundary is crossed. Only the part of the WAL prior to the last ** sector boundary is synced; the part of the last frame that extends ** past the sector boundary is written after the sync. */ | | | | 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 | ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not ** needed and only the sync is done. If padding is needed, then the ** final frame is repeated (with its commit mark) until the next sector ** boundary is crossed. Only the part of the WAL prior to the last ** sector boundary is synced; the part of the last frame that extends ** past the sector boundary is written after the sync. */ if( isCommit && WAL_SYNC_FLAGS(sync_flags)!=0 ){ int bSync = 1; if( pWal->padToSectorBoundary ){ int sectorSize = sqlite3SectorSize(pWal->pWalFd); w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize; bSync = (w.iSyncPoint==iOffset); testcase( bSync ); while( iOffset<w.iSyncPoint ){ rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset); if( rc ) return rc; iOffset += szFrame; nExtra++; } } if( bSync ){ assert( rc==SQLITE_OK ); rc = sqlite3OsSync(w.pFd, WAL_SYNC_FLAGS(sync_flags)); } } /* If this frame set completes the first transaction in the WAL and ** if PRAGMA journal_size_limit is set, then truncate the WAL to the ** journal size limit, if possible. */ |
︙ | ︙ | |||
3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 | /* Create a snapshot object. The content of a snapshot is opaque to ** every other subsystem, so the WAL module can put whatever it needs ** in the object. */ int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){ int rc = SQLITE_OK; WalIndexHdr *pRet; assert( pWal->readLock>=0 && pWal->writeLock==0 ); pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr)); if( pRet==0 ){ rc = SQLITE_NOMEM_BKPT; }else{ memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr)); *ppSnapshot = (sqlite3_snapshot*)pRet; } | > > > > > | 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 | /* Create a snapshot object. The content of a snapshot is opaque to ** every other subsystem, so the WAL module can put whatever it needs ** in the object. */ int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){ int rc = SQLITE_OK; WalIndexHdr *pRet; static const u32 aZero[4] = { 0, 0, 0, 0 }; assert( pWal->readLock>=0 && pWal->writeLock==0 ); if( memcmp(&pWal->hdr.aFrameCksum[0],aZero,16)==0 ){ *ppSnapshot = 0; return SQLITE_ERROR; } pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr)); if( pRet==0 ){ rc = SQLITE_NOMEM_BKPT; }else{ memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr)); *ppSnapshot = (sqlite3_snapshot*)pRet; } |
︙ | ︙ |
Changes to src/wal.h.
︙ | ︙ | |||
15 16 17 18 19 20 21 | */ #ifndef SQLITE_WAL_H #define SQLITE_WAL_H #include "sqliteInt.h" | | | | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | */ #ifndef SQLITE_WAL_H #define SQLITE_WAL_H #include "sqliteInt.h" /* Macros for extracting appropriate sync flags for either transaction ** commits (WAL_SYNC_FLAGS(X)) or for checkpoint ops (CKPT_SYNC_FLAGS(X)): */ #define WAL_SYNC_FLAGS(X) ((X)&0x03) #define CKPT_SYNC_FLAGS(X) (((X)>>2)&0x03) #ifdef SQLITE_OMIT_WAL # define sqlite3WalOpen(x,y,z) 0 # define sqlite3WalLimit(x,y) # define sqlite3WalClose(v,w,x,y,z) 0 # define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) |
︙ | ︙ | |||
127 128 129 130 131 132 133 134 135 136 137 138 139 140 | ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal); #ifdef SQLITE_ENABLE_SNAPSHOT int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot); void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot); #endif #ifdef SQLITE_ENABLE_ZIPVFS /* If the WAL file is not empty, return the number of bytes of content ** stored in each frame (i.e. the db page-size when the WAL was created). */ int sqlite3WalFramesize(Wal *pWal); | > | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal); #ifdef SQLITE_ENABLE_SNAPSHOT int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot); void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot); int sqlite3WalSnapshotRecover(Wal *pWal); #endif #ifdef SQLITE_ENABLE_ZIPVFS /* If the WAL file is not empty, return the number of bytes of content ** stored in each frame (i.e. the db page-size when the WAL was created). */ int sqlite3WalFramesize(Wal *pWal); |
︙ | ︙ |
Changes to src/walker.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 | ** is invoked before visiting children.) ** ** The return value from the callback should be one of the WRC_* ** constants to specify how to proceed with the walk. ** ** WRC_Continue Continue descending down the tree. ** | | | > | > | < < | > | > > | | | | > > > | 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 | ** is invoked before visiting children.) ** ** The return value from the callback should be one of the WRC_* ** constants to specify how to proceed with the walk. ** ** WRC_Continue Continue descending down the tree. ** ** WRC_Prune Do not descend into child nodes, but allow ** the walk to continue with sibling nodes. ** ** WRC_Abort Do no more callbacks. Unwind the stack and ** return from the top-level walk call. ** ** The return value from this routine is WRC_Abort to abandon the tree walk ** and WRC_Continue to continue. */ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ int rc; testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); testcase( ExprHasProperty(pExpr, EP_Reduced) ); while(1){ rc = pWalker->xExprCallback(pWalker, pExpr); if( rc ) return rc & WRC_Abort; if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; assert( pExpr->x.pList==0 || pExpr->pRight==0 ); if( pExpr->pRight ){ pExpr = pExpr->pRight; continue; }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; }else if( pExpr->x.pList ){ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; } } break; } return WRC_Continue; } int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue; } |
︙ | ︙ | |||
100 101 102 103 104 105 106 | SrcList *pSrc; int i; struct SrcList_item *pItem; pSrc = p->pSrc; if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ | | | > | < | < < < < | > | | < < | < | | 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 | SrcList *pSrc; int i; struct SrcList_item *pItem; pSrc = p->pSrc; if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){ return WRC_Abort; } if( pItem->fg.isTabFunc && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg) ){ return WRC_Abort; } } } return WRC_Continue; } /* ** Call sqlite3WalkExpr() for every expression in Select statement p. ** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and ** on the compound select chain, p->pPrior. ** ** If it is not NULL, the xSelectCallback() callback is invoked before ** the walk of the expressions and FROM clause. The xSelectCallback2() ** method is invoked following the walk of the expressions and FROM clause, ** but only if both xSelectCallback and xSelectCallback2 are both non-NULL ** and if the expressions and FROM clause both return WRC_Continue; ** ** Return WRC_Continue under normal conditions. Return WRC_Abort if ** there is an abort request. ** ** If the Walker does not have an xSelectCallback() then this routine ** is a no-op returning WRC_Continue. */ int sqlite3WalkSelect(Walker *pWalker, Select *p){ int rc; if( p==0 ) return WRC_Continue; if( pWalker->xSelectCallback==0 ) return WRC_Continue; do{ rc = pWalker->xSelectCallback(pWalker, p); if( rc ) return rc & WRC_Abort; if( sqlite3WalkSelectExpr(pWalker, p) || sqlite3WalkSelectFrom(pWalker, p) ){ return WRC_Abort; } if( pWalker->xSelectCallback2 ){ pWalker->xSelectCallback2(pWalker, p); } p = p->pPrior; }while( p!=0 ); return WRC_Continue; } |
Changes to src/where.c.
︙ | ︙ | |||
194 195 196 197 198 199 200 | int iCur; /* The cursor on the LHS of the term */ i16 iColumn; /* The column on the LHS of the term. -1 for IPK */ Expr *pX; /* An expression being tested */ WhereClause *pWC; /* Shorthand for pScan->pWC */ WhereTerm *pTerm; /* The term being tested */ int k = pScan->k; /* Where to start scanning */ | | | > | | > > | | 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 | int iCur; /* The cursor on the LHS of the term */ i16 iColumn; /* The column on the LHS of the term. -1 for IPK */ Expr *pX; /* An expression being tested */ WhereClause *pWC; /* Shorthand for pScan->pWC */ WhereTerm *pTerm; /* The term being tested */ int k = pScan->k; /* Where to start scanning */ assert( pScan->iEquiv<=pScan->nEquiv ); pWC = pScan->pWC; while(1){ iColumn = pScan->aiColumn[pScan->iEquiv-1]; iCur = pScan->aiCur[pScan->iEquiv-1]; assert( pWC!=0 ); do{ for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){ if( pTerm->leftCursor==iCur && pTerm->u.leftColumn==iColumn && (iColumn!=XN_EXPR || sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, pScan->pIdxExpr,iCur)==0) && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) ){ if( (pTerm->eOperator & WO_EQUIV)!=0 && pScan->nEquiv<ArraySize(pScan->aiCur) && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN ){ int j; |
︙ | ︙ | |||
248 249 250 251 252 253 254 255 256 257 258 259 | && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN && pX->iTable==pScan->aiCur[0] && pX->iColumn==pScan->aiColumn[0] ){ testcase( pTerm->eOperator & WO_IS ); continue; } pScan->k = k+1; return pTerm; } } } | > | | > | | 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 | && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN && pX->iTable==pScan->aiCur[0] && pX->iColumn==pScan->aiColumn[0] ){ testcase( pTerm->eOperator & WO_IS ); continue; } pScan->pWC = pWC; pScan->k = k+1; return pTerm; } } } pWC = pWC->pOuter; k = 0; }while( pWC!=0 ); if( pScan->iEquiv>=pScan->nEquiv ) break; pWC = pScan->pOrigWC; k = 0; pScan->iEquiv++; } return 0; } /* |
︙ | ︙ | |||
290 291 292 293 294 295 296 | WhereScan *pScan, /* The WhereScan object being initialized */ WhereClause *pWC, /* The WHERE clause to be scanned */ int iCur, /* Cursor to scan for */ int iColumn, /* Column to scan for */ u32 opMask, /* Operator(s) to scan for */ Index *pIdx /* Must be compatible with this index */ ){ | < < < > > | > | > | < > | | | > | | < | 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 | WhereScan *pScan, /* The WhereScan object being initialized */ WhereClause *pWC, /* The WHERE clause to be scanned */ int iCur, /* Cursor to scan for */ int iColumn, /* Column to scan for */ u32 opMask, /* Operator(s) to scan for */ Index *pIdx /* Must be compatible with this index */ ){ pScan->pOrigWC = pWC; pScan->pWC = pWC; pScan->pIdxExpr = 0; pScan->idxaff = 0; pScan->zCollName = 0; if( pIdx ){ int j = iColumn; iColumn = pIdx->aiColumn[j]; if( iColumn==XN_EXPR ){ pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; pScan->zCollName = pIdx->azColl[j]; }else if( iColumn==pIdx->pTable->iPKey ){ iColumn = XN_ROWID; }else if( iColumn>=0 ){ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; pScan->zCollName = pIdx->azColl[j]; } }else if( iColumn==XN_EXPR ){ return 0; } pScan->opMask = opMask; pScan->k = 0; pScan->aiCur[0] = iCur; pScan->aiColumn[0] = iColumn; pScan->nEquiv = 1; pScan->iEquiv = 1; |
︙ | ︙ | |||
507 508 509 510 511 512 513 | ** ** If the bIncrRowid parameter is 0, then any OP_Rowid instructions on ** cursor iTabCur are transformed into OP_Null. Or, if bIncrRowid is non-zero, ** then each OP_Rowid is transformed into an instruction to increment the ** value stored in its output register. */ static void translateColumnToCopy( | | > > | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 | ** ** If the bIncrRowid parameter is 0, then any OP_Rowid instructions on ** cursor iTabCur are transformed into OP_Null. Or, if bIncrRowid is non-zero, ** then each OP_Rowid is transformed into an instruction to increment the ** value stored in its output register. */ static void translateColumnToCopy( Parse *pParse, /* Parsing context */ int iStart, /* Translate from this opcode to the end */ int iTabCur, /* OP_Column/OP_Rowid references to this table */ int iRegister, /* The first column is in this register */ int bIncrRowid /* If non-zero, transform OP_rowid to OP_AddImm(1) */ ){ 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; |
︙ | ︙ | |||
596 597 598 599 600 601 602 603 604 605 606 607 608 609 | WhereTerm *pTerm, /* WHERE clause term to check */ struct SrcList_item *pSrc, /* Table we are trying to access */ Bitmask notReady /* Tables in outer loops of the join */ ){ char aff; if( pTerm->leftCursor!=pSrc->iCursor ) return 0; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; if( (pTerm->prereqRight & notReady)!=0 ) return 0; if( pTerm->u.leftColumn<0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; testcase( pTerm->pExpr->op==TK_IS ); return 1; } | > > > > > > > > > | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 | WhereTerm *pTerm, /* WHERE clause term to check */ struct SrcList_item *pSrc, /* Table we are trying to access */ Bitmask notReady /* Tables in outer loops of the join */ ){ char aff; if( pTerm->leftCursor!=pSrc->iCursor ) return 0; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; if( (pSrc->fg.jointype & JT_LEFT) && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) && (pTerm->eOperator & WO_IS) ){ /* Cannot use an IS term from the WHERE clause as an index driver for ** the RHS of a LEFT JOIN. Such a term can only be used if it is from ** the ON clause. */ return 0; } if( (pTerm->prereqRight & notReady)!=0 ) return 0; if( pTerm->u.leftColumn<0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; testcase( pTerm->pExpr->op==TK_IS ); return 1; } |
︙ | ︙ | |||
792 793 794 795 796 797 798 | pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0 ); sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); if( pTabItem->fg.viaCoroutine ){ sqlite3VdbeChangeP2(v, addrCounter, regBase+n); | > > | | 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 | pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0 ); sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); if( pTabItem->fg.viaCoroutine ){ sqlite3VdbeChangeP2(v, addrCounter, regBase+n); testcase( pParse->db->mallocFailed ); translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, pTabItem->regResult, 1); sqlite3VdbeGoto(v, addrTop); pTabItem->fg.viaCoroutine = 0; }else{ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); } sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); sqlite3VdbeJumpHere(v, addrTop); |
︙ | ︙ | |||
845 846 847 848 849 850 851 | if( pTerm->leftCursor != pSrc->iCursor ) continue; if( pTerm->prereqRight & mUnusable ) continue; assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IS ); testcase( pTerm->eOperator & WO_ALL ); | | | 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 | if( pTerm->leftCursor != pSrc->iCursor ) continue; if( pTerm->prereqRight & mUnusable ) continue; assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IS ); testcase( pTerm->eOperator & WO_ALL ); if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; assert( pTerm->u.leftColumn>=(-1) ); nTerm++; } /* If the ORDER BY clause contains only columns in the current ** virtual table then allocate space for the aOrderBy part of |
︙ | ︙ | |||
893 894 895 896 897 898 899 | *(int*)&pIdxInfo->nOrderBy = nOrderBy; *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint = pIdxCons; *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy; *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage = pUsage; for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ | | | | | | > > > > > | > | | | | | | | | | < | | | | | | | > | 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 | *(int*)&pIdxInfo->nOrderBy = nOrderBy; *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint = pIdxCons; *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy; *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage = pUsage; for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ u16 op; if( pTerm->leftCursor != pSrc->iCursor ) continue; if( pTerm->prereqRight & mUnusable ) continue; assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_IS ); testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_ALL ); if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; assert( pTerm->u.leftColumn>=(-1) ); pIdxCons[j].iColumn = pTerm->u.leftColumn; pIdxCons[j].iTermOffset = i; op = pTerm->eOperator & WO_ALL; if( op==WO_IN ) op = WO_EQ; if( op==WO_AUX ){ pIdxCons[j].op = pTerm->eMatchOp; }else if( op & (WO_ISNULL|WO_IS) ){ if( op==WO_ISNULL ){ pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; }else{ pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; } }else{ pIdxCons[j].op = (u8)op; /* The direct assignment in the previous line is possible only because ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The ** following asserts verify this fact. */ assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) ); if( op & (WO_LT|WO_LE|WO_GT|WO_GE) && sqlite3ExprIsVector(pTerm->pExpr->pRight) ){ if( i<16 ) mNoOmit |= (1 << i); if( op==WO_LT ) pIdxCons[j].op = WO_LE; if( op==WO_GT ) pIdxCons[j].op = WO_GE; } } j++; } for(i=0; i<nOrderBy; i++){ Expr *pExpr = pOrderBy->a[i].pExpr; pIdxOrderBy[i].iColumn = pExpr->iColumn; |
︙ | ︙ | |||
1178 1179 1180 1181 1182 1183 1184 | } if( roundUp ){ iGap = (iGap*2)/3; }else{ iGap = iGap/3; } aStat[0] = iLower + iGap; | | | 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 | } if( roundUp ){ iGap = (iGap*2)/3; }else{ iGap = iGap/3; } aStat[0] = iLower + iGap; aStat[1] = pIdx->aAvgEq[nField-1]; } /* Restore the pRec->nField value before returning. */ pRec->nField = nField; return i; } #endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ |
︙ | ︙ | |||
1772 1773 1774 1775 1776 1777 1778 | if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX) ){ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree ){ sqlite3_free(p->u.vtab.idxStr); p->u.vtab.needFree = 0; p->u.vtab.idxStr = 0; }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){ sqlite3DbFree(db, p->u.btree.pIndex->zColAff); | | | | | 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 | if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX) ){ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree ){ sqlite3_free(p->u.vtab.idxStr); p->u.vtab.needFree = 0; p->u.vtab.idxStr = 0; }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){ sqlite3DbFree(db, p->u.btree.pIndex->zColAff); sqlite3DbFreeNN(db, p->u.btree.pIndex); p->u.btree.pIndex = 0; } } } /* ** Deallocate internal memory used by a WhereLoop object */ static void whereLoopClear(sqlite3 *db, WhereLoop *p){ if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFreeNN(db, p->aLTerm); whereLoopClearUnion(db, p); whereLoopInit(p); } /* ** Increase the memory allocation for pLoop->aLTerm[] to be at least n. */ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){ WhereTerm **paNew; if( p->nLSlot>=n ) return SQLITE_OK; n = (n+7)&~7; paNew = sqlite3DbMallocRawNN(db, sizeof(p->aLTerm[0])*n); if( paNew==0 ) return SQLITE_NOMEM_BKPT; memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot); if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFreeNN(db, p->aLTerm); p->aLTerm = paNew; p->nLSlot = n; return SQLITE_OK; } /* ** Transfer content from the second pLoop into the first. |
︙ | ︙ | |||
1827 1828 1829 1830 1831 1832 1833 | } /* ** Delete a WhereLoop object */ static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ whereLoopClear(db, p); | | | | 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 | } /* ** Delete a WhereLoop object */ static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ whereLoopClear(db, p); sqlite3DbFreeNN(db, p); } /* ** Free a WhereInfo structure */ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ if( ALWAYS(pWInfo) ){ int i; for(i=0; i<pWInfo->nLevel; i++){ WhereLevel *pLevel = &pWInfo->a[i]; if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){ sqlite3DbFree(db, pLevel->u.in.aInLoop); } } sqlite3WhereClauseClear(&pWInfo->sWC); while( pWInfo->pLoops ){ WhereLoop *p = pWInfo->pLoops; pWInfo->pLoops = p->pNextLoop; whereLoopDelete(db, p); } sqlite3DbFreeNN(db, pWInfo); } } /* ** Return TRUE if all of the following are true: ** ** (1) X has the same or lower cost that Y |
︙ | ︙ | |||
1931 1932 1933 1934 1935 1936 1937 | pTemplate->nOut = p->nOut + 1; } } } /* ** Search the list of WhereLoops in *ppPrev looking for one that can be | | | | | | | > | 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 | pTemplate->nOut = p->nOut + 1; } } } /* ** Search the list of WhereLoops in *ppPrev looking for one that can be ** replaced by pTemplate. ** ** Return NULL if pTemplate does not belong on the WhereLoop list. ** In other words if pTemplate ought to be dropped from further consideration. ** ** If pX is a WhereLoop that pTemplate can replace, then return the ** link that points to pX. ** ** If pTemplate cannot replace any existing element of the list but needs ** to be added to the list as a new entry, then return a pointer to the ** tail of the list. */ static WhereLoop **whereLoopFindLesser( WhereLoop **ppPrev, const WhereLoop *pTemplate ){ WhereLoop *p; for(p=(*ppPrev); p; ppPrev=&p->pNextLoop, p=*ppPrev){ |
︙ | ︙ | |||
2085 2086 2087 2088 2089 2090 2091 | ** WhereLoop and insert it. */ #if WHERETRACE_ENABLED /* 0x8 */ if( sqlite3WhereTrace & 0x8 ){ if( p!=0 ){ sqlite3DebugPrintf("replace: "); whereLoopPrint(p, pBuilder->pWC); | > | | > | 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 | ** WhereLoop and insert it. */ #if WHERETRACE_ENABLED /* 0x8 */ if( sqlite3WhereTrace & 0x8 ){ if( p!=0 ){ sqlite3DebugPrintf("replace: "); whereLoopPrint(p, pBuilder->pWC); sqlite3DebugPrintf(" with: "); }else{ sqlite3DebugPrintf(" add: "); } whereLoopPrint(pTemplate, pBuilder->pWC); } #endif if( p==0 ){ /* Allocate a new WhereLoop to add to the end of the list */ *ppPrev = p = sqlite3DbMallocRawNN(db, sizeof(WhereLoop)); if( p==0 ) return SQLITE_NOMEM_BKPT; |
︙ | ︙ | |||
2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 | && (eOp & (WO_IS|WO_ISNULL))!=0 ){ testcase( eOp & WO_IS ); testcase( eOp & WO_ISNULL ); continue; } pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; pNew->u.btree.nBtm = saved_nBtm; pNew->u.btree.nTop = saved_nTop; pNew->nLTerm = saved_nLTerm; if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ pNew->aLTerm[pNew->nLTerm++] = pTerm; | > > > > > | 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 | && (eOp & (WO_IS|WO_ISNULL))!=0 ){ testcase( eOp & WO_IS ); testcase( eOp & WO_ISNULL ); continue; } if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ pBuilder->bldFlags |= SQLITE_BLDF_UNIQUE; }else{ pBuilder->bldFlags |= SQLITE_BLDF_INDEXED; } pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; pNew->u.btree.nBtm = saved_nBtm; pNew->u.btree.nTop = saved_nTop; pNew->nLTerm = saved_nLTerm; if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ pNew->aLTerm[pNew->nLTerm++] = pTerm; |
︙ | ︙ | |||
2632 2633 2634 2635 2636 2637 2638 | 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; | | | 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 | 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; if( sqlite3ExprCompare(0, pExpr,aColExpr->a[jj].pExpr,iCursor)==0 ){ return 1; } } } } return 0; } |
︙ | ︙ | |||
2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 | /* Check to see if a partial index with pPartIndexWhere can be used ** in the current query. Return true if it can be and false if not. */ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ int i; WhereTerm *pTerm; while( pWhere->op==TK_AND ){ if( !whereUsablePartialIndex(iTab,pWC,pWhere->pLeft) ) return 0; pWhere = pWhere->pRight; } for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ Expr *pExpr = pTerm->pExpr; | > > < | > | 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 | /* Check to see if a partial index with pPartIndexWhere can be used ** in the current query. Return true if it can be and false if not. */ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ int i; WhereTerm *pTerm; Parse *pParse = pWC->pWInfo->pParse; while( pWhere->op==TK_AND ){ if( !whereUsablePartialIndex(iTab,pWC,pWhere->pLeft) ) return 0; pWhere = pWhere->pRight; } if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ Expr *pExpr = pTerm->pExpr; if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) ){ return 1; } } return 0; } |
︙ | ︙ | |||
2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 | whereLoopOutputAdjust(pWC, pNew, rSize); rc = whereLoopInsert(pBuilder, pNew); pNew->nOut = rSize; if( rc ) break; } } rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 sqlite3Stat4ProbeFree(pBuilder->pRec); pBuilder->nRecValid = 0; pBuilder->pRec = 0; #endif /* If there was an INDEXED BY clause, then only that one index is | > > > > > > > > | 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 | whereLoopOutputAdjust(pWC, pNew, rSize); rc = whereLoopInsert(pBuilder, pNew); pNew->nOut = rSize; if( rc ) break; } } pBuilder->bldFlags = 0; rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); if( pBuilder->bldFlags==SQLITE_BLDF_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_StatsUsed; } #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 sqlite3Stat4ProbeFree(pBuilder->pRec); pBuilder->nRecValid = 0; pBuilder->pRec = 0; #endif /* If there was an INDEXED BY clause, then only that one index is |
︙ | ︙ | |||
3226 3227 3228 3229 3230 3231 3232 | WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); } } if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); | | | 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 | WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); } } if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); sqlite3DbFreeNN(pParse->db, p); return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** Add WhereLoop entries to handle OR terms. This works for either ** btrees or virtual tables. |
︙ | ︙ | |||
3410 3411 3412 3413 3414 3415 3416 | } whereLoopClear(db, pNew); return rc; } /* | | | 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 | } whereLoopClear(db, pNew); return rc; } /* ** Examine a WherePath (with the addition of the extra WhereLoop of the 6th ** parameters) to see if it outputs rows in the requested ORDER BY ** (or GROUP BY) without requiring a separate sort operation. Return N: ** ** N>0: N terms of the ORDER BY clause are satisfied ** N==0: No terms of the ORDER BY clause are satisfied ** N<0: Unknown yet how many terms of ORDER BY might be satisfied. ** |
︙ | ︙ | |||
3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 | if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue; }else{ pLoop = pLast; } if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ if( pLoop->u.vtab.isOrdered ) obSat = obDone; break; } iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; /* Mark off any ORDER BY term X that is a column in the table of ** the current loop for which there is term in the WHERE ** clause of the form X IS NULL or X=? that reference only outer ** loops. | > > | 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 | if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue; }else{ pLoop = pLast; } if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ if( pLoop->u.vtab.isOrdered ) obSat = obDone; break; }else{ pLoop->u.btree.nIdxCol = 0; } iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; /* Mark off any ORDER BY term X that is a column in the table of ** the current loop for which there is term in the WHERE ** clause of the form X IS NULL or X=? that reference only outer ** loops. |
︙ | ︙ | |||
3641 3642 3643 3644 3645 3646 3647 | testcase( wctrlFlags & WHERE_DISTINCTBY ); if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; if( iColumn>=(-1) ){ if( pOBExpr->op!=TK_COLUMN ) continue; if( pOBExpr->iTable!=iCur ) continue; if( pOBExpr->iColumn!=iColumn ) continue; }else{ | > | | > | 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 | testcase( wctrlFlags & WHERE_DISTINCTBY ); if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; if( iColumn>=(-1) ){ if( pOBExpr->op!=TK_COLUMN ) continue; if( pOBExpr->iTable!=iCur ) continue; if( pOBExpr->iColumn!=iColumn ) continue; }else{ if( sqlite3ExprCompare(0, pOBExpr,pIndex->aColExpr->a[j].pExpr,iCur) ){ continue; } } if( iColumn!=XN_ROWID ){ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr); if( !pColl ) pColl = db->pDfltColl; if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue; } pLoop->u.btree.nIdxCol = j+1; isMatch = 1; break; } if( isMatch && (wctrlFlags & WHERE_GROUPBY)==0 ){ /* Make sure the sort order is compatible in an ORDER BY clause. ** Sort order is irrelevant for a GROUP BY clause. */ if( revSet ){ |
︙ | ︙ | |||
3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 | WHERETRACE(0x002, ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n", aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy, rUnsorted, rCost)); }else{ rCost = rUnsorted; } /* Check to see if pWLoop should be added to the set of ** mxChoice best-so-far paths. ** ** First look for an existing path among best-so-far paths ** that covers the same set of loops and has the same isOrdered | > | 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 | WHERETRACE(0x002, ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n", aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy, rUnsorted, rCost)); }else{ rCost = rUnsorted; rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */ } /* Check to see if pWLoop should be added to the set of ** mxChoice best-so-far paths. ** ** First look for an existing path among best-so-far paths ** that covers the same set of loops and has the same isOrdered |
︙ | ︙ | |||
3970 3971 3972 3973 3974 3975 3976 | && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted)) ){ /* The current candidate is no better than any of the mxChoice ** paths currently in the best-so-far buffer. So discard ** this candidate as not viable. */ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ | | | | | | | > > > > | > > > > > > | | | | | | | | | 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 | && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted)) ){ /* The current candidate is no better than any of the mxChoice ** paths currently in the best-so-far buffer. So discard ** this candidate as not viable. */ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, isOrdered>=0 ? isOrdered+'0' : '?'); } #endif continue; } /* If we reach this points it means that the new candidate path ** needs to be added to the set of best-so-far paths. */ if( nTo<mxChoice ){ /* Increase the size of the aTo set by one */ jj = nTo++; }else{ /* New path replaces the prior worst to keep count below mxChoice */ jj = mxI; } pTo = &aTo[jj]; #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf("New %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, isOrdered>=0 ? isOrdered+'0' : '?'); } #endif }else{ /* Control reaches here if best-so-far path pTo=aTo[jj] covers the ** same set of loops and has the same isOrdered setting as the ** candidate path. Check to see if the candidate should replace ** pTo or if the candidate should be skipped. ** ** The conditional is an expanded vector comparison equivalent to: ** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted) */ if( pTo->rCost<rCost || (pTo->rCost==rCost && (pTo->nRow<nOut || (pTo->nRow==nOut && pTo->rUnsorted<=rUnsorted) ) ) ){ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf( "Skip %s cost=%-3d,%3d,%3d order=%c", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, isOrdered>=0 ? isOrdered+'0' : '?'); sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); } #endif /* Discard the candidate path from further consideration */ testcase( pTo->rCost==rCost ); continue; } testcase( pTo->rCost==rCost+1 ); /* Control reaches here if the candidate path is better than the ** pTo path. Replace pTo with the candidate. */ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf( "Update %s cost=%-3d,%3d,%3d order=%c", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, isOrdered>=0 ? isOrdered+'0' : '?'); sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); } #endif } /* pWLoop is a winner. Add it to the set of best so far */ pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf; pTo->revLoop = revMask; pTo->nRow = nOut; |
︙ | ︙ | |||
4081 4082 4083 4084 4085 4086 4087 | aTo = aFrom; aFrom = pFrom; nFrom = nTo; } if( nFrom==0 ){ sqlite3ErrorMsg(pParse, "no query solution"); | | | 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 | aTo = aFrom; aFrom = pFrom; nFrom = nTo; } if( nFrom==0 ){ sqlite3ErrorMsg(pParse, "no query solution"); sqlite3DbFreeNN(db, pSpace); return SQLITE_ERROR; } /* Find the lowest cost path. pFrom will be left pointing to that path */ pFrom = aFrom; for(ii=1; ii<nFrom; ii++){ if( pFrom->rCost>aFrom[ii].rCost ) pFrom = &aFrom[ii]; |
︙ | ︙ | |||
4104 4105 4106 4107 4108 4109 4110 | } if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 && (pWInfo->wctrlFlags & WHERE_DISTINCTBY)==0 && pWInfo->eDistinct==WHERE_DISTINCT_NOOP && nRowEst ){ Bitmask notUsed; | | | | 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 | } if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 && (pWInfo->wctrlFlags & WHERE_DISTINCTBY)==0 && pWInfo->eDistinct==WHERE_DISTINCT_NOOP && nRowEst ){ Bitmask notUsed; int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pResultSet, pFrom, WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], ¬Used); if( rc==pWInfo->pResultSet->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } } if( pWInfo->pOrderBy ){ if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; |
︙ | ︙ | |||
4157 4158 4159 4160 4161 4162 4163 | } } pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ | | | 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 | } } pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ sqlite3DbFreeNN(db, pSpace); return SQLITE_OK; } /* ** Most queries use only a single table (they are not joins) and have ** simple == constraints against indexed fields. This routine attempts ** to plan those simple cases using much less ceremony than the |
︙ | ︙ | |||
4235 4236 4237 4238 4239 4240 4241 | pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */ break; } } if( pLoop->wsFlags ){ pLoop->nOut = (LogEst)1; pWInfo->a[0].pWLoop = pLoop; | > | > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */ break; } } if( pLoop->wsFlags ){ pLoop->nOut = (LogEst)1; pWInfo->a[0].pWLoop = pLoop; assert( pWInfo->sMaskSet.n==1 && iCur==pWInfo->sMaskSet.ix[0] ); pLoop->maskSelf = 1; /* sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); */ pWInfo->a[0].iTabCur = iCur; pWInfo->nRowOut = 1; if( pWInfo->pOrderBy ) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr; if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } #ifdef SQLITE_DEBUG pLoop->cId = '0'; #endif return 1; } return 0; } /* ** Helper function for exprIsDeterministic(). */ static int exprNodeIsDeterministic(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_ConstFunc)==0 ){ pWalker->eCode = 0; return WRC_Abort; } return WRC_Continue; } /* ** Return true if the expression contains no non-deterministic SQL ** functions. Do not consider non-deterministic SQL functions that are ** part of sub-select statements. */ static int exprIsDeterministic(Expr *p){ Walker w; memset(&w, 0, sizeof(w)); w.eCode = 1; w.xExprCallback = exprNodeIsDeterministic; w.xSelectCallback = sqlite3SelectWalkFail; sqlite3WalkExpr(&w, p); return w.eCode; } /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains ** information needed to terminate the loop. Later, the calling routine ** should invoke sqlite3WhereEnd() with the return value of this function ** in order to complete the WHERE clause processing. |
︙ | ︙ | |||
4343 4344 4345 4346 4347 4348 4349 | ** used. */ WhereInfo *sqlite3WhereBegin( Parse *pParse, /* The parser context */ SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */ Expr *pWhere, /* The WHERE clause */ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ | | | 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 | ** used. */ WhereInfo *sqlite3WhereBegin( Parse *pParse, /* The parser context */ SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */ Expr *pWhere, /* The WHERE clause */ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number ** If WHERE_USE_LIMIT, then the limit amount */ ){ int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ int nTabList; /* Number of elements in pTabList */ WhereInfo *pWInfo; /* Will become the return value of this function */ |
︙ | ︙ | |||
4419 4420 4421 4422 4423 4424 4425 | sqlite3DbFree(db, pWInfo); pWInfo = 0; goto whereBeginError; } pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->pOrderBy = pOrderBy; | | > | 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 | sqlite3DbFree(db, pWInfo); pWInfo = 0; goto whereBeginError; } pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->pOrderBy = pOrderBy; pWInfo->pWhere = pWhere; pWInfo->pResultSet = pResultSet; pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; pWInfo->nLevel = nTabList; pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v); pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; memset(&pWInfo->nOBSat, 0, |
︙ | ︙ | |||
4447 4448 4449 4450 4451 4452 4453 | /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. */ initMaskSet(pMaskSet); sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo); sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND); | < < < < < < < < < < < | < | | | | | | | | | | | | | | | > | | | | > > | | | > | > | | > > > > > > > > > > > > > > > > > > > > | | | 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 | /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. */ initMaskSet(pMaskSet); sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo); sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND); /* Special case: No FROM clause */ if( nTabList==0 ){ if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr; if( wctrlFlags & WHERE_WANT_DISTINCT ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } }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 thta if X is the bitmask for ** a table T, then X-1 is the bitmask for all other tables to the left of T. ** Knowing the bitmask for all tables to the left of a left join is ** important. Ticket #3015. ** ** Note that bitmasks are created for all pTabList->nSrc tables in ** pTabList, not just the first nTabList tables. nTabList is normally ** equal to pTabList->nSrc but might be shortened to 1 if the ** WHERE_OR_SUBCLAUSE flag is set. */ ii = 0; do{ createMask(pMaskSet, pTabList->a[ii].iCursor); sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC); }while( (++ii)<pTabList->nSrc ); #ifdef SQLITE_DEBUG { Bitmask mx = 0; for(ii=0; ii<pTabList->nSrc; ii++){ Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor); assert( m>=mx ); mx = m; } } #endif } /* Analyze all of the subexpressions. */ sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC); if( db->mallocFailed ) goto whereBeginError; /* Special case: WHERE terms that do not refer to any tables in the join ** (constant expressions). Evaluate each such term, and jump over all the ** generated code if the result is not true. ** ** Do not do this if the expression contains non-deterministic functions ** that are not within a sub-select. This is not strictly required, but ** preserves SQLite's legacy behaviour in the following two cases: ** ** FROM ... WHERE random()>0; -- eval random() once per row ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall */ for(ii=0; ii<sWLB.pWC->nTerm; ii++){ WhereTerm *pT = &sWLB.pWC->a[ii]; if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); pT->wtFlags |= TERM_CODED; } } if( wctrlFlags & WHERE_WANT_DISTINCT ){ if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ /* The DISTINCT marking is pointless. Ignore it. */ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; }else if( pOrderBy==0 ){ /* Try to ORDER BY the result set to make distinct processing easier */ pWInfo->wctrlFlags |= WHERE_DISTINCTBY; pWInfo->pOrderBy = pResultSet; } } /* Construct the WhereLoop objects */ #if defined(WHERETRACE_ENABLED) if( sqlite3WhereTrace & 0xffff ){ sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags); |
︙ | ︙ | |||
4532 4533 4534 4535 4536 4537 4538 | #ifdef WHERETRACE_ENABLED if( sqlite3WhereTrace ){ /* Display all of the WhereLoop objects */ WhereLoop *p; int i; static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz" "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){ | | | 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 | #ifdef WHERETRACE_ENABLED if( sqlite3WhereTrace ){ /* Display all of the WhereLoop objects */ WhereLoop *p; int i; static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz" "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){ p->cId = zLabel[i%(sizeof(zLabel)-1)]; whereLoopPrint(p, sWLB.pWC); } } #endif wherePathSolver(pWInfo, 0); if( db->mallocFailed ) goto whereBeginError; |
︙ | ︙ | |||
4579 4580 4581 4582 4583 4584 4585 | for(ii=0; ii<pWInfo->nLevel; ii++){ whereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC); } } #endif /* Attempt to omit tables from the join that do not effect the result */ if( pWInfo->nLevel>=2 | | | | 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 | for(ii=0; ii<pWInfo->nLevel; ii++){ whereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC); } } #endif /* Attempt to omit tables from the join that do not effect the result */ if( pWInfo->nLevel>=2 && pResultSet!=0 && OptimizationEnabled(db, SQLITE_OmitNoopJoin) ){ Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet); if( sWLB.pOrderBy ){ tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); } while( pWInfo->nLevel>=2 ){ WhereTerm *pTerm, *pEnd; pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop; if( (pWInfo->pTabList->a[pLoop->iTab].fg.jointype & JT_LEFT)==0 ) break; |
︙ | ︙ | |||
4695 4696 4697 4698 4699 4700 4701 | }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); } if( pLoop->wsFlags & WHERE_INDEXED ){ Index *pIx = pLoop->u.btree.pIndex; int iIndexCur; int op = OP_OpenRead; | | | 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 | }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); } if( pLoop->wsFlags & WHERE_INDEXED ){ Index *pIx = pLoop->u.btree.pIndex; int iIndexCur; int op = OP_OpenRead; /* iAuxArg is always set to a positive value if ONEPASS is possible */ assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 ); if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx) && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){ /* This is one term of an OR-optimization using the PRIMARY KEY of a ** WITHOUT ROWID table. No need for a separate index */ iIndexCur = pLevel->iTabCur; |
︙ | ︙ | |||
4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 | assert( iIndexCur>=0 ); if( op ){ sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIx); if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0 && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 ){ sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */ } VdbeComment((v, "%s", pIx->zName)); #ifdef SQLITE_ENABLE_COLUMN_USED_MASK { u64 colUsed = 0; | > | 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 | assert( iIndexCur>=0 ); if( op ){ sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIx); if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0 && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 && pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED ){ sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */ } VdbeComment((v, "%s", pIx->zName)); #ifdef SQLITE_ENABLE_COLUMN_USED_MASK { u64 colUsed = 0; |
︙ | ︙ | |||
4817 4818 4819 4820 4821 4822 4823 | */ VdbeModuleComment((v, "End WHERE-core")); sqlite3ExprCacheClear(pParse); for(i=pWInfo->nLevel-1; i>=0; i--){ int addr; pLevel = &pWInfo->a[i]; pLoop = pLevel->pWLoop; | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | */ VdbeModuleComment((v, "End WHERE-core")); sqlite3ExprCacheClear(pParse); for(i=pWInfo->nLevel-1; i>=0; i--){ int addr; pLevel = &pWInfo->a[i]; pLoop = pLevel->pWLoop; if( pLevel->op!=OP_Noop ){ #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT int addrSeek = 0; Index *pIdx; int n; if( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED && (pLoop->wsFlags & WHERE_INDEXED)!=0 && (pIdx = pLoop->u.btree.pIndex)->hasStat1 && (n = pLoop->u.btree.nIdxCol)>0 && pIdx->aiRowLogEst[n]>=36 ){ int r1 = pParse->nMem+1; int j, op; for(j=0; j<n; j++){ sqlite3VdbeAddOp3(v, OP_Column, pLevel->iIdxCur, j, r1+j); } pParse->nMem += n+1; op = pLevel->op==OP_Prev ? OP_SeekLT : OP_SeekGT; addrSeek = sqlite3VdbeAddOp4Int(v, op, pLevel->iIdxCur, 0, r1, n); VdbeCoverageIf(v, op==OP_SeekLT); VdbeCoverageIf(v, op==OP_SeekGT); sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2); } #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ /* The common case: Advance to the next row */ sqlite3VdbeResolveLabel(v, pLevel->addrCont); sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); sqlite3VdbeChangeP5(v, pLevel->p5); VdbeCoverage(v); VdbeCoverageIf(v, pLevel->op==OP_Next); VdbeCoverageIf(v, pLevel->op==OP_Prev); VdbeCoverageIf(v, pLevel->op==OP_VNext); #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek); #endif }else{ sqlite3VdbeResolveLabel(v, pLevel->addrCont); } if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ struct InLoop *pIn; int j; sqlite3VdbeResolveLabel(v, pLevel->addrNxt); for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){ sqlite3VdbeJumpHere(v, pIn->addrInTop+1); |
︙ | ︙ | |||
4856 4857 4858 4859 4860 4861 4862 4863 | if( pLevel->addrLikeRep ){ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, (int)(pLevel->iLikeRepCntr>>1), pLevel->addrLikeRep); VdbeCoverage(v); } #endif if( pLevel->iLeftJoin ){ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); | > < | | | > > | 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 | if( pLevel->addrLikeRep ){ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, (int)(pLevel->iLikeRepCntr>>1), pLevel->addrLikeRep); VdbeCoverage(v); } #endif if( pLevel->iLeftJoin ){ int ws = pLoop->wsFlags; addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); if( (ws & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor); } if( (ws & WHERE_INDEXED) || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx) ){ sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); } if( pLevel->op==OP_Return ){ sqlite3VdbeAddOp2(v, OP_Gosub, pLevel->p1, pLevel->addrFirst); }else{ sqlite3VdbeGoto(v, pLevel->addrFirst); } |
︙ | ︙ | |||
4895 4896 4897 4898 4899 4900 4901 | assert( pTab!=0 ); pLoop = pLevel->pWLoop; /* 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. */ | | > | < < < < < < < < < < < < < < < < < < < < < | 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 | assert( pTab!=0 ); pLoop = pLevel->pWLoop; /* 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 ** this optimization prevents the table from ever being read, which can ** yield a significant performance boost. ** ** Calls to the code generator in between sqlite3WhereBegin and ** sqlite3WhereEnd will have created code that references the table |
︙ | ︙ | |||
4960 4961 4962 4963 4964 4965 4966 | assert( x>=0 ); } x = sqlite3ColumnOfIndex(pIdx, x); if( x>=0 ){ pOp->p2 = x; pOp->p1 = pLevel->iIdxCur; } | | > > > | 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 | assert( x>=0 ); } x = sqlite3ColumnOfIndex(pIdx, x); if( x>=0 ){ pOp->p2 = x; pOp->p1 = pLevel->iIdxCur; } assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 || pWInfo->eOnePass ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; }else if( pOp->opcode==OP_IfNullRow ){ pOp->p1 = pLevel->iIdxCur; } } } } /* Final cleanup */ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); return; } |
Changes to src/whereInt.h.
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 | LogEst rRun; /* Cost of running each loop */ LogEst nOut; /* Estimated number of output rows */ union { struct { /* Information for internal btree tables */ u16 nEq; /* Number of equality constraints */ u16 nBtm; /* Size of BTM vector */ u16 nTop; /* Size of TOP vector */ Index *pIndex; /* Index used, or NULL */ } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ u8 needFree; /* True if sqlite3_free(idxStr) is needed */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ | > | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | LogEst rRun; /* Cost of running each loop */ LogEst nOut; /* Estimated number of output rows */ union { struct { /* Information for internal btree tables */ u16 nEq; /* Number of equality constraints */ u16 nBtm; /* Size of BTM vector */ u16 nTop; /* Size of TOP vector */ u16 nIdxCol; /* Index column used for ORDER BY */ Index *pIndex; /* Index used, or NULL */ } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ u8 needFree; /* True if sqlite3_free(idxStr) is needed */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ |
︙ | ︙ | |||
279 280 281 282 283 284 285 286 287 288 289 290 291 292 | #else # define TERM_VNULL 0x00 /* Disabled if not using stat3 */ #endif #define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */ #define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */ #define TERM_LIKE 0x400 /* The original LIKE operator */ #define TERM_IS 0x800 /* Term.pExpr is an IS operator */ /* ** An instance of the WhereScan object is used as an iterator for locating ** terms in the WHERE clause that are useful to the query planner. */ struct WhereScan { WhereClause *pOrigWC; /* Original, innermost WhereClause */ | > | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | #else # define TERM_VNULL 0x00 /* Disabled if not using stat3 */ #endif #define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */ #define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */ #define TERM_LIKE 0x400 /* The original LIKE operator */ #define TERM_IS 0x800 /* Term.pExpr is an IS operator */ #define TERM_VARSELECT 0x1000 /* Term.pExpr contains a correlated sub-query */ /* ** An instance of the WhereScan object is used as an iterator for locating ** terms in the WHERE clause that are useful to the query planner. */ struct WhereScan { WhereClause *pOrigWC; /* Original, innermost WhereClause */ |
︙ | ︙ | |||
368 369 370 371 372 373 374 375 376 377 378 379 380 381 | ** above, the mapping might go like this: 4->3, 5->1, 8->2, 29->0, ** 57->5, 73->4. Or one of 719 other combinations might be used. It ** does not really matter. What is important is that sparse cursor ** numbers all get mapped into bit numbers that begin with 0 and contain ** no gaps. */ struct WhereMaskSet { int n; /* Number of assigned cursor values */ int ix[BMS]; /* Cursor assigned to each bit */ }; /* ** Initialize a WhereMaskSet object */ | > | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | ** above, the mapping might go like this: 4->3, 5->1, 8->2, 29->0, ** 57->5, 73->4. Or one of 719 other combinations might be used. It ** does not really matter. What is important is that sparse cursor ** numbers all get mapped into bit numbers that begin with 0 and contain ** no gaps. */ struct WhereMaskSet { int bVarSelect; /* Used by sqlite3WhereExprUsage() */ int n; /* Number of assigned cursor values */ int ix[BMS]; /* Cursor assigned to each bit */ }; /* ** Initialize a WhereMaskSet object */ |
︙ | ︙ | |||
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | ExprList *pOrderBy; /* ORDER BY clause */ WhereLoop *pNew; /* Template WhereLoop */ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 UnpackedRecord *pRec; /* Probe for stat4 (if required) */ int nRecValid; /* Number of valid fields currently in pRec */ #endif }; /* ** The WHERE clause processing routine has two halves. The ** first part does the start of the WHERE loop and the second ** half does the tail of the WHERE loop. An instance of ** this structure is returned by the first half and passed ** into the second half to give some continuity. ** ** An instance of this object holds the complete state of the query ** planner. */ struct WhereInfo { Parse *pParse; /* Parsing and code generating context */ SrcList *pTabList; /* List of tables in the join */ ExprList *pOrderBy; /* The ORDER BY clause or NULL */ | > > > > > | > | 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 | ExprList *pOrderBy; /* ORDER BY clause */ WhereLoop *pNew; /* Template WhereLoop */ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 UnpackedRecord *pRec; /* Probe for stat4 (if required) */ int nRecValid; /* Number of valid fields currently in pRec */ #endif unsigned int bldFlags; /* SQLITE_BLDF_* flags */ }; /* Allowed values for WhereLoopBuider.bldFlags */ #define SQLITE_BLDF_INDEXED 0x0001 /* An index is used */ #define SQLITE_BLDF_UNIQUE 0x0002 /* All keys of a UNIQUE index used */ /* ** The WHERE clause processing routine has two halves. The ** first part does the start of the WHERE loop and the second ** half does the tail of the WHERE loop. An instance of ** this structure is returned by the first half and passed ** into the second half to give some continuity. ** ** An instance of this object holds the complete state of the query ** planner. */ struct WhereInfo { Parse *pParse; /* Parsing and code generating context */ SrcList *pTabList; /* List of tables in the join */ ExprList *pOrderBy; /* The ORDER BY clause or NULL */ ExprList *pResultSet; /* Result set of the query */ Expr *pWhere; /* The complete WHERE clause */ LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */ int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ u8 nLevel; /* Number of nested loop */ |
︙ | ︙ | |||
502 503 504 505 506 507 508 | ** ** Value constraints: ** WO_EQ == SQLITE_INDEX_CONSTRAINT_EQ ** WO_LT == SQLITE_INDEX_CONSTRAINT_LT ** WO_LE == SQLITE_INDEX_CONSTRAINT_LE ** WO_GT == SQLITE_INDEX_CONSTRAINT_GT ** WO_GE == SQLITE_INDEX_CONSTRAINT_GE | < | | 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | ** ** Value constraints: ** WO_EQ == SQLITE_INDEX_CONSTRAINT_EQ ** WO_LT == SQLITE_INDEX_CONSTRAINT_LT ** WO_LE == SQLITE_INDEX_CONSTRAINT_LE ** WO_GT == SQLITE_INDEX_CONSTRAINT_GT ** WO_GE == SQLITE_INDEX_CONSTRAINT_GE */ #define WO_IN 0x0001 #define WO_EQ 0x0002 #define WO_LT (WO_EQ<<(TK_LT-TK_EQ)) #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) #define WO_AUX 0x0040 /* Op useful to virtual tables only */ #define WO_IS 0x0080 #define WO_ISNULL 0x0100 #define WO_OR 0x0200 /* Two or more OR-connected terms */ #define WO_AND 0x0400 /* Two or more AND-connected terms */ #define WO_EQUIV 0x0800 /* Of the form A==B, both columns */ #define WO_NOOP 0x1000 /* This term does not restrict search space */ |
︙ | ︙ |
Changes to src/wherecode.c.
︙ | ︙ | |||
442 443 444 445 446 447 448 449 450 451 452 453 454 455 | } if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0); }else{ Select *pSelect = pX->x.pSelect; sqlite3 *db = pParse->db; ExprList *pOrigRhs = pSelect->pEList; ExprList *pOrigLhs = pX->pLeft->x.pList; ExprList *pRhs = 0; /* New Select.pEList for RHS */ ExprList *pLhs = 0; /* New pX->pLeft vector */ for(i=iEq;i<pLoop->nLTerm; i++){ if( pLoop->aLTerm[i]->pExpr==pX ){ | > | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | } if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0); }else{ Select *pSelect = pX->x.pSelect; sqlite3 *db = pParse->db; u16 savedDbOptFlags = db->dbOptFlags; ExprList *pOrigRhs = pSelect->pEList; ExprList *pOrigLhs = pX->pLeft->x.pList; ExprList *pRhs = 0; /* New Select.pEList for RHS */ ExprList *pLhs = 0; /* New pX->pLeft vector */ for(i=iEq;i<pLoop->nLTerm; i++){ if( pLoop->aLTerm[i]->pExpr==pX ){ |
︙ | ︙ | |||
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | pX->pLeft = pLhs->a[0].pExpr; }else{ pLeft->x.pList = pLhs; aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq); testcase( aiMap==0 ); } pSelect->pEList = pRhs; eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap); testcase( aiMap!=0 && aiMap[0]!=0 ); pSelect->pEList = pOrigRhs; pLeft->x.pList = pOrigLhs; pX->pLeft = pLeft; } sqlite3ExprListDelete(pParse->db, pLhs); sqlite3ExprListDelete(pParse->db, pRhs); | > > | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | pX->pLeft = pLhs->a[0].pExpr; }else{ pLeft->x.pList = pLhs; aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq); testcase( aiMap==0 ); } pSelect->pEList = pRhs; db->dbOptFlags |= SQLITE_QueryFlattener; eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap); db->dbOptFlags = savedDbOptFlags; testcase( aiMap!=0 && aiMap[0]!=0 ); pSelect->pEList = pOrigRhs; pLeft->x.pList = pOrigLhs; pX->pLeft = pLeft; } sqlite3ExprListDelete(pParse->db, pLhs); sqlite3ExprListDelete(pParse->db, pRhs); |
︙ | ︙ | |||
787 788 789 790 791 792 793 | if( pExpr->op==TK_IS || pExpr->op==TK_ISNULL || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL || pExpr->op==TK_CASE ){ pWalker->eCode = 1; }else if( pExpr->op==TK_FUNCTION ){ int d1; | | | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 | if( pExpr->op==TK_IS || pExpr->op==TK_ISNULL || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL || pExpr->op==TK_CASE ){ pWalker->eCode = 1; }else if( pExpr->op==TK_FUNCTION ){ int d1; char d2[4]; if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){ pWalker->eCode = 1; } } return WRC_Continue; } |
︙ | ︙ | |||
959 960 961 962 963 964 965 | ** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains ** a rowid value just read from cursor iIdxCur, open on index pIdx. This ** function generates code to do a deferred seek of cursor iCur to the ** rowid stored in register iRowid. ** ** Normally, this is just: ** | | | | | 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 | ** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains ** a rowid value just read from cursor iIdxCur, open on index pIdx. This ** function generates code to do a deferred seek of cursor iCur to the ** rowid stored in register iRowid. ** ** Normally, this is just: ** ** OP_DeferredSeek $iCur $iRowid ** ** However, if the scan currently being coded is a branch of an OR-loop and ** the statement currently being coded is a SELECT, then P3 of OP_DeferredSeek ** is set to iIdxCur and P4 is set to point to an array of integers ** containing one entry for each column of the table cursor iCur is open ** on. For each table column, if the column is the i'th column of the ** index, then the corresponding array entry is set to (i+1). If the column ** does not appear in the index at all, the array entry is set to 0. */ static void codeDeferredSeek( WhereInfo *pWInfo, /* Where clause context */ Index *pIdx, /* Index scan is using */ int iCur, /* Cursor for IPK b-tree */ int iIdxCur /* Index cursor */ ){ Parse *pParse = pWInfo->pParse; /* Parse context */ Vdbe *v = pParse->pVdbe; /* Vdbe to generate code within */ assert( iIdxCur>0 ); assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 ); sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur); if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) ){ int i; Table *pTab = pIdx->pTable; int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1)); if( ai ){ |
︙ | ︙ | |||
1010 1011 1012 1013 1014 1015 1016 | ** ** If the expression is not a vector, then nReg must be passed 1. In ** this case, generate code to evaluate the expression and leave the ** result in register iReg. */ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ assert( nReg>0 ); | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** ** If the expression is not a vector, then nReg must be passed 1. In ** this case, generate code to evaluate the expression and leave the ** result in register iReg. */ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ assert( nReg>0 ); if( p && sqlite3ExprIsVector(p) ){ #ifndef SQLITE_OMIT_SUBQUERY if( (p->flags & EP_xIsSelect) ){ Vdbe *v = pParse->pVdbe; int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0); sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1); }else #endif { int i; ExprList *pList = p->x.pList; assert( nReg<=pList->nExpr ); for(i=0; i<nReg; i++){ sqlite3ExprCode(pParse, pList->a[i].pExpr, iReg+i); } } }else{ assert( nReg==1 ); sqlite3ExprCode(pParse, p, iReg); } } /* An instance of the IdxExprTrans object carries information about a ** mapping from an expression on table columns into a column in an index ** down through the Walker. */ typedef struct IdxExprTrans { Expr *pIdxExpr; /* The index expression */ int iTabCur; /* The cursor of the corresponding table */ int iIdxCur; /* The cursor for the index */ int iIdxCol; /* The column for the index */ } IdxExprTrans; /* The walker node callback used to transform matching expressions into ** a reference to an index column for an index on an expression. ** ** If pExpr matches, then transform it into a reference to the index column ** that contains the value of pExpr. */ static int whereIndexExprTransNode(Walker *p, Expr *pExpr){ IdxExprTrans *pX = p->u.pIdxTrans; if( sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){ pExpr->op = TK_COLUMN; pExpr->iTable = pX->iIdxCur; pExpr->iColumn = pX->iIdxCol; pExpr->pTab = 0; return WRC_Prune; }else{ return WRC_Continue; } } /* ** For an indexes on expression X, locate every instance of expression X ** in pExpr and change that subexpression into a reference to the appropriate ** column of the index. */ static void whereIndexExprTrans( Index *pIdx, /* The Index */ int iTabCur, /* Cursor of the table that is being indexed */ int iIdxCur, /* Cursor of the index itself */ WhereInfo *pWInfo /* Transform expressions in this WHERE clause */ ){ int iIdxCol; /* Column number of the index */ ExprList *aColExpr; /* Expressions that are indexed */ Walker w; IdxExprTrans x; aColExpr = pIdx->aColExpr; if( aColExpr==0 ) return; /* Not an index on expressions */ memset(&w, 0, sizeof(w)); w.xExprCallback = whereIndexExprTransNode; w.u.pIdxTrans = &x; x.iTabCur = iTabCur; x.iIdxCur = iIdxCur; for(iIdxCol=0; iIdxCol<aColExpr->nExpr; iIdxCol++){ if( pIdx->aiColumn[iIdxCol]!=XN_EXPR ) continue; assert( aColExpr->a[iIdxCol].pExpr!=0 ); x.iIdxCol = iIdxCol; x.pIdxExpr = aColExpr->a[iIdxCol].pExpr; sqlite3WalkExpr(&w, pWInfo->pWhere); sqlite3WalkExprList(&w, pWInfo->pOrderBy); sqlite3WalkExprList(&w, pWInfo->pResultSet); } } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ Bitmask sqlite3WhereCodeOneLoopStart( WhereInfo *pWInfo, /* Complete information about the WHERE clause */ |
︙ | ︙ | |||
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 | WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ Parse *pParse; /* Parsing context */ sqlite3 *db; /* Database connection */ Vdbe *v; /* The prepared stmt under constructions */ struct SrcList_item *pTabItem; /* FROM clause term being coded */ int addrBrk; /* Jump here to break out of the loop */ int addrCont; /* Jump here to continue with next cycle */ int iRowidReg = 0; /* Rowid is stored in this register, if not zero */ int iReleaseReg = 0; /* Temp register to free before returning */ pParse = pWInfo->pParse; v = pParse->pVdbe; pWC = &pWInfo->sWC; db = pParse->db; pLevel = &pWInfo->a[iLevel]; pLoop = pLevel->pWLoop; | > > > | 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 | WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ Parse *pParse; /* Parsing context */ sqlite3 *db; /* Database connection */ Vdbe *v; /* The prepared stmt under constructions */ struct SrcList_item *pTabItem; /* FROM clause term being coded */ int addrBrk; /* Jump here to break out of the loop */ int addrHalt; /* addrBrk for the outermost loop */ int addrCont; /* Jump here to continue with next cycle */ int iRowidReg = 0; /* Rowid is stored in this register, if not zero */ int iReleaseReg = 0; /* Temp register to free before returning */ Index *pIdx = 0; /* Index used by loop (if any) */ int iLoop; /* Iteration of constraint generator loop */ pParse = pWInfo->pParse; v = pParse->pVdbe; pWC = &pWInfo->sWC; db = pParse->db; pLevel = &pWInfo->a[iLevel]; pLoop = pLevel->pWLoop; |
︙ | ︙ | |||
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 | ** row of the left table of the join. */ if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN no-match flag")); } /* Special case of a FROM clause subquery implemented as a co-routine */ if( pTabItem->fg.viaCoroutine ){ int regYield = pTabItem->regReturn; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); VdbeCoverage(v); | > > > > > | 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 | ** row of the left table of the join. */ if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN no-match flag")); } /* Compute a safe address to jump to if we discover that the table for ** this loop is empty and can never contribute content. */ for(j=iLevel; j>0 && pWInfo->a[j].iLeftJoin==0; j--){} addrHalt = pWInfo->a[j].addrBrk; /* Special case of a FROM clause subquery implemented as a co-routine */ if( pTabItem->fg.viaCoroutine ){ int regYield = pTabItem->regReturn; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); VdbeCoverage(v); |
︙ | ︙ | |||
1135 1136 1137 1138 1139 1140 1141 | codeExprOrVector(pParse, pRight, iTarget, 1); } } sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1); sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pLoop->u.vtab.idxStr, | | | 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 | codeExprOrVector(pParse, pRight, iTarget, 1); } } sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1); sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pLoop->u.vtab.idxStr, pLoop->u.vtab.needFree ? P4_DYNAMIC : P4_STATIC); VdbeCoverage(v); pLoop->u.vtab.needFree = 0; pLevel->p1 = iCur; pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext; pLevel->p2 = sqlite3VdbeCurrentAddr(v); iIn = pLevel->u.in.nIn; for(j=nConstraint-1; j>=0; j--){ |
︙ | ︙ | |||
1168 1169 1170 1171 1172 1173 1174 | assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 ); testcase( pOp->opcode==OP_Rowid ); sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); } /* Generate code that will continue to the next row if ** the IN constraint is not satisfied */ | | | 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 | assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 ); testcase( pOp->opcode==OP_Rowid ); sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); } /* Generate code that will continue to the next row if ** the IN constraint is not satisfied */ pCompare = sqlite3PExpr(pParse, TK_EQ, 0, 0); assert( pCompare!=0 || db->mallocFailed ); if( pCompare ){ pCompare->pLeft = pTerm->pExpr->pLeft; pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0); if( pRight ){ pRight->iTable = iReg+j+2; sqlite3ExprIfFalse(pParse, pCompare, pLevel->addrCont, 0); |
︙ | ︙ | |||
1280 1281 1282 1283 1284 1285 1286 | VdbeCoverageIf(v, pX->op==TK_GT); VdbeCoverageIf(v, pX->op==TK_LE); VdbeCoverageIf(v, pX->op==TK_LT); VdbeCoverageIf(v, pX->op==TK_GE); sqlite3ExprCacheAffinityChange(pParse, r1, 1); sqlite3ReleaseTempReg(pParse, rTemp); }else{ | | | 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 | VdbeCoverageIf(v, pX->op==TK_GT); VdbeCoverageIf(v, pX->op==TK_LE); VdbeCoverageIf(v, pX->op==TK_LT); VdbeCoverageIf(v, pX->op==TK_GE); sqlite3ExprCacheAffinityChange(pParse, r1, 1); sqlite3ReleaseTempReg(pParse, rTemp); }else{ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrHalt); VdbeCoverageIf(v, bRev==0); VdbeCoverageIf(v, bRev!=0); } if( pEnd ){ Expr *pX; pX = pEnd->pExpr; assert( pX!=0 ); |
︙ | ︙ | |||
1378 1379 1380 1381 1382 1383 1384 | int regBase; /* Base register holding constraint values */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */ int startEq; /* True if range start uses ==, >= or <= */ int endEq; /* True if range end uses ==, >= or <= */ int start_constraints; /* Start of range is constrained */ int nConstraint; /* Number of constraint terms */ | < | 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 | int regBase; /* Base register holding constraint values */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */ int startEq; /* True if range start uses ==, >= or <= */ int endEq; /* True if range end uses ==, >= or <= */ int start_constraints; /* Start of range is constrained */ int nConstraint; /* Number of constraint terms */ int iIdxCur; /* The VDBE cursor for the index */ int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ char *zStartAff; /* Affinity for start of range constraint */ char *zEndAff = 0; /* Affinity for end of range constraint */ u8 bSeekPastNull = 0; /* True to seek past initial nulls */ u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */ |
︙ | ︙ | |||
1584 1585 1586 1587 1588 1589 1590 | testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); } /* Seek the table cursor, if required */ if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ | | > > > > > > > > > > > | 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 | testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); } /* Seek the table cursor, if required */ if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE) || ( (pWInfo->wctrlFlags & WHERE_SEEK_UNIQ_TABLE) && (pWInfo->eOnePass==ONEPASS_SINGLE) )){ iRowidReg = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg); VdbeCoverage(v); }else{ codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur); } }else if( iCur!=iIdxCur ){ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol); for(j=0; j<pPk->nKeyCol; j++){ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]); sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j); } sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont, iRowidReg, pPk->nKeyCol); VdbeCoverage(v); } /* If pIdx is an index on one or more expressions, then look through ** all the expressions in pWInfo and try to transform matching expressions ** into reference to index columns. */ whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo); /* Record the instruction used to terminate the loop. */ if( pLoop->wsFlags & WHERE_ONEROW ){ pLevel->op = OP_Noop; }else if( bRev ){ pLevel->op = OP_Prev; }else{ pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; pLevel->p3 = (pLoop->wsFlags&WHERE_UNQ_WANTED)!=0 ? 1:0; if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; }else{ assert( pLevel->p5==0 ); } if( omitTable ) pIdx = 0; }else #ifndef SQLITE_OMIT_OR_OPTIMIZATION if( pLoop->wsFlags & WHERE_MULTI_OR ){ /* Case 5: Two or more separately indexed terms connected by OR ** ** Example: |
︙ | ︙ | |||
1767 1768 1769 1770 1771 1772 1773 | if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue; if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); pExpr = sqlite3ExprDup(db, pExpr, 0); pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr); } if( pAndExpr ){ | | | 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 | if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue; if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); pExpr = sqlite3ExprDup(db, pExpr, 0); pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr); } if( pAndExpr ){ pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr); } } /* Run a separate WHERE clause for each term of the OR clause. After ** eliminating duplicates from other WHERE clauses, the action for each ** sub-WHERE clause is to to invoke the main loop body as a subroutine. */ |
︙ | ︙ | |||
1840 1841 1842 1843 1844 1845 1846 | ** be tested for. */ if( iSet ){ jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk); VdbeCoverage(v); } if( iSet>=0 ){ sqlite3VdbeAddOp3(v, OP_MakeRecord, r, nPk, regRowid); | | > | 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 | ** be tested for. */ if( iSet ){ jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk); VdbeCoverage(v); } if( iSet>=0 ){ sqlite3VdbeAddOp3(v, OP_MakeRecord, r, nPk, regRowid); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, regRowset, regRowid, r, nPk); if( iSet ) sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); } /* Release the array of temp registers */ sqlite3ReleaseTempRange(pParse, r, nPk); } } |
︙ | ︙ | |||
1922 1923 1924 1925 1926 1927 1928 | /* Tables marked isRecursive have only a single row that is stored in ** a pseudo-cursor. No need to Rewind or Next such cursors. */ pLevel->op = OP_Noop; }else{ codeCursorHint(pTabItem, pWInfo, pLevel, 0); pLevel->op = aStep[bRev]; pLevel->p1 = iCur; | | > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | > > > > > > > > > > | | | | | | | | | | | | > > > > > > | | | | > > | 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 | /* Tables marked isRecursive have only a single row that is stored in ** a pseudo-cursor. No need to Rewind or Next such cursors. */ pLevel->op = OP_Noop; }else{ codeCursorHint(pTabItem, pWInfo, pLevel, 0); pLevel->op = aStep[bRev]; pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrHalt); VdbeCoverageIf(v, bRev==0); VdbeCoverageIf(v, bRev!=0); pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; } } #ifdef SQLITE_ENABLE_STMT_SCANSTATUS pLevel->addrVisit = sqlite3VdbeCurrentAddr(v); #endif /* Insert code to test every subexpression that can be completely ** computed using the current set of tables. ** ** This loop may run between one and three times, depending on the ** constraints to be generated. The value of stack variable iLoop ** determines the constraints coded by each iteration, as follows: ** ** iLoop==1: Code only expressions that are entirely covered by pIdx. ** iLoop==2: Code remaining expressions that do not contain correlated ** sub-queries. ** iLoop==3: Code all remaining expressions. ** ** An effort is made to skip unnecessary iterations of the loop. */ iLoop = (pIdx ? 1 : 2); do{ int iNext = 0; /* Next value for iLoop */ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; int skipLikeAddr = 0; testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( (pTerm->prereqAll & pLevel->notReady)!=0 ){ testcase( pWInfo->untestedTerms==0 && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ); pWInfo->untestedTerms = 1; continue; } pE = pTerm->pExpr; assert( pE!=0 ); if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){ continue; } if( iLoop==1 && !sqlite3ExprCoveredByIndex(pE, pLevel->iTabCur, pIdx) ){ iNext = 2; continue; } if( iLoop<3 && (pTerm->wtFlags & TERM_VARSELECT) ){ if( iNext==0 ) iNext = 3; continue; } if( pTerm->wtFlags & TERM_LIKECOND ){ /* If the TERM_LIKECOND flag is set, that means that the range search ** is sufficient to guarantee that the LIKE operator is true, so we ** can skip the call to the like(A,B) function. But this only works ** for strings. So do not skip the call to the function on the pass ** that compares BLOBs. */ #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS continue; #else u32 x = pLevel->iLikeRepCntr; assert( x>0 ); skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If, (int)(x>>1)); VdbeCoverage(v); #endif } #ifdef WHERETRACE_ENABLED /* 0xffff */ if( sqlite3WhereTrace ){ VdbeNoopComment((v, "WhereTerm[%d] (%p) priority=%d", pWC->nTerm-j, pTerm, iLoop)); } #endif sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); if( skipLikeAddr ) sqlite3VdbeJumpHere(v, skipLikeAddr); pTerm->wtFlags |= TERM_CODED; } iLoop = iNext; }while( iLoop>0 ); /* Insert code to test for implied constraints based on transitivity ** of the "==" operator. ** ** Example: If the WHERE clause contains "t1.a=t2.b" and "t2.b=123" ** and we are coding the t1 loop and the t2 loop has not yet coded, ** then we cannot use the "t1.a=t2.b" constraint, but we can code |
︙ | ︙ |
Changes to src/whereexpr.c.
︙ | ︙ | |||
190 191 192 193 194 195 196 | static int isLikeOrGlob( Parse *pParse, /* Parsing and code generating context */ Expr *pExpr, /* Test this expression */ Expr **ppPrefix, /* Pointer to TK_STRING expression with pattern prefix */ int *pisComplete, /* True if the only wildcard is % in the last character */ int *pnoCase /* True if uppercase is equivalent to lowercase */ ){ | | | < < < < < < < < < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > | 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 | static int isLikeOrGlob( Parse *pParse, /* Parsing and code generating context */ Expr *pExpr, /* Test this expression */ Expr **ppPrefix, /* Pointer to TK_STRING expression with pattern prefix */ int *pisComplete, /* True if the only wildcard is % in the last character */ int *pnoCase /* True if uppercase is equivalent to lowercase */ ){ const u8 *z = 0; /* String on RHS of LIKE operator */ Expr *pRight, *pLeft; /* Right and left size of LIKE operator */ ExprList *pList; /* List of operands to the LIKE operator */ int c; /* One character in z[] */ int cnt; /* Number of non-wildcard prefix characters */ char wc[4]; /* Wildcard characters */ sqlite3 *db = pParse->db; /* Database connection */ sqlite3_value *pVal = 0; int op; /* Opcode of pRight */ int rc; /* Result code to return */ if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){ return 0; } #ifdef SQLITE_EBCDIC if( *pnoCase ) return 0; #endif pList = pExpr->x.pList; pLeft = pList->a[1].pExpr; pRight = sqlite3ExprSkipCollate(pList->a[0].pExpr); op = pRight->op; if( op==TK_VARIABLE && (db->flags & SQLITE_EnableQPSG)==0 ){ Vdbe *pReprepare = pParse->pReprepare; int iCol = pRight->iColumn; pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB); if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ z = sqlite3_value_text(pVal); } sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ z = (u8*)pRight->u.zToken; } if( z ){ /* If the RHS begins with a digit or a minus sign, then the LHS must ** be an ordinary column (not a virtual table column) with TEXT affinity. ** Otherwise the LHS might be numeric and "lhs >= rhs" would be false ** even though "lhs LIKE rhs" is true. But if the RHS does not start ** with a digit or '-', then "lhs LIKE rhs" will always be false if ** the LHS is numeric and so the optimization still works. */ if( sqlite3Isdigit(z[0]) || z[0]=='-' ){ if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT || IsVirtual(pLeft->pTab) /* Value might be numeric */ ){ sqlite3ValueFree(pVal); return 0; } } /* Count the number of prefix characters prior to the first wildcard */ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; if( c==wc[3] && z[cnt]!=0 ) cnt++; } /* The optimization is possible only if (1) the pattern does not begin ** with a wildcard and if (2) the non-wildcard prefix does not end with ** an (illegal 0xff) character. The second condition is necessary so ** that we can increment the prefix key to find an upper bound for the ** range search. */ if( cnt!=0 && 255!=(u8)z[cnt-1] ){ Expr *pPrefix; /* A "complete" match if the pattern ends with "*" or "%" */ *pisComplete = c==wc[0] && z[cnt+1]==0; /* Get the pattern prefix. Remove all escapes from the prefix. */ pPrefix = sqlite3Expr(db, TK_STRING, (char*)z); if( pPrefix ){ int iFrom, iTo; char *zNew = pPrefix->u.zToken; zNew[cnt] = 0; for(iFrom=iTo=0; iFrom<cnt; iFrom++){ if( zNew[iFrom]==wc[3] ) iFrom++; zNew[iTo++] = zNew[iFrom]; } zNew[iTo] = 0; } *ppPrefix = pPrefix; /* If the RHS pattern is a bound parameter, make arrangements to ** reprepare the statement when that parameter is rebound */ if( op==TK_VARIABLE ){ Vdbe *v = pParse->pVdbe; sqlite3VdbeSetVarmask(v, pRight->iColumn); if( *pisComplete && pRight->u.zToken[1] ){ /* If the rhs of the LIKE expression is a variable, and the current ** value of the variable means there is no need to invoke the LIKE ** function, then no OP_Variable will be added to the program. |
︙ | ︙ | |||
274 275 276 277 278 279 280 | return rc; } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ #ifndef SQLITE_OMIT_VIRTUALTABLE /* | | > | > > > > > > > > > > < | > > > > > > | | | > > > | | | | | | | | | | | | < < < | | | | | | | | | | | > > | | > > > > > > > > > > > > > > > > > > | 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 | return rc; } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Check to see if the pExpr expression is a form that needs to be passed ** to the xBestIndex method of virtual tables. Forms of interest include: ** ** Expression Virtual Table Operator ** ----------------------- --------------------------------- ** 1. column MATCH expr SQLITE_INDEX_CONSTRAINT_MATCH ** 2. column GLOB expr SQLITE_INDEX_CONSTRAINT_GLOB ** 3. column LIKE expr SQLITE_INDEX_CONSTRAINT_LIKE ** 4. column REGEXP expr SQLITE_INDEX_CONSTRAINT_REGEXP ** 5. column != expr SQLITE_INDEX_CONSTRAINT_NE ** 6. expr != column SQLITE_INDEX_CONSTRAINT_NE ** 7. column IS NOT expr SQLITE_INDEX_CONSTRAINT_ISNOT ** 8. expr IS NOT column SQLITE_INDEX_CONSTRAINT_ISNOT ** 9. column IS NOT NULL SQLITE_INDEX_CONSTRAINT_ISNOTNULL ** ** In every case, "column" must be a column of a virtual table. If there ** is a match, set *ppLeft to the "column" expression, set *ppRight to the ** "expr" expression (even though in forms (6) and (8) the column is on the ** right and the expression is on the left). Also set *peOp2 to the ** appropriate virtual table operator. The return value is 1 or 2 if there ** is a match. The usual return is 1, but if the RHS is also a column ** of virtual table in forms (5) or (7) then return 2. ** ** If the expression matches none of the patterns above, return 0. */ static int isAuxiliaryVtabOperator( Expr *pExpr, /* Test this expression */ unsigned char *peOp2, /* OUT: 0 for MATCH, or else an op2 value */ Expr **ppLeft, /* Column expression to left of MATCH/op2 */ Expr **ppRight /* Expression to left of MATCH/op2 */ ){ if( pExpr->op==TK_FUNCTION ){ static const struct Op2 { const char *zOp; unsigned char eOp2; } aOp[] = { { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } }; ExprList *pList; Expr *pCol; /* Column reference */ int i; pList = pExpr->x.pList; if( pList==0 || pList->nExpr!=2 ){ return 0; } pCol = pList->a[1].pExpr; if( pCol->op!=TK_COLUMN || !IsVirtual(pCol->pTab) ){ return 0; } for(i=0; i<ArraySize(aOp); i++){ if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){ *peOp2 = aOp[i].eOp2; *ppRight = pList->a[0].pExpr; *ppLeft = pCol; return 1; } } }else if( pExpr->op==TK_NE || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL ){ int res = 0; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->pTab) ){ res++; } if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->pTab) ){ res++; SWAP(Expr*, pLeft, pRight); } *ppLeft = pLeft; *ppRight = pRight; if( pExpr->op==TK_NE ) *peOp2 = SQLITE_INDEX_CONSTRAINT_NE; if( pExpr->op==TK_ISNOT ) *peOp2 = SQLITE_INDEX_CONSTRAINT_ISNOT; if( pExpr->op==TK_NOTNULL ) *peOp2 = SQLITE_INDEX_CONSTRAINT_ISNOTNULL; return res; } return 0; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** If the pBase expression originated in the ON or USING clause of |
︙ | ︙ | |||
394 395 396 397 398 399 400 | if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return; assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 ); assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 ); | | | | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return; assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 ); assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 ); if( sqlite3ExprCompare(0,pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return; if( sqlite3ExprCompare(0,pOne->pExpr->pRight, pTwo->pExpr->pRight,-1) )return; /* If we reach this point, it means the two subterms can be combined */ if( (eOp & (eOp-1))!=0 ){ if( eOp & (WO_LT|WO_LE) ){ eOp = WO_LE; }else{ assert( eOp & (WO_GT|WO_GE) ); eOp = WO_GE; |
︙ | ︙ | |||
566 567 568 569 570 571 572 | sqlite3WhereSplit(pAndWC, pOrTerm->pExpr, TK_AND); sqlite3WhereExprAnalyze(pSrc, pAndWC); pAndWC->pOuter = pWC; if( !db->mallocFailed ){ for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){ assert( pAndTerm->pExpr ); if( allowedOp(pAndTerm->pExpr->op) | | | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 | sqlite3WhereSplit(pAndWC, pOrTerm->pExpr, TK_AND); sqlite3WhereExprAnalyze(pSrc, pAndWC); pAndWC->pOuter = pWC; if( !db->mallocFailed ){ for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){ assert( pAndTerm->pExpr ); if( allowedOp(pAndTerm->pExpr->op) || pAndTerm->eOperator==WO_AUX ){ b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pAndTerm->leftCursor); } } } indexable &= b; } |
︙ | ︙ | |||
730 731 732 733 734 735 736 | assert( pOrTerm->u.leftColumn==iColumn ); pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup); pLeft = pOrTerm->pExpr->pLeft; } assert( pLeft!=0 ); pDup = sqlite3ExprDup(db, pLeft, 0); | | | 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 | assert( pOrTerm->u.leftColumn==iColumn ); pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup); pLeft = pOrTerm->pExpr->pLeft; } assert( pLeft!=0 ); pDup = sqlite3ExprDup(db, pLeft, 0); pNew = sqlite3PExpr(pParse, TK_IN, pDup, 0); if( pNew ){ int idxNew; transferJoinMarkings(pNew, pExpr); assert( !ExprHasProperty(pNew, EP_xIsSelect) ); pNew->x.pList = pList; idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); |
︙ | ︙ | |||
818 819 820 821 822 823 824 | return mask; } /* ** Expression pExpr is one operand of a comparison operator that might ** be useful for indexing. This routine checks to see if pExpr appears ** in any index. Return TRUE (1) if pExpr is an indexed term and return | | | | < > | < < > > > > > > > > > > | > > > > > > > > > > > | | < < < < < < < < < | < < < < | 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 | return mask; } /* ** Expression pExpr is one operand of a comparison operator that might ** be useful for indexing. This routine checks to see if pExpr appears ** in any index. Return TRUE (1) if pExpr is an indexed term and return ** FALSE (0) if not. If TRUE is returned, also set aiCurCol[0] to the cursor ** number of the table that is indexed and aiCurCol[1] to the column number ** of the column that is indexed, or XN_EXPR (-2) if an expression is being ** indexed. ** ** If pExpr is a TK_COLUMN column reference, then this routine always returns ** true even if that particular column is not indexed, because the column ** might be added to an automatic index later. */ static SQLITE_NOINLINE int exprMightBeIndexed2( SrcList *pFrom, /* The FROM clause */ Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ int *aiCurCol, /* Write the referenced table cursor and column here */ Expr *pExpr /* An operand of a comparison operator */ ){ Index *pIdx; int i; int iCur; for(i=0; mPrereq>1; i++, mPrereq>>=1){} iCur = pFrom->a[i].iCursor; for(pIdx=pFrom->a[i].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; if( sqlite3ExprCompareSkip(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){ aiCurCol[0] = iCur; aiCurCol[1] = XN_EXPR; return 1; } } } return 0; } static int exprMightBeIndexed( SrcList *pFrom, /* The FROM clause */ Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ int *aiCurCol, /* Write the referenced table cursor & column here */ Expr *pExpr, /* An operand of a comparison operator */ int op /* The specific comparison operator */ ){ /* If this expression is a vector to the left or right of a ** inequality constraint (>, <, >= or <=), perform the processing ** on the first element of the vector. */ assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE ); assert( TK_IS<TK_GE && TK_ISNULL<TK_GE && TK_IN<TK_GE ); assert( op<=TK_GE ); if( pExpr->op==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){ pExpr = pExpr->x.pList->a[0].pExpr; } if( pExpr->op==TK_COLUMN ){ aiCurCol[0] = pExpr->iTable; aiCurCol[1] = pExpr->iColumn; return 1; } if( mPrereq==0 ) return 0; /* No table references */ if( (mPrereq&(mPrereq-1))!=0 ) return 0; /* Refs more than one table */ return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr); } /* ** The input to this routine is an WhereTerm structure with only the ** "pExpr" field filled in. The job of this routine is to analyze the ** subexpression and populate all the other fields of the WhereTerm ** structure. |
︙ | ︙ | |||
909 910 911 912 913 914 915 916 917 918 919 920 921 922 | Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ int noCase = 0; /* uppercase equivalent to lowercase */ int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */ if( db->mallocFailed ){ return; } pTerm = &pWC->a[idxTerm]; pMaskSet = &pWInfo->sMaskSet; pExpr = pTerm->pExpr; | > | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 | Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ int noCase = 0; /* uppercase equivalent to lowercase */ int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */ int nLeft; /* Number of elements on left side vector */ if( db->mallocFailed ){ return; } pTerm = &pWC->a[idxTerm]; pMaskSet = &pWInfo->sMaskSet; pExpr = pTerm->pExpr; |
︙ | ︙ | |||
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 | pTerm->prereqRight = sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList); } }else if( op==TK_ISNULL ){ pTerm->prereqRight = 0; }else{ pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight); } prereqAll = sqlite3WhereExprUsage(pMaskSet, pExpr); if( ExprHasProperty(pExpr, EP_FromJoin) ){ Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable); prereqAll |= x; extraRight = x-1; /* ON clause terms may not be used with an index ** on left table of a LEFT JOIN. Ticket #3015 */ } pTerm->prereqAll = prereqAll; pTerm->leftCursor = -1; pTerm->iParent = -1; pTerm->eOperator = 0; if( allowedOp(op) ){ | > > > > > > | | | | | | 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 | pTerm->prereqRight = sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList); } }else if( op==TK_ISNULL ){ pTerm->prereqRight = 0; }else{ pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight); } pMaskSet->bVarSelect = 0; prereqAll = sqlite3WhereExprUsage(pMaskSet, pExpr); if( pMaskSet->bVarSelect ) pTerm->wtFlags |= TERM_VARSELECT; if( ExprHasProperty(pExpr, EP_FromJoin) ){ Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable); prereqAll |= x; extraRight = x-1; /* ON clause terms may not be used with an index ** on left table of a LEFT JOIN. Ticket #3015 */ if( (prereqAll>>1)>=x ){ sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); return; } } pTerm->prereqAll = prereqAll; pTerm->leftCursor = -1; pTerm->iParent = -1; pTerm->eOperator = 0; if( allowedOp(op) ){ int aiCurCol[2]; Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; if( pTerm->iField>0 ){ assert( op==TK_IN ); assert( pLeft->op==TK_VECTOR ); pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr; } if( exprMightBeIndexed(pSrc, prereqLeft, aiCurCol, pLeft, op) ){ pTerm->leftCursor = aiCurCol[0]; pTerm->u.leftColumn = aiCurCol[1]; pTerm->eOperator = operatorMask(op) & opMask; } if( op==TK_IS ) pTerm->wtFlags |= TERM_IS; if( pRight && exprMightBeIndexed(pSrc, pTerm->prereqRight, aiCurCol, pRight, op) ){ WhereTerm *pNew; Expr *pDup; u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ assert( pTerm->iField==0 ); if( pTerm->leftCursor>=0 ){ int idxNew; |
︙ | ︙ | |||
992 993 994 995 996 997 998 | eExtraOp = WO_EQUIV; } }else{ pDup = pExpr; pNew = pTerm; } exprCommute(pParse, pDup); | | | | 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 | eExtraOp = WO_EQUIV; } }else{ pDup = pExpr; pNew = pTerm; } exprCommute(pParse, pDup); pNew->leftCursor = aiCurCol[0]; pNew->u.leftColumn = aiCurCol[1]; testcase( (prereqLeft | extraRight) != prereqLeft ); pNew->prereqRight = prereqLeft | extraRight; pNew->prereqAll = prereqAll; pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; } } |
︙ | ︙ | |||
1028 1029 1030 1031 1032 1033 1034 | assert( pList!=0 ); assert( pList->nExpr==2 ); for(i=0; i<2; i++){ Expr *pNewExpr; int idxNew; pNewExpr = sqlite3PExpr(pParse, ops[i], sqlite3ExprDup(db, pExpr->pLeft, 0), | | | 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 | assert( pList!=0 ); assert( pList->nExpr==2 ); for(i=0; i<2; i++){ Expr *pNewExpr; int idxNew; pNewExpr = sqlite3PExpr(pParse, ops[i], sqlite3ExprDup(db, pExpr->pLeft, 0), sqlite3ExprDup(db, pList->a[i].pExpr, 0)); transferJoinMarkings(pNewExpr, pExpr); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); exprAnalyze(pSrc, pWC, idxNew); pTerm = &pWC->a[idxTerm]; markTermAsChild(pWC, idxNew, idxTerm); } |
︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 | } *pC = c + 1; } zCollSeqName = noCase ? "NOCASE" : "BINARY"; pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName), | | | | | > > > | < > > > | | < < | | | | | | > > > | | | | | | | | | | | | > > > | | | < < | > | 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 | } *pC = c + 1; } zCollSeqName = noCase ? "NOCASE" : "BINARY"; pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName), pStr1); transferJoinMarkings(pNewExpr1, pExpr); idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags); testcase( idxNew1==0 ); exprAnalyze(pSrc, pWC, idxNew1); pNewExpr2 = sqlite3ExprDup(db, pLeft, 0); pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName), pStr2); transferJoinMarkings(pNewExpr2, pExpr); idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags); testcase( idxNew2==0 ); exprAnalyze(pSrc, pWC, idxNew2); pTerm = &pWC->a[idxTerm]; if( isComplete ){ markTermAsChild(pWC, idxNew1, idxTerm); markTermAsChild(pWC, idxNew2, idxTerm); } } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ #ifndef SQLITE_OMIT_VIRTUALTABLE /* Add a WO_AUX auxiliary term to the constraint set if the ** current expression is of the form "column OP expr" where OP ** is an operator that gets passed into virtual tables but which is ** not normally optimized for ordinary tables. In other words, OP ** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL. ** This information is used by the xBestIndex methods of ** virtual tables. The native query optimizer does not attempt ** to do anything with MATCH functions. */ if( pWC->op==TK_AND ){ Expr *pRight, *pLeft; int res = isAuxiliaryVtabOperator(pExpr, &eOp2, &pLeft, &pRight); while( res-- > 0 ){ int idxNew; WhereTerm *pNewTerm; Bitmask prereqColumn, prereqExpr; prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight); prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft); if( (prereqExpr & prereqColumn)==0 ){ Expr *pNewExpr; pNewExpr = sqlite3PExpr(pParse, TK_MATCH, 0, sqlite3ExprDup(db, pRight, 0)); if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){ ExprSetProperty(pNewExpr, EP_FromJoin); } idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); pNewTerm = &pWC->a[idxNew]; pNewTerm->prereqRight = prereqExpr; pNewTerm->leftCursor = pLeft->iTable; pNewTerm->u.leftColumn = pLeft->iColumn; pNewTerm->eOperator = WO_AUX; pNewTerm->eMatchOp = eOp2; markTermAsChild(pWC, idxNew, idxTerm); pTerm = &pWC->a[idxTerm]; pTerm->wtFlags |= TERM_COPIED; pNewTerm->prereqAll = pTerm->prereqAll; } SWAP(Expr*, pLeft, pRight); } } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create ** new terms for each component comparison - "a = ?" and "b = ?". The ** new terms completely replace the original vector comparison, which is ** no longer used. ** ** This is only required if at least one side of the comparison operation ** is not a sub-select. */ if( pWC->op==TK_AND && (pExpr->op==TK_EQ || pExpr->op==TK_IS) && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1 && sqlite3ExprVectorSize(pExpr->pRight)==nLeft && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 || (pExpr->pRight->flags & EP_xIsSelect)==0) ){ int i; for(i=0; i<nLeft; i++){ int idxNew; Expr *pNew; Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i); Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i); pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight); transferJoinMarkings(pNew, pExpr); idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); exprAnalyze(pSrc, pWC, idxNew); } pTerm = &pWC->a[idxTerm]; pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */ pTerm->eOperator = 0; } |
︙ | ︙ | |||
1244 1245 1246 1247 1248 1249 1250 | Expr *pNewExpr; Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; pNewExpr = sqlite3PExpr(pParse, TK_GT, sqlite3ExprDup(db, pLeft, 0), | | > > | 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 | Expr *pNewExpr; Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; pNewExpr = sqlite3PExpr(pParse, TK_GT, sqlite3ExprDup(db, pLeft, 0), sqlite3ExprAlloc(db, TK_NULL, 0, 0)); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); if( idxNew ){ pNewTerm = &pWC->a[idxNew]; pNewTerm->prereqRight = 0; pNewTerm->leftCursor = pLeft->iTable; pNewTerm->u.leftColumn = pLeft->iColumn; pNewTerm->eOperator = WO_GT; markTermAsChild(pWC, idxNew, idxTerm); pTerm = &pWC->a[idxTerm]; pTerm->wtFlags |= TERM_COPIED; pNewTerm->prereqAll = pTerm->prereqAll; } } #endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. */ testcase( pTerm!=&pWC->a[idxTerm] ); pTerm = &pWC->a[idxTerm]; pTerm->prereqRight |= extraRight; } /*************************************************************************** ** Routines with file scope above. Interface to the rest of the where.c ** subsystem follows. ***************************************************************************/ |
︙ | ︙ | |||
1350 1351 1352 1353 1354 1355 1356 | ** a bitmask indicating which tables are used in that expression ** tree. */ Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ Bitmask mask; if( p==0 ) return 0; if( p->op==TK_COLUMN ){ | | < > | > | > | > | 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 | ** a bitmask indicating which tables are used in that expression ** tree. */ Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ Bitmask mask; if( p==0 ) return 0; if( p->op==TK_COLUMN ){ return sqlite3WhereGetMask(pMaskSet, p->iTable); } mask = (p->op==TK_IF_NULL_ROW) ? sqlite3WhereGetMask(pMaskSet, p->iTable) : 0; assert( !ExprHasProperty(p, EP_TokenOnly) ); if( p->pLeft ) mask |= sqlite3WhereExprUsage(pMaskSet, p->pLeft); if( p->pRight ){ mask |= sqlite3WhereExprUsage(pMaskSet, p->pRight); assert( p->x.pList==0 ); }else if( ExprHasProperty(p, EP_xIsSelect) ){ if( ExprHasProperty(p, EP_VarSelect) ) pMaskSet->bVarSelect = 1; mask |= exprSelectUsage(pMaskSet, p->x.pSelect); }else if( p->x.pList ){ mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList); } return mask; } Bitmask sqlite3WhereExprListUsage(WhereMaskSet *pMaskSet, ExprList *pList){ |
︙ | ︙ | |||
1428 1429 1430 1431 1432 1433 1434 | } pColRef = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); if( pColRef==0 ) return; pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; pColRef->pTab = pTab; pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, | | | 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 | } pColRef = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); if( pColRef==0 ) return; pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; pColRef->pTab = pTab; pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0)); whereClauseInsert(pWC, pTerm, TERM_DYNAMIC); } } |
Added test/affinity3.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 | # 2017-01-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. # #*********************************************************************** # # Test cases for bugs: # # https://www.sqlite.org/src/info/91e2e8ba6ff2e2 # https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf # set testdir [file dirname $argv0] source $testdir/tester.tcl # Ticket https://www.sqlite.org/src/info/91e2e8ba6ff2e2 (2011-09-19) # Automatic index causes undesired type conversions # do_execsql_test affinity3-100 { CREATE TABLE customer (id INT PRIMARY KEY); CREATE TABLE apr (id INT PRIMARY KEY, apr REAL); CREATE VIEW v1 AS SELECT c.id, i.apr FROM customer c LEFT JOIN apr i ON i.id=c.id; CREATE VIEW v2 AS SELECT c.id, v1.apr FROM customer c LEFT JOIN v1 ON v1.id=c.id; INSERT INTO customer (id) VALUES (1); INSERT INTO apr (id, apr) VALUES (1, 12); INSERT INTO customer (id) VALUES (2); INSERT INTO apr (id, apr) VALUES (2, 12.01); } do_execsql_test affinity3-110 { PRAGMA automatic_index=ON; SELECT id, (apr / 100), typeof(apr) apr_type FROM v1; } {1 0.12 real 2 0.1201 real} do_execsql_test affinity3-120 { SELECT id, (apr / 100), typeof(apr) apr_type FROM v2; } {1 0.12 real 2 0.1201 real} do_execsql_test affinity3-130 { PRAGMA automatic_index=OFF; SELECT id, (apr / 100), typeof(apr) apr_type FROM v1; } {1 0.12 real 2 0.1201 real} do_execsql_test affinity3-140 { SELECT id, (apr / 100), typeof(apr) apr_type FROM v2; } {1 0.12 real 2 0.1201 real} # Ticket https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf (2017-01-16) # Incorrect affinity when using automatic indexes # do_execsql_test affinity3-200 { CREATE TABLE map_integer (id INT, name); INSERT INTO map_integer VALUES(1,'a'); CREATE TABLE map_text (id TEXT, name); INSERT INTO map_text VALUES('4','e'); CREATE TABLE data (id TEXT, name); INSERT INTO data VALUES(1,'abc'); INSERT INTO data VALUES('4','xyz'); CREATE VIEW idmap as SELECT * FROM map_integer UNION SELECT * FROM map_text; CREATE TABLE mzed AS SELECT * FROM idmap; } do_execsql_test affinity3-210 { PRAGMA automatic_index=ON; SELECT * FROM data JOIN idmap USING(id); } {1 abc a 4 xyz e} do_execsql_test affinity3-220 { SELECT * FROM data JOIN mzed USING(id); } {1 abc a 4 xyz e} do_execsql_test affinity3-250 { PRAGMA automatic_index=OFF; SELECT * FROM data JOIN idmap USING(id); } {1 abc a 4 xyz e} do_execsql_test affinity3-260 { SELECT * FROM data JOIN mzed USING(id); } {1 abc a 4 xyz e} finish_test |
Changes to test/alter.test.
︙ | ︙ | |||
73 74 75 76 77 78 79 | CREATE $::temp TABLE objlist(type, name, tbl_name); INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master WHERE NAME!='objlist'; }] ifcapable tempdb { execsql { INSERT INTO objlist SELECT type, name, tbl_name | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | CREATE $::temp TABLE objlist(type, name, tbl_name); INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master WHERE NAME!='objlist'; }] ifcapable tempdb { execsql { INSERT INTO objlist SELECT type, name, tbl_name FROM temp.sqlite_master WHERE NAME!='objlist'; } } execsql { SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name; } } [list \ |
︙ | ︙ | |||
149 150 151 152 153 154 155 | db close sqlite3 db test.db set DB [sqlite3_connection_pointer db] execsql { CREATE TEMP TABLE objlist(type, name, tbl_name); INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master; INSERT INTO objlist | | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | db close sqlite3 db test.db set DB [sqlite3_connection_pointer db] execsql { CREATE TEMP TABLE objlist(type, name, tbl_name); INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master; INSERT INTO objlist SELECT type, name, tbl_name FROM temp.sqlite_master WHERE NAME!='objlist'; SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name; } } [list \ table -t1- -t1- \ index t1i1 -t1- \ |
︙ | ︙ | |||
520 521 522 523 524 525 526 | execsql { DROP TABLE tbl3; } } {} ifcapable tempdb { do_test alter-3.3.8 { execsql { | | | 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | execsql { DROP TABLE tbl3; } } {} ifcapable tempdb { do_test alter-3.3.8 { execsql { SELECT * FROM temp.sqlite_master WHERE type = 'trigger'; } } {} } } ;# ifcapable trigger # If the build does not include AUTOINCREMENT fields, omit alter-4.*. |
︙ | ︙ |
Changes to test/alter4.test.
︙ | ︙ | |||
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 | # do_test alter4-1.1 { execsql { CREATE TEMP TABLE abc(a, b, c); SELECT sql FROM sqlite_temp_master; } } {{CREATE TABLE abc(a, b, c)}} do_test alter4-1.2 { execsql {ALTER TABLE abc ADD d INTEGER;} execsql { SELECT sql FROM sqlite_temp_master; } } {{CREATE TABLE abc(a, b, c, d INTEGER)}} do_test alter4-1.3 { execsql {ALTER TABLE abc ADD e} execsql { SELECT sql FROM sqlite_temp_master; } } {{CREATE TABLE abc(a, b, c, d INTEGER, e)}} do_test alter4-1.4 { execsql { CREATE TABLE temp.t1(a, b); ALTER TABLE t1 ADD c; SELECT sql FROM sqlite_temp_master WHERE tbl_name = 't1'; } } {{CREATE TABLE t1(a, b, c)}} do_test alter4-1.5 { execsql { ALTER TABLE t1 ADD d CHECK (a>d); SELECT sql FROM sqlite_temp_master WHERE tbl_name = 't1'; } } {{CREATE TABLE t1(a, b, c, d CHECK (a>d))}} | > > > > > > > > > > > > > > > > > > > > | 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 | # do_test alter4-1.1 { execsql { CREATE TEMP TABLE abc(a, b, c); SELECT sql FROM sqlite_temp_master; } } {{CREATE TABLE abc(a, b, c)}} do_test alter4-1.1b { execsql { SELECT sql FROM temp.sqlite_master; } } {{CREATE TABLE abc(a, b, c)}} do_test alter4-1.2 { execsql {ALTER TABLE abc ADD d INTEGER;} execsql { SELECT sql FROM sqlite_temp_master; } } {{CREATE TABLE abc(a, b, c, d INTEGER)}} do_test alter4-1.2b { execsql { SELECT sql FROM temp.sqlite_master; } } {{CREATE TABLE abc(a, b, c, d INTEGER)}} do_test alter4-1.3 { execsql {ALTER TABLE abc ADD e} execsql { SELECT sql FROM sqlite_temp_master; } } {{CREATE TABLE abc(a, b, c, d INTEGER, e)}} do_test alter4-1.3b { execsql { SELECT sql FROM temp.sqlite_master; } } {{CREATE TABLE abc(a, b, c, d INTEGER, e)}} do_test alter4-1.4 { execsql { CREATE TABLE temp.t1(a, b); ALTER TABLE t1 ADD c; SELECT sql FROM sqlite_temp_master WHERE tbl_name = 't1'; } } {{CREATE TABLE t1(a, b, c)}} do_test alter4-1.4b { execsql { SELECT sql FROM temp.sqlite_master WHERE tbl_name = 't1'; } } {{CREATE TABLE t1(a, b, c)}} do_test alter4-1.5 { execsql { ALTER TABLE t1 ADD d CHECK (a>d); SELECT sql FROM sqlite_temp_master WHERE tbl_name = 't1'; } } {{CREATE TABLE t1(a, b, c, d CHECK (a>d))}} |
︙ | ︙ |
Changes to test/analyze3.test.
︙ | ︙ | |||
118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # do_eqp_test analyze3-1.1.2 { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}} do_eqp_test analyze3-1.1.3 { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 } {0 0 0 {SCAN TABLE t1}} do_test analyze3-1.1.4 { sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } } {199 0 14850} do_test analyze3-1.1.5 { set l [string range "200" 0 end] set u [string range "300" 0 end] | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # do_eqp_test analyze3-1.1.2 { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}} do_eqp_test analyze3-1.1.3 { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 } {0 0 0 {SCAN TABLE t1}} # 2017-06-26: Verify that the SQLITE_DBCONFIG_ENABLE_QPSG setting disables # the use of bound parameters by STAT4 # db cache flush unset -nocomplain l unset -nocomplain u do_eqp_test analyze3-1.1.3.100 { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}} set l 200 set u 300 do_eqp_test analyze3-1.1.3.101 { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}} set l 0 set u 1100 do_eqp_test analyze3-1.1.3.102 { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } {0 0 0 {SCAN TABLE t1}} db cache flush sqlite3_db_config db ENABLE_QPSG 1 do_eqp_test analyze3-1.1.3.103 { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}} db cache flush sqlite3_db_config db ENABLE_QPSG 0 do_eqp_test analyze3-1.1.3.104 { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } {0 0 0 {SCAN TABLE t1}} do_test analyze3-1.1.4 { sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } } {199 0 14850} do_test analyze3-1.1.5 { set l [string range "200" 0 end] set u [string range "300" 0 end] |
︙ | ︙ |
Changes to test/analyzeF.test.
︙ | ︙ | |||
116 117 118 119 120 121 122 123 124 | SELECT * FROM t1 WHERE x = dstr() AND y = 11; } {1 {string or blob too big}} do_catchsql_test 4.4 { SELECT * FROM t1 WHERE x = test_zeroblob(1100000) AND y = 4; } {1 {string or blob too big}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | SELECT * FROM t1 WHERE x = dstr() AND y = 11; } {1 {string or blob too big}} do_catchsql_test 4.4 { SELECT * FROM t1 WHERE x = test_zeroblob(1100000) AND y = 4; } {1 {string or blob too big}} # 2016-12-08: Constraints of the form "x=? AND x IS NOT NULL" were being # mishandled. The sqlite3Stat4ProbeSetValue() routine was assuming that # valueNew() was returning a Mem object that was preset to NULL, which is # not the case. The consequence was the the "x IS NOT NULL" constraint # was used to drive the index (via the "x>NULL" pseudo-constraint) rather # than the "x=?" constraint. # do_execsql_test 5.1 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c INT); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<10000) INSERT INTO t1(a, c) SELECT x, x FROM c; UPDATE t1 SET b=printf('x%02x',a/500) WHERE a>4000; UPDATE t1 SET b='xyz' where a>=9998; CREATE INDEX t1b ON t1(b); ANALYZE; SELECT count(*), b FROM t1 GROUP BY 2 ORDER BY 2; } {4000 {} 499 x08 500 x09 500 x0a 500 x0b 500 x0c 500 x0d 500 x0e 500 x0f 500 x10 500 x11 500 x12 498 x13 3 xyz} do_execsql_test 5.2 { explain query plan SELECT * FROM t1 WHERE b='xyz' AND b IS NOT NULL ORDER BY +a; /* v---- Should be "=", not ">" */ } {/USING INDEX t1b .b=/} do_execsql_test 5.3 { SELECT * FROM t1 WHERE b='xyz' AND b IS NOT NULL ORDER BY +a; } {9998 xyz 9998 9999 xyz 9999 10000 xyz 10000} finish_test |
Added test/atomic.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 | # 2015-11-07 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the WITH clause. # set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix atomic db close if {[atomic_batch_write test.db]==0} { puts "No f2fs atomic-batch-write support. Skipping tests..." finish_test return } reset_db do_execsql_test 1.0 { CREATE TABLE t1(x, y); BEGIN; INSERT INTO t1 VALUES(1, 2); } do_test 1.1 { file exists test.db-journal } {0} do_execsql_test 1.2 { COMMIT; } finish_test |
Changes to test/attach.test.
︙ | ︙ | |||
189 190 191 192 193 194 195 | ifcapable schema_pragmas { do_test attach-1.20.2 { db_list db } {0 main 2 db2 3 db3 4 db4 5 db6 6 db7 7 db8 8 db9 9 db10 10 db11} } ;# ifcapable schema_pragmas integrity_check attach-1.20.3 ifcapable tempdb { | | | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | ifcapable schema_pragmas { do_test attach-1.20.2 { db_list db } {0 main 2 db2 3 db3 4 db4 5 db6 6 db7 7 db8 8 db9 9 db10 10 db11} } ;# ifcapable schema_pragmas integrity_check attach-1.20.3 ifcapable tempdb { execsql {select * from temp.sqlite_master} } do_test attach-1.21 { catchsql { ATTACH 'test.db' as db12; } } {0 {}} if {$SQLITE_MAX_ATTACHED==10} { |
︙ | ︙ | |||
788 789 790 791 792 793 794 | do_test attach-8.1 { set fd [open test2.db w] puts $fd "This file is not a valid SQLite database" close $fd catchsql { ATTACH 'test2.db' AS t2; } | | | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 | do_test attach-8.1 { set fd [open test2.db w] puts $fd "This file is not a valid SQLite database" close $fd catchsql { ATTACH 'test2.db' AS t2; } } {1 {file is not a database}} do_test attach-8.2 { db errorcode } {26} forcedelete test2.db do_test attach-8.3 { sqlite3 db2 test2.db db2 eval {CREATE TABLE t1(x); BEGIN EXCLUSIVE} |
︙ | ︙ | |||
866 867 868 869 870 871 872 | do_execsql_test attach-11.1 { ATTACH printf('file:%09000x/x.db?mode=memory&cache=shared',1) AS aux1; CREATE TABLE aux1.t1(x,y); INSERT INTO aux1.t1(x,y) VALUES(1,2),(3,4); SELECT * FROM aux1.t1; } {1 2 3 4} | > > > | > > > > > > > > > > > > > | 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 | do_execsql_test attach-11.1 { ATTACH printf('file:%09000x/x.db?mode=memory&cache=shared',1) AS aux1; CREATE TABLE aux1.t1(x,y); INSERT INTO aux1.t1(x,y) VALUES(1,2),(3,4); SELECT * FROM aux1.t1; } {1 2 3 4} # Ticket https://sqlite.org/src/tktview/a4e06e75a9ab61a1 2017-07-15 # False positive when running integrity_check on a connection with # attached databases. # db close sqlite3 db :memory: do_execsql_test attach-12.1 { CREATE TABLE Table1 (col TEXT NOT NULL PRIMARY KEY); ATTACH ':memory:' AS db2; CREATE TABLE db2.Table2(col1 INTEGER, col2 INTEGER, col3 INTEGER, col4); CREATE UNIQUE INDEX db2.idx_col1_unique ON Table2 (col1); CREATE UNIQUE INDEX db2.idx_col23_unique ON Table2 (col2, col3); CREATE INDEX db2.idx_col2 ON Table2 (col2); INSERT INTO Table2 VALUES(1,2,3,4); PRAGMA integrity_check; } {ok} finish_test |
Changes to test/attach2.test.
︙ | ︙ | |||
370 371 372 373 374 375 376 377 | execsql { BEGIN; } } {} do_test attach2-6.2 { catchsql { ATTACH 'test3.db' as aux2; } | > | < < < < < < | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | execsql { BEGIN; } } {} do_test attach2-6.2 { catchsql { ATTACH 'test3.db' as aux2; DETACH aux2; } } {0 {}} # EVIDENCE-OF: R-59740-55581 This statement will fail if SQLite is in # the middle of a transaction. # do_test attach2-6.3 { catchsql { DETACH aux; } } {0 {}} db close finish_test |
Changes to test/attach3.test.
︙ | ︙ | |||
203 204 205 206 207 208 209 | do_test attach3-9.0 { execsql { CREATE TABLE main.t4(a, b, c); CREATE TABLE aux.t4(a, b, c); CREATE TEMP TRIGGER tst_trigger BEFORE INSERT ON aux.t4 BEGIN SELECT 'hello world'; END; | | | | 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 | do_test attach3-9.0 { execsql { CREATE TABLE main.t4(a, b, c); CREATE TABLE aux.t4(a, b, c); CREATE TEMP TRIGGER tst_trigger BEFORE INSERT ON aux.t4 BEGIN SELECT 'hello world'; END; SELECT count(*) FROM temp.sqlite_master; } } {1} do_test attach3-9.1 { execsql { DROP TABLE main.t4; SELECT count(*) FROM sqlite_temp_master; } } {1} do_test attach3-9.2 { execsql { DROP TABLE aux.t4; SELECT count(*) FROM temp.sqlite_master; } } {0} } } ;# endif trigger # Make sure the aux.sqlite_master table is read-only do_test attach3-10.0 { |
︙ | ︙ |
Changes to test/auth.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 | db authorizer ::auth } } do_test auth-1.1.1 { db close set ::DB [sqlite3 db test.db] proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} { return SQLITE_DENY } return SQLITE_OK } db authorizer ::auth catchsql {CREATE TABLE t1(a,b,c)} } {1 {not authorized}} do_test auth-1.1.2 { db errorcode } {23} do_test auth-1.1.3 { db authorizer } {::auth} do_test auth-1.1.4 { # Ticket #896. catchsql { SELECT x; } } {1 {no such column: x}} do_test auth-1.2 { execsql {SELECT name FROM sqlite_master} } {} do_test auth-1.3.1 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_CREATE_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK | > > > > > > > > > > > | 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 | db authorizer ::auth } } do_test auth-1.1.1 { db close set ::DB [sqlite3 db test.db] proc authx {code arg1 arg2 arg3 arg4 args} {return SQLITE_DENY} proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} { return SQLITE_DENY } return SQLITE_OK } db authorizer ::authx # EVIDENCE-OF: R-03993-24285 Only a single authorizer can be in place on # a database connection at a time. Each call to sqlite3_set_authorizer # overrides the previous call. # # The authx authorizer above is overridden by the auth authorizer below # authx is never invoked. db authorizer ::auth catchsql {CREATE TABLE t1(a,b,c)} } {1 {not authorized}} do_test auth-1.1.2 { db errorcode } {23} do_test auth-1.1.3 { db authorizer } {::auth} do_test auth-1.1.4 { # Ticket #896. catchsql { SELECT x; } } {1 {no such column: x}} do_test auth-1.2 { execsql {SELECT name FROM sqlite_master} } {} # EVIDENCE-OF: R-04452-49349 When the callback returns SQLITE_DENY, the # sqlite3_prepare_v2() or equivalent call that triggered the authorizer # will fail with an error message explaining that access is denied. do_test auth-1.3.1 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_CREATE_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK |
︙ | ︙ | |||
87 88 89 90 91 92 93 | return SQLITE_DENY } return SQLITE_OK } catchsql {CREATE TEMP TABLE t1(a,b,c)} } {1 {not authorized}} do_test auth-1.6 { | | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | return SQLITE_DENY } return SQLITE_OK } catchsql {CREATE TEMP TABLE t1(a,b,c)} } {1 {not authorized}} do_test auth-1.6 { execsql {SELECT name FROM temp.sqlite_master} } {} do_test auth-1.7.1 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_CREATE_TEMP_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } |
︙ | ︙ | |||
144 145 146 147 148 149 150 | return SQLITE_IGNORE } return SQLITE_OK } catchsql {CREATE TEMP TABLE t1(a,b,c)} } {0 {}} do_test auth-1.14 { | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | return SQLITE_IGNORE } return SQLITE_OK } catchsql {CREATE TEMP TABLE t1(a,b,c)} } {0 {}} do_test auth-1.14 { execsql {SELECT name FROM temp.sqlite_master} } {} do_test auth-1.15 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_CREATE_TEMP_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_IGNORE } |
︙ | ︙ | |||
308 309 310 311 312 313 314 315 316 317 318 319 320 321 | ifcapable attach { do_test auth-1.35.2 { execsql {ATTACH DATABASE 'test.db' AS two} catchsql {SELECT * FROM two.t2} } {1 {access to two.t2.b is prohibited}} execsql {DETACH DATABASE two} } do_test auth-1.36 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="b"} { return SQLITE_IGNORE } return SQLITE_OK } | > > > > | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | ifcapable attach { do_test auth-1.35.2 { execsql {ATTACH DATABASE 'test.db' AS two} catchsql {SELECT * FROM two.t2} } {1 {access to two.t2.b is prohibited}} execsql {DETACH DATABASE two} } # EVIDENCE-OF: R-38392-49970 If the action code is SQLITE_READ and the # callback returns SQLITE_IGNORE then the prepared statement statement # is constructed to substitute a NULL value in place of the table column # that would have been read if SQLITE_OK had been returned. do_test auth-1.36 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="b"} { return SQLITE_IGNORE } return SQLITE_OK } |
︙ | ︙ | |||
557 558 559 560 561 562 563 | return SQLITE_IGNORE } return SQLITE_OK } catchsql {DROP TABLE t1} } {0 {}} do_test auth-1.78 { | | | 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 | return SQLITE_IGNORE } return SQLITE_OK } catchsql {DROP TABLE t1} } {0 {}} do_test auth-1.78 { execsql {SELECT name FROM temp.sqlite_master} } {t1} } # Test cases auth-1.79 to auth-1.124 test creating and dropping views. # Omit these if the library was compiled with views omitted. ifcapable view { do_test auth-1.79 { |
︙ | ︙ | |||
628 629 630 631 632 633 634 | } catchsql {CREATE TEMPORARY VIEW v1 AS SELECT a+1,b+1 FROM t2} } {0 {}} do_test auth-1.89 { set ::authargs } {v1 {} temp {}} do_test auth-1.90 { | | | 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 | } catchsql {CREATE TEMPORARY VIEW v1 AS SELECT a+1,b+1 FROM t2} } {0 {}} do_test auth-1.89 { set ::authargs } {v1 {} temp {}} do_test auth-1.90 { execsql {SELECT name FROM temp.sqlite_master} } {t1} } do_test auth-1.91 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} { return SQLITE_DENY |
︙ | ︙ | |||
775 776 777 778 779 780 781 | } catchsql { CREATE TEMP VIEW v1 AS SELECT a+1,b+1 FROM t1; DROP VIEW v1 } } {1 {not authorized}} do_test auth-1.113 { | | | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 | } catchsql { CREATE TEMP VIEW v1 AS SELECT a+1,b+1 FROM t1; DROP VIEW v1 } } {1 {not authorized}} do_test auth-1.113 { execsql {SELECT name FROM temp.sqlite_master} } {t1 v1} do_test auth-1.114 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TEMP_VIEW"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } |
︙ | ︙ | |||
819 820 821 822 823 824 825 | } catchsql {DROP VIEW v1} } {0 {}} do_test auth-1.120 { set ::authargs } {v1 {} temp {}} do_test auth-1.121 { | | | 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 | } catchsql {DROP VIEW v1} } {0 {}} do_test auth-1.120 { set ::authargs } {v1 {} temp {}} do_test auth-1.121 { execsql {SELECT name FROM temp.sqlite_master} } {t1 v1} do_test auth-1.122 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TEMP_VIEW"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_OK } |
︙ | ︙ | |||
976 977 978 979 980 981 982 | END; } } {1 {not authorized}} do_test auth-1.139 { set ::authargs } {r1 t1 temp {}} do_test auth-1.140 { | | | 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 | END; } } {1 {not authorized}} do_test auth-1.139 { set ::authargs } {r1 t1 temp {}} do_test auth-1.140 { execsql {SELECT name FROM temp.sqlite_master} } {t1} do_test auth-1.141 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} { return SQLITE_DENY } return SQLITE_OK |
︙ | ︙ | |||
1012 1013 1014 1015 1016 1017 1018 | END; } } {0 {}} do_test auth-1.144 { set ::authargs } {r1 t1 temp {}} do_test auth-1.145 { | | | 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | END; } } {0 {}} do_test auth-1.144 { set ::authargs } {r1 t1 temp {}} do_test auth-1.145 { execsql {SELECT name FROM temp.sqlite_master} } {t1} do_test auth-1.146 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} { return SQLITE_IGNORE } return SQLITE_OK |
︙ | ︙ | |||
1048 1049 1050 1051 1052 1053 1054 | END; } } {0 {}} do_test auth-1.149 { set ::authargs } {r1 t1 temp {}} do_test auth-1.150 { | | | 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 | END; } } {0 {}} do_test auth-1.149 { set ::authargs } {r1 t1 temp {}} do_test auth-1.150 { execsql {SELECT name FROM temp.sqlite_master} } {t1 r1} do_test auth-1.151 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} { return SQLITE_DENY } |
︙ | ︙ | |||
1138 1139 1140 1141 1142 1143 1144 | return SQLITE_DENY } return SQLITE_OK } catchsql {DROP TRIGGER r1} } {1 {not authorized}} do_test auth-1.165 { | | | 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 | return SQLITE_DENY } return SQLITE_OK } catchsql {DROP TRIGGER r1} } {1 {not authorized}} do_test auth-1.165 { execsql {SELECT name FROM temp.sqlite_master} } {t1 r1} do_test auth-1.166 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TEMP_TRIGGER"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } |
︙ | ︙ | |||
1166 1167 1168 1169 1170 1171 1172 | return SQLITE_IGNORE } return SQLITE_OK } catchsql {DROP TRIGGER r1} } {0 {}} do_test auth-1.170 { | | | 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 | return SQLITE_IGNORE } return SQLITE_OK } catchsql {DROP TRIGGER r1} } {0 {}} do_test auth-1.170 { execsql {SELECT name FROM temp.sqlite_master} } {t1 r1} do_test auth-1.171 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TEMP_TRIGGER"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_IGNORE } |
︙ | ︙ | |||
1198 1199 1200 1201 1202 1203 1204 | } catchsql {DROP TRIGGER r1} } {0 {}} do_test auth-1.175 { set ::authargs } {r1 t1 temp {}} do_test auth-1.176 { | | | 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 | } catchsql {DROP TRIGGER r1} } {0 {}} do_test auth-1.175 { set ::authargs } {r1 t1 temp {}} do_test auth-1.176 { execsql {SELECT name FROM temp.sqlite_master} } {t1} } ;# ifcapable trigger do_test auth-1.177 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_CREATE_INDEX"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] |
︙ | ︙ | |||
1302 1303 1304 1305 1306 1307 1308 | return SQLITE_DENY } return SQLITE_OK } catchsql {CREATE INDEX i1 ON t1(b)} } {1 {not authorized}} do_test auth-1.194 { | | | 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 | return SQLITE_DENY } return SQLITE_OK } catchsql {CREATE INDEX i1 ON t1(b)} } {1 {not authorized}} do_test auth-1.194 { execsql {SELECT name FROM temp.sqlite_master} } {t1} do_test auth-1.195 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_CREATE_TEMP_INDEX"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_IGNORE } |
︙ | ︙ | |||
1346 1347 1348 1349 1350 1351 1352 | } catchsql {CREATE INDEX i1 ON t1(a)} } {0 {}} do_test auth-1.201 { set ::authargs } {i1 t1 temp {}} do_test auth-1.202 { | | | 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 | } catchsql {CREATE INDEX i1 ON t1(a)} } {0 {}} do_test auth-1.201 { set ::authargs } {i1 t1 temp {}} do_test auth-1.202 { execsql {SELECT name FROM temp.sqlite_master} } {t1 i1} } do_test auth-1.203 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} { return SQLITE_DENY |
︙ | ︙ | |||
1462 1463 1464 1465 1466 1467 1468 | return SQLITE_IGNORE } return SQLITE_OK } catchsql {DROP INDEX i1} } {0 {}} do_test auth-1.222 { | | | | | 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 | return SQLITE_IGNORE } return SQLITE_OK } catchsql {DROP INDEX i1} } {0 {}} do_test auth-1.222 { execsql {SELECT name FROM temp.sqlite_master} } {t1 i1} do_test auth-1.223 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TEMP_INDEX"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_IGNORE } return SQLITE_OK } catchsql {DROP INDEX i1} } {0 {}} do_test auth-1.224 { set ::authargs } {i1 t1 temp {}} do_test auth-1.225 { execsql {SELECT name FROM temp.sqlite_master} } {t1 i1} do_test auth-1.226 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TEMP_INDEX"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_OK } return SQLITE_OK } catchsql {DROP INDEX i1} } {0 {}} do_test auth-1.227 { set ::authargs } {i1 t1 temp {}} do_test auth-1.228 { execsql {SELECT name FROM temp.sqlite_master} } {t1} } do_test auth-1.229 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_PRAGMA"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] |
︙ | ︙ | |||
1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 | do_test auth-1.247 { catchsql {END TRANSACTION} } {1 {not authorized}} do_test auth-1.248 { set ::authargs } {COMMIT {} {} {}} do_test auth-1.249 { db authorizer {} catchsql {ROLLBACK} } {0 {}} do_test auth-1.250 { execsql {SELECT * FROM t2} } {11 2 33 7 8 9} | > > | 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 | do_test auth-1.247 { catchsql {END TRANSACTION} } {1 {not authorized}} do_test auth-1.248 { set ::authargs } {COMMIT {} {} {}} do_test auth-1.249 { # EVIDENCE-OF: R-52112-44167 Disable the authorizer by installing a NULL # callback. db authorizer {} catchsql {ROLLBACK} } {0 {}} do_test auth-1.250 { execsql {SELECT * FROM t2} } {11 2 33 7 8 9} |
︙ | ︙ | |||
1761 1762 1763 1764 1765 1766 1767 | return SQLITE_OK } catchsql { ALTER TABLE t1x RENAME TO t1 } } {0 {}} do_test auth-1.267 { | | | 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 | return SQLITE_OK } catchsql { ALTER TABLE t1x RENAME TO t1 } } {0 {}} do_test auth-1.267 { execsql {SELECT name FROM temp.sqlite_master WHERE type='table'} } {t1x} do_test auth-1.268 { set authargs } {temp t1x {} {}} do_test auth-1.269 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_ALTER_TABLE"} { |
︙ | ︙ | |||
2066 2067 2068 2069 2070 2071 2072 | return SQLITE_OK } catchsql { ALTER TABLE t5 ADD COLUMN new_col_3 } } {1 {not authorized}} do_test auth-1.307 { | | | 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 | return SQLITE_OK } catchsql { ALTER TABLE t5 ADD COLUMN new_col_3 } } {1 {not authorized}} do_test auth-1.307 { set x [execsql {SELECT sql FROM temp.sqlite_master WHERE type='t5'}] regexp new_col_3 $x } {0} do_test auth-1.308 { set authargs } {main t5 {} {}} execsql {DROP TABLE t5} |
︙ | ︙ | |||
2369 2370 2371 2372 2373 2374 2375 | } else { set stat4 "" } } do_test auth-5.2 { execsql { SELECT name FROM ( | | | 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 | } else { set stat4 "" } } do_test auth-5.2 { execsql { SELECT name FROM ( SELECT * FROM sqlite_master UNION ALL SELECT * FROM temp.sqlite_master) WHERE type='table' ORDER BY name } } "sqlite_stat1 ${stat4}t1 t2 t3 t4" } # Ticket #3944 |
︙ | ︙ | |||
2473 2474 2475 2476 2477 2478 2479 2480 | execsql {SELECT a, c FROM t7 AS v7} set ::authargs } [list \ SQLITE_SELECT {} {} {} {} \ SQLITE_READ t7 a main {} \ SQLITE_READ t7 c main {} \ ] | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | execsql {SELECT a, c FROM t7 AS v7} set ::authargs } [list \ SQLITE_SELECT {} {} {} {} \ SQLITE_READ t7 a main {} \ SQLITE_READ t7 c main {} \ ] # If a table is referenced but no columns are read from the table, # that causes a single SQLITE_READ authorization with a NULL column # name. # # EVIDENCE-OF: R-31520-16302 When a table is referenced by a SELECT but # no column values are extracted from that table (for example in a query # like "SELECT count(*) FROM tab") then the SQLITE_READ authorizer # callback is invoked once for that table with a column name that is an # empty string. # set ::authargs [list] do_test auth-8.1 { execsql {SELECT count(*) FROM t7} set ::authargs } [list \ SQLITE_SELECT {} {} {} {} \ SQLITE_FUNCTION {} count {} {} \ SQLITE_READ t7 {} {} {} \ ] set ::authargs [list] do_test auth-8.2 { execsql {SELECT t6.a FROM t6, t7} set ::authargs } [list \ SQLITE_SELECT {} {} {} {} \ SQLITE_READ t6 a main {} \ SQLITE_READ t7 {} {} {} \ ] # Test also that if SQLITE_DENY is returned from an SQLITE_READ authorizer # invocation with no column name specified, compilation fails. # set ::authargs [list] proc auth {op a b c d} { lappend ::authargs $op $a $b $c $d if {$op == "SQLITE_READ"} { return "SQLITE_DENY" } return "SQLITE_OK" } set ::authargs [list] do_catchsql_test auth-8.3 { SELECT count(*) FROM t7 } {1 {not authorized}} do_test auth-8.4 { set ::authargs } [list \ SQLITE_SELECT {} {} {} {} \ SQLITE_FUNCTION {} count {} {} \ SQLITE_READ t7 {} {} {} \ ] rename proc {} rename proc_real proc finish_test |
Changes to test/auth3.test.
︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 62 | INSERT INTO t1 VALUES(4, 5, 6); } } {} do_test auth3.1.2 { set ::authcode SQLITE_DENY catchsql { DELETE FROM t1 } } {1 {not authorized}} do_test auth3.1.3 { set ::authcode SQLITE_INVALID catchsql { DELETE FROM t1 } } {1 {authorizer malfunction}} do_test auth3.1.4 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6} | > > > > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | INSERT INTO t1 VALUES(4, 5, 6); } } {} do_test auth3.1.2 { set ::authcode SQLITE_DENY catchsql { DELETE FROM t1 } } {1 {not authorized}} # EVIDENCE-OF: R-64962-58611 If the authorizer callback returns any # value other than SQLITE_IGNORE, SQLITE_OK, or SQLITE_DENY then the # sqlite3_prepare_v2() or equivalent call that triggered the authorizer # will fail with an error message. do_test auth3.1.3 { set ::authcode SQLITE_INVALID catchsql { DELETE FROM t1 } } {1 {authorizer malfunction}} do_test auth3.1.4 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6} |
︙ | ︙ | |||
118 119 120 121 122 123 124 | return SQLITE_OK } do_execsql_test auth3-3.0 { CREATE TEMPORARY TABLE TempTable ( key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL); ALTER TABLE TempTable RENAME TO DoNotRead; | | | 122 123 124 125 126 127 128 129 130 131 132 | return SQLITE_OK } do_execsql_test auth3-3.0 { CREATE TEMPORARY TABLE TempTable ( key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL); ALTER TABLE TempTable RENAME TO DoNotRead; SELECT name FROM temp.sqlite_master; } {DoNotRead sqlite_autoindex_DoNotRead_1} finish_test |
Added test/autoanalyze1.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 | # 2017-02-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. # #*********************************************************************** # # This file implements tests for the logic used to estimate when # running ANALYZE would be beneficial. # # Note that this test uses some hard-coded bitmask values from sqliteInt.h. # If any of the following constants changes: # # define TF_HasStat1 0x0010 # define TF_StatsUsed 0x0100 # # then some of the magic numbers in test results below might need to be # adjusted. # set testdir [file dirname $argv0] source $testdir/tester.tcl # There is nothing to test if ANALYZE is disable for this build. # These tests also use "PRAGMA stats" which are only enabled for # debugging builds. # ifcapable {!debug || !analyze || !vtab} { finish_test return } do_execsql_test autoanalyze1-100 { -- Build up a test table with some indexes CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d); CREATE UNIQUE INDEX t1bc ON t1(b,c); CREATE INDEX t1d ON t1(d); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) INSERT INTO t1(a,b,c,d) SELECT x, x, x, x FROM c; -- Verify that the hasStat1 flag is clear on on indexes SELECT idx, flgs FROM pragma_stats WHERE idx IS NOT NULL ORDER BY idx; -- Verify that the TF_HasStat1 flag is clear on the table SELECT tbl, (flgs & 0x10)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {t1bc 0 t1d 0 t1 0} # No use of stat1 recorded so far do_execsql_test autoanalyze1-110 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {0} # Access using a unique index does not set the TF_StatsUsed flag. # do_execsql_test autoanalyze1-200 { SELECT * FROM t1 WHERE a=55; } {55 55 55 55} do_execsql_test autoanalyze1-201 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {0} do_execsql_test autoanalyze1-210 { SELECT * FROM t1 WHERE a IN (55,199,299); } {55 55 55 55} do_execsql_test autoanalyze1-211 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {0} do_execsql_test autoanalyze1-220 { SELECT * FROM t1 WHERE (b,c)=(45,45); } {45 45 45 45} do_execsql_test autoanalyze1-221 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {0} # Any use of the non-unique t1d index triggers the use of stats. # sqlite3 db test.db do_execsql_test autoanalyze1-300 { SELECT * FROM t1 WHERE d=45; } {45 45 45 45} do_execsql_test autoanalyze1-301 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {1} sqlite3 db test.db do_execsql_test autoanalyze1-310 { SELECT * FROM t1 WHERE d=45 AND a=45; } {45 45 45 45} do_execsql_test autoanalyze1-311 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {0} ;# The ROWID lookup short-circuits the d=45 constraint sqlite3 db test.db do_execsql_test autoanalyze1-320 { SELECT * FROM t1 WHERE d=45 AND a IN (45,46); } {45 45 45 45} do_execsql_test autoanalyze1-321 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {1} # Any use of prefix of a unique index triggers the use of stats # sqlite3 db test.db do_execsql_test autoanalyze1-400 { SELECT * FROM t1 WHERE b=45; } {45 45 45 45} do_execsql_test autoanalyze1-401 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {1} # The TF_StatsUsed flag is reset when the database is reopened # sqlite3 db test.db do_execsql_test autoanalyze1-500 { SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL; } {0} finish_test |
Changes to test/autoinc.test.
︙ | ︙ | |||
340 341 342 343 344 345 346 | # AUTOINCREMENT on TEMP tables. # ifcapable tempdb { do_test autoinc-4.1 { execsql { SELECT 1, name FROM sqlite_master WHERE type='table'; | | | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | # AUTOINCREMENT on TEMP tables. # ifcapable tempdb { do_test autoinc-4.1 { execsql { SELECT 1, name FROM sqlite_master WHERE type='table'; SELECT 2, name FROM temp.sqlite_master WHERE type='table'; } } {1 sqlite_sequence} do_test autoinc-4.2 { execsql { CREATE TABLE t1(x INTEGER PRIMARY KEY AUTOINCREMENT, y); CREATE TEMP TABLE t3(a INTEGER PRIMARY KEY AUTOINCREMENT, b); SELECT 1, name FROM sqlite_master WHERE type='table'; |
︙ | ︙ |
Changes to test/autoindex1.test.
︙ | ︙ | |||
515 516 517 518 519 520 521 522 523 | # 2015-04-15: A NULL CollSeq pointer in automatic index creation. # do_execsql_test autoindex1-920 { CREATE TABLE t920(x); INSERT INTO t920 VALUES(3),(4),(5); SELECT * FROM t920,(SELECT 0 FROM t920),(VALUES(9)) WHERE 5 IN (x); } {5 0 9 5 0 9 5 0 9} finish_test | > > > > > > > > > > > > > > > > > > | 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 | # 2015-04-15: A NULL CollSeq pointer in automatic index creation. # do_execsql_test autoindex1-920 { CREATE TABLE t920(x); INSERT INTO t920 VALUES(3),(4),(5); SELECT * FROM t920,(SELECT 0 FROM t920),(VALUES(9)) WHERE 5 IN (x); } {5 0 9 5 0 9 5 0 9} #------------------------------------------------------------------------- # An IS term from the WHERE clause of a LEFT JOIN cannot be used as an # index driver for the RHS of a LEFT JOIN. Prior to this being fixed, # the following SELECT count(*) would incorrectly return 1. # do_execsql_test autoindex1-1010 { CREATE TABLE t11(w); CREATE TABLE t12(y); INSERT INTO t11 VALUES(NULL); INSERT INTO t12 VALUES('notnull'); } do_execsql_test autoindex1-1020 { SELECT count(*) FROM t11 LEFT JOIN t12 WHERE t12.y IS t11.w; } 0 finish_test |
Changes to test/autovacuum.test.
︙ | ︙ | |||
701 702 703 704 705 706 707 708 709 | execsql { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 } } {} do_test autovacuum-9.5 { execsql { DELETE FROM t1 WHERE rowid > (SELECT max(a)/2 FROM t1) } file size test.db } $::sqlite_pending_byte finish_test | > > > > > > > | 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 | execsql { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 } } {} do_test autovacuum-9.5 { execsql { DELETE FROM t1 WHERE rowid > (SELECT max(a)/2 FROM t1) } file size test.db } $::sqlite_pending_byte do_execsql_test autovacuum-10.1 { DROP TABLE t1; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(25, randomblob(104)); REPLACE INTO t1 VALUES(25, randomblob(1117)); PRAGMA integrity_check; } {ok} finish_test |
Changes to test/backup2.test.
︙ | ︙ | |||
118 119 120 121 122 123 124 | catch {file attributes bu2.db -readonly 0} catch {file attributes bu2.db -permissions rw-------} set out [open bu2.db w] puts $out "This is not a valid database file" close $out set rc [catch {db backup temp bu2.db} res] lappend rc $res | | | | | | 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 | catch {file attributes bu2.db -readonly 0} catch {file attributes bu2.db -permissions rw-------} set out [open bu2.db w] puts $out "This is not a valid database file" close $out set rc [catch {db backup temp bu2.db} res] lappend rc $res } {1 {backup failed: file is not a database}} # Try to backup database that does not exist # do_test backup2-8 { forcedelete bu1.db set rc [catch {db backup aux1 bu1.db} res] lappend rc $res } {1 {backup failed: unknown database aux1}} # Invalid syntax on the backup method # do_test backup2-9 { set rc [catch {db backup} res] lappend rc $res } {1 {wrong # args: should be "db backup ?DATABASE? FILENAME"}} # Try to restore from an unreadable file. # if {$tcl_platform(platform)=="windows"} { set msg {cannot open source database: unable to open database file} } elseif {[string match *BSD $tcl_platform(os)]} { set msg {restore failed: file is not a database} } else { set msg {cannot open source database: disk I/O error} } do_test backup2-10 { forcedelete bu3.db file mkdir bu3.db set rc [catch {db restore temp bu3.db} res] lappend rc $res } [list 1 $msg] # Try to restore from something that is not a database file. # do_test backup2-11 { set rc [catch {db restore temp bu2.db} res] lappend rc $res } {1 {restore failed: file is not a database}} # Try to restore a database that does not exist # do_test backup2-12 { set rc [catch {db restore aux1 bu2.db} res] lappend rc $res } {1 {restore failed: unknown database aux1}} |
︙ | ︙ |
Changes to test/bestindex3.test.
︙ | ︙ | |||
168 169 170 171 172 173 174 | } } do_execsql_test 3.1 { CREATE VIRTUAL TABLE t3 USING tcl('vvv_command') } do_execsql_test 3.2 { CREATE VIRTUAL TABLE t4 USING tcl('yyy_command') } finish_test | < | 168 169 170 171 172 173 174 | } } do_execsql_test 3.1 { CREATE VIRTUAL TABLE t3 USING tcl('vvv_command') } do_execsql_test 3.2 { CREATE VIRTUAL TABLE t4 USING tcl('yyy_command') } finish_test |
Added test/bestindex4.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 | # 2016 November 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. # #*********************************************************************** # Test the virtual table interface. In particular the xBestIndex # method. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix bestindex4 ifcapable !vtab { finish_test return } #------------------------------------------------------------------------- # Virtual table callback for a virtual table named $tbl. # # The table created is: # # "CREATE TABLE t1 (id, host, class)" # # The virtual table supports == operators on a subset of its columns. The # exact subset depends on the value of bitmask paramater $param. # # 0x01 - == on "id" supported # 0x02 - == on "host" supported # 0x04 - == on "class" supported # # $param also supports the following bits: # # 0x08 - ignore the "usable" flag (malfunction) # # # proc vtab_cmd {param method args} { switch -- $method { xConnect { return "CREATE TABLE t1(id TEXT, host TEXT, class TEXT)" } xBestIndex { foreach {clist orderby mask} $args {} set ret [list] set use use for {set i 0} {$i < [llength $clist]} {incr i} { array unset C array set C [lindex $clist $i] if { ($C(usable) || ($param & 0x08)) && $C(op)=="eq" && ($param & 1<<$C(column)) } { lappend ret $use $i break } } set score 1000000 if {$ret!=""} { set score [expr $score / [llength $ret]] } lappend ret cost $score rows $score return $ret } xFilter { } } return "" } register_tcl_module db for {set param1 0} {$param1<16} {incr param1} { for {set param2 0} {$param2<16} {incr param2} { reset_db register_tcl_module db do_execsql_test 1.$param1.$param2.1 " CREATE VIRTUAL TABLE t1 USING tcl('vtab_cmd $param1'); CREATE VIRTUAL TABLE t2 USING tcl('vtab_cmd $param2'); " foreach {tn sql} { 2 "select t1.id as ID from t1, t2 where t1.id=t2.host and t2.class='xx'" 3 { select t1.id as ID from t1, t2 where t2.class ='xx' and t2.id = t1.host } 4 { select t1.id as ID from t1, t2 where t1.host = t2.id and t2. class ='xx' } } { if {($param1 & 0x08)==0 && ($param2 & 0x08)==0} { do_execsql_test 1.$param1.$param2.$tn.a $sql {} } else { do_test 1.$param1.$param2.$tn.b { catchsql $sql set {} {} } {} } } } } finish_test |
Added test/bestindex5.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 | # 2017 September 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. # #*********************************************************************** # Test the virtual table interface. In particular the xBestIndex # method. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix bestindex4 ifcapable !vtab { finish_test return } #------------------------------------------------------------------------- # Virtual table callback for a virtual table named $tbl. # proc vtab_cmd {method args} { set binops(ne) != set binops(eq) = set binops(isnot) "IS NOT" set binops(is) "IS" set unops(isnotnull) "IS NOT NULL" set unops(isnull) "IS NULL" set cols(0) a set cols(1) b set cols(2) c switch -- $method { xConnect { return "CREATE TABLE t1(a, b, c)" } xBestIndex { foreach {clist orderby mask} $args {} set cost 1000000.0 set ret [list] set str [list] set v 0 for {set i 0} {$i < [llength $clist]} {incr i} { array unset C array set C [lindex $clist $i] if {$C(usable)} { if {[info exists binops($C(op))]} { lappend ret omit $i lappend str "$cols($C(column)) $binops($C(op)) %$v%" incr v set cost [expr $cost / 2] } if {[info exists unops($C(op))]} { lappend ret omit $i lappend str "$cols($C(column)) $unops($C(op))" incr v set cost [expr $cost / 2] } } } lappend ret idxstr [join $str " AND "] lappend ret cost $cost return $ret } xFilter { set q [lindex $args 1] set a [lindex $args 2] for {set v 0} {$v < [llength $a]} {incr v} { set val [lindex $a $v] set q [string map [list %$v% '$val'] $q] } if {$q==""} { set q 1 } lappend ::xFilterQueries "WHERE $q" return [list sql "SELECT rowid, * FROM t1x WHERE $q"] } } return "" } proc vtab_simple {method args} { switch -- $method { xConnect { return "CREATE TABLE t2(x)" } xBestIndex { return [list cost 999999.0] } xFilter { return [list sql "SELECT rowid, * FROM t2x"] } } return "" } register_tcl_module db proc do_vtab_query_test {tn query result} { set ::xFilterQueries [list] uplevel [list do_test $tn [string map [list %QUERY% $query] { set r [execsql {%QUERY%}] set r [concat $::xFilterQueries $r] set r }] [list {*}$result] ] } do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING tcl('vtab_cmd'); CREATE TABLE t1x(a INTEGER, b TEXT, c REAL); INSERT INTO t1x VALUES(1, 2, 3); INSERT INTO t1x VALUES(4, 5, 6); INSERT INTO t1x VALUES(7, 8, 9); CREATE VIRTUAL TABLE t2 USING tcl('vtab_simple'); CREATE TABLE t2x(x INTEGER); INSERT INTO t2x VALUES(1); } do_vtab_query_test 1.1 { SELECT * FROM t1 WHERE a!='hello'; } { "WHERE a != 'hello'" 1 2 3.0 4 5 6.0 7 8 9.0 } do_vtab_query_test 1.2.1 { SELECT * FROM t1 WHERE b!=8 } { "WHERE b != '8'" 1 2 3.0 4 5 6.0 } do_vtab_query_test 1.2.2 { SELECT * FROM t1 WHERE 8!=b } { "WHERE b != '8'" 1 2 3.0 4 5 6.0 } do_vtab_query_test 1.3 { SELECT * FROM t1 WHERE c IS NOT 3 } { "WHERE c IS NOT '3'" 4 5 6.0 7 8 9.0 } do_vtab_query_test 1.3.2 { SELECT * FROM t1 WHERE 3 IS NOT c } { "WHERE c IS NOT '3'" 4 5 6.0 7 8 9.0 } do_vtab_query_test 1.4.1 { SELECT * FROM t1, t2 WHERE x != a } { "WHERE a != '1'" 4 5 6.0 1 7 8 9.0 1 } do_vtab_query_test 1.4.2 { SELECT * FROM t1, t2 WHERE a != x } { "WHERE a != '1'" 4 5 6.0 1 7 8 9.0 1 } do_vtab_query_test 1.5.1 { SELECT * FROM t1 WHERE a IS NOT NULL } { "WHERE a IS NOT NULL" 1 2 3.0 4 5 6.0 7 8 9.0 } do_vtab_query_test 1.5.2 { SELECT * FROM t1 WHERE NULL IS NOT a } { "WHERE a IS NOT ''" 1 2 3.0 4 5 6.0 7 8 9.0 } do_vtab_query_test 1.6.1 { SELECT * FROM t1 WHERE a IS NULL } { "WHERE a IS NULL" } do_vtab_query_test 1.6.2 { SELECT * FROM t1 WHERE NULL IS a } { "WHERE a IS ''" } do_vtab_query_test 1.7.1 { SELECT * FROM t1 WHERE (a, b) IS (1, 2) } { "WHERE a IS '1' AND b IS '2'" 1 2 3.0 } do_vtab_query_test 1.7.2 { SELECT * FROM t1 WHERE (5, 4) IS (b, a) } { {WHERE b IS '5' AND a IS '4'} 4 5 6.0 } #--------------------------------------------------------------------- do_execsql_test 2.0.0 { DELETE FROM t1x; INSERT INTO t1x VALUES('a', 'b', 'c'); } do_execsql_test 2.0.1 { SELECT * FROM t1 } {a b c} do_execsql_test 2.0.2 { SELECT * FROM t1 WHERE (a, b) != ('a', 'b'); } {} do_execsql_test 2.1.0 { DELETE FROM t1x; INSERT INTO t1x VALUES(7, 8, 9); } do_execsql_test 2.1.1 { SELECT * FROM t1 } {7 8 9.0} do_execsql_test 2.1.2 { SELECT * FROM t1 WHERE (a, b) != (7, '8') } {} do_execsql_test 2.1.3 { SELECT * FROM t1 WHERE a!=7 OR b!='8' } do_execsql_test 2.1.4 { SELECT * FROM t1 WHERE a!=7 OR b!='8' } do_execsql_test 2.2.1 { CREATE TABLE t3(a INTEGER, b TEXT); INSERT INTO t3 VALUES(45, 46); } do_execsql_test 2.2.2 { SELECT * FROM t3 WHERE (a, b) != (45, 46); } do_execsql_test 2.2.3 { SELECT * FROM t3 WHERE (a, b) != ('45', '46'); } do_execsql_test 2.2.4 { SELECT * FROM t3 WHERE (a, b) == (45, 46); } {45 46} do_execsql_test 2.2.5 { SELECT * FROM t3 WHERE (a, b) == ('45', '46'); } {45 46} #--------------------------------------------------------------------- # Test the != operator on a virtual table with column affinities. # proc vtab_simple_integer {method args} { switch -- $method { xConnect { return "CREATE TABLE t4(x INTEGER)" } xBestIndex { return [list cost 999999.0] } xFilter { return [list sql "SELECT rowid, * FROM t4x"] } } return "" } do_execsql_test 3.0 { CREATE TABLE t4x(a INTEGER); INSERT INTO t4x VALUES(245); CREATE VIRTUAL TABLE t4 USING tcl('vtab_simple_integer'); } do_execsql_test 3.1 { SELECT rowid, * FROM t4 WHERE x=245; } {1 245} do_execsql_test 3.2 { SELECT rowid, * FROM t4 WHERE x='245'; } {1 245} do_execsql_test 3.3 { SELECT rowid, * FROM t4 WHERE x!=245; } {} do_execsql_test 3.4 { SELECT rowid, * FROM t4 WHERE x!='245'; } {} do_execsql_test 3.5 { SELECT rowid, * FROM t4 WHERE rowid!=1 OR x!='245'; } {} finish_test |
Added test/bigmmap.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 | # 2017 August 07 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script testing the ability of SQLite to use mmap # to access files larger than 4GiB. # if {[file exists skip-big-file]} return if {$tcl_platform(os)=="Darwin"} return set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix bigmmap ifcapable !mmap { finish_test return } set mmap_limit 0 db eval { SELECT compile_options AS x FROM pragma_compile_options WHERE x LIKE 'max_mmap_size=%' } { regexp {MAX_MMAP_SIZE=([0-9]*)} $x -> mmap_limit } if {$mmap_limit < [expr 8 * 1<<30]} { puts "Skipping bigmmap.test - requires SQLITE_MAX_MMAP_SIZE >= 8G" finish_test return } #------------------------------------------------------------------------- # Create the database file roughly 8GiB in size. Most pages are unused, # except that there is a table and index clustered around each 1GiB # boundary. # do_execsql_test 1.0 { PRAGMA page_size = 4096; CREATE TABLE t0(a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c)); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 100 ) INSERT INTO t0 SELECT i, 't0', randomblob(800) FROM s; } for {set i 1} {$i < 8} {incr i} { fake_big_file [expr $i*1024] [get_pwd]/test.db hexio_write test.db 28 [format %.8x [expr ($i*1024*1024*1024/4096) - 5]] do_execsql_test 1.$i " CREATE TABLE t$i (a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c)); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 100 ) INSERT INTO t$i SELECT i, 't$i', randomblob(800) FROM s; " } #------------------------------------------------------------------------- # Check that data can be retrieved from the db with a variety of # configured mmap size limits. # for {set i 0} {$i < 9} {incr i} { # Configure a memory mapping $i GB in size. # set val [expr $i*1024*1024*1024] execsql "PRAGMA main.mmap_size = $val" do_execsql_test 2.$i.0 { PRAGMA main.mmap_size } $val for {set t 0} {$t < 8} {incr t} { do_execsql_test 2.$i.$t.1 " SELECT count(*) FROM t$t; SELECT count(b || c) FROM t$t GROUP BY b; " {100 100} do_execsql_test 2.$i.$t.2 " SELECT * FROM t$t AS o WHERE NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c ) ORDER BY b, c; " {} do_eqp_test 2.$i.$t.3 " SELECT * FROM t$t AS o WHERE NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c ) ORDER BY b, c; " " 0 0 0 {SCAN TABLE t$t AS o USING COVERING INDEX sqlite_autoindex_t${t}_1} 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1} 1 0 0 {SEARCH TABLE t$t AS i USING INTEGER PRIMARY KEY (rowid=?)} " } } finish_test |
Changes to test/bind.test.
︙ | ︙ | |||
362 363 364 365 366 367 368 | # Test that the 'out of range' error works. do_test bind-8.1 { catch { sqlite3_bind_null $VM 0 } } {1} do_test bind-8.2 { sqlite3_errmsg $DB | | | | | | 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 | # Test that the 'out of range' error works. do_test bind-8.1 { catch { sqlite3_bind_null $VM 0 } } {1} do_test bind-8.2 { sqlite3_errmsg $DB } {column index out of range} ifcapable {utf16} { do_test bind-8.3 { encoding convertfrom unicode [sqlite3_errmsg16 $DB] } {column index out of range} } do_test bind-8.4 { sqlite3_bind_null $VM 1 sqlite3_errmsg $DB } {not an error} do_test bind-8.5 { catch { sqlite3_bind_null $VM 4 } } {1} do_test bind-8.6 { sqlite3_errmsg $DB } {column index out of range} ifcapable {utf16} { do_test bind-8.7 { encoding convertfrom unicode [sqlite3_errmsg16 $DB] } {column index out of range} } do_test bind-8.8 { catch { sqlite3_bind_blob $VM 0 "abc" 3 } } {1} do_test bind-8.9 { catch { sqlite3_bind_blob $VM 4 "abc" 3 } |
︙ | ︙ |
Added test/cachespill.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 | # 2017 April 26 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix cachespill ifcapable !pager_pragmas { finish_test return } #------------------------------------------------------------------------- # Test that "PRAGMA cache_spill = 0" completely disables cache spilling. # do_execsql_test 1.1 { PRAGMA auto_vacuum = 0; PRAGMA page_size = 1024; PRAGMA cache_size = 100; CREATE TABLE t1(a); } do_test 1.2 { file size test.db } {2048} do_test 1.3 { execsql { BEGIN; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<200 ) INSERT INTO t1 SELECT randomblob(900) FROM s; } expr {[file size test.db] > 50000} } {1} do_test 1.4 { execsql ROLLBACK file size test.db } {2048} do_test 1.5 { execsql { PRAGMA cache_spill = 0; BEGIN; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<200 ) INSERT INTO t1 SELECT randomblob(900) FROM s; } file size test.db } {2048} do_test 1.5 { execsql { ROLLBACK; PRAGMA cache_spill = 1; BEGIN; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<200 ) INSERT INTO t1 SELECT randomblob(900) FROM s; } expr {[file size test.db] > 50000} } {1} do_execsql_test 1.6 { ROLLBACK } finish_test |
Changes to test/capi3.test.
︙ | ︙ | |||
183 184 185 186 187 188 189 | } [list $::capi3_errno SQLITE_OK] if {[clang_sanitize_address]==0} { do_test capi3-3.6.1-misuse { sqlite3_close $db2 } {SQLITE_MISUSE} do_test capi3-3.6.2-misuse { sqlite3_errmsg $db2 | | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | } [list $::capi3_errno SQLITE_OK] if {[clang_sanitize_address]==0} { do_test capi3-3.6.1-misuse { sqlite3_close $db2 } {SQLITE_MISUSE} do_test capi3-3.6.2-misuse { sqlite3_errmsg $db2 } {bad parameter or other API misuse} ifcapable {utf16} { do_test capi3-3.6.3-misuse { utf8 [sqlite3_errmsg16 $db2] } {bad parameter or other API misuse} } } do_test capi3-3.7 { set db2 [sqlite3_open] sqlite3_errcode $db2 } {SQLITE_OK} |
︙ | ︙ | |||
765 766 767 768 769 770 771 | forcedelete test.db forcedelete test.db-journal # Test the english language string equivalents for sqlite error codes set code2english [list \ SQLITE_OK {not an error} \ | | | < | < < | | | 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 | forcedelete test.db forcedelete test.db-journal # Test the english language string equivalents for sqlite error codes set code2english [list \ SQLITE_OK {not an error} \ SQLITE_ERROR {SQL logic error} \ SQLITE_PERM {access permission denied} \ SQLITE_ABORT {query aborted} \ SQLITE_BUSY {database is locked} \ SQLITE_LOCKED {database table is locked} \ SQLITE_NOMEM {out of memory} \ SQLITE_READONLY {attempt to write a readonly database} \ SQLITE_INTERRUPT {interrupted} \ SQLITE_IOERR {disk I/O error} \ SQLITE_CORRUPT {database disk image is malformed} \ SQLITE_FULL {database or disk is full} \ SQLITE_CANTOPEN {unable to open database file} \ SQLITE_SCHEMA {database schema has changed} \ SQLITE_CONSTRAINT {constraint failed} \ SQLITE_MISMATCH {datatype mismatch} \ SQLITE_MISUSE {bad parameter or other API misuse} \ SQLITE_AUTH {authorization denied} \ SQLITE_RANGE {column index out of range} \ SQLITE_NOTADB {file is not a database} \ unknownerror {unknown error} \ ] set test_number 1 foreach {code english} $code2english { do_test capi3-9.$test_number "sqlite3_test_errstr $code" $english incr test_number |
︙ | ︙ |
Changes to test/capi3c.test.
︙ | ︙ | |||
172 173 174 175 176 177 178 | } {SQLITE_OK} if {[clang_sanitize_address]==0} { do_test capi3c-3.6.1-misuse { sqlite3_close $db2 } {SQLITE_MISUSE} do_test capi3c-3.6.2-misuse { sqlite3_errmsg $db2 | | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | } {SQLITE_OK} if {[clang_sanitize_address]==0} { do_test capi3c-3.6.1-misuse { sqlite3_close $db2 } {SQLITE_MISUSE} do_test capi3c-3.6.2-misuse { sqlite3_errmsg $db2 } {bad parameter or other API misuse} ifcapable {utf16} { do_test capi3c-3.6.3-misuse { utf8 [sqlite3_errmsg16 $db2] } {bad parameter or other API misuse} } } # rename sqlite3_open "" # rename sqlite3_open_old sqlite3_open ifcapable {utf16} { |
︙ | ︙ | |||
722 723 724 725 726 727 728 | forcedelete test.db forcedelete test.db-journal # Test the english language string equivalents for sqlite error codes set code2english [list \ SQLITE_OK {not an error} \ | | | | | < < | | | 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 | forcedelete test.db forcedelete test.db-journal # Test the english language string equivalents for sqlite error codes set code2english [list \ SQLITE_OK {not an error} \ SQLITE_ERROR {SQL logic error} \ SQLITE_PERM {access permission denied} \ SQLITE_ABORT {query aborted} \ SQLITE_BUSY {database is locked} \ SQLITE_LOCKED {database table is locked} \ SQLITE_NOMEM {out of memory} \ SQLITE_READONLY {attempt to write a readonly database} \ SQLITE_INTERRUPT {interrupted} \ SQLITE_IOERR {disk I/O error} \ SQLITE_CORRUPT {database disk image is malformed} \ SQLITE_FULL {database or disk is full} \ SQLITE_CANTOPEN {unable to open database file} \ SQLITE_EMPTY {unknown error} \ SQLITE_SCHEMA {database schema has changed} \ SQLITE_CONSTRAINT {constraint failed} \ SQLITE_MISMATCH {datatype mismatch} \ SQLITE_MISUSE {bad parameter or other API misuse} \ SQLITE_AUTH {authorization denied} \ SQLITE_RANGE {column index out of range} \ SQLITE_NOTADB {file is not a database} \ unknownerror {unknown error} \ ] set test_number 1 foreach {code english} $code2english { do_test capi3c-9.$test_number "sqlite3_test_errstr $code" $english incr test_number |
︙ | ︙ |
Changes to test/check.test.
︙ | ︙ | |||
305 306 307 308 309 310 311 312 | } } {12 -22} do_test check-4.8 { execsql { PRAGMA ignore_check_constraints=ON; UPDATE t4 SET x=0, y=1; SELECT * FROM t4; } | > | > > > > < | 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | } } {12 -22} do_test check-4.8 { execsql { PRAGMA ignore_check_constraints=ON; UPDATE t4 SET x=0, y=1; SELECT * FROM t4; PRAGMA integrity_check; } } {0 1 ok} do_execsql_test check-4.8.1 { PRAGMA ignore_check_constraints=OFF; PRAGMA integrity_check; } {{CHECK constraint failed in t4}} do_test check-4.9 { catchsql { UPDATE t4 SET x=0, y=2; } } {1 {CHECK constraint failed: t4}} ifcapable vacuum { do_test check_4.10 { catchsql { VACUUM |
︙ | ︙ | |||
473 474 475 476 477 478 479 | do_catchsql_test 9.2 { UPDATE t1 SET b=0 WHERE a=1; } {1 {CHECK constraint failed: b-check}} do_catchsql_test 9.3 { UPDATE t1 SET c=a*2 WHERE a=1; } {1 {CHECK constraint failed: c-check}} | > | > > > > > > > > > | 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 | do_catchsql_test 9.2 { UPDATE t1 SET b=0 WHERE a=1; } {1 {CHECK constraint failed: b-check}} do_catchsql_test 9.3 { UPDATE t1 SET c=a*2 WHERE a=1; } {1 {CHECK constraint failed: c-check}} # Integrity check on a VIEW with columns. # db close db2 close forcedelete test.db sqlite3 db test.db do_execsql_test 10.1 { CREATE TABLE t1(x); CREATE VIEW v1(y) AS SELECT x FROM t1; PRAGMA integrity_check; } {ok} finish_test |
Changes to test/close.test.
︙ | ︙ | |||
69 70 71 72 73 74 75 | list [sqlite3_step $STMT] [sqlite3_column_text $STMT 0] } {SQLITE_ROW two} do_test 1.4.3 { list [catch { sqlite3_prepare $DB "SELECT * FROM sqlite_master" -1 dummy } msg] $msg | | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | list [sqlite3_step $STMT] [sqlite3_column_text $STMT 0] } {SQLITE_ROW two} do_test 1.4.3 { list [catch { sqlite3_prepare $DB "SELECT * FROM sqlite_master" -1 dummy } msg] $msg } {1 {(21) bad parameter or other API misuse}} do_test 1.4.4 { sqlite3_finalize $STMT } {SQLITE_OK} finish_test |
Changes to test/collateB.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # #*********************************************************************** # Test cases for a crash bug. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_execsql_test collateB-1.1 { CREATE TABLE t1(a INTEGER PRIMARY KEY); CREATE TABLE t2(b INTEGER PRIMARY KEY, x1 INT COLLATE NOCASE); CREATE TABLE t3(x2 INT); SELECT * FROM t3, t2, t1 WHERE x2=b AND x1=a AND a=1; } {} | > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # Test cases for a crash bug. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix collateB do_execsql_test collateB-1.1 { CREATE TABLE t1(a INTEGER PRIMARY KEY); CREATE TABLE t2(b INTEGER PRIMARY KEY, x1 INT COLLATE NOCASE); CREATE TABLE t3(x2 INT); SELECT * FROM t3, t2, t1 WHERE x2=b AND x1=a AND a=1; } {} |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 | } {11 1 1 11 |} do_execsql_test collateB-1.16 { SELECT *,'|' FROM t1, t2, t3 WHERE b=x2 AND a=x1 AND 1=a; } {1 11 1 11 |} do_execsql_test collateB-1.17 { SELECT *,'|' FROM t1, t2, t3 WHERE b=x2 AND a=x1 AND 1=a; } {1 11 1 11 |} finish_test | > > > > > > > > > > > > > | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | } {11 1 1 11 |} do_execsql_test collateB-1.16 { SELECT *,'|' FROM t1, t2, t3 WHERE b=x2 AND a=x1 AND 1=a; } {1 11 1 11 |} do_execsql_test collateB-1.17 { SELECT *,'|' FROM t1, t2, t3 WHERE b=x2 AND a=x1 AND 1=a; } {1 11 1 11 |} #------------------------------------------------------------------------- # Test an assert() failure that was occuring if an index were created # on a column explicitly declared "COLLATE binary". reset_db do_execsql_test 2.1 { CREATE TABLE t4(a COLLATE binary); CREATE INDEX i4 ON t4(a); INSERT INTO t4 VALUES('one'), ('two'), ('three'); VACUUM; } integrity_check 2.2 finish_test |
Changes to test/colname.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # The focus of this file is testing how SQLite generates the names # of columns in a result set. # | < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # The focus of this file is testing how SQLite generates the names # of columns in a result set. # set testdir [file dirname $argv0] source $testdir/tester.tcl # Rules (applied in order): # # (1) If there is an AS clause, use it. |
︙ | ︙ | |||
321 322 323 324 325 326 327 328 329 | do_test colname-8.1 { db eval { CREATE TABLE "t3893"("x"); INSERT INTO t3893 VALUES(123); SELECT "y"."x" FROM (SELECT "x" FROM "t3893") AS "y"; } } {123} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_test colname-8.1 { db eval { CREATE TABLE "t3893"("x"); INSERT INTO t3893 VALUES(123); SELECT "y"."x" FROM (SELECT "x" FROM "t3893") AS "y"; } } {123} # 2017-07-29: Interaction between column naming and query flattening. # For years now, the query flattener has inserted AS clauses on the # outer query that were the original SQL text of the column. This caused # column-name shifts when the query flattener was enhanced, breaking # legacy applications. See https://sqlite.org/src/info/41c27bc0ff1d3135 # for details. # # To fix this, the column naming logic was moved ahead of the query # flattener so that column names are assigned before the query flattener # runs. # db close sqlite3 db :memory: do_test colname-9.100 { db eval { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); CREATE VIEW v1(x,y) AS SELECT a,b FROM t1; } execsql2 {SELECT v1.x, (Y) FROM v1} # Prior to the fix, this would return: "v1.x 1 (Y) 2" } {x 1 y 2} do_test colname-9.110 { execsql2 {SELECT * FROM v1} } {x 1 y 2} do_test colname-9.120 { db eval { CREATE VIEW v2(x,y) AS SELECT a,b FROM t1 LIMIT 10; } execsql2 {SELECT * FROM v2 WHERE 1} } {x 1 y 2} do_test colname-9.130 { execsql2 {SELECT v2.x, [v2].[y] FROM v2 WHERE 1} } {x 1 y 2} do_test colname-9.140 { execsql2 {SELECT +x, +y FROM v2 WHERE 1} } {+x 1 +y 2} do_test colname-9.200 { db eval { CREATE TABLE t2(c,d); INSERT INTO t2 VALUES(3,4); CREATE VIEW v3 AS SELECT c AS a, d AS b FROM t2; } execsql2 {SELECT t1.a, v3.a AS n FROM t1 LEFT JOIN v3} } {a 1 n 3} do_test colname-9.211 { execsql2 {SELECT t1.a AS n, v3.a FROM t1 JOIN v3} } {n 1 a 3} do_test colname-9.210 { execsql2 {SELECT t1.a, v3.a AS n FROM t1 JOIN v3} } {a 1 n 3} # Make sure the quotation marks get removed from the column names # when constructing a new table from an aggregate SELECT. # Email from Juergen Palm on 2017-07-11. # do_execsql_test colname-10.100 { DROP TABLE IF EXISTS t1; CREATE TABLE t1("with space" TEXT); DROP TABLE IF EXISTS t2; CREATE TABLE t2 AS SELECT "with space" FROM t1; PRAGMA table_info(t2); } {0 {with space} TEXT 0 {} 0} do_execsql_test colname-10.110 { DROP TABLE IF EXISTS t3; CREATE TABLE t3 AS SELECT "with space" FROM t1 GROUP BY 1; PRAGMA table_info(t3); } {0 {with space} TEXT 0 {} 0} finish_test |
Changes to test/conflict3.test.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 | # # This file focuses on making sure that combinations of REPLACE, # IGNORE, and FAIL conflict resolution play well together. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !conflict { finish_test return } | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > | 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 | # # This file focuses on making sure that combinations of REPLACE, # IGNORE, and FAIL conflict resolution play well together. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix conflict3 ifcapable !conflict { finish_test return } do_execsql_test 1.1 { CREATE TABLE t1( a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b UNIQUE ON CONFLICT IGNORE, c UNIQUE ON CONFLICT FAIL ); INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 1.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 1.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 1.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Replete the tests above, but this time on a table non-INTEGER primary key. # do_execsql_test 2.1 { DROP TABLE t1; CREATE TABLE t1( a INT PRIMARY KEY ON CONFLICT REPLACE, b UNIQUE ON CONFLICT IGNORE, c UNIQUE ON CONFLICT FAIL ); INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 2.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 2.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 2.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Replete again on a WITHOUT ROWID table. # do_execsql_test 3.1 { DROP TABLE t1; CREATE TABLE t1( a INT PRIMARY KEY ON CONFLICT REPLACE, b UNIQUE ON CONFLICT IGNORE, c UNIQUE ON CONFLICT FAIL ) WITHOUT ROWID; INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 3.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 3.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 3.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Arrange the table rows in a different order and repeat. # do_execsql_test 4.1 { DROP TABLE t1; CREATE TABLE t1( b UNIQUE ON CONFLICT IGNORE, c UNIQUE ON CONFLICT FAIL, a INT PRIMARY KEY ON CONFLICT REPLACE ) WITHOUT ROWID; INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 4.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 4.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 4.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Arrange the table rows in a different order and repeat. # do_execsql_test 5.1 { DROP TABLE t1; CREATE TABLE t1( b UNIQUE ON CONFLICT IGNORE, a INT PRIMARY KEY ON CONFLICT REPLACE, c UNIQUE ON CONFLICT FAIL ); INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 5.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 5.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 5.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Arrange the table rows in a different order and repeat. # do_execsql_test 6.1 { DROP TABLE t1; CREATE TABLE t1( c UNIQUE ON CONFLICT FAIL, a INT PRIMARY KEY ON CONFLICT REPLACE, b UNIQUE ON CONFLICT IGNORE ); INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 6.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 6.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 6.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Change which column is the PRIMARY KEY # do_execsql_test 7.1 { DROP TABLE t1; CREATE TABLE t1( a UNIQUE ON CONFLICT REPLACE, b INTEGER PRIMARY KEY ON CONFLICT IGNORE, c UNIQUE ON CONFLICT FAIL ); INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 7.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 7.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 7.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Change which column is the PRIMARY KEY # do_execsql_test 8.1 { DROP TABLE t1; CREATE TABLE t1( a UNIQUE ON CONFLICT REPLACE, b INT PRIMARY KEY ON CONFLICT IGNORE, c UNIQUE ON CONFLICT FAIL ); INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 8.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 8.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 8.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Change which column is the PRIMARY KEY # do_execsql_test 9.1 { DROP TABLE t1; CREATE TABLE t1( a UNIQUE ON CONFLICT REPLACE, b INT PRIMARY KEY ON CONFLICT IGNORE, c UNIQUE ON CONFLICT FAIL ) WITHOUT ROWID; INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 9.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 9.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 9.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Change which column is the PRIMARY KEY # do_execsql_test 10.1 { DROP TABLE t1; CREATE TABLE t1( a UNIQUE ON CONFLICT REPLACE, b UNIQUE ON CONFLICT IGNORE, c INTEGER PRIMARY KEY ON CONFLICT FAIL ); INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 10.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 10.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 10.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Change which column is the PRIMARY KEY # do_execsql_test 11.1 { DROP TABLE t1; CREATE TABLE t1( a UNIQUE ON CONFLICT REPLACE, b UNIQUE ON CONFLICT IGNORE, c PRIMARY KEY ON CONFLICT FAIL ) WITHOUT ROWID; INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert a row that conflicts on column B. The insert should be ignored. # do_execsql_test 11.2 { INSERT INTO t1(a,b,c) VALUES(3,2,5); SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4} # Insert two rows where the second conflicts on C. The first row show go # and and then there should be a constraint error. # do_test 11.3 { catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);} } {1 {UNIQUE constraint failed: t1.c}} do_execsql_test 11.4 { SELECT a,b,c FROM t1 ORDER BY a; } {1 2 3 2 3 4 4 5 6} # Check that ticket [f68dc596c4] has been fixed. # do_execsql_test 12.1 { CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT); INSERT INTO t2 VALUES(111, '111'); } do_execsql_test 12.2 { REPLACE INTO t2 VALUES(NULL, '112'), (111, '111B'); } do_execsql_test 12.3 { SELECT * FROM t2; } {111 111B 112 112} finish_test |
Changes to test/corrupt2.test.
︙ | ︙ | |||
55 56 57 58 59 60 61 | close $f sqlite3 db2 corrupt.db catchsql " $::presql SELECT * FROM sqlite_master; " db2 | | | | 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 | close $f sqlite3 db2 corrupt.db catchsql " $::presql SELECT * FROM sqlite_master; " db2 } {1 {file is not a database}} do_test corrupt2-1.3 { db2 close # Corrupt the page-size (bytes 16 and 17 of page 1). forcedelete corrupt.db forcedelete corrupt.db-journal forcecopy test.db corrupt.db set f [open corrupt.db RDWR] fconfigure $f -encoding binary seek $f 16 start puts -nonewline $f "\x00\xFF" close $f sqlite3 db2 corrupt.db catchsql " $::presql SELECT * FROM sqlite_master; " db2 } {1 {file is not a database}} do_test corrupt2-1.4 { db2 close # Corrupt the free-block list on page 1. forcedelete corrupt.db forcedelete corrupt.db-journal |
︙ | ︙ |
Changes to test/corruptA.test.
︙ | ︙ | |||
49 50 51 52 53 54 55 | set unreadable_version 02 ifcapable wal { set unreadable_version 03 } do_test corruptA-2.1 { forcecopy test.db-template test.db hexio_write test.db 19 $unreadable_version ;# the read format number sqlite3 db test.db catchsql {SELECT * FROM t1} | | | | | | 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 | set unreadable_version 02 ifcapable wal { set unreadable_version 03 } do_test corruptA-2.1 { forcecopy test.db-template test.db hexio_write test.db 19 $unreadable_version ;# the read format number sqlite3 db test.db catchsql {SELECT * FROM t1} } {1 {file is not a database}} do_test corruptA-2.2 { db close forcecopy test.db-template test.db hexio_write test.db 21 41 ;# max embedded payload fraction sqlite3 db test.db catchsql {SELECT * FROM t1} } {1 {file is not a database}} do_test corruptA-2.3 { db close forcecopy test.db-template test.db hexio_write test.db 22 1f ;# min embedded payload fraction sqlite3 db test.db catchsql {SELECT * FROM t1} } {1 {file is not a database}} do_test corruptA-2.4 { db close forcecopy test.db-template test.db hexio_write test.db 23 21 ;# min leaf payload fraction sqlite3 db test.db catchsql {SELECT * FROM t1} } {1 {file is not a database}} finish_test |
Changes to test/corruptC.test.
︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | hexio_write test.db 2053 [format %02x 0x04] sqlite3 db test.db catchsql {PRAGMA integrity_check} } {1 {database disk image is malformed}} # test that a corrupt content offset size is handled (seed 5649) do_test corruptC-2.2 { db close forcecopy test.bu test.db # insert corrupt byte(s) hexio_write test.db 27 [format %02x 0x08] hexio_write test.db 233 [format %02x 0x6a] hexio_write test.db 328 [format %02x 0x67] hexio_write test.db 750 [format %02x 0x1f] hexio_write test.db 1132 [format %02x 0x52] hexio_write test.db 1133 [format %02x 0x84] hexio_write test.db 1220 [format %02x 0x01] hexio_write test.db 3688 [format %02x 0xc1] hexio_write test.db 3714 [format %02x 0x58] hexio_write test.db 3746 [format %02x 0x9a] sqlite3 db test.db | > > > > > > | | > | 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 | hexio_write test.db 2053 [format %02x 0x04] sqlite3 db test.db catchsql {PRAGMA integrity_check} } {1 {database disk image is malformed}} # 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 # integrity_check to see it. do_test corruptC-2.2 { db close forcecopy test.bu test.db # insert corrupt byte(s) hexio_write test.db 27 [format %02x 0x08] hexio_write test.db 233 [format %02x 0x6a] hexio_write test.db 328 [format %02x 0x67] hexio_write test.db 750 [format %02x 0x1f] hexio_write test.db 1132 [format %02x 0x52] hexio_write test.db 1133 [format %02x 0x84] hexio_write test.db 1220 [format %02x 0x01] hexio_write test.db 3688 [format %02x 0xc1] hexio_write test.db 3714 [format %02x 0x58] hexio_write test.db 3746 [format %02x 0x9a] sqlite3 db test.db db eval {UPDATE t1 SET y=1} db eval {PRAGMA integrity_check} } {/Offset .* out of range/} # test that a corrupt free cell size is handled (seed 13329) do_test corruptC-2.3 { db close forcecopy test.bu test.db # insert corrupt byte(s) |
︙ | ︙ | |||
153 154 155 156 157 158 159 | hexio_write test.db 3119 [format %02x 0xdf] hexio_write test.db 4073 [format %02x 0xbf] sqlite3 db test.db catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;} catchsql {PRAGMA integrity_check} } {0 {{*** in database main *** | | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | hexio_write test.db 3119 [format %02x 0xdf] hexio_write test.db 4073 [format %02x 0xbf] sqlite3 db test.db catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;} catchsql {PRAGMA integrity_check} } {0 {{*** in database main *** On tree page 4 cell 19: Extends off end of page} {database disk image is malformed}}} # {0 {{*** in database main *** # Corruption detected in cell 710 on page 4 # Multiple uses for byte 661 of page 4 # Fragmented space is 249 byte reported as 21 on page 4}}} # test that a corrupt free cell size is handled (seed 169595) |
︙ | ︙ |
Added test/corruptK.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 | # 2017-03-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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix corruptK if {[permutation]=="mmap"} { finish_test return } # This module uses hard-coded offsets which do not work if the reserved_bytes # value is nonzero. if {[nonzero_reserved_bytes]} {finish_test; return;} database_may_be_corrupt # Initialize the database. # do_execsql_test 1.1 { PRAGMA page_size=1024; PRAGMA auto_vacuum=0; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(20)); INSERT INTO t1 VALUES(randomblob(100)); -- make this into a free slot INSERT INTO t1 VALUES(randomblob(27)); -- this one will be corrupt INSERT INTO t1 VALUES(randomblob(800)); DELETE FROM t1 WHERE rowid=2; -- free the 100 byte slot PRAGMA page_count } {2} # Corrupt the database so that the blob stored immediately before # the free slot (rowid==3) has an overlarge length field. So that # we can use sqlite3_blob_write() to manipulate the size field of # the free slot. # # Then use sqlite3_blob_write() to set the size of said free slot # to 24 bytes (instead of the actual 100). # # Then use the new 24 byte slot. Leaving the in-memory version of # the page with zero free slots and a large nFree value. Then try # to allocate another slot to get to defragmentPage(). # do_test 1.2 { db close hexio_write test.db [expr 1024 + 0x360] 21 hexio_write test.db [expr 1024 + 0x363] [format %x [expr 31*2 + 12]] sqlite3 db test.db set fd [db incrblob t1 x 3] fconfigure $fd -translation binary -encoding binary seek $fd 30 puts -nonewline $fd "\x18" close $fd } {} do_execsql_test 1.3 { INSERT INTO t1 VALUES(randomblob(20)); } do_catchsql_test 1.4 { INSERT INTO t1 VALUES(randomblob(90)); } {1 {database disk image is malformed}} #------------------------------------------------------------------------- reset_db do_execsql_test 2.1 { PRAGMA page_size=1024; PRAGMA auto_vacuum=0; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(20)); INSERT INTO t1 VALUES(randomblob(20)); -- free this one INSERT INTO t1 VALUES(randomblob(20)); INSERT INTO t1 VALUES(randomblob(20)); -- and this one INSERT INTO t1 VALUES(randomblob(20)); -- corrupt this one. DELETE FROM t1 WHERE rowid IN(2, 4); PRAGMA page_count } {2} do_test 2.2 { db close hexio_write test.db [expr 1024 + 0x388] 53 hexio_write test.db [expr 1024 + 0x38A] 03812C sqlite3 db test.db set fd [db incrblob t1 x 5] fconfigure $fd -translation binary -encoding binary seek $fd 22 puts -nonewline $fd "\x5d" close $fd } {} do_catchsql_test 2.3 { INSERT INTO t1 VALUES(randomblob(900)); } {1 {database disk image is malformed}} finish_test |
Changes to test/csv01.test.
︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 99 100 101 102 | } {5 9} # The rowid column is not visible on a WITHOUT ROWID virtual table do_catchsql_test 3.2 { SELECT rowid, a FROM t3; } {1 {no such column: rowid}} do_catchsql_test 4.0 { DROP TABLE t3; CREATE VIRTUAL TABLE temp.t4 USING csv_wr( data= '1,2,3,4 5,6,7,8 9,10,11,12 | > | > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > | | | > | 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 | } {5 9} # The rowid column is not visible on a WITHOUT ROWID virtual table do_catchsql_test 3.2 { SELECT rowid, a FROM t3; } {1 {no such column: rowid}} # Multi-column WITHOUT ROWID virtual tables may not be writable. do_catchsql_test 4.0 { DROP TABLE t3; CREATE VIRTUAL TABLE temp.t4 USING csv_wr( data= '1,2,3,4 5,6,7,8 9,10,11,12 13,14,15,16', columns=4, schema= 'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(a,b)) WITHOUT ROWID', testflags=1 ); } {1 {vtable constructor failed: t4}} # WITHOUT ROWID tables with a single-column PRIMARY KEY may be writable. do_catchsql_test 4.1 { DROP TABLE IF EXISTS t4; CREATE VIRTUAL TABLE temp.t4 USING csv_wr( data= '1,2,3,4 5,6,7,8 9,10,11,12 13,14,15,16', columns=4, schema= 'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(b)) WITHOUT ROWID', testflags=1 ); } {0 {}} do_catchsql_test 4.2 { DROP TABLE IF EXISTS t5; CREATE VIRTUAL TABLE temp.t5 USING csv_wr( data= '1,2,3,4 5,6,7,8 9,10,11,12 13,14,15,16', columns=4, schema= 'CREATE TABLE t3(a,b,c,d) WITHOUT ROWID', testflags=1 ); } {1 {vtable constructor failed: t5}} finish_test |
Changes to test/ctime.test.
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 | # the results should be in sorted order already do_test ctime-1.2.2 { set ans [ catchsql { PRAGMA compile_options; } ] list [ lindex $ans 0 ] [ expr { [lsort [lindex $ans 1]]==[lindex $ans 1] } ] } {0 1} # SQLITE_THREADSAFE should pretty much always be defined # one way or the other, and it must have a value of 0 or 1. do_test ctime-1.4.1 { catchsql { SELECT sqlite_compileoption_used('SQLITE_THREADSAFE'); } | > > > > > > > > > > > > > > > > > > | 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 | # the results should be in sorted order already do_test ctime-1.2.2 { set ans [ catchsql { PRAGMA compile_options; } ] list [ lindex $ans 0 ] [ expr { [lsort [lindex $ans 1]]==[lindex $ans 1] } ] } {0 1} # Check the THREADSAFE option for SQLITE_THREADSAFE=2 builds (there are # a couple of these configurations in releasetest.tcl). # ifcapable threadsafe2 { foreach {tn opt res} { 1 SQLITE_THREADSAFE 1 2 THREADSAFE 1 3 THREADSAFE=0 0 4 THREADSAFE=1 0 5 THREADSAFE=2 1 6 THREADSAFE= 0 } { do_execsql_test ctime-1.3.$tn { SELECT sqlite_compileoption_used($opt) } $res } } # SQLITE_THREADSAFE should pretty much always be defined # one way or the other, and it must have a value of 0 or 1. do_test ctime-1.4.1 { catchsql { SELECT sqlite_compileoption_used('SQLITE_THREADSAFE'); } |
︙ | ︙ | |||
220 221 222 223 224 225 226 | do_test ctime-2.5.$tc { set N -1 set ans [ catchsql { SELECT sqlite_compileoption_get($N); } ] } {0 {{}}} | > > > | > > > > > > > > > | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | do_test ctime-2.5.$tc { set N -1 set ans [ catchsql { SELECT sqlite_compileoption_get($N); } ] } {0 {{}}} #-------------------------------------------------------------------------- # Test that SQLITE_DIRECT_OVERFLOW_READ is reflected in the output of # "PRAGMA compile_options". # ifcapable direct_read { set res 1 } else { set res 0 } do_test ctime-3.0.1 { expr [lsearch [db eval {PRAGMA compile_options}] DIRECT_OVERFLOW_READ]>=0 } $res finish_test |
Changes to test/cursorhint2.test.
︙ | ︙ | |||
87 88 89 90 91 92 93 | do_extract_hints_test 1.6 { SELECT * FROM t1 LEFT JOIN t2 ON (a=c) LEFT JOIN t3 ON (d=f); } { t2 {EQ(r[2],c0)} t3 {EQ(r[6],c1)} } | > | | | | > | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | do_extract_hints_test 1.6 { SELECT * FROM t1 LEFT JOIN t2 ON (a=c) LEFT JOIN t3 ON (d=f); } { t2 {EQ(r[2],c0)} t3 {EQ(r[6],c1)} } if 0 { do_extract_hints_test 1.7 { SELECT * FROM t1 LEFT JOIN t2 ON (a=c AND d=e) LEFT JOIN t3 ON (d=f); } { t2 {EQ(r[2],c0)} t3 {AND(EQ(r[6],c0),EQ(r[7],c1))} } } #------------------------------------------------------------------------- # do_execsql_test 2.0 { CREATE TABLE x1(x, y); CREATE TABLE x2(a, b); |
︙ | ︙ | |||
160 161 162 163 164 165 166 | do_extract_hints_test 2.10 { SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b = 32+32 } { x2 {AND(EQ(c1,ADD(32,32)),EQ(c0,r[2]))} } | > > | | | | | | > | < | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | do_extract_hints_test 2.10 { SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b = 32+32 } { x2 {AND(EQ(c1,ADD(32,32)),EQ(c0,r[2]))} } ifcapable !icu { # This test only works using the built-in LIKE, not the ICU LIKE extension. do_extract_hints_test 2.11 { SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b LIKE 'abc%' } { x2 {AND(expr,EQ(c0,r[2]))} } } do_extract_hints_test 2.12 { SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE coalesce(x2.b, 1) } { x2 {EQ(c0,r[2])} } finish_test |
Changes to test/date.test.
︙ | ︙ | |||
58 59 60 61 62 63 64 | datetest 1.18.4 {julianday('2000-01-01T 12:00:00')} 2451545.0 datetest 1.18.4 {julianday('2000-01-01 T 12:00:00')} 2451545.0 datetest 1.19 {julianday('2000-01-01 12:00:00.1')} 2451545.00000116 datetest 1.20 {julianday('2000-01-01 12:00:00.01')} 2451545.00000012 datetest 1.21 {julianday('2000-01-01 12:00:00.001')} 2451545.00000001 datetest 1.22 {julianday('2000-01-01 12:00:00.')} NULL datetest 1.23 julianday(12345.6) 12345.6 | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | datetest 1.18.4 {julianday('2000-01-01T 12:00:00')} 2451545.0 datetest 1.18.4 {julianday('2000-01-01 T 12:00:00')} 2451545.0 datetest 1.19 {julianday('2000-01-01 12:00:00.1')} 2451545.00000116 datetest 1.20 {julianday('2000-01-01 12:00:00.01')} 2451545.00000012 datetest 1.21 {julianday('2000-01-01 12:00:00.001')} 2451545.00000001 datetest 1.22 {julianday('2000-01-01 12:00:00.')} NULL datetest 1.23 julianday(12345.6) 12345.6 datetest 1.23b julianday(1721059.5) 1721059.5 datetest 1.24 {julianday('2001-01-01 12:00:00 bogus')} NULL datetest 1.25 {julianday('2001-01-01 bogus')} NULL datetest 1.26 {julianday('2001-01-01 12:60:00')} NULL datetest 1.27 {julianday('2001-01-01 12:59:60')} NULL datetest 1.28 {julianday('2001-00-01')} NULL datetest 1.29 {julianday('2001-01-00')} NULL |
︙ | ︙ | |||
555 556 557 558 559 560 561 562 563 | } {0.0} do_test date-15.2 { db eval { SELECT a==b FROM (SELECT current_timestamp AS a, sleeper(), current_timestamp AS b); } } {1} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {0.0} do_test date-15.2 { db eval { SELECT a==b FROM (SELECT current_timestamp AS a, sleeper(), current_timestamp AS b); } } {1} # Tests of extreme values in date/time functions. Run with UBSan or the # equivalent to verify no signed interger overflow warnings. # datetest 16.1 {date(147483649)} NULL datetest 16.2 {datetime(0)} {-4713-11-24 12:00:00} datetest 16.3 {datetime(5373484.49999999)} {9999-12-31 23:59:59} datetest 16.4 {julianday('-4713-11-24 12:00:00')} 0.0 datetest 16.5 {julianday('9999-12-31 23:59:59.999')} 5373484.49999999 datetest 16.6 {datetime(0,'+464269060799 seconds')} {9999-12-31 23:59:59} datetest 16.7 {datetime(0,'+464269060800 seconds')} NULL datetest 16.8 {datetime(0,'+7737817679 minutes')} {9999-12-31 23:59:00} datetest 16.9 {datetime(0,'+7737817680 minutes')} NULL datetest 16.10 {datetime(0,'+128963627 hours')} {9999-12-31 23:00:00} datetest 16.11 {datetime(0,'+128963628 hours')} NULL datetest 16.12 {datetime(0,'+5373484 days')} {9999-12-31 12:00:00} datetest 16.13 {datetime(0,'+5373485 days')} NULL datetest 16.14 {datetime(0,'+176545 months')} {9999-12-24 12:00:00} datetest 16.15 {datetime(0,'+176546 months')} NULL datetest 16.16 {datetime(0,'+14712 years')} {9999-11-24 12:00:00} datetest 16.17 {datetime(0,'+14713 years')} NULL datetest 16.20 {datetime(5373484.4999999,'-464269060799 seconds')} \ {-4713-11-24 12:00:00} datetest 16.21 {datetime(5373484,'-464269060800 seconds')} NULL datetest 16.22 {datetime(5373484.4999999,'-7737817679 minutes')} \ {-4713-11-24 12:00:59} datetest 16.23 {datetime(5373484,'-7737817680 minutes')} NULL datetest 16.24 {datetime(5373484.4999999,'-128963627 hours')} \ {-4713-11-24 12:59:59} datetest 16.25 {datetime(5373484,'-128963628 hours')} NULL datetest 16.26 {datetime(5373484,'-5373484 days')} {-4713-11-24 12:00:00} datetest 16.27 {datetime(5373484,'-5373485 days')} NULL datetest 16.28 {datetime(5373484,'-176545 months')} {-4713-12-01 12:00:00} datetest 16.29 {datetime(5373484,'-176546 months')} NULL datetest 16.30 {datetime(5373484,'-14712 years')} {-4713-12-31 12:00:00} datetest 16.31 {datetime(5373484,'-14713 years')} NULL # 2017-03-02: Wrong 'start of day' computation. # https://www.sqlite.org/src/info/6097cb92745327a1 # datetest 17.1 {datetime(2457754, 'start of day')} {2016-12-31 00:00:00} datetest 17.2 {datetime(2457828)} {2017-03-15 12:00:00} datetest 17.3 {datetime(2457828,'start of day')} {2017-03-15 00:00:00} datetest 17.4 {datetime(2457828,'start of month')} {2017-03-01 00:00:00} datetest 17.5 {datetime(2457828,'start of year')} {2017-01-01 00:00:00} datetest 17.6 {datetime(37,'start of year')} NULL datetest 17.7 {datetime(38,'start of year')} {-4712-01-01 00:00:00} finish_test |
Added test/date2.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 | # 2017-07-20 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing date and time functions used in # check constraints and index expressions. # set testdir [file dirname $argv0] source $testdir/tester.tcl # Skip this whole file if date and time functions are omitted # at compile-time # ifcapable {!datetime} { finish_test return } do_execsql_test date2-100 { CREATE TABLE t1(x, y, CHECK( date(x) BETWEEN '2017-07-01' AND '2017-07-31' )); INSERT INTO t1(x,y) VALUES('2017-07-20','one'); } {} do_catchsql_test date2-110 { INSERT INTO t1(x,y) VALUES('now','two'); } {1 {non-deterministic function in index expression or CHECK constraint}} do_execsql_test date2-120 { SELECT * FROM t1; } {2017-07-20 one} do_catchsql_test date2-130 { INSERT INTO t1(x,y) VALUES('2017-08-01','two'); } {1 {CHECK constraint failed: t1}} do_execsql_test date2-200 { CREATE TABLE t2(x,y); INSERT INTO t2(x,y) VALUES(1, '2017-07-20'), (2, 'xyzzy'); CREATE INDEX t2y ON t2(date(y)); } do_catchsql_test date2-210 { INSERT INTO t2(x,y) VALUES(3, 'now'); } {1 {non-deterministic function in index expression or CHECK constraint}} do_execsql_test date2-220 { SELECT x, y FROM t2 ORDER BY x; } {1 2017-07-20 2 xyzzy} do_execsql_test date2-300 { CREATE TABLE t3(a INTEGER PRIMARY KEY,b); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000) INSERT INTO t3(a,b) SELECT x, julianday('2017-07-01')+x FROM c; UPDATE t3 SET b='now' WHERE a=500; } do_catchsql_test date2-310 { CREATE INDEX t3b1 ON t3(datetime(b)); } {1 {non-deterministic function in index expression or CHECK constraint}} do_catchsql_test date2-320 { CREATE INDEX t3b1 ON t3(datetime(b)) WHERE typeof(b)='real'; } {0 {}} do_execsql_test date2-330 { EXPLAIN QUERY PLAN SELECT a FROM t3 WHERE typeof(b)='real' AND datetime(b) BETWEEN '2017-07-04' AND '2017-07-08'; } {/USING INDEX t3b/} do_execsql_test date2-331 { SELECT a FROM t3 WHERE typeof(b)='real' AND datetime(b) BETWEEN '2017-07-04' AND '2017-07-08' ORDER BY a; } {3 4 5 6} do_execsql_test date2-400 { CREATE TABLE t4(a INTEGER PRIMARY KEY,b); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000) INSERT INTO t4(a,b) SELECT x, julianday('2017-07-01')+x FROM c; UPDATE t4 SET b='now' WHERE a=500; } do_catchsql_test date2-410 { CREATE INDEX t4b1 ON t4(b) WHERE date(b) BETWEEN '2017-06-01' AND '2017-08-31'; } {1 {non-deterministic function in index expression or CHECK constraint}} do_execsql_test date2-420 { DELETE FROM t4 WHERE a=500; CREATE INDEX t4b1 ON t4(b) WHERE date(b) BETWEEN '2017-06-01' AND '2017-08-31'; } do_catchsql_test date2-430 { INSERT INTO t4(a,b) VALUES(9999,'now'); } {1 {non-deterministic function in index expression or CHECK constraint}} do_execsql_test date2-500 { CREATE TABLE mods(x); INSERT INTO mods(x) VALUES ('+10 days'), ('-10 days'), ('+10 hours'), ('-10 hours'), ('+10 minutes'), ('-10 minutes'), ('+10 seconds'), ('-10 seconds'), ('+10 months'), ('-10 months'), ('+10 years'), ('-10 years'), ('start of month'), ('start of year'), ('start of day'), ('weekday 1'), ('unixepoch'); CREATE TABLE t5(y,m); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5) INSERT INTO t5(y,m) SELECT julianday('2017-07-01')+c.x, mods.x FROM c, mods; CREATE INDEX t5x1 on t5(y) WHERE datetime(y,m) IS NOT NULL; } do_catchsql_test date2-510 { INSERT INTO t5(y,m) VALUES('2017-07-20','localtime'); } {1 {non-deterministic function in index expression or CHECK constraint}} do_catchsql_test date2-520 { INSERT INTO t5(y,m) VALUES('2017-07-20','utc'); } {1 {non-deterministic function in index expression or CHECK constraint}} finish_test |
Added test/dbfuzz.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 | /* ** 2016-12-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. ** ************************************************************************* ** ** This program is designed for fuzz-testing SQLite database files. ** ** This program reads fuzzed database files from the disk files named ** on the command-line. Each database is loaded into an in-memory ** filesystem so that the original database file is unmolested. ** ** The fuzzed database is then opened, and series of SQL statements ** are run against the database to ensure that SQLite can safely handle ** the fuzzed database. */ #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #define ISSPACE(X) isspace((unsigned char)(X)) #define ISDIGIT(X) isdigit((unsigned char)(X)) #include "sqlite3.h" #ifdef __unix__ # include <signal.h> # include <unistd.h> #endif /* ** Print sketchy documentation for this utility program */ static void showHelp(const char *zArgv0){ printf("Usage: %s [options] DATABASE ...\n", zArgv0); printf( "Read databases into an in-memory filesystem. Run test SQL as specified\n" "by command-line arguments or from\n" "\n" " SELECT group_concat(sql) FROM autoexec;\n" "\n" "Options:\n" " --help Show this help text\n" " -q|--quiet Reduced output\n" " --limit-mem N Limit memory used by test SQLite instances to N bytes\n" " --limit-vdbe Panic if any test runs for more than 100,000 cycles\n" " --no-lookaside Disable the lookaside memory allocator\n" " --timeout N Timeout after N seconds.\n" " --trace Show the results of each SQL command\n" " -v|--verbose Increased output. Repeat for more output.\n" ); exit(0); } /* ** Print an error message and quit. */ static void fatalError(const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); vfprintf(stderr, zFormat, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } /* ** Files in the virtual file system. */ typedef struct VFile VFile; typedef struct VHandle VHandle; struct VFile { char *zFilename; /* Filename. NULL for delete-on-close. From malloc() */ int sz; /* Size of the file in bytes */ int nRef; /* Number of references to this file */ unsigned char *a; /* Content of the file. From malloc() */ }; struct VHandle { sqlite3_file base; /* Base class. Must be first */ VFile *pVFile; /* The underlying file */ }; /* ** Maximum number of files in the in-memory virtual filesystem. */ #define MX_FILE 10 /* ** Maximum allowed file size */ #define MX_FILE_SZ 1000000 /* ** All global variables are gathered into the "g" singleton. */ static struct GlobalVars { VFile aFile[MX_FILE]; /* The virtual filesystem */ } g; /* ** Initialize the virtual file system. */ static void formatVfs(void){ int i; for(i=0; i<MX_FILE; i++){ g.aFile[i].sz = -1; g.aFile[i].zFilename = 0; g.aFile[i].a = 0; g.aFile[i].nRef = 0; } } /* ** Erase all information in the virtual file system. */ static void reformatVfs(void){ int i; for(i=0; i<MX_FILE; i++){ if( g.aFile[i].sz<0 ) continue; if( g.aFile[i].zFilename ){ free(g.aFile[i].zFilename); g.aFile[i].zFilename = 0; } if( g.aFile[i].nRef>0 ){ fatalError("file %d still open. nRef=%d", i, g.aFile[i].nRef); } g.aFile[i].sz = -1; free(g.aFile[i].a); g.aFile[i].a = 0; g.aFile[i].nRef = 0; } } /* ** Find a VFile by name */ static VFile *findVFile(const char *zName){ int i; if( zName==0 ) return 0; for(i=0; i<MX_FILE; i++){ if( g.aFile[i].zFilename==0 ) continue; if( strcmp(g.aFile[i].zFilename, zName)==0 ) return &g.aFile[i]; } return 0; } /* ** Find a VFile called zName. Initialize it to the content of ** disk file zDiskFile. ** ** Return NULL if the filesystem is full. */ static VFile *createVFile(const char *zName, const char *zDiskFile){ VFile *pNew = findVFile(zName); int i; FILE *in = 0; long sz = 0; if( pNew ) return pNew; for(i=0; i<MX_FILE && g.aFile[i].sz>=0; i++){} if( i>=MX_FILE ) return 0; if( zDiskFile ){ in = fopen(zDiskFile, "rb"); if( in==0 ) fatalError("no such file: \"%s\"", zDiskFile); fseek(in, 0, SEEK_END); sz = ftell(in); rewind(in); } pNew = &g.aFile[i]; if( zName ){ int nName = (int)strlen(zName)+1; pNew->zFilename = malloc(nName); if( pNew->zFilename==0 ){ if( in ) fclose(in); return 0; } memcpy(pNew->zFilename, zName, nName); }else{ pNew->zFilename = 0; } pNew->nRef = 0; pNew->sz = sz; pNew->a = malloc(sz); if( sz>0 ){ if( pNew->a==0 || fread(pNew->a, sz, 1, in)<1 ){ free(pNew->zFilename); free(pNew->a); pNew->a = 0; pNew->zFilename = 0; pNew->sz = -1; pNew = 0; } } if( in ) fclose(in); return pNew; } /* Methods for the VHandle object */ static int inmemClose(sqlite3_file *pFile){ VHandle *p = (VHandle*)pFile; VFile *pVFile = p->pVFile; pVFile->nRef--; if( pVFile->nRef==0 && pVFile->zFilename==0 ){ pVFile->sz = -1; free(pVFile->a); pVFile->a = 0; } return SQLITE_OK; } static int inmemRead( sqlite3_file *pFile, /* Read from this open file */ void *pData, /* Store content in this buffer */ int iAmt, /* Bytes of content */ sqlite3_int64 iOfst /* Start reading here */ ){ VHandle *pHandle = (VHandle*)pFile; VFile *pVFile = pHandle->pVFile; if( iOfst<0 || iOfst>=pVFile->sz ){ memset(pData, 0, iAmt); return SQLITE_IOERR_SHORT_READ; } if( iOfst+iAmt>pVFile->sz ){ memset(pData, 0, iAmt); iAmt = (int)(pVFile->sz - iOfst); memcpy(pData, pVFile->a, iAmt); return SQLITE_IOERR_SHORT_READ; } memcpy(pData, pVFile->a + iOfst, iAmt); return SQLITE_OK; } static int inmemWrite( sqlite3_file *pFile, /* Write to this file */ const void *pData, /* Content to write */ int iAmt, /* bytes to write */ sqlite3_int64 iOfst /* Start writing here */ ){ VHandle *pHandle = (VHandle*)pFile; VFile *pVFile = pHandle->pVFile; if( iOfst+iAmt > pVFile->sz ){ unsigned char *aNew; if( iOfst+iAmt >= MX_FILE_SZ ){ return SQLITE_FULL; } aNew = realloc(pVFile->a, (int)(iOfst+iAmt)); if( aNew==0 ){ return SQLITE_FULL; } pVFile->a = aNew; if( iOfst > pVFile->sz ){ memset(pVFile->a + pVFile->sz, 0, (int)(iOfst - pVFile->sz)); } pVFile->sz = (int)(iOfst + iAmt); } memcpy(pVFile->a + iOfst, pData, iAmt); return SQLITE_OK; } static int inmemTruncate(sqlite3_file *pFile, sqlite3_int64 iSize){ VHandle *pHandle = (VHandle*)pFile; VFile *pVFile = pHandle->pVFile; if( pVFile->sz>iSize && iSize>=0 ) pVFile->sz = (int)iSize; return SQLITE_OK; } static int inmemSync(sqlite3_file *pFile, int flags){ return SQLITE_OK; } static int inmemFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){ *pSize = ((VHandle*)pFile)->pVFile->sz; return SQLITE_OK; } static int inmemLock(sqlite3_file *pFile, int type){ return SQLITE_OK; } static int inmemUnlock(sqlite3_file *pFile, int type){ return SQLITE_OK; } static int inmemCheckReservedLock(sqlite3_file *pFile, int *pOut){ *pOut = 0; return SQLITE_OK; } static int inmemFileControl(sqlite3_file *pFile, int op, void *pArg){ return SQLITE_NOTFOUND; } static int inmemSectorSize(sqlite3_file *pFile){ return 512; } static int inmemDeviceCharacteristics(sqlite3_file *pFile){ return SQLITE_IOCAP_SAFE_APPEND | SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_POWERSAFE_OVERWRITE; } /* Method table for VHandle */ static sqlite3_io_methods VHandleMethods = { /* iVersion */ 1, /* xClose */ inmemClose, /* xRead */ inmemRead, /* xWrite */ inmemWrite, /* xTruncate */ inmemTruncate, /* xSync */ inmemSync, /* xFileSize */ inmemFileSize, /* xLock */ inmemLock, /* xUnlock */ inmemUnlock, /* xCheck... */ inmemCheckReservedLock, /* xFileCtrl */ inmemFileControl, /* xSectorSz */ inmemSectorSize, /* xDevchar */ inmemDeviceCharacteristics, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ 0, /* xShmUnmap */ 0, /* xFetch */ 0, /* xUnfetch */ 0 }; /* ** Open a new file in the inmem VFS. All files are anonymous and are ** delete-on-close. */ static int inmemOpen( sqlite3_vfs *pVfs, const char *zFilename, sqlite3_file *pFile, int openFlags, int *pOutFlags ){ VFile *pVFile = createVFile(zFilename, 0); VHandle *pHandle = (VHandle*)pFile; if( pVFile==0 ){ return SQLITE_FULL; } pHandle->pVFile = pVFile; pVFile->nRef++; pFile->pMethods = &VHandleMethods; if( pOutFlags ) *pOutFlags = openFlags; return SQLITE_OK; } /* ** Delete a file by name */ static int inmemDelete( sqlite3_vfs *pVfs, const char *zFilename, int syncdir ){ VFile *pVFile = findVFile(zFilename); if( pVFile==0 ) return SQLITE_OK; if( pVFile->nRef==0 ){ free(pVFile->zFilename); pVFile->zFilename = 0; pVFile->sz = -1; free(pVFile->a); pVFile->a = 0; return SQLITE_OK; } return SQLITE_IOERR_DELETE; } /* Check for the existance of a file */ static int inmemAccess( sqlite3_vfs *pVfs, const char *zFilename, int flags, int *pResOut ){ VFile *pVFile = findVFile(zFilename); *pResOut = pVFile!=0; return SQLITE_OK; } /* Get the canonical pathname for a file */ static int inmemFullPathname( sqlite3_vfs *pVfs, const char *zFilename, int nOut, char *zOut ){ sqlite3_snprintf(nOut, zOut, "%s", zFilename); return SQLITE_OK; } /* ** Register the VFS that reads from the g.aFile[] set of files. */ static void inmemVfsRegister(void){ static sqlite3_vfs inmemVfs; sqlite3_vfs *pDefault = sqlite3_vfs_find(0); inmemVfs.iVersion = 3; inmemVfs.szOsFile = sizeof(VHandle); inmemVfs.mxPathname = 200; inmemVfs.zName = "inmem"; inmemVfs.xOpen = inmemOpen; inmemVfs.xDelete = inmemDelete; inmemVfs.xAccess = inmemAccess; inmemVfs.xFullPathname = inmemFullPathname; inmemVfs.xRandomness = pDefault->xRandomness; inmemVfs.xSleep = pDefault->xSleep; inmemVfs.xCurrentTimeInt64 = pDefault->xCurrentTimeInt64; sqlite3_vfs_register(&inmemVfs, 0); }; /* ** Timeout handler */ #ifdef __unix__ static void timeoutHandler(int NotUsed){ (void)NotUsed; fatalError("timeout\n"); } #endif /* ** Set the an alarm to go off after N seconds. Disable the alarm ** if N==0 */ static void setAlarm(int N){ #ifdef __unix__ alarm(N); #else (void)N; #endif } /*************************************************************************** ** String accumulator object */ typedef struct Str Str; struct Str { char *z; /* The string. Memory from malloc() */ sqlite3_uint64 n; /* Bytes of input used */ sqlite3_uint64 nAlloc; /* Bytes allocated to z[] */ int oomErr; /* OOM error has been seen */ }; /* Initialize a Str object */ static void StrInit(Str *p){ memset(p, 0, sizeof(*p)); } /* Append text to the end of a Str object */ static void StrAppend(Str *p, const char *z){ sqlite3_uint64 n = strlen(z); if( p->n + n >= p->nAlloc ){ char *zNew; sqlite3_uint64 nNew; if( p->oomErr ) return; nNew = p->nAlloc*2 + 100 + n; zNew = sqlite3_realloc64(p->z, nNew); if( zNew==0 ){ sqlite3_free(p->z); memset(p, 0, sizeof(*p)); p->oomErr = 1; return; } p->z = zNew; p->nAlloc = nNew; } memcpy(p->z + p->n, z, (int)n); p->n += n; p->z[p->n] = 0; } /* Return the current string content */ static char *StrStr(Str *p){ return p->z; } /* Free the string */ static void StrFree(Str *p){ sqlite3_free(p->z); StrInit(p); } /* ** Return the value of a hexadecimal digit. Return -1 if the input ** is not a hex digit. */ static int hexDigitValue(char c){ if( c>='0' && c<='9' ) return c - '0'; if( c>='a' && c<='f' ) return c - 'a' + 10; if( c>='A' && c<='F' ) return c - 'A' + 10; return -1; } /* ** Interpret zArg as an integer value, possibly with suffixes. */ static int integerValue(const char *zArg){ sqlite3_int64 v = 0; static const struct { char *zSuffix; int iMult; } aMult[] = { { "KiB", 1024 }, { "MiB", 1024*1024 }, { "GiB", 1024*1024*1024 }, { "KB", 1000 }, { "MB", 1000000 }, { "GB", 1000000000 }, { "K", 1000 }, { "M", 1000000 }, { "G", 1000000000 }, }; int i; int isNeg = 0; if( zArg[0]=='-' ){ isNeg = 1; zArg++; }else if( zArg[0]=='+' ){ zArg++; } if( zArg[0]=='0' && zArg[1]=='x' ){ int x; zArg += 2; while( (x = hexDigitValue(zArg[0]))>=0 ){ v = (v<<4) + x; zArg++; } }else{ while( ISDIGIT(zArg[0]) ){ v = v*10 + zArg[0] - '0'; zArg++; } } for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){ if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ v *= aMult[i].iMult; break; } } if( v>0x7fffffff ) fatalError("parameter too large - max 2147483648"); return (int)(isNeg? -v : v); } /* ** This callback is invoked by sqlite3_log(). */ static void sqlLog(void *pNotUsed, int iErrCode, const char *zMsg){ printf("LOG: (%d) %s\n", iErrCode, zMsg); fflush(stdout); } #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* ** This an SQL progress handler. After an SQL statement has run for ** many steps, we want to interrupt it. This guards against infinite ** loops from recursive common table expressions. ** ** *pVdbeLimitFlag is true if the --limit-vdbe command-line option is used. ** In that case, hitting the progress handler is a fatal error. */ static int progressHandler(void *pVdbeLimitFlag){ if( *(int*)pVdbeLimitFlag ) fatalError("too many VDBE cycles"); return 1; } #endif /* ** Allowed values for the runFlags parameter to runSql() */ #define SQL_TRACE 0x0001 /* Print each SQL statement as it is prepared */ #define SQL_OUTPUT 0x0002 /* Show the SQL output */ /* ** Run multiple commands of SQL. Similar to sqlite3_exec(), but does not ** stop if an error is encountered. */ static void runSql(sqlite3 *db, const char *zSql, unsigned runFlags){ const char *zMore; const char *zEnd = &zSql[strlen(zSql)]; sqlite3_stmt *pStmt; while( zSql && zSql[0] ){ zMore = 0; pStmt = 0; sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zMore); assert( zMore<=zEnd ); if( zMore==zSql ) break; if( runFlags & SQL_TRACE ){ const char *z = zSql; int n; while( z<zMore && ISSPACE(z[0]) ) z++; n = (int)(zMore - z); while( n>0 && ISSPACE(z[n-1]) ) n--; if( n==0 ) break; if( pStmt==0 ){ printf("TRACE: %.*s (error: %s)\n", n, z, sqlite3_errmsg(db)); }else{ printf("TRACE: %.*s\n", n, z); } } zSql = zMore; if( pStmt ){ if( (runFlags & SQL_OUTPUT)==0 ){ while( SQLITE_ROW==sqlite3_step(pStmt) ){} }else{ int nCol = -1; int nRow; for(nRow=0; SQLITE_ROW==sqlite3_step(pStmt); nRow++){ int i; if( nCol<0 ){ nCol = sqlite3_column_count(pStmt); } for(i=0; i<nCol; i++){ int eType = sqlite3_column_type(pStmt,i); printf("ROW[%d].%s = ", nRow, sqlite3_column_name(pStmt,i)); switch( eType ){ case SQLITE_NULL: { printf("NULL\n"); break; } case SQLITE_INTEGER: { printf("INT %s\n", sqlite3_column_text(pStmt,i)); break; } case SQLITE_FLOAT: { printf("FLOAT %s\n", sqlite3_column_text(pStmt,i)); break; } case SQLITE_TEXT: { printf("TEXT [%s]\n", sqlite3_column_text(pStmt,i)); break; } case SQLITE_BLOB: { printf("BLOB (%d bytes)\n", sqlite3_column_bytes(pStmt,i)); break; } } } } } sqlite3_finalize(pStmt); } } } int main(int argc, char **argv){ int i; /* Loop counter */ int nDb = 0; /* Number of databases to fuzz */ char **azDb = 0; /* Names of the databases (limit: 20) */ int verboseFlag = 0; /* True for extra output */ int noLookaside = 0; /* Disable lookaside if true */ int vdbeLimitFlag = 0; /* Stop after 100,000 VDBE ops */ int nHeap = 0; /* True for fixed heap size */ int iTimeout = 0; /* Timeout delay in seconds */ int rc; /* Result code from SQLite3 API calls */ sqlite3 *db; /* The database connection */ sqlite3_stmt *pStmt; /* A single SQL statement */ Str sql; /* SQL to run */ unsigned runFlags = 0; /* Flags passed to runSql */ for(i=1; i<argc; i++){ char *z = argv[i]; if( z[0]!='-' ){ azDb = realloc(azDb, sizeof(azDb[0])*(nDb+1)); if( azDb==0 ) fatalError("out of memory"); azDb[nDb++] = z; continue; } z++; if( z[0]=='-' ) z++; if( strcmp(z, "help")==0 ){ showHelp(argv[0]); }else if( strcmp(z, "limit-mem")==0 ){ if( i==argc-1 ) fatalError("missing argument to %s", argv[i]); nHeap = integerValue(argv[++i]); }else if( strcmp(z, "no-lookaside")==0 ){ noLookaside = 1; }else if( strcmp(z, "timeout")==0 ){ if( i==argc-1 ) fatalError("missing argument to %s", argv[i]); iTimeout = integerValue(argv[++i]); }else if( strcmp(z, "trace")==0 ){ runFlags |= SQL_OUTPUT|SQL_TRACE; }else if( strcmp(z, "limit-vdbe")==0 ){ vdbeLimitFlag = 1; }else if( strcmp(z, "v")==0 || strcmp(z, "verbose")==0 ){ verboseFlag = 1; runFlags |= SQL_TRACE; }else{ fatalError("unknown command-line option: \"%s\"\n", argv[i]); } } if( nDb==0 ){ showHelp(argv[0]); } if( verboseFlag ){ sqlite3_config(SQLITE_CONFIG_LOG, sqlLog); } if( nHeap>0 ){ void *pHeap = malloc( nHeap ); if( pHeap==0 ) fatalError("cannot allocate %d-byte heap\n", nHeap); rc = sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nHeap, 32); if( rc ) fatalError("heap configuration failed: %d\n", rc); } if( noLookaside ){ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0); } inmemVfsRegister(); formatVfs(); StrInit(&sql); #ifdef __unix__ signal(SIGALRM, timeoutHandler); #endif for(i=0; i<nDb; i++){ if( verboseFlag && nDb>1 ){ printf("DATABASE-FILE: %s\n", azDb[i]); fflush(stdout); } if( iTimeout ) setAlarm(iTimeout); createVFile("test.db", azDb[i]); rc = sqlite3_open_v2("test.db", &db, SQLITE_OPEN_READWRITE, "inmem"); if( rc ){ printf("cannot open test.db for \"%s\"\n", azDb[i]); reformatVfs(); continue; } #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( vdbeLimitFlag ){ sqlite3_progress_handler(db, 100000, progressHandler, &vdbeLimitFlag); } #endif rc = sqlite3_prepare_v2(db, "SELECT sql FROM autoexec", -1, &pStmt, 0); if( rc==SQLITE_OK ){ while( SQLITE_ROW==sqlite3_step(pStmt) ){ StrAppend(&sql, (const char*)sqlite3_column_text(pStmt, 0)); StrAppend(&sql, "\n"); } } sqlite3_finalize(pStmt); StrAppend(&sql, "PRAGMA integrity_check;\n"); runSql(db, StrStr(&sql), runFlags); sqlite3_close(db); reformatVfs(); StrFree(&sql); if( sqlite3_memory_used()>0 ){ free(azDb); reformatVfs(); fatalError("memory leak of %lld bytes", sqlite3_memory_used()); } } StrFree(&sql); reformatVfs(); return 0; } |
Added test/distinct2.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 | # 2016-04-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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is DISTINCT queries using the skip-ahead # optimization. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix distinct2 do_execsql_test 100 { CREATE TABLE t1(x INTEGER PRIMARY KEY); INSERT INTO t1 VALUES(0),(1),(2); CREATE TABLE t2 AS SELECT DISTINCT a.x AS aa, b.x AS bb FROM t1 a, t1 b; SELECT *, '|' FROM t2 ORDER BY aa, bb; } {0 0 | 0 1 | 0 2 | 1 0 | 1 1 | 1 2 | 2 0 | 2 1 | 2 2 |} do_execsql_test 110 { DROP TABLE t2; CREATE TABLE t2 AS SELECT DISTINCT a.x AS aa, b.x AS bb FROM t1 a, t1 b WHERE a.x IN t1 AND b.x IN t1; SELECT *, '|' FROM t2 ORDER BY aa, bb; } {0 0 | 0 1 | 0 2 | 1 0 | 1 1 | 1 2 | 2 0 | 2 1 | 2 2 |} do_execsql_test 120 { CREATE TABLE t102 (i0 TEXT UNIQUE NOT NULL); INSERT INTO t102 VALUES ('0'),('1'),('2'); DROP TABLE t2; CREATE TABLE t2 AS SELECT DISTINCT * FROM t102 AS t0 JOIN t102 AS t4 ON (t2.i0 IN t102) NATURAL JOIN t102 AS t3 JOIN t102 AS t1 ON (t0.i0 IN t102) JOIN t102 AS t2 ON (t2.i0=+t0.i0 OR (t0.i0<>500 AND t2.i0=t1.i0)); SELECT *, '|' FROM t2 ORDER BY 1, 2, 3, 4, 5; } {0 0 0 0 | 0 0 1 0 | 0 0 1 1 | 0 0 2 0 | 0 0 2 2 | 0 1 0 0 | 0 1 1 0 | 0 1 1 1 | 0 1 2 0 | 0 1 2 2 | 0 2 0 0 | 0 2 1 0 | 0 2 1 1 | 0 2 2 0 | 0 2 2 2 | 1 0 0 0 | 1 0 0 1 | 1 0 1 1 | 1 0 2 1 | 1 0 2 2 | 1 1 0 0 | 1 1 0 1 | 1 1 1 1 | 1 1 2 1 | 1 1 2 2 | 1 2 0 0 | 1 2 0 1 | 1 2 1 1 | 1 2 2 1 | 1 2 2 2 | 2 0 0 0 | 2 0 0 2 | 2 0 1 1 | 2 0 1 2 | 2 0 2 2 | 2 1 0 0 | 2 1 0 2 | 2 1 1 1 | 2 1 1 2 | 2 1 2 2 | 2 2 0 0 | 2 2 0 2 | 2 2 1 1 | 2 2 1 2 | 2 2 2 2 |} do_execsql_test 400 { CREATE TABLE t4(a,b,c,d,e,f,g,h,i,j); INSERT INTO t4 VALUES(0,1,2,3,4,5,6,7,8,9); INSERT INTO t4 SELECT * FROM t4; INSERT INTO t4 SELECT * FROM t4; CREATE INDEX t4x ON t4(c,d,e); SELECT DISTINCT a,b,c FROM t4 WHERE a=0 AND b=1; } {0 1 2} do_execsql_test 410 { SELECT DISTINCT a,b,c,d FROM t4 WHERE a=0 AND b=1; } {0 1 2 3} do_execsql_test 411 { SELECT DISTINCT d,a,b,c FROM t4 WHERE a=0 AND b=1; } {3 0 1 2} do_execsql_test 420 { SELECT DISTINCT a,b,c,d,e FROM t4 WHERE a=0 AND b=1; } {0 1 2 3 4} do_execsql_test 430 { SELECT DISTINCT a,b,c,d,e,f FROM t4 WHERE a=0 AND b=1; } {0 1 2 3 4 5} do_execsql_test 500 { CREATE TABLE t5(a INT, b INT); CREATE UNIQUE INDEX t5x ON t5(a+b); INSERT INTO t5(a,b) VALUES(0,0),(1,0),(1,1),(0,3); CREATE TEMP TABLE out AS SELECT DISTINCT a+b FROM t5; SELECT * FROM out ORDER BY 1; } {0 1 2 3} do_execsql_test 600 { CREATE TABLE t6a(x INTEGER PRIMARY KEY); INSERT INTO t6a VALUES(1); CREATE TABLE t6b(y INTEGER PRIMARY KEY); INSERT INTO t6b VALUES(2),(3); SELECT DISTINCT x, x FROM t6a, t6b; } {1 1} do_execsql_test 700 { CREATE TABLE t7(a, b, c); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE (i+1)<200 ) INSERT INTO t7 SELECT i/100, i/50, i FROM s; } do_execsql_test 710 { SELECT DISTINCT a, b FROM t7; } { 0 0 0 1 1 2 1 3 } do_execsql_test 720 { SELECT DISTINCT a, b+1 FROM t7; } { 0 1 0 2 1 3 1 4 } do_execsql_test 730 { CREATE INDEX i7 ON t7(a, b+1); ANALYZE; SELECT DISTINCT a, b+1 FROM t7; } { 0 1 0 2 1 3 1 4 } do_execsql_test 800 { CREATE TABLE t8(a, b, c); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE (i+1)<100 ) INSERT INTO t8 SELECT i/40, i/20, i/40 FROM s; } do_execsql_test 820 { SELECT DISTINCT a, b, c FROM t8; } { 0 0 0 0 1 0 1 2 1 1 3 1 2 4 2 } do_execsql_test 820 { SELECT DISTINCT a, b, c FROM t8 WHERE b=3; } {1 3 1} do_execsql_test 830 { CREATE INDEX i8 ON t8(a, c); ANALYZE; SELECT DISTINCT a, b, c FROM t8 WHERE b=3; } {1 3 1} do_execsql_test 900 { CREATE TABLE t9(v); INSERT INTO t9 VALUES ('abcd'), ('Abcd'), ('aBcd'), ('ABcd'), ('abCd'), ('AbCd'), ('aBCd'), ('ABCd'), ('abcD'), ('AbcD'), ('aBcD'), ('ABcD'), ('abCD'), ('AbCD'), ('aBCD'), ('ABCD'), ('wxyz'), ('Wxyz'), ('wXyz'), ('WXyz'), ('wxYz'), ('WxYz'), ('wXYz'), ('WXYz'), ('wxyZ'), ('WxyZ'), ('wXyZ'), ('WXyZ'), ('wxYZ'), ('WxYZ'), ('wXYZ'), ('WXYZ'); } do_execsql_test 910 { SELECT DISTINCT v COLLATE NOCASE, v FROM t9 ORDER BY +v; } { ABCD ABCD ABCd ABCd ABcD ABcD ABcd ABcd AbCD AbCD AbCd AbCd AbcD AbcD Abcd Abcd WXYZ WXYZ WXYz WXYz WXyZ WXyZ WXyz WXyz WxYZ WxYZ WxYz WxYz WxyZ WxyZ Wxyz Wxyz aBCD aBCD aBCd aBCd aBcD aBcD aBcd aBcd abCD abCD abCd abCd abcD abcD abcd abcd wXYZ wXYZ wXYz wXYz wXyZ wXyZ wXyz wXyz wxYZ wxYZ wxYz wxYz wxyZ wxyZ wxyz wxyz } do_execsql_test 920 { CREATE INDEX i9 ON t9(v COLLATE NOCASE, v); ANALYZE; SELECT DISTINCT v COLLATE NOCASE, v FROM t9 ORDER BY +v; } { ABCD ABCD ABCd ABCd ABcD ABcD ABcd ABcd AbCD AbCD AbCd AbCd AbcD AbcD Abcd Abcd WXYZ WXYZ WXYz WXYz WXyZ WXyZ WXyz WXyz WxYZ WxYZ WxYz WxYz WxyZ WxyZ Wxyz Wxyz aBCD aBCD aBCd aBCd aBcD aBcD aBcd aBcd abCD abCD abCd abCd abcD abcD abcd abcd wXYZ wXYZ wXYz wXYz wXyZ wXyZ wXyz wXyz wxYZ wxYZ wxYz wxYz wxyZ wxyZ wxyz wxyz } finish_test |
Changes to test/e_blobwrite.test.
︙ | ︙ | |||
106 107 108 109 110 111 112 | sqlite3_blob_close $B # EVIDENCE-OF: R-29804-27366 If offset iOffset is less than N bytes from # the end of the BLOB, SQLITE_ERROR is returned and no data is written. # sqlite3_blob_open db main t2 a 44 3 B blob_write_error_test 2.2.1 $B 31 $blob 10 \ | | | | | | | 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 | sqlite3_blob_close $B # EVIDENCE-OF: R-29804-27366 If offset iOffset is less than N bytes from # the end of the BLOB, SQLITE_ERROR is returned and no data is written. # sqlite3_blob_open db main t2 a 44 3 B blob_write_error_test 2.2.1 $B 31 $blob 10 \ SQLITE_ERROR {SQL logic error} # Make a successful write to the blob handle. This shows that the # sqlite3_errcode() and sqlite3_errmsg() values are set even if the # blob_write() call succeeds (see requirement in the [blob_write_error_test] # proc). blob_write_error_test 2.2.1 $B 30 $blob 10 SQLITE_OK {not an error} # EVIDENCE-OF: R-58570-38916 If N or iOffset are less than zero # SQLITE_ERROR is returned and no data is written. # blob_write_error_test 2.2.2 $B 31 $blob -1 \ SQLITE_ERROR {SQL logic error} blob_write_error_test 2.2.3 $B 20 $blob 10 SQLITE_OK {not an error} blob_write_error_test 2.2.4 $B -1 $blob 10 \ SQLITE_ERROR {SQL logic error} sqlite3_blob_close $B # EVIDENCE-OF: R-20958-54138 An attempt to write to an expired BLOB # handle fails with an error code of SQLITE_ABORT. # do_test 2.3 { sqlite3_blob_open db main t2 a 43 0 B execsql { DELETE FROM t2 WHERE b=43 } } {} blob_write_error_test 2.3.1 $B 5 $blob 5 \ SQLITE_ABORT {query aborted} do_test 2.3.2 { execsql { SELECT 1, 2, 3 } sqlite3_errcode db } {SQLITE_OK} blob_write_error_test 2.3.3 $B 5 $blob 5 \ SQLITE_ABORT {query aborted} sqlite3_blob_close $B # EVIDENCE-OF: R-08382-59936 Writes to the BLOB that occurred before the # BLOB handle expired are not rolled back by the expiration of the # handle, though of course those changes might have been overwritten by # the statement that expired the BLOB handle or by other independent # statements. |
︙ | ︙ | |||
169 170 171 172 173 174 175 | do_execsql_test 3.1.2 { UPDATE t3 SET k = 'xyz' WHERE i=1; SELECT * FROM t3 WHERE i=1; } { 1 .....0123456789......................... xyz } blob_write_error_test 3.1.3 $B 15 $blob 10 \ | | | | 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 | do_execsql_test 3.1.2 { UPDATE t3 SET k = 'xyz' WHERE i=1; SELECT * FROM t3 WHERE i=1; } { 1 .....0123456789......................... xyz } blob_write_error_test 3.1.3 $B 15 $blob 10 \ SQLITE_ABORT {query aborted} sqlite3_blob_close $B do_execsql_test 3.1.4 { SELECT * FROM t3 WHERE i=1; } { 1 .....0123456789......................... xyz } sqlite3_blob_open db main t3 j 2 1 B blob_write_error_test 3.2.1 $B 5 $blob 10 SQLITE_OK {not an error} do_execsql_test 3.2.2 { UPDATE t3 SET j = 'xyz' WHERE i=2; SELECT * FROM t3 WHERE i=2; } { 2 xyz ........................................ } blob_write_error_test 3.2.3 $B 15 $blob 10 \ SQLITE_ABORT {query aborted} sqlite3_blob_close $B do_execsql_test 3.2.4 { SELECT * FROM t3 WHERE i=2; } { 2 xyz ........................................ } finish_test |
Changes to test/e_dropview.test.
︙ | ︙ | |||
41 42 43 44 45 46 47 | } } proc list_all_views {{db db}} { set res [list] $db eval { PRAGMA database_list } { set tbl "$name.sqlite_master" | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | } } proc list_all_views {{db db}} { set res [list] $db eval { PRAGMA database_list } { set tbl "$name.sqlite_master" if {$name == "temp"} { set tbl temp.sqlite_master } set sql "SELECT '$name.' || name FROM $tbl WHERE type = 'view'" lappend res {*}[$db eval $sql] } set res } |
︙ | ︙ |
Changes to test/e_expr.test.
︙ | ︙ | |||
250 251 252 253 254 255 256 | do_execsql_test e_expr-5.$tn "SELECT $a || $b" [list "${as}${bs}"] } #------------------------------------------------------------------------- # Test the % operator. # | | | > | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | do_execsql_test e_expr-5.$tn "SELECT $a || $b" [list "${as}${bs}"] } #------------------------------------------------------------------------- # Test the % operator. # # EVIDENCE-OF: R-04223-04352 The operator % outputs the integer value of # its left operand modulo its right operand. # do_execsql_test e_expr-6.1 {SELECT 72%5} {2} do_execsql_test e_expr-6.2 {SELECT 72%-5} {2} do_execsql_test e_expr-6.3 {SELECT -72%-5} {-2} do_execsql_test e_expr-6.4 {SELECT -72%5} {-2} do_execsql_test e_expr-6.5 {SELECT 72.35%5} {2.0} #------------------------------------------------------------------------- # Test that the results of all binary operators are either numeric or # NULL, except for the || operator, which may evaluate to either a text # value or NULL. # # EVIDENCE-OF: R-20665-17792 The result of any binary operator is either |
︙ | ︙ | |||
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 | # result of the CAST expression is also NULL. # do_expr_test e_expr-27.2.1 { CAST(NULL AS integer) } null {} do_expr_test e_expr-27.2.2 { CAST(NULL AS text) } null {} do_expr_test e_expr-27.2.3 { CAST(NULL AS blob) } null {} do_expr_test e_expr-27.2.4 { CAST(NULL AS number) } null {} # EVIDENCE-OF: R-43522-35548 Casting a value to a type-name with no # affinity causes the value to be converted into a BLOB. # do_expr_test e_expr-27.3.1 { CAST('abc' AS blob) } blob abc do_expr_test e_expr-27.3.2 { CAST('def' AS shobblob_x) } blob def do_expr_test e_expr-27.3.3 { CAST('ghi' AS abbLOb10) } blob ghi | > > > > > > > | 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 | # result of the CAST expression is also NULL. # do_expr_test e_expr-27.2.1 { CAST(NULL AS integer) } null {} do_expr_test e_expr-27.2.2 { CAST(NULL AS text) } null {} do_expr_test e_expr-27.2.3 { CAST(NULL AS blob) } null {} do_expr_test e_expr-27.2.4 { CAST(NULL AS number) } null {} # EVIDENCE-OF: R-29283-15561 Otherwise, the storage class of the result # is determined by applying the rules for determining column affinity to # the type-name. # # The R-29283-15561 requirement above is demonstrated by all of the # subsequent e_expr-26 tests. # # EVIDENCE-OF: R-43522-35548 Casting a value to a type-name with no # affinity causes the value to be converted into a BLOB. # do_expr_test e_expr-27.3.1 { CAST('abc' AS blob) } blob abc do_expr_test e_expr-27.3.2 { CAST('def' AS shobblob_x) } blob def do_expr_test e_expr-27.3.3 { CAST('ghi' AS abbLOb10) } blob ghi |
︙ | ︙ | |||
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 | do_expr_test e_expr-32.2.3 { CAST(-9223372036854775808 AS NUMERIC) } integer -9223372036854775808 do_expr_test e_expr-32.2.4 { CAST(9223372036854775807 AS NUMERIC) } integer 9223372036854775807 # EVIDENCE-OF: R-64550-29191 Note that the result from casting any # non-BLOB value into a BLOB and the result from casting any BLOB value # into a non-BLOB value may be different depending on whether the # database encoding is UTF-8, UTF-16be, or UTF-16le. # ifcapable {utf16} { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_expr_test e_expr-32.2.3 { CAST(-9223372036854775808 AS NUMERIC) } integer -9223372036854775808 do_expr_test e_expr-32.2.4 { CAST(9223372036854775807 AS NUMERIC) } integer 9223372036854775807 do_expr_test e_expr-32.2.5 { CAST('9223372036854775807 ' AS NUMERIC) } integer 9223372036854775807 do_expr_test e_expr-32.2.6 { CAST(' 9223372036854775807 ' AS NUMERIC) } integer 9223372036854775807 do_expr_test e_expr-32.2.7 { CAST(' ' AS NUMERIC) } integer 0 do_execsql_test e_expr-32.2.8 { WITH t1(x) AS (VALUES ('9000000000000000001'), ('9000000000000000001x'), ('9000000000000000001 '), (' 9000000000000000001 '), (' 9000000000000000001'), (' 9000000000000000001.'), ('9223372036854775807'), ('9223372036854775807 '), (' 9223372036854775807 '), ('9223372036854775808'), (' 9223372036854775808 '), ('9223372036854775807.0'), ('9223372036854775807e+0'), ('-5.0'), ('-5e+0')) SELECT typeof(CAST(x AS NUMERIC)), CAST(x AS NUMERIC)||'' FROM t1; } [list \ integer 9000000000000000001 \ integer 9000000000000000001 \ integer 9000000000000000001 \ integer 9000000000000000001 \ integer 9000000000000000001 \ integer 9000000000000000001 \ integer 9223372036854775807 \ integer 9223372036854775807 \ integer 9223372036854775807 \ real 9.22337203685478e+18 \ real 9.22337203685478e+18 \ integer 9223372036854775807 \ integer 9223372036854775807 \ integer -5 \ integer -5 \ ] # EVIDENCE-OF: R-64550-29191 Note that the result from casting any # non-BLOB value into a BLOB and the result from casting any BLOB value # into a non-BLOB value may be different depending on whether the # database encoding is UTF-8, UTF-16be, or UTF-16le. # ifcapable {utf16} { |
︙ | ︙ | |||
1782 1783 1784 1785 1786 1787 1788 | CREATE TABLE t2(a, b); INSERT INTO t2 VALUES('one', 'two'); INSERT INTO t2 VALUES('three', NULL); INSERT INTO t2 VALUES(4, 5.0); } } {} | | | | | > | | | | | 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 | CREATE TABLE t2(a, b); INSERT INTO t2 VALUES('one', 'two'); INSERT INTO t2 VALUES('three', NULL); INSERT INTO t2 VALUES(4, 5.0); } } {} # EVIDENCE-OF: R-43573-23448 A SELECT statement enclosed in parentheses # is a subquery. # # EVIDENCE-OF: R-56294-03966 All types of SELECT statement, including # aggregate and compound SELECT queries (queries with keywords like # UNION or EXCEPT) are allowed as scalar subqueries. # do_expr_test e_expr-35.1.1 { (SELECT 35) } integer 35 do_expr_test e_expr-35.1.2 { (SELECT NULL) } null {} do_expr_test e_expr-35.1.3 { (SELECT count(*) FROM t2) } integer 3 do_expr_test e_expr-35.1.4 { (SELECT 4 FROM t2) } integer 4 do_expr_test e_expr-35.1.5 { (SELECT b FROM t2 UNION SELECT a+1 FROM t2) } null {} do_expr_test e_expr-35.1.6 { (SELECT a FROM t2 UNION SELECT COALESCE(b, 55) FROM t2 ORDER BY 1) } integer 4 # EVIDENCE-OF: R-22239-33740 A subquery that returns two or more columns # is a row value subquery and can only be used as the operand of a # comparison operator. # # The following block tests that errors are returned in a bunch of cases # where a subquery returns more than one column. # set M {/1 {sub-select returns [23] columns - expected 1}/} foreach {tn sql} { 1 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2) } 2 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2 ORDER BY 1) } 3 { SELECT (SELECT 1, 2) } 4 { SELECT (SELECT NULL, NULL, NULL) } 5 { SELECT (SELECT * FROM t2) } 6 { SELECT (SELECT * FROM (SELECT 1, 2, 3)) } } { do_catchsql_test e_expr-35.2.$tn $sql $M } # EVIDENCE-OF: R-18318-14995 The value of a subquery expression is the # first row of the result from the enclosed SELECT statement. # # EVIDENCE-OF: R-15900-52156 In other words, an implied "LIMIT 1" is # added to the subquery, overriding an explicitly coded LIMIT. # do_execsql_test e_expr-36.3.1 { CREATE TABLE t4(x, y); INSERT INTO t4 VALUES(1, 'one'); INSERT INTO t4 VALUES(2, 'two'); INSERT INTO t4 VALUES(3, 'three'); } {} |
︙ | ︙ | |||
1848 1849 1850 1851 1852 1853 1854 | 8 { ( SELECT group_concat(y,'') FROM t4 ) } text onetwothree 9 { ( SELECT max(x) FROM t4 WHERE y LIKE '___') } integer 2 } { do_expr_test e_expr-36.3.$tn $expr $restype $resval } | | | | 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 | 8 { ( SELECT group_concat(y,'') FROM t4 ) } text onetwothree 9 { ( SELECT max(x) FROM t4 WHERE y LIKE '___') } integer 2 } { do_expr_test e_expr-36.3.$tn $expr $restype $resval } # EVIDENCE-OF: R-52325-25449 The value of a subquery expression is NULL # if the enclosed SELECT statement returns no rows. # foreach {tn expr} { 1 { ( SELECT x FROM t4 WHERE x>3 ORDER BY x ) } 2 { ( SELECT x FROM t4 WHERE y<'one' ORDER BY y ) } } { do_expr_test e_expr-36.4.$tn $expr null {} } |
︙ | ︙ |
Changes to test/e_fkey.test.
︙ | ︙ | |||
725 726 727 728 729 730 731 | # application from preparing SQL statements that modify the content of # the child or parent tables in ways that use the foreign keys. # # EVIDENCE-OF: R-03108-63659 The English language error message for # foreign key DML errors is usually "foreign key mismatch" but can also # be "no such table" if the parent table does not exist. # | | | | | | | | | | | 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 | # application from preparing SQL statements that modify the content of # the child or parent tables in ways that use the foreign keys. # # EVIDENCE-OF: R-03108-63659 The English language error message for # foreign key DML errors is usually "foreign key mismatch" but can also # be "no such table" if the parent table does not exist. # # EVIDENCE-OF: R-35763-48267 Foreign key DML errors are reported if: The # parent table does not exist, or The parent key columns named in the # foreign key constraint do not exist, or The parent key columns named # in the foreign key constraint are not the primary key of the parent # table and are not subject to a unique constraint using collating # sequence specified in the CREATE TABLE, or The child table references # the primary key of the parent without specifying the primary key # columns and the number of primary key columns in the parent do not # match the number of child key columns. # do_test e_fkey-20.1 { execsql { CREATE TABLE c1(c REFERENCES nosuchtable, d); CREATE TABLE p2(a, b, UNIQUE(a, b)); CREATE TABLE c2(c, d, FOREIGN KEY(c, d) REFERENCES p2(a, x)); |
︙ | ︙ |
Changes to test/e_fts3.test.
︙ | ︙ | |||
173 174 175 176 177 178 179 | } {53 {Home Page} {SQLite is a software...} 54 {Download SQLite} {All SQLite source code...}} write_test 1.2.1.8 pages_content { DELETE FROM pages } read_test 1.2.1.9 { SELECT docid, * FROM pages } {} do_error_test fts3-1.2.1.10 { INSERT INTO pages(rowid, docid, title, body) VALUES(1, 2, 'A title', 'A document body'); | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | } {53 {Home Page} {SQLite is a software...} 54 {Download SQLite} {All SQLite source code...}} write_test 1.2.1.8 pages_content { DELETE FROM pages } read_test 1.2.1.9 { SELECT docid, * FROM pages } {} do_error_test fts3-1.2.1.10 { INSERT INTO pages(rowid, docid, title, body) VALUES(1, 2, 'A title', 'A document body'); } {SQL logic error} # Test the optimize() function example: ddl_test 1.2.2.1 { CREATE VIRTUAL TABLE docs USING fts3 } write_test 1.2.2.2 docs_content { INSERT INTO docs VALUES('Others translate the first clause as'); } write_test 1.2.2.3 docs_content { |
︙ | ︙ |
Changes to test/e_insert.test.
︙ | ︙ | |||
344 345 346 347 348 349 350 | 5.1 "INSERT INTO a1 DEFAULT VALUES" {} 5.2 "SELECT * FROM a1" {{} {}} 6.1 "INSERT INTO a1 DEFAULT VALUES" {} 6.2 "SELECT * FROM a1" {{} {} {} {}} } | | > | | | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | 5.1 "INSERT INTO a1 DEFAULT VALUES" {} 5.2 "SELECT * FROM a1" {{} {}} 6.1 "INSERT INTO a1 DEFAULT VALUES" {} 6.2 "SELECT * FROM a1" {{} {} {} {}} } # EVIDENCE-OF: R-00267-47727 The initial "INSERT" keyword can be # replaced by "REPLACE" or "INSERT OR action" to specify an alternative # constraint conflict resolution algorithm to use during that one INSERT # command. # # EVIDENCE-OF: R-23110-47146 the parser allows the use of the single # keyword REPLACE as an alias for "INSERT OR REPLACE". # # The two requirements above are tested by e_select-4.1.* and # e_select-4.2.*, respectively. # |
︙ | ︙ |
Changes to test/e_select.test.
︙ | ︙ | |||
1248 1249 1250 1251 1252 1253 1254 | # EVIDENCE-OF: R-02054-15343 For the purposes of detecting duplicate # rows, two NULL values are considered to be equal. # do_select_tests e_select-5.5 { 1 "SELECT DISTINCT d FROM h3" {{} 2 2,3 2,4 3} } | | | | 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 | # EVIDENCE-OF: R-02054-15343 For the purposes of detecting duplicate # rows, two NULL values are considered to be equal. # do_select_tests e_select-5.5 { 1 "SELECT DISTINCT d FROM h3" {{} 2 2,3 2,4 3} } # EVIDENCE-OF: R-47709-27231 The usual rules apply for selecting a # collation sequence to compare text values. # do_select_tests e_select-5.6 { 1 "SELECT DISTINCT b FROM h1" {one I i four IV iv} 2 "SELECT DISTINCT b COLLATE nocase FROM h1" {one I four IV} 3 "SELECT DISTINCT x FROM h2" {One Two Three Four} 4 "SELECT DISTINCT x COLLATE binary FROM h2" { One Two Three Four one two three four |
︙ | ︙ |
Changes to test/e_walhook.test.
︙ | ︙ | |||
117 118 119 120 121 122 123 | # will propagate back up through the SQLite code base to cause the # statement that provoked the callback to report an error, though the # commit will have still occurred. # proc my_wal_hook {args} { return 1 ;# SQLITE_ERROR } do_catchsql_test 4.1 { INSERT INTO t1 VALUES(7) | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # will propagate back up through the SQLite code base to cause the # statement that provoked the callback to report an error, though the # commit will have still occurred. # proc my_wal_hook {args} { return 1 ;# SQLITE_ERROR } do_catchsql_test 4.1 { INSERT INTO t1 VALUES(7) } {1 {SQL logic error}} proc my_wal_hook {args} { return 5 ;# SQLITE_BUSY } do_catchsql_test 4.2 { INSERT INTO t1 VALUES(8) } {1 {database is locked}} proc my_wal_hook {args} { return 14 ;# SQLITE_CANTOPEN } |
︙ | ︙ |
Added test/emptytable.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 | # 2017-02-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 to show that a join involving an empty table is very fast. # set testdir [file dirname $argv0] source $testdir/tester.tcl # Build some test data # do_execsql_test emptytable-100 { CREATE TABLE t1(a); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) INSERT INTO t1(a) SELECT x FROM c; CREATE TABLE empty(x); SELECT count(*) FROM t1; } {100} # Interrupt queries after 1M cycles to prevent burning excess CPU proc stopDb {args} { db interrupt } db progress 1000000 {stopDb} # Prior to the query planner optimization on 2017-02-15, this query would # take a ridiculous amount of time. If that optimization stops working, # the result here will be in interrupt for running too long. # do_catchsql_test emptytable-110 { SELECT count(*) FROM t1, t1, t1, t1, t1, t1, empty; } {0 0} do_catchsql_test emptytable-120 { SELECT count(*) FROM t1, t1 LEFT JOIN empty; } {0 10000} do_catchsql_test emptytable-121 { SELECT count(*) FROM t1, t1 LEFT JOIN t1, empty; } {0 0} finish_test |
Changes to test/errmsg.test.
︙ | ︙ | |||
52 53 54 55 56 57 58 | #------------------------------------------------------------------------- # Test error messages returned by user-defined SQL functions. # do_test 1.1 { error_messages "SELECT sql_error('custom message')" } [list {*}{ | | | | 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 | #------------------------------------------------------------------------- # Test error messages returned by user-defined SQL functions. # do_test 1.1 { error_messages "SELECT sql_error('custom message')" } [list {*}{ SQLITE_ERROR {SQL logic error} SQLITE_ERROR {custom message} }] do_test 1.2 { error_messages_v2 "SELECT sql_error('custom message')" } [list {*}{ SQLITE_ERROR {custom message} SQLITE_ERROR {custom message} }] #------------------------------------------------------------------------- # Test error messages generated directly by VDBE code (e.g. constraint # failures). # do_execsql_test 2.1 { CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); INSERT INTO t1 VALUES('abc', 'def'); } do_test 2.2 { error_messages "INSERT INTO t1 VALUES('ghi', 'def')" } [list {*}{ SQLITE_ERROR {SQL logic error} SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.b} }] verify_ex_errcode 2.2b SQLITE_CONSTRAINT_UNIQUE do_test 2.3 { error_messages_v2 "INSERT INTO t1 VALUES('ghi', 'def')" } [list {*}{ SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.b} |
︙ | ︙ | |||
97 98 99 100 101 102 103 | do_execsql_test 3.1.1 { CREATE TABLE t2(a PRIMARY KEY, b UNIQUE); INSERT INTO t2 VALUES('abc', 'def'); } do_test 3.1.2 { error_messages "SELECT a FROM t2" "DROP TABLE t2" } [list {*}{ | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | do_execsql_test 3.1.1 { CREATE TABLE t2(a PRIMARY KEY, b UNIQUE); INSERT INTO t2 VALUES('abc', 'def'); } do_test 3.1.2 { error_messages "SELECT a FROM t2" "DROP TABLE t2" } [list {*}{ SQLITE_ERROR {SQL logic error} SQLITE_SCHEMA {database schema has changed} }] do_execsql_test 3.2.1 { CREATE TABLE t2(a PRIMARY KEY, b UNIQUE); INSERT INTO t2 VALUES('abc', 'def'); } do_test 3.2.2 { |
︙ | ︙ |
Changes to test/exclusive2.test.
︙ | ︙ | |||
117 118 119 120 121 122 123 | # allocation of 24 pages (shared between all pagers). This is not enough for # this test. # do_test exclusive2-1.1 { execsql { BEGIN; CREATE TABLE t1(a, b); | | | | | | | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | # allocation of 24 pages (shared between all pagers). This is not enough for # this test. # do_test exclusive2-1.1 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1(a, b) VALUES(randstr(10, 400), 0); INSERT INTO t1(a, b) VALUES(randstr(10, 400), 0); INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1; COMMIT; SELECT count(*) FROM t1; } } {64} do_test exclusive2-1.2.1 { # Make sure the pager cache is large enough to store the # entire database. |
︙ | ︙ | |||
150 151 152 153 154 155 156 | } $::sig do_test exclusive2-1.4 { sqlite3 db2 test.db t1sig db2 } $::sig do_test exclusive2-1.5 { execsql { | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | } $::sig do_test exclusive2-1.4 { sqlite3 db2 test.db t1sig db2 } $::sig do_test exclusive2-1.5 { execsql { UPDATE t1 SET b=a, a=0; } db2 expr {[t1sig db2] eq $::sig} } 0 do_test exclusive2-1.6 { readPagerChangeCounter test.db } {2} do_test exclusive2-1.7 { |
︙ | ︙ |
Changes to test/fallocate.test.
︙ | ︙ | |||
55 56 57 58 59 60 61 | # # We need to check this to verify that if in the unlikely event a rollback # causes a database file to grow, the database grows to its previous size # on disk, not to the minimum size required to hold the database image. # do_test fallocate-1.7 { execsql { BEGIN; INSERT INTO t1 VALUES(1, 2); } | | > > | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | # # We need to check this to verify that if in the unlikely event a rollback # causes a database file to grow, the database grows to its previous size # on disk, not to the minimum size required to hold the database image. # do_test fallocate-1.7 { execsql { BEGIN; INSERT INTO t1 VALUES(1, 2); } if {[permutation] != "inmemory_journal" && [permutation] != "atomic-batch-write" } { hexio_get_int [hexio_read test.db-journal 16 4] } else { set {} 1024 } } {1024} do_test fallocate-1.8 { execsql { COMMIT } } {} |
︙ | ︙ |
Changes to test/filefmt.test.
︙ | ︙ | |||
41 42 43 44 45 46 47 | set x [catch {sqlite3 db test.db} err] lappend x $err } {0 {}} do_test filefmt-1.3 { catchsql { SELECT count(*) FROM sqlite_master } | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | set x [catch {sqlite3 db test.db} err] lappend x $err } {0 {}} do_test filefmt-1.3 { catchsql { SELECT count(*) FROM sqlite_master } } {1 {file is not a database}} do_test filefmt-1.4 { db close hexio_write test.db 0 53 sqlite3 db test.db catchsql { SELECT count(*) FROM sqlite_master } |
︙ | ︙ | |||
81 82 83 84 85 86 87 | do_test filefmt-1.6 { db close hexio_write test.db 16 [hexio_render_int16 1025] sqlite3 db test.db catchsql { SELECT count(*) FROM sqlite_master } | | | | | 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 | do_test filefmt-1.6 { db close hexio_write test.db 16 [hexio_render_int16 1025] sqlite3 db test.db catchsql { SELECT count(*) FROM sqlite_master } } {1 {file is not a database}} # The page-size must be at least 512 bytes # do_test filefmt-1.7 { db close hexio_write test.db 16 [hexio_render_int16 256] sqlite3 db test.db catchsql { SELECT count(*) FROM sqlite_master } } {1 {file is not a database}} # Usable space per page (page-size minus unused space per page) # must be at least 480 bytes # ifcapable pager_pragmas { do_test filefmt-1.8 { db close forcedelete test.db sqlite3 db test.db db eval {PRAGMA page_size=512; CREATE TABLE t1(x)} db close hexio_write test.db 20 21 sqlite3 db test.db catchsql { SELECT count(*) FROM sqlite_master } } {1 {file is not a database}} } #------------------------------------------------------------------------- # The following block of tests - filefmt-2.* - test that versions 3.7.0 # and later can read and write databases that have been modified or created # by 3.6.23.1 and earlier. The difference difference is that 3.7.0 stores # the size of the database in the database file header, whereas 3.6.23.1 |
︙ | ︙ |
Changes to test/fkey1.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # This file implements regression tests for SQLite library. # # This file implements tests for foreign keys. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable {!foreignkey} { finish_test return } # Create a table and some data to work with. | > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # This file implements regression tests for SQLite library. # # This file implements tests for foreign keys. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix fkey1 ifcapable {!foreignkey} { finish_test return } # Create a table and some data to work with. |
︙ | ︙ | |||
180 181 182 183 184 185 186 187 188 | INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 1, 'A-2-1'); INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (3, 2, 'A-3-2'); INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (4, 3, 'A-4-3'); } do_catchsql_test fkey1-5.4 { INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 3, 'A-2-3'); } {1 {FOREIGN KEY constraint failed}} finish_test | > > > > > > > > > > > > > > > > > > > > > | 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 | INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 1, 'A-2-1'); INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (3, 2, 'A-3-2'); INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (4, 3, 'A-4-3'); } do_catchsql_test fkey1-5.4 { INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 3, 'A-2-3'); } {1 {FOREIGN KEY constraint failed}} #------------------------------------------------------------------------- # Check that foreign key processing is not fooled by partial indexes # on the parent table. # do_execsql_test 6.0 { CREATE TABLE p1(x, y); CREATE UNIQUE INDEX p1x ON p1(x) WHERE y<2; INSERT INTO p1 VALUES(1, 1); CREATE TABLE c1(a REFERENCES p1(x)); } do_catchsql_test 6.1 { INSERT INTO c1 VALUES(1); } {1 {foreign key mismatch - "c1" referencing "p1"}} do_execsql_test 6.2 { CREATE UNIQUE INDEX p1x2 ON p1(x); INSERT INTO c1 VALUES(1); } {} finish_test |
Changes to test/fkey2.test.
︙ | ︙ | |||
1058 1059 1060 1061 1062 1063 1064 | catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 } } {1 {Cannot add a REFERENCES column with non-NULL default value}} do_test fkey2-14.1tmp.6 { execsql { PRAGMA foreign_keys = off; ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; PRAGMA foreign_keys = on; | | | 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 | catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 } } {1 {Cannot add a REFERENCES column with non-NULL default value}} do_test fkey2-14.1tmp.6 { execsql { PRAGMA foreign_keys = off; ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; PRAGMA foreign_keys = on; SELECT sql FROM temp.sqlite_master WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} do_test fkey2-14.2tmp.1.1 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} do_test fkey2-14.2tmp.1.2 { |
︙ | ︙ | |||
1089 1090 1091 1092 1093 1094 1095 | } [list \ {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1)} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)} \ {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \ ] do_test fkey2-14.2tmp.2.2 { execsql { ALTER TABLE t1 RENAME TO t4 } | | | 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | } [list \ {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1)} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)} \ {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \ ] do_test fkey2-14.2tmp.2.2 { execsql { ALTER TABLE t1 RENAME TO t4 } execsql { SELECT sql FROM temp.sqlite_master WHERE type = 'table'} } [list \ {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4")} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2)} \ {CREATE TABLE t3(a REFERENCES "t4", b REFERENCES t2, c REFERENCES "t4")} \ ] do_test fkey2-14.2tmp.2.3 { catchsql { INSERT INTO t3 VALUES(1, 2, 3) } |
︙ | ︙ |
Changes to test/fkey5.test.
︙ | ︙ | |||
111 112 113 114 115 116 117 | } {1 {no such table: temp.c2}} # EVIDENCE-OF: R-45728-08709 There are four columns in each result row. # # EVIDENCE-OF: R-55672-01620 The first column is the name of the table # that contains the REFERENCES clause. # | | | > > > | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | } {1 {no such table: temp.c2}} # EVIDENCE-OF: R-45728-08709 There are four columns in each result row. # # EVIDENCE-OF: R-55672-01620 The first column is the name of the table # that contains the REFERENCES clause. # # EVIDENCE-OF: R-00471-55166 The second column is the rowid of the row # that contains the invalid REFERENCES clause, or NULL if the child # table is a WITHOUT ROWID table. # # The second clause in the previous is tested by fkey5-10.3. # # EVIDENCE-OF: R-40482-20265 The third column is the name of the table # that is referred to. # # EVIDENCE-OF: R-62839-07969 The fourth column is the index of the # specific foreign key constraint that failed. # |
︙ | ︙ | |||
384 385 386 387 388 389 390 | PRAGMA foreign_key_check(k2); } {} do_execsql_test 9.4 { INSERT INTO k2 VALUES('six', 'seven'); PRAGMA foreign_key_check(k2); } {k2 3 s1 0} | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | PRAGMA foreign_key_check(k2); } {} do_execsql_test 9.4 { INSERT INTO k2 VALUES('six', 'seven'); PRAGMA foreign_key_check(k2); } {k2 3 s1 0} #------------------------------------------------------------------------- # Test using a WITHOUT ROWID table as the child table with an INTEGER # PRIMARY KEY as the parent key. # reset_db do_execsql_test 10.1 { CREATE TABLE p30 (id INTEGER PRIMARY KEY); CREATE TABLE IF NOT EXISTS c30 ( line INTEGER, master REFERENCES p30(id), PRIMARY KEY(master) ) WITHOUT ROWID; INSERT INTO p30 (id) VALUES (1); INSERT INTO c30 (master, line) VALUES (1, 999); } do_execsql_test 10.2 { PRAGMA foreign_key_check; } # EVIDENCE-OF: R-00471-55166 The second column is the rowid of the row # that contains the invalid REFERENCES clause, or NULL if the child # table is a WITHOUT ROWID table. do_execsql_test 10.3 { INSERT INTO c30 VALUES(45, 45); PRAGMA foreign_key_check; } {c30 {} p30 0} #------------------------------------------------------------------------- # Test "foreign key mismatch" errors. # reset_db do_execsql_test 11.0 { CREATE TABLE tt(y); CREATE TABLE c11(x REFERENCES tt(y)); } do_catchsql_test 11.1 { PRAGMA foreign_key_check; } {1 {foreign key mismatch - "c11" referencing "tt"}} finish_test |
Changes to test/fkey8.test.
︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 | set stmt [sqlite3_prepare_v2 db $sql -1 dummy] set ret [uses_stmt_journal $stmt] sqlite3_finalize $stmt set ret } $use_stmt } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | set stmt [sqlite3_prepare_v2 db $sql -1 dummy] set ret [uses_stmt_journal $stmt] sqlite3_finalize $stmt set ret } $use_stmt } #------------------------------------------------------------------------- # The following tests check that foreign key constaint counters are # correctly updated for any implicit DELETE operations that occur # when a REPLACE command is executed against a WITHOUT ROWID table # that has no triggers or auxiliary indexes. # reset_db do_execsql_test 2.1.0 { PRAGMA foreign_keys = on; CREATE TABLE p1(a PRIMARY KEY, b) WITHOUT ROWID; CREATE TABLE c1(x REFERENCES p1 DEFERRABLE INITIALLY DEFERRED); INSERT INTO p1 VALUES(1, 'one'); INSERT INTO p1 VALUES(2, 'two'); INSERT INTO c1 VALUES(1); INSERT INTO c1 VALUES(2); } do_catchsql_test 2.1.2 { BEGIN; DELETE FROM p1 WHERE a=1; INSERT OR REPLACE INTO p1 VALUES(2, 'two'); COMMIT; } {1 {FOREIGN KEY constraint failed}} reset_db do_execsql_test 2.2.0 { PRAGMA foreign_keys = on; CREATE TABLE p2(a PRIMARY KEY, b); CREATE TABLE c2( x PRIMARY KEY, y REFERENCES p2 DEFERRABLE INITIALLY DEFERRED ) WITHOUT ROWID; } do_catchsql_test 2.2.1 { BEGIN; INSERT INTO c2 VALUES(13, 13); INSERT OR REPLACE INTO c2 VALUES(13, 13); DELETE FROM c2; COMMIT; } {0 {}} reset_db do_execsql_test 2.3.0 { PRAGMA foreign_keys = on; CREATE TABLE p3(a PRIMARY KEY, b) WITHOUT ROWID; CREATE TABLE c3(x REFERENCES p3); INSERT INTO p3 VALUES(1, 'one'); INSERT INTO p3 VALUES(2, 'two'); INSERT INTO c3 VALUES(1); INSERT INTO c3 VALUES(2); CREATE TRIGGER p3d AFTER DELETE ON p3 WHEN old.a=1 BEGIN INSERT OR REPLACE INTO p3 VALUES(2, 'three'); END; } do_catchsql_test 2.3.1 { DELETE FROM p3 WHERE a=1 } {1 {FOREIGN KEY constraint failed}} finish_test |
Changes to test/fts1o.test.
︙ | ︙ | |||
48 49 50 51 52 53 54 | # See what happens when renaming the fts1 table fails. # do_test fts1o-2.1 { catchsql { CREATE TABLE t1_term(a, b, c); ALTER TABLE fts_t1 RENAME to t1; } | | | | 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 | # See what happens when renaming the fts1 table fails. # do_test fts1o-2.1 { catchsql { CREATE TABLE t1_term(a, b, c); ALTER TABLE fts_t1 RENAME to t1; } } {1 {SQL logic error}} do_test fts1o-2.2 { execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; } } {1 {one three <b>four</b>}} do_test fts1o-2.3 { execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'} } {fts_t1 fts_t1_content fts_t1_term t1_term} # See what happens when renaming the fts1 table fails inside a transaction. # do_test fts1o-3.1 { execsql { BEGIN; INSERT INTO fts_t1(a, b, c) VALUES('one two three', 'one four', 'one two'); } } {} do_test fts1o-3.2 { catchsql { ALTER TABLE fts_t1 RENAME to t1; } } {1 {SQL logic error}} # NOTE(shess) rowid AS rowid to defeat caching. Otherwise, this # seg-faults, I suspect that there's something up with a stale # virtual-table reference, but I'm not quite sure how it happens here # but not for fts2o.test. do_test fts1o-3.3 { execsql { SELECT rowid AS rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; } } {1 {one three <b>four</b>}} |
︙ | ︙ |
Changes to test/fts2g.test.
︙ | ︙ | |||
69 70 71 72 73 74 75 | do_test fts2g-1.9 { execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this something'} } {} # No support for all-except queries. do_test fts2g-1.10 { catchsql {SELECT rowid FROM t1 WHERE t1 MATCH '-this -something'} | | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | do_test fts2g-1.9 { execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this something'} } {} # No support for all-except queries. do_test fts2g-1.10 { catchsql {SELECT rowid FROM t1 WHERE t1 MATCH '-this -something'} } {1 {SQL logic error}} # Test that docListOrMerge() correctly handles reaching the end of one # doclist before it reaches the end of the other. do_test fts2g-1.11 { execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this OR also'} } {1 2} do_test fts2g-1.12 { |
︙ | ︙ |
Changes to test/fts2o.test.
︙ | ︙ | |||
65 66 67 68 69 70 71 | # See what happens when renaming the fts2 table fails. # do_test fts2o-2.5 { catchsql { CREATE TABLE t1_segdir(a, b, c); ALTER TABLE fts_t1 RENAME to t1; } | | | | 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 | # See what happens when renaming the fts2 table fails. # do_test fts2o-2.5 { catchsql { CREATE TABLE t1_segdir(a, b, c); ALTER TABLE fts_t1 RENAME to t1; } } {1 {SQL logic error}} do_test fts2o-2.6 { execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; } } {1 {one three <b>four</b>}} do_test fts2o-2.7 { execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'} } {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir t1_segdir} # See what happens when renaming the fts2 table fails inside a transaction. # do_test fts2o-2.8 { execsql { BEGIN; INSERT INTO fts_t1(a, b, c) VALUES('one two three', 'one four', 'one two'); } } {} do_test fts2o-2.9 { catchsql { ALTER TABLE fts_t1 RENAME to t1; } } {1 {SQL logic error}} do_test fts2o-2.10 { execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; } } {1 {one three <b>four</b>}} do_test fts2o-2.11 { execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'} } {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir t1_segdir} do_test fts2o-2.12 { |
︙ | ︙ |
Changes to test/fts3aa.test.
︙ | ︙ | |||
239 240 241 242 243 244 245 246 247 | do_execsql_test 8.4 { SELECT docid FROM t0 WHERE t0 MATCH 'abc'; } {6 5} do_execsql_test 8.5 { SELECT docid FROM t0 WHERE t0 MATCH '"abc abc"'; } {} finish_test | > > > > > > | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | do_execsql_test 8.4 { SELECT docid FROM t0 WHERE t0 MATCH 'abc'; } {6 5} do_execsql_test 8.5 { SELECT docid FROM t0 WHERE t0 MATCH '"abc abc"'; } {} do_execsql_test 9.1 { CREATE VIRTUAL TABLE t9 USING fts4(a, "", '---'); } do_execsql_test 9.2 { CREATE VIRTUAL TABLE t10 USING fts3(<, b, c); } finish_test |
Changes to test/fts3ao.test.
︙ | ︙ | |||
67 68 69 70 71 72 73 | # See what happens when renaming the fts3 table fails. # do_test fts3ao-2.5 { catchsql { CREATE TABLE t1_segdir(a, b, c); ALTER TABLE fts_t1 RENAME to t1; } | | | | 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 | # See what happens when renaming the fts3 table fails. # do_test fts3ao-2.5 { catchsql { CREATE TABLE t1_segdir(a, b, c); ALTER TABLE fts_t1 RENAME to t1; } } {1 {SQL logic error}} do_test fts3ao-2.6 { execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; } } {1 {one three <b>four</b>}} do_test fts3ao-2.7 { execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'} } {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir t1_segdir} # See what happens when renaming the fts3 table fails inside a transaction. # do_test fts3ao-2.8 { execsql { BEGIN; INSERT INTO fts_t1(a, b, c) VALUES('one two three', 'one four', 'one two'); } } {} do_test fts3ao-2.9 { catchsql { ALTER TABLE fts_t1 RENAME to t1; } } {1 {SQL logic error}} do_test fts3ao-2.10 { execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; } } {1 {one three <b>four</b>}} do_test fts3ao-2.11 { execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'} } {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir t1_segdir} do_test fts3ao-2.12 { |
︙ | ︙ |
Changes to test/fts3aux1.test.
︙ | ︙ | |||
360 361 362 363 364 365 366 | } {1 {invalid arguments to fts4aux constructor}} do_execsql_test 3.2.1 { CREATE VIRTUAL TABLE terms3 USING fts4aux(does_not_exist) } do_catchsql_test 3.2.2 { SELECT * FROM terms3 | | | | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | } {1 {invalid arguments to fts4aux constructor}} do_execsql_test 3.2.1 { CREATE VIRTUAL TABLE terms3 USING fts4aux(does_not_exist) } do_catchsql_test 3.2.2 { SELECT * FROM terms3 } {1 {SQL logic error}} do_catchsql_test 3.2.3 { SELECT * FROM terms3 WHERE term = 'abc' } {1 {SQL logic error}} do_catchsql_test 3.3.1 { INSERT INTO terms VALUES(1,2,3); } {1 {table terms may not be modified}} do_catchsql_test 3.3.2 { DELETE FROM terms } {1 {table terms may not be modified}} |
︙ | ︙ | |||
512 513 514 515 516 517 518 | do_test 8.1 { catchsql { CREATE VIRTUAL TABLE att.aux3 USING fts4aux(main, ft1) } } {1 {invalid arguments to fts4aux constructor}} do_test 8.2 { execsql {DETACH att} catchsql { SELECT * FROM aux2 } | | | 512 513 514 515 516 517 518 519 520 521 | do_test 8.1 { catchsql { CREATE VIRTUAL TABLE att.aux3 USING fts4aux(main, ft1) } } {1 {invalid arguments to fts4aux constructor}} do_test 8.2 { execsql {DETACH att} catchsql { SELECT * FROM aux2 } } {1 {SQL logic error}} finish_test |
Changes to test/fts3b.test.
︙ | ︙ | |||
202 203 204 205 206 207 208 | # If an insert tries to set both docid and rowid, require an error. do_test fts3b-4.8 { catchsql { INSERT INTO t4 (rowid, docid, c) VALUES (14, 15, 'bad test'); SELECT * FROM t4 WHERE docid = 14; } | | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | # If an insert tries to set both docid and rowid, require an error. do_test fts3b-4.8 { catchsql { INSERT INTO t4 (rowid, docid, c) VALUES (14, 15, 'bad test'); SELECT * FROM t4 WHERE docid = 14; } } {1 {SQL logic error}} do_test fts3b-4.9 { execsql { SELECT docid FROM t4 WHERE t4 MATCH 'testing' } } {12} do_test fts3b-4.10 { execsql { UPDATE t4 SET docid = 14 WHERE docid = 12; |
︙ | ︙ |
Changes to test/fts3conf.test.
︙ | ︙ | |||
132 133 134 135 136 137 138 | INSERT INTO t1(docid, x) VALUES(1, 'a b c'); REPLACE INTO t1(docid, x) VALUES('zero', 'd e f'); } {1 {datatype mismatch}} do_execsql_test 2.2.2 { COMMIT } do_execsql_test 2.2.3 { SELECT * FROM t1 } {{a b c} {a b c}} fts3_integrity 2.2.4 db t1 | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 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 | INSERT INTO t1(docid, x) VALUES(1, 'a b c'); REPLACE INTO t1(docid, x) VALUES('zero', 'd e f'); } {1 {datatype mismatch}} do_execsql_test 2.2.2 { COMMIT } do_execsql_test 2.2.3 { SELECT * FROM t1 } {{a b c} {a b c}} fts3_integrity 2.2.4 db t1 if {$tcl_platform(byteOrder)=="littleEndian"} { do_execsql_test 3.1 { CREATE VIRTUAL TABLE t3 USING fts4; REPLACE INTO t3(docid, content) VALUES (1, 'one two'); SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' } {X'0100000002000000'} do_execsql_test 3.2 { REPLACE INTO t3(docid, content) VALUES (2, 'one two three four'); SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'four' } {X'0200000003000000'} do_execsql_test 3.3 { REPLACE INTO t3(docid, content) VALUES (1, 'one two three four five six'); SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' } {X'0200000005000000'} do_execsql_test 3.4 { UPDATE OR REPLACE t3 SET docid = 2 WHERE docid=1; SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' } {X'0100000006000000'} do_execsql_test 3.5 { UPDATE OR REPLACE t3 SET docid = 3 WHERE docid=2; SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' } {X'0100000006000000'} do_execsql_test 3.6 { REPLACE INTO t3(docid, content) VALUES (3, 'one two'); SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' } {X'0100000002000000'} do_execsql_test 3.7 { REPLACE INTO t3(docid, content) VALUES(NULL,'one two three four'); REPLACE INTO t3(docid, content) VALUES(NULL,'one two three four five six'); SELECT docid FROM t3; } {3 4 5} do_execsql_test 3.8 { UPDATE OR REPLACE t3 SET docid = 5, content='three four' WHERE docid = 4; SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' } {X'0200000002000000'} } #------------------------------------------------------------------------- # Test that the xSavepoint is invoked correctly if the first write # operation within a transaction is to a virtual table. # do_catchsql_test 4.1.1 { CREATE VIRTUAL TABLE t0 USING fts4; |
︙ | ︙ |
Added test/fts3corrupt3.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 | # 2010 October 27 # # 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 that the FTS3 extension does not crash when it encounters a # corrupt data structure on disk. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } set ::testprefix fts3corrupt3 #------------------------------------------------------------------------- # Test that fts3 does not choke on an oversized varint. # do_execsql_test 1.0 { PRAGMA page_size = 512; CREATE VIRTUAL TABLE t1 USING fts3; BEGIN; INSERT INTO t1 VALUES('one'); INSERT INTO t1 VALUES('one'); INSERT INTO t1 VALUES('one'); COMMIT; } do_execsql_test 1.1 { SELECT quote(root) from t1_segdir; } {X'00036F6E6509010200010200010200'} do_execsql_test 1.2 { UPDATE t1_segdir SET root = X'00036F6E650EFFFFFFFFFFFFFFFFFFFFFFFF0200'; } do_catchsql_test 1.3 { SELECT rowid FROM t1 WHERE t1 MATCH 'one' } {0 -1} #------------------------------------------------------------------------- # Interior node with the prefix or suffix count of an entry set to a # negative value. # set doc1 [string repeat "x " 600] set doc2 [string repeat "y " 600] set doc3 [string repeat "z " 600] do_execsql_test 2.0 { CREATE VIRTUAL TABLE t2 USING fts3; BEGIN; INSERT INTO t2 VALUES($doc1); INSERT INTO t2 VALUES($doc2); INSERT INTO t2 VALUES($doc3); COMMIT; } do_execsql_test 2.1 { SELECT quote(root) from t2_segdir; } {X'0101017900017A'} finish_test |
Changes to test/fts3cov.test.
︙ | ︙ | |||
92 93 94 95 96 97 98 | do_test fts3cov-2.2 { set root [db one {SELECT root FROM t1_segdir}] read_fts3varint [string range $root 1 end] left_child execsql { DELETE FROM t1_segments WHERE blockid = $left_child } } {} do_error_test fts3cov-2.3 { SELECT * FROM t1 WHERE t1 MATCH 'c*' | | | | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | do_test fts3cov-2.2 { set root [db one {SELECT root FROM t1_segdir}] read_fts3varint [string range $root 1 end] left_child execsql { DELETE FROM t1_segments WHERE blockid = $left_child } } {} do_error_test fts3cov-2.3 { SELECT * FROM t1 WHERE t1 MATCH 'c*' } {SQL logic error} # Test the "replaced with NULL" case: do_test fts3cov-2.4 { execsql { INSERT INTO t1_segments VALUES($left_child, NULL) } } {} do_error_test fts3cov-2.5 { SELECT * FROM t1 WHERE t1 MATCH 'cloud' } {SQL logic error} #-------------------------------------------------------------------------- # The following tests are to test the effects of OOM errors while storing # terms in the pending-hash table. Specifically, while creating doclist # blobs to store in the table. More specifically, to test OOM errors while # appending column numbers to doclists. For example, if a doclist consists # of: |
︙ | ︙ | |||
304 305 306 307 308 309 310 | # set DO_MALLOC_TEST 0 do_test fts3cov-9.1 { execsql { CREATE VIRTUAL TABLE xx USING fts3 } } {} do_error_test fts3cov-9.2 { INSERT INTO xx(xx) VALUES('optimise'); -- British spelling | | | | | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | # set DO_MALLOC_TEST 0 do_test fts3cov-9.1 { execsql { CREATE VIRTUAL TABLE xx USING fts3 } } {} do_error_test fts3cov-9.2 { INSERT INTO xx(xx) VALUES('optimise'); -- British spelling } {SQL logic error} do_error_test fts3cov-9.3 { INSERT INTO xx(xx) VALUES('short'); } {SQL logic error} do_error_test fts3cov-9.4 { INSERT INTO xx(xx) VALUES('waytoolongtobecorrect'); } {SQL logic error} do_test fts3cov-9.5 { execsql { INSERT INTO xx(xx) VALUES('optimize') } } {} #------------------------------------------------------------------------- # Test that a table can be optimized in the middle of a transaction when # the pending-terms table is non-empty. This case involves some extra |
︙ | ︙ |
Changes to test/fts3fault.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 | source $testdir/tester.tcl set ::testprefix fts3fault # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } | < < | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | source $testdir/tester.tcl set ::testprefix fts3fault # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } # Test error handling in the sqlite3Fts3Init() function. This is the # function that registers the FTS3 module and various support functions # with SQLite. # do_faultsim_test 1 -body { sqlite3 db test.db expr 0 |
︙ | ︙ | |||
143 144 145 146 147 148 149 | do_faultsim_test 7.2 -prep { faultsim_delete_and_reopen } -body { execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchinfo=fs3) } } -test { faultsim_test_result {1 {unrecognized matchinfo: fs3}} \ {1 {vtable constructor failed: t1}} \ | | | < < > | | | | | | | > | 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 | do_faultsim_test 7.2 -prep { faultsim_delete_and_reopen } -body { execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchinfo=fs3) } } -test { faultsim_test_result {1 {unrecognized matchinfo: fs3}} \ {1 {vtable constructor failed: t1}} \ {1 {SQL logic error}} } do_faultsim_test 7.3 -prep { faultsim_delete_and_reopen } -body { execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchnfo=fts3) } } -test { faultsim_test_result {1 {unrecognized parameter: matchnfo=fts3}} \ {1 {vtable constructor failed: t1}} \ {1 {SQL logic error}} } proc mit {blob} { set scan(littleEndian) i* set scan(bigEndian) I* binary scan $blob $scan($::tcl_platform(byteOrder)) r return $r } do_test 8.0 { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE t8 USING fts4 } execsql "INSERT INTO t8 VALUES('a b c')" execsql "INSERT INTO t8 VALUES('b b b')" execsql "INSERT INTO t8 VALUES('[string repeat {c } 50000]')" execsql "INSERT INTO t8 VALUES('d d d')" execsql "INSERT INTO t8 VALUES('e e e')" execsql "INSERT INTO t8(t8) VALUES('optimize')" faultsim_save_and_close } {} ifcapable fts4_deferred { do_faultsim_test 8.1 -faults oom-t* -prep { faultsim_restore_and_reopen db func mit mit } -body { execsql { SELECT mit(matchinfo(t8, 'x')) FROM t8 WHERE t8 MATCH 'a b c' } } -test { faultsim_test_result {0 {{1 1 1 1 4 2 1 5 5}}} } } do_faultsim_test 8.2 -faults oom-t* -prep { faultsim_restore_and_reopen db func mit mit } -body { execsql { SELECT mit(matchinfo(t8, 's')) FROM t8 WHERE t8 MATCH 'a b c' } |
︙ | ︙ | |||
229 230 231 232 233 234 235 236 237 | do_faultsim_test 9.1 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT offsets(t9) FROM t9 WHERE t9 MATCH 'to*' } } -test { faultsim_test_result {0 {{0 0 20 39 0 0 64 2}}} } finish_test | > > > > > > > > | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | do_faultsim_test 9.1 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT offsets(t9) FROM t9 WHERE t9 MATCH 'to*' } } -test { faultsim_test_result {0 {{0 0 20 39 0 0 64 2}}} } do_faultsim_test 10.1 -prep { faultsim_delete_and_reopen } -body { execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, languageid=d) } } -test { faultsim_test_result {0 {}} } finish_test |
Changes to test/fts3fault2.test.
︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 | faultsim_restore_and_reopen db eval {SELECT * FROM sqlite_master} } -body { execsql { SELECT docid FROM t6 WHERE t6 MATCH '"a* b"' } } -test { faultsim_test_result {0 -1} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | faultsim_restore_and_reopen db eval {SELECT * FROM sqlite_master} } -body { execsql { SELECT docid FROM t6 WHERE t6 MATCH '"a* b"' } } -test { faultsim_test_result {0 -1} } #------------------------------------------------------------------------- # Inject faults into a query for an N-byte prefix that uses a prefix=N+1 # index. reset_db do_execsql_test 7.0 { CREATE VIRTUAL TABLE t7 USING fts4(x,prefix=2); INSERT INTO t7 VALUES('the quick brown fox'); INSERT INTO t7 VALUES('jumped over the'); INSERT INTO t7 VALUES('lazy dog'); } do_faultsim_test 7.1 -faults oom* -body { execsql { SELECT docid FROM t7 WHERE t7 MATCH 't*' } } -test { faultsim_test_result {0 {1 2}} } #------------------------------------------------------------------------- # Inject faults into a opening an existing fts3 table that has been # upgraded to add an %_stat table. # reset_db do_execsql_test 8.0 { CREATE VIRTUAL TABLE t8 USING fts3; INSERT INTO t8 VALUES('the quick brown fox'); INSERT INTO t8 VALUES('jumped over the'); INSERT INTO t8 VALUES('lazy dog'); INSERT INTO t8(t8) VALUES('automerge=8'); SELECT name FROM sqlite_master WHERE name LIKE 't8%'; } { t8 t8_content t8_segments t8_segdir t8_stat } faultsim_save_and_close do_faultsim_test 8.1 -faults oom* -prep { faultsim_restore_and_reopen } -body { execsql { INSERT INTO t8 VALUES('one two three') } } -test { faultsim_test_result {0 {}} } do_faultsim_test 8.2 -faults oom* -prep { faultsim_restore_and_reopen } -body { execsql { ALTER TABLE t8 RENAME TO t8ii } } -test { faultsim_test_result {0 {}} } #------------------------------------------------------------------------- reset_db set chunkconfig [fts3_configure_incr_load 1 1] do_execsql_test 9.0 { PRAGMA page_size = 512; CREATE VIRTUAL TABLE t9 USING fts3; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50 ) INSERT INTO t9 SELECT 'one two three' FROM s; } do_faultsim_test 8.2 -faults io* -body { execsql { SELECT count(*) FROM t9 WHERE t9 MATCH '"one two three"' } } -test { faultsim_test_result {0 50} } eval fts3_configure_incr_load $chunkconfig finish_test |
Changes to test/fts3join.test.
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 | do_execsql_test 2.2 { SELECT * FROM ft2, ft3 WHERE y MATCH x; } {abc abc} do_execsql_test 2.3 { SELECT * FROM ft3, ft2 WHERE x MATCH y; } {abc abc} do_execsql_test 2.4 { SELECT * FROM ft3, ft2 WHERE y MATCH x; } {abc abc} do_catchsql_test 2.5 { SELECT * FROM ft3, ft2 WHERE y MATCH x AND x MATCH y; } {1 {unable to use function MATCH in the requested context}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_execsql_test 2.2 { SELECT * FROM ft2, ft3 WHERE y MATCH x; } {abc abc} do_execsql_test 2.3 { SELECT * FROM ft3, ft2 WHERE x MATCH y; } {abc abc} do_execsql_test 2.4 { SELECT * FROM ft3, ft2 WHERE y MATCH x; } {abc abc} do_catchsql_test 2.5 { SELECT * FROM ft3, ft2 WHERE y MATCH x AND x MATCH y; } {1 {unable to use function MATCH in the requested context}} do_execsql_test 3.0 { CREATE VIRTUAL TABLE vt USING fts3(x); INSERT INTO vt VALUES('abc'); INSERT INTO vt VALUES('xyz'); CREATE TABLE tt(a INTEGER PRIMARY KEY); INSERT INTO tt VALUES(1), (2); } do_execsql_test 3.1 { SELECT * FROM tt LEFT JOIN ( SELECT rowid AS rrr, * FROM vt WHERE vt MATCH 'abc' ) ON tt.a = rrr } {1 1 abc 2 {} {}} do_execsql_test 3.2 { SELECT * FROM tt LEFT JOIN vt ON (vt MATCH 'abc') } {1 abc 2 abc} #------------------------------------------------------------------------- # Test that queries of the form found in test case 4.2 use an automatic # index to avoid running multiple fts queries. # do_execsql_test 4.1 { CREATE VIRTUAL TABLE ft4 USING fts3(x); CREATE TABLE t4(y, z); CREATE INDEX t4y ON t1(y); } do_eqp_test 4.2 { SELECT * FROM t4 LEFT JOIN ( SELECT docid, * FROM ft4 WHERE ft4 MATCH ? ) AS rr ON t4.rowid=rr.docid WHERE t4.y = ?; } { 1 0 0 {SCAN TABLE ft4 VIRTUAL TABLE INDEX 3:} 0 0 0 {SCAN TABLE t4} 0 1 1 {SEARCH SUBQUERY 1 AS rr USING AUTOMATIC COVERING INDEX (docid=?)} } finish_test |
Added test/fts3misc.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 | # 2017 March 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. # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the FTS3 module. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix fts3misc # If SQLITE_ENABLE_FTS3 is defined, omit this file. ifcapable !fts3 { finish_test return } #------------------------------------------------------------------------- # A self-join. # do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts3(a, b); INSERT INTO t1 VALUES('one', 'i'); INSERT INTO t1 VALUES('one', 'ii'); INSERT INTO t1 VALUES('two', 'i'); INSERT INTO t1 VALUES('two', 'ii'); } do_execsql_test 1.1 { SELECT a.a, b.b FROM t1 a, t1 b WHERE a.t1 MATCH 'two' AND b.t1 MATCH 'i' } {two i two i two i two i} #------------------------------------------------------------------------- # FTS tables with 128 or more columns. # proc v1 {v} { set vector [list a b c d e f g h] set res [list] for {set i 0} {$i<8} {incr i} { if {$v & (1 << $i)} { lappend res [lindex $vector $i] } } set res } proc v2 {v} { set vector [list d e f g h i j k] set res [list] for {set i 0} {$i<8} {incr i} { if {$v & (1 << $i)} { lappend res [lindex $vector $i] } } set res } db func v1 v1 db func v2 v2 do_test 2.0 { set cols [list] for {set i 0} {$i<200} {incr i} { lappend cols "c$i" } execsql "CREATE VIRTUAL TABLE t2 USING fts3([join $cols ,])" execsql { WITH data(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM data WHERE i<200 ) INSERT INTO t2(c198, c199) SELECT v1(i), v2(i) FROM data; } } {} do_execsql_test 2.1 { SELECT rowid FROM t2 WHERE t2 MATCH '"a b c"' } { 7 15 23 31 39 47 55 63 71 79 87 95 103 111 119 127 135 143 151 159 167 175 183 191 199 } do_execsql_test 2.2 { SELECT rowid FROM t2 WHERE t2 MATCH '"g h i"' } { 56 57 58 59 60 61 62 63 120 121 122 123 124 125 126 127 184 185 186 187 188 189 190 191 } do_execsql_test 2.3 { SELECT rowid FROM t2 WHERE t2 MATCH '"i h"' } { } do_execsql_test 2.4 { SELECT rowid FROM t2 WHERE t2 MATCH '"f e"' } { } do_execsql_test 2.5 { SELECT rowid FROM t2 WHERE t2 MATCH '"e f"' } { 6 7 14 15 22 23 30 31 38 39 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 70 71 78 79 86 87 94 95 102 103 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 134 135 142 143 150 151 158 159 166 167 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 198 199 } #------------------------------------------------------------------------- # Range constraints on the docid using non-integer values. # do_execsql_test 2.6 { SELECT rowid FROM t2 WHERE t2 MATCH 'e' AND rowid BETWEEN NULL AND 45; } {} do_execsql_test 2.7 { SELECT rowid FROM t2 WHERE t2 MATCH 'e' AND rowid BETWEEN 11.5 AND 48.2; } { 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 34 35 38 39 42 43 46 47 48 } do_execsql_test 2.8 { SELECT rowid FROM t2 WHERE t2 MATCH 'e' AND rowid BETWEEN '11.5' AND '48.2'; } { 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 34 35 38 39 42 43 46 47 48 } #------------------------------------------------------------------------- # Phrase query tests. # do_execsql_test 3.1.1 { CREATE VIRTUAL TABLE t3 USING fts3; INSERT INTO t3 VALUES('a b c'); INSERT INTO t3 VALUES('d e f'); INSERT INTO t3 VALUES('a b d'); INSERT INTO t3 VALUES('1 2 3 4 5 6 7 8 9 10 11'); } do_execsql_test 3.1.2 { SELECT * FROM t3 WHERE t3 MATCH '"a b x y"' ORDER BY docid DESC } do_execsql_test 3.1.3 { SELECT * FROM t3 WHERE t3 MATCH '"a b c" OR "a b x y"' ORDER BY docid DESC } {{a b c}} do_execsql_test 3.1.4 { SELECT * FROM t3 WHERE t3 MATCH '"a* b* x* a*"' } do_execsql_test 3.1.5 { SELECT rowid FROM t3 WHERE t3 MATCH '"2 3 4 5 6 7 8 9"' } {4} #------------------------------------------------------------------------- # reset_db ifcapable fts4_deferred { do_execsql_test 4.0 { PRAGMA page_size = 512; CREATE VIRTUAL TABLE t4 USING fts4; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<8000 ) INSERT INTO t4 SELECT 'a b c a b c a b c' FROM s; } do_execsql_test 4.1 { SELECT count(*) FROM t4 WHERE t4 MATCH '"a b c" OR "c a b"' } {8000} do_execsql_test 4.2 { SELECT quote(value) from t4_stat where id=0 } {X'C03EC0B204C0A608'} do_execsql_test 4.3 { UPDATE t4_stat SET value = X'C03EC0B204C0A60800' WHERE id=0; } do_catchsql_test 4.4 { SELECT count(*) FROM t4 WHERE t4 MATCH '"a b c" OR "c a b"' } {1 {database disk image is malformed}} do_execsql_test 4.5 { UPDATE t4_stat SET value = X'00C03EC0B204C0A608' WHERE id=0; } do_catchsql_test 4.6 { SELECT count(*) FROM t4 WHERE t4 MATCH '"a b c" OR "c a b"' } {1 {database disk image is malformed}} } #------------------------------------------------------------------------- # reset_db do_execsql_test 5.0 { CREATE VIRTUAL TABLE t5 USING fts4; INSERT INTO t5 VALUES('a x x x x b x x x x c'); INSERT INTO t5 VALUES('a x x x x b x x x x c'); INSERT INTO t5 VALUES('a x x x x b x x x x c'); } do_execsql_test 5.1 { SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/4 b NEAR/4 c' } {1 2 3} do_execsql_test 5.2 { SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/3 b NEAR/4 c' } {} do_execsql_test 5.3 { SELECT rowid FROM t5 WHERE t5 MATCH 'a NEAR/4 b NEAR/3 c' } {} do_execsql_test 5.4 { SELECT rowid FROM t5 WHERE t5 MATCH 'y NEAR/4 b NEAR/4 c' } {} do_execsql_test 5.5 { SELECT rowid FROM t5 WHERE t5 MATCH 'x OR a NEAR/3 b NEAR/3 c' } {1 2 3} do_execsql_test 5.5 { SELECT rowid FROM t5 WHERE t5 MATCH 'x OR y NEAR/3 b NEAR/3 c' } {1 2 3} #------------------------------------------------------------------------- # reset_db do_execsql_test 6.0 { CREATE VIRTUAL TABLE t6 USING fts4; BEGIN; WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000) INSERT INTO t6 SELECT 'x x x x x x x x x x x' FROM s; INSERT INTO t6 VALUES('x x x x x x x x x x x A'); INSERT INTO t6 VALUES('x x x x x x x x x x x B'); INSERT INTO t6 VALUES('x x x x x x x x x x x A'); INSERT INTO t6 VALUES('x x x x x x x x x x x B'); WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000) INSERT INTO t6 SELECT 'x x x x x x x x x x x' FROM s; COMMIT; } do_execsql_test 6.1 { SELECT rowid FROM t6 WHERE t6 MATCH 'b OR "x a"' } {50001 50002 50003 50004} finish_test |
Changes to test/fts3tok1.test.
︙ | ︙ | |||
104 105 106 107 108 109 110 | do_catchsql_test 2.0 { CREATE VIRTUAL TABLE tX USING fts3tokenize(nosuchtokenizer); } {1 {unknown tokenizer: nosuchtokenizer}} do_catchsql_test 2.1 { CREATE VIRTUAL TABLE t4 USING fts3tokenize; SELECT * FROM t4; | | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | do_catchsql_test 2.0 { CREATE VIRTUAL TABLE tX USING fts3tokenize(nosuchtokenizer); } {1 {unknown tokenizer: nosuchtokenizer}} do_catchsql_test 2.1 { CREATE VIRTUAL TABLE t4 USING fts3tokenize; SELECT * FROM t4; } {1 {SQL logic error}} do_catchsql_test 2.2 { CREATE VIRTUAL TABLE t USING fts4(tokenize=simple""); } {0 {}} ifcapable fts3_unicode { do_catchsql_test 2.3 { |
︙ | ︙ |
Changes to test/fts4content.test.
︙ | ︙ | |||
420 421 422 423 424 425 426 | } { {A B} {B A} {C D} {A A} } do_catchsql_test 6.2.2 { DROP TABLE t7; SELECT * FROM ft7; | | | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | } { {A B} {B A} {C D} {A A} } do_catchsql_test 6.2.2 { DROP TABLE t7; SELECT * FROM ft7; } {1 {SQL logic error}} db close sqlite3 db test.db do_execsql_test 6.2.3 { SELECT name FROM sqlite_master WHERE name LIKE '%t7%' } { ft7 ft7_segments ft7_segdir sqlite_autoindex_ft7_segdir_1 |
︙ | ︙ | |||
453 454 455 456 457 458 459 | do_execsql_test 6.2.7 { DROP TABLE t7; CREATE TABLE t7(x); } do_catchsql_test 6.2.8 { SELECT * FROM ft7 WHERE ft7 MATCH '"A A"'; | | | | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | do_execsql_test 6.2.7 { DROP TABLE t7; CREATE TABLE t7(x); } do_catchsql_test 6.2.8 { SELECT * FROM ft7 WHERE ft7 MATCH '"A A"'; } {1 {SQL logic error}} do_catchsql_test 6.2.9 { SELECT * FROM ft7 WHERE ft7 MATCH '"A A"'; } {1 {SQL logic error}} db close sqlite3 db test.db do_catchsql_test 6.2.10 { SELECT rowid FROM ft7 WHERE ft7 MATCH '"A A"'; } {0 2} do_catchsql_test 6.2.11 { |
︙ | ︙ | |||
500 501 502 503 504 505 506 | } {13 15} do_execsql_test 7.2.3 { SELECT name FROM sqlite_master WHERE name LIKE 'ft9_%'; } {ft9_segments ft9_segdir ft9_docsize ft9_stat} do_catchsql_test 7.2.4 { SELECT * FROM ft9 WHERE ft9 MATCH 'N'; | | | 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 | } {13 15} do_execsql_test 7.2.3 { SELECT name FROM sqlite_master WHERE name LIKE 'ft9_%'; } {ft9_segments ft9_segdir ft9_docsize ft9_stat} do_catchsql_test 7.2.4 { SELECT * FROM ft9 WHERE ft9 MATCH 'N'; } {1 {SQL logic error}} #------------------------------------------------------------------------- # Test cases 8.* # do_execsql_test 8.1 { CREATE TABLE t10(a, b); INSERT INTO t10 VALUES( |
︙ | ︙ |
Changes to test/fts4langid.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 | #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the languageid=xxx FTS4 option. # set testdir [file dirname $argv0] source $testdir/tester.tcl | < | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the languageid=xxx FTS4 option. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is defined, omit this file. ifcapable !fts3 { finish_test return } |
︙ | ︙ | |||
336 337 338 339 340 341 342 343 344 345 346 347 348 349 | do_test_query1 3.3.2 {"zero one two"} { rowid_list "zero one two" } do_test_query1 3.3.3 {zero one two} { and_merge_lists [rowid_list zero] [rowid_list one] [rowid_list two] } do_test_query1 3.3.4 {"zero one" OR "one two"} { or_merge_lists [rowid_list "zero one"] [rowid_list "one two"] } #------------------------------------------------------------------------- # Test cases 4.* # proc build_multilingual_db_2 {db} { $db eval { CREATE VIRTUAL TABLE t4 USING fts4( | > > > > > > > | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | do_test_query1 3.3.2 {"zero one two"} { rowid_list "zero one two" } do_test_query1 3.3.3 {zero one two} { and_merge_lists [rowid_list zero] [rowid_list one] [rowid_list two] } do_test_query1 3.3.4 {"zero one" OR "one two"} { or_merge_lists [rowid_list "zero one"] [rowid_list "one two"] } do_execsql_test 3.4 { CREATE TABLE t8c(a, b); CREATE VIRTUAL TABLE t8 USING fts4(content=t8c, languageid=langid); INSERT INTO t8(docid, a, b) VALUES(-1, 'one two three', 'x y z'); SELECT docid FROM t8 WHERE t8 MATCH 'one x' AND langid=0 } {-1} #------------------------------------------------------------------------- # Test cases 4.* # proc build_multilingual_db_2 {db} { $db eval { CREATE VIRTUAL TABLE t4 USING fts4( |
︙ | ︙ | |||
377 378 379 380 381 382 383 | for {set i 0} {$i < 50} {incr i} { do_execsql_test 4.1.4.$i { SELECT count(*) FROM t4 WHERE t4 MATCH 'fox' AND lid=$i; } [expr 0==($i%2)] } do_catchsql_test 4.1.5 { INSERT INTO t4(content, lid) VALUES('hello world', 101) | | | 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | for {set i 0} {$i < 50} {incr i} { do_execsql_test 4.1.4.$i { SELECT count(*) FROM t4 WHERE t4 MATCH 'fox' AND lid=$i; } [expr 0==($i%2)] } do_catchsql_test 4.1.5 { INSERT INTO t4(content, lid) VALUES('hello world', 101) } {1 {SQL logic error}} #------------------------------------------------------------------------- # Test cases 5.* # # The following test cases are designed to detect a 32-bit overflow bug # that existed at one point. # |
︙ | ︙ |
Added test/fts4lastrowid.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 | # 2017 Feb 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. # #*********************************************************************** # # Tests of the last_insert_rowid functionality with fts4. # set testdir [file dirname $argv0] source [file join [file dirname [info script]] tester.tcl] set testprefix fts4lastrowid ifcapable !fts3 { finish_test return } do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts4(str); } do_execsql_test 1.1 { INSERT INTO t1 VALUES('one string'); INSERT INTO t1 VALUES('two string'); INSERT INTO t1 VALUES('three string'); SELECT last_insert_rowid(); } {3} do_execsql_test 1.2 { BEGIN; INSERT INTO t1 VALUES('one string'); INSERT INTO t1 VALUES('two string'); INSERT INTO t1 VALUES('three string'); COMMIT; SELECT last_insert_rowid(); } {6} do_execsql_test 1.3 { INSERT INTO t1(rowid, str) VALUES(-22, 'some more text'); SELECT last_insert_rowid(); } {-22} do_execsql_test 1.4 { BEGIN; INSERT INTO t1(rowid, str) VALUES(45, 'some more text'); INSERT INTO t1(rowid, str) VALUES(46, 'some more text'); INSERT INTO t1(rowid, str) VALUES(222, 'some more text'); SELECT last_insert_rowid(); COMMIT; SELECT last_insert_rowid(); } {222 222} do_execsql_test 1.5 { CREATE TABLE x1(x); INSERT INTO x1 VALUES('john'), ('paul'), ('george'), ('ringo'); INSERT INTO t1 SELECT x FROM x1; SELECT last_insert_rowid(); } {226} do_execsql_test 1.6 { INSERT INTO t1(rowid, str) SELECT rowid+10, x FROM x1; SELECT last_insert_rowid(); } {14} finish_test |
Changes to test/fts4merge.test.
︙ | ︙ | |||
85 86 87 88 89 90 91 | 4 {merge=5,} 5 {merge=6,%} 6 {merge=6,six} 7 {merge=6,1} } { do_catchsql_test 2.$tn { INSERT INTO t2(t2) VALUES($arg); | | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | 4 {merge=5,} 5 {merge=6,%} 6 {merge=6,six} 7 {merge=6,1} } { do_catchsql_test 2.$tn { INSERT INTO t2(t2) VALUES($arg); } {1 {SQL logic error}} } #------------------------------------------------------------------------- # Test cases 3.* # do_test 3.0 { reset_db |
︙ | ︙ |
Changes to test/fts4unicode.test.
︙ | ︙ | |||
380 381 382 383 384 385 386 | do_isspace_test 6.$T.9 $T 8196 do_isspace_test 6.$T.10 $T 8197 do_isspace_test 6.$T.11 $T 8198 do_isspace_test 6.$T.12 $T 8199 do_isspace_test 6.$T.13 $T 8200 do_isspace_test 6.$T.14 $T 8201 do_isspace_test 6.$T.15 $T 8202 | > | > | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | do_isspace_test 6.$T.9 $T 8196 do_isspace_test 6.$T.10 $T 8197 do_isspace_test 6.$T.11 $T 8198 do_isspace_test 6.$T.12 $T 8199 do_isspace_test 6.$T.13 $T 8200 do_isspace_test 6.$T.14 $T 8201 do_isspace_test 6.$T.15 $T 8202 if {$T!="icu"} { do_isspace_test 6.$T.16 $T 8239 } do_isspace_test 6.$T.17 $T 8287 do_isspace_test 6.$T.18 $T 12288 if {$T!="icu"} { do_isspace_test 6.$T.19 $T {32 160 5760 6158} } else { do_isspace_test 6.$T.19 $T {32 160 5760 8192} |
︙ | ︙ |
Changes to test/fuzz-oss1.test.
︙ | ︙ | |||
1993 1994 1995 1996 1997 1998 1999 2000 2001 | "nao:Property14"."ID" AND "9_u" IS NOT NULL AND "10_u" IS NOT NULL AND ("9_u" COLLATE NOCASE = ? COLLATE NOCASE))) FROM (SELECT "nco:PersonContact1"."ID" AS "1_u" FROM "nco:PersonContact" AS "nco:PersonContact1") ORDER BY "1_u"; } } {/.* Goto .*/} finish_test | > > > > | 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 | "nao:Property14"."ID" AND "9_u" IS NOT NULL AND "10_u" IS NOT NULL AND ("9_u" COLLATE NOCASE = ? COLLATE NOCASE))) FROM (SELECT "nco:PersonContact1"."ID" AS "1_u" FROM "nco:PersonContact" AS "nco:PersonContact1") ORDER BY "1_u"; } } {/.* Goto .*/} # Crash reported by OSS-FUZZ on 2016-11-10 do_catchsql_test fuzz-oss1-detach { DETACH x IS #1; } {1 {near "#1": syntax error}} finish_test |
Changes to test/fuzz3.test.
︙ | ︙ | |||
148 149 150 151 152 153 154 | {PRAGMA integrity_check} } { do_test fuzz3-$ii.$iNew.[incr iTest] { foreach {rc msg} [catchsql $sql] {} if {$rc == 0 || $msg eq "database or disk is full" || $msg eq "database disk image is malformed" | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | {PRAGMA integrity_check} } { do_test fuzz3-$ii.$iNew.[incr iTest] { foreach {rc msg} [catchsql $sql] {} if {$rc == 0 || $msg eq "database or disk is full" || $msg eq "database disk image is malformed" || $msg eq "file is not a database" || [string match "malformed database schema*" $msg] } { set msg ok } set msg } {ok} } |
︙ | ︙ |
Changes to test/fuzzcheck.c.
︙ | ︙ | |||
66 67 68 69 70 71 72 | */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #include "sqlite3.h" | < > > > > > | 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 | */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #include "sqlite3.h" #define ISSPACE(X) isspace((unsigned char)(X)) #define ISDIGIT(X) isdigit((unsigned char)(X)) #ifdef __unix__ # include <signal.h> # include <unistd.h> #endif #ifdef SQLITE_OSS_FUZZ # include <stddef.h> # include <stdint.h> #endif /* ** Files in the virtual file system. */ typedef struct VFile VFile; struct VFile { char *zFilename; /* Filename. NULL for delete-on-close. From malloc() */ |
︙ | ︙ | |||
124 125 126 127 128 129 130 131 132 133 134 135 136 137 | static struct GlobalVars { const char *zArgv0; /* Name of program */ VFile aFile[MX_FILE]; /* The virtual filesystem */ int nDb; /* Number of template databases */ Blob *pFirstDb; /* Content of first template database */ int nSql; /* Number of SQL scripts */ Blob *pFirstSql; /* First SQL script */ char zTestName[100]; /* Name of current test */ } g; /* ** Print an error message and quit. */ static void fatalError(const char *zFormat, ...){ | > | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | static struct GlobalVars { const char *zArgv0; /* Name of program */ VFile aFile[MX_FILE]; /* The virtual filesystem */ int nDb; /* Number of template databases */ Blob *pFirstDb; /* Content of first template database */ int nSql; /* Number of SQL scripts */ Blob *pFirstSql; /* First SQL script */ unsigned int uRandom; /* Seed for the SQLite PRNG */ char zTestName[100]; /* Name of current test */ } g; /* ** Print an error message and quit. */ static void fatalError(const char *zFormat, ...){ |
︙ | ︙ | |||
585 586 587 588 589 590 591 592 593 594 595 | const char *zFilename, int nOut, char *zOut ){ sqlite3_snprintf(nOut, zOut, "%s", zFilename); return SQLITE_OK; } /* ** Register the VFS that reads from the g.aFile[] set of files. */ | > > > > > > > > | | | < < | 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 | const char *zFilename, int nOut, char *zOut ){ sqlite3_snprintf(nOut, zOut, "%s", zFilename); return SQLITE_OK; } /* Always use the same random see, for repeatability. */ static int inmemRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ memset(zBuf, 0, nBuf); memcpy(zBuf, &g.uRandom, nBuf<sizeof(g.uRandom) ? nBuf : sizeof(g.uRandom)); return nBuf; } /* ** Register the VFS that reads from the g.aFile[] set of files. */ static void inmemVfsRegister(int makeDefault){ static sqlite3_vfs inmemVfs; sqlite3_vfs *pDefault = sqlite3_vfs_find(0); inmemVfs.iVersion = 3; inmemVfs.szOsFile = sizeof(VHandle); inmemVfs.mxPathname = 200; inmemVfs.zName = "inmem"; inmemVfs.xOpen = inmemOpen; inmemVfs.xDelete = inmemDelete; inmemVfs.xAccess = inmemAccess; inmemVfs.xFullPathname = inmemFullPathname; inmemVfs.xRandomness = inmemRandomness; inmemVfs.xSleep = pDefault->xSleep; inmemVfs.xCurrentTimeInt64 = pDefault->xCurrentTimeInt64; sqlite3_vfs_register(&inmemVfs, makeDefault); }; /* ** Allowed values for the runFlags parameter to runSql() */ #define SQL_TRACE 0x0001 /* Print each SQL statement as it is prepared */ #define SQL_OUTPUT 0x0002 /* Show the SQL output */ /* ** Run multiple commands of SQL. Similar to sqlite3_exec(), but does not ** stop if an error is encountered. */ static void runSql(sqlite3 *db, const char *zSql, unsigned runFlags){ const char *zMore; sqlite3_stmt *pStmt; while( zSql && zSql[0] ){ zMore = 0; pStmt = 0; sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zMore); if( zMore==zSql ) break; if( runFlags & SQL_TRACE ){ const char *z = zSql; int n; while( z<zMore && ISSPACE(z[0]) ) z++; n = (int)(zMore - z); while( n>0 && ISSPACE(z[n-1]) ) n--; |
︙ | ︙ | |||
790 791 792 793 794 795 796 797 798 799 800 801 802 803 | " -q|--quiet Reduced output\n" " --limit-mem N Limit memory used by test SQLite instance to N bytes\n" " --limit-vdbe Panic if any test runs for more than 100,000 cycles\n" " --load-sql ARGS... Load SQL scripts fro files into SOURCE-DB\n" " --load-db ARGS... Load template databases from files into SOURCE_DB\n" " -m TEXT Add a description to the database\n" " --native-vfs Use the native VFS for initially empty database files\n" " --rebuild Rebuild and vacuum the database file\n" " --result-trace Show the results of each SQL command\n" " --sqlid N Use only SQL where sqlid=N\n" " --timeout N Abort if any single test needs more than N seconds\n" " -v|--verbose Increased output. Repeat for more output.\n" ); } | > > > | 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 | " -q|--quiet Reduced output\n" " --limit-mem N Limit memory used by test SQLite instance to N bytes\n" " --limit-vdbe Panic if any test runs for more than 100,000 cycles\n" " --load-sql ARGS... Load SQL scripts fro files into SOURCE-DB\n" " --load-db ARGS... Load template databases from files into SOURCE_DB\n" " -m TEXT Add a description to the database\n" " --native-vfs Use the native VFS for initially empty database files\n" " --native-malloc Turn off MEMSYS3/5 and Lookaside\n" " --oss-fuzz Enable OSS-FUZZ testing\n" " --prng-seed N Seed value for the PRGN inside of SQLite\n" " --rebuild Rebuild and vacuum the database file\n" " --result-trace Show the results of each SQL command\n" " --sqlid N Use only SQL where sqlid=N\n" " --timeout N Abort if any single test needs more than N seconds\n" " -v|--verbose Increased output. Repeat for more output.\n" ); } |
︙ | ︙ | |||
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 | int nTest = 0; /* Total number of tests performed */ char *zDbName = ""; /* Appreviated name of a source database */ const char *zFailCode = 0; /* Value of the TEST_FAILURE environment variable */ int cellSzCkFlag = 0; /* --cell-size-check */ int sqlFuzz = 0; /* True for SQL fuzz testing. False for DB fuzz */ int iTimeout = 120; /* Default 120-second timeout */ int nMem = 0; /* Memory limit */ char *zExpDb = 0; /* Write Databases to files in this directory */ char *zExpSql = 0; /* Write SQL to files in this directory */ void *pHeap = 0; /* Heap for use by SQLite */ iBegin = timeOfDay(); #ifdef __unix__ signal(SIGALRM, timeoutHandler); #endif g.zArgv0 = argv[0]; zFailCode = getenv("TEST_FAILURE"); for(i=1; i<argc; i++){ const char *z = argv[i]; if( z[0]=='-' ){ z++; if( z[0]=='-' ) z++; if( strcmp(z,"cell-size-check")==0 ){ cellSzCkFlag = 1; | > > > > > > > | 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 | int nTest = 0; /* Total number of tests performed */ char *zDbName = ""; /* Appreviated name of a source database */ const char *zFailCode = 0; /* Value of the TEST_FAILURE environment variable */ int cellSzCkFlag = 0; /* --cell-size-check */ int sqlFuzz = 0; /* True for SQL fuzz testing. False for DB fuzz */ int iTimeout = 120; /* Default 120-second timeout */ int nMem = 0; /* Memory limit */ int nMemThisDb = 0; /* Memory limit set by the CONFIG table */ char *zExpDb = 0; /* Write Databases to files in this directory */ char *zExpSql = 0; /* Write SQL to files in this directory */ void *pHeap = 0; /* Heap for use by SQLite */ int ossFuzz = 0; /* enable OSS-FUZZ testing */ int ossFuzzThisDb = 0; /* ossFuzz value for this particular database */ int nativeMalloc = 0; /* Turn off MEMSYS3/5 and lookaside if true */ sqlite3_vfs *pDfltVfs; /* The default VFS */ iBegin = timeOfDay(); #ifdef __unix__ signal(SIGALRM, timeoutHandler); #endif g.zArgv0 = argv[0]; zFailCode = getenv("TEST_FAILURE"); pDfltVfs = sqlite3_vfs_find(0); inmemVfsRegister(1); for(i=1; i<argc; i++){ const char *z = argv[i]; if( z[0]=='-' ){ z++; if( z[0]=='-' ) z++; if( strcmp(z,"cell-size-check")==0 ){ cellSzCkFlag = 1; |
︙ | ︙ | |||
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 | zInsSql = "INSERT INTO db(dbcontent) VALUES(readfile(?1))"; iFirstInsArg = i+1; break; }else if( strcmp(z,"m")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); zMsg = argv[++i]; }else if( strcmp(z,"native-vfs")==0 ){ nativeFlag = 1; }else if( strcmp(z,"quiet")==0 || strcmp(z,"q")==0 ){ quietFlag = 1; verboseFlag = 0; }else if( strcmp(z,"rebuild")==0 ){ rebuildFlag = 1; | > > > > > > > > > > | 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 | zInsSql = "INSERT INTO db(dbcontent) VALUES(readfile(?1))"; iFirstInsArg = i+1; break; }else if( strcmp(z,"m")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); zMsg = argv[++i]; }else if( strcmp(z,"native-malloc")==0 ){ nativeMalloc = 1; }else if( strcmp(z,"native-vfs")==0 ){ nativeFlag = 1; }else if( strcmp(z,"oss-fuzz")==0 ){ ossFuzz = 1; }else if( strcmp(z,"prng-seed")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); g.uRandom = atoi(argv[++i]); }else if( strcmp(z,"quiet")==0 || strcmp(z,"q")==0 ){ quietFlag = 1; verboseFlag = 0; }else if( strcmp(z,"rebuild")==0 ){ rebuildFlag = 1; |
︙ | ︙ | |||
941 942 943 944 945 946 947 | if( zInsSql ){ fatalError("cannot import into more than one database"); } } /* Process each source database separately */ for(iSrcDb=0; iSrcDb<nSrcDb; iSrcDb++){ | | > | 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | if( zInsSql ){ fatalError("cannot import into more than one database"); } } /* Process each source database separately */ for(iSrcDb=0; iSrcDb<nSrcDb; iSrcDb++){ rc = sqlite3_open_v2(azSrcDb[iSrcDb], &db, SQLITE_OPEN_READWRITE, pDfltVfs->zName); if( rc ){ fatalError("cannot open source database %s - %s", azSrcDb[iSrcDb], sqlite3_errmsg(db)); } rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS db(\n" " dbid INTEGER PRIMARY KEY, -- database id\n" |
︙ | ︙ | |||
967 968 969 970 971 972 973 974 975 976 977 978 979 980 | char *zSql; zSql = sqlite3_mprintf( "DELETE FROM readme; INSERT INTO readme(msg) VALUES(%Q)", zMsg); rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc ) fatalError("cannot change description: %s", sqlite3_errmsg(db)); } if( zInsSql ){ sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, readfileFunc, 0, 0); rc = sqlite3_prepare_v2(db, zInsSql, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare statement [%s]: %s", zInsSql, sqlite3_errmsg(db)); rc = sqlite3_exec(db, "BEGIN", 0, 0, 0); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | char *zSql; zSql = sqlite3_mprintf( "DELETE FROM readme; INSERT INTO readme(msg) VALUES(%Q)", zMsg); rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc ) fatalError("cannot change description: %s", sqlite3_errmsg(db)); } ossFuzzThisDb = ossFuzz; /* If the CONFIG(name,value) table exists, read db-specific settings ** from that table */ if( sqlite3_table_column_metadata(db,0,"config",0,0,0,0,0,0)==SQLITE_OK ){ rc = sqlite3_prepare_v2(db, "SELECT name, value FROM config", -1, &pStmt, 0); if( rc ) fatalError("cannot prepare query of CONFIG table: %s", sqlite3_errmsg(db)); while( SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zName = (const char *)sqlite3_column_text(pStmt,0); if( zName==0 ) continue; if( strcmp(zName, "oss-fuzz")==0 ){ ossFuzzThisDb = sqlite3_column_int(pStmt,1); if( verboseFlag ) printf("Config: oss-fuzz=%d\n", ossFuzzThisDb); } if( strcmp(zName, "limit-mem")==0 && !nativeMalloc ){ #if !defined(SQLITE_ENABLE_MEMSYS3) && !defined(SQLITE_ENABLE_MEMSYS5) fatalError("the limit-mem option requires -DSQLITE_ENABLE_MEMSYS5" " or _MEMSYS3"); #else nMemThisDb = sqlite3_column_int(pStmt,1); if( verboseFlag ) printf("Config: limit-mem=%d\n", nMemThisDb); #endif } } sqlite3_finalize(pStmt); } if( zInsSql ){ sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, readfileFunc, 0, 0); rc = sqlite3_prepare_v2(db, zInsSql, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare statement [%s]: %s", zInsSql, sqlite3_errmsg(db)); rc = sqlite3_exec(db, "BEGIN", 0, 0, 0); |
︙ | ︙ | |||
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 | sqlite3_finalize(pStmt); rc = sqlite3_exec(db, "COMMIT", 0, 0, 0); if( rc ) fatalError("cannot commit the transaction: %s", sqlite3_errmsg(db)); rebuild_database(db); sqlite3_close(db); return 0; } if( zExpDb!=0 || zExpSql!=0 ){ sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, writefileFunc, 0, 0); if( zExpDb!=0 ){ const char *zExDb = "SELECT writefile(printf('%s/db%06d.db',?1,dbid),dbcontent)," " dbid, printf('%s/db%06d.db',?1,dbid), length(dbcontent)" | > > | 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 | sqlite3_finalize(pStmt); rc = sqlite3_exec(db, "COMMIT", 0, 0, 0); if( rc ) fatalError("cannot commit the transaction: %s", sqlite3_errmsg(db)); rebuild_database(db); sqlite3_close(db); return 0; } rc = sqlite3_exec(db, "PRAGMA query_only=1;", 0, 0, 0); if( rc ) fatalError("cannot set database to query-only"); if( zExpDb!=0 || zExpSql!=0 ){ sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, writefileFunc, 0, 0); if( zExpDb!=0 ){ const char *zExDb = "SELECT writefile(printf('%s/db%06d.db',?1,dbid),dbcontent)," " dbid, printf('%s/db%06d.db',?1,dbid), length(dbcontent)" |
︙ | ︙ | |||
1082 1083 1084 1085 1086 1087 1088 | */ sqlite3_close(db); if( sqlite3_memory_used()>0 ){ fatalError("SQLite has memory in use before the start of testing"); } /* Limit available memory, if requested */ | < | > | | | > > > > > | < < | 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 | */ sqlite3_close(db); if( sqlite3_memory_used()>0 ){ fatalError("SQLite has memory in use before the start of testing"); } /* Limit available memory, if requested */ sqlite3_shutdown(); if( nMemThisDb>0 && !nativeMalloc ){ pHeap = realloc(pHeap, nMemThisDb); if( pHeap==0 ){ fatalError("failed to allocate %d bytes of heap memory", nMem); } sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nMemThisDb, 128); } /* Disable lookaside with the --native-malloc option */ if( nativeMalloc ){ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0); } /* Reset the in-memory virtual filesystem */ formatVfs(); /* Run a test using each SQL script against each database. */ if( !verboseFlag && !quietFlag ) printf("%s:", zDbName); for(pSql=g.pFirstSql; pSql; pSql=pSql->pNext){ for(pDb=g.pFirstDb; pDb; pDb=pDb->pNext){ int openFlags; |
︙ | ︙ | |||
1119 1120 1121 1122 1123 1124 1125 | if( amt!=prevAmt ){ printf(" %d%%", amt*10); fflush(stdout); prevAmt = amt; } } createVFile("main.db", pDb->sz, pDb->a); | > > > > > > > > > | | | | | | | > > | | | | | | | | | > | > | > > > | 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 | if( amt!=prevAmt ){ printf(" %d%%", amt*10); fflush(stdout); prevAmt = amt; } } createVFile("main.db", pDb->sz, pDb->a); sqlite3_randomness(0,0); if( ossFuzzThisDb ){ #ifndef SQLITE_OSS_FUZZ fatalError("--oss-fuzz not supported: recompile with -DSQLITE_OSS_FUZZ"); #else extern int LLVMFuzzerTestOneInput(const uint8_t*, size_t); LLVMFuzzerTestOneInput((const uint8_t*)pSql->a, (size_t)pSql->sz); #endif }else{ openFlags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; if( nativeFlag && pDb->sz==0 ){ openFlags |= SQLITE_OPEN_MEMORY; zVfs = 0; } rc = sqlite3_open_v2("main.db", &db, openFlags, zVfs); if( rc ) fatalError("cannot open inmem database"); sqlite3_limit(db, SQLITE_LIMIT_LENGTH, 100000000); sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 50); if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags); setAlarm(iTimeout); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( sqlFuzz || vdbeLimitFlag ){ sqlite3_progress_handler(db, 100000, progressHandler, &vdbeLimitFlag); } #endif do{ runSql(db, (char*)pSql->a, runFlags); }while( timeoutTest ); setAlarm(0); sqlite3_exec(db, "PRAGMA temp_store_directory=''", 0, 0, 0); sqlite3_close(db); } if( sqlite3_memory_used()>0 ){ fatalError("memory leak: %lld bytes outstanding", sqlite3_memory_used()); } reformatVfs(); nTest++; g.zTestName[0] = 0; /* Simulate an error if the TEST_FAILURE environment variable is "5". ** This is used to verify that automated test script really do spot ** errors that occur in this test program. |
︙ | ︙ |
Added test/fuzzdata5.db.
cannot compute difference between binary files
Added test/gcfault.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 | # 2016 December 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 implements regression tests for SQLite library. The # focus of this file is testing OOM error handling within the built-in # group_concat() function. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix gcfault foreach {enc} { utf16 utf8 } { reset_db sqlite3_db_config_lookaside db 0 0 0 execsql "PRAGMA encoding = $enc" do_execsql_test 1.$enc.1 { CREATE TABLE s(i, s); INSERT INTO s VALUES(1, ',0123456789,'); INSERT INTO s VALUES(2, X'2c303132333435363738392c'); CREATE TABLE e(e); INSERT INTO e VALUES('v1'), ('v2'); } {} do_faultsim_test 1.$enc.1 -faults oom* -body { execsql { SELECT group_concat(e, (SELECT s FROM s WHERE i=1)) FROM e } } do_faultsim_test 1.$enc.2 -faults oom-t* -body { execsql { SELECT group_concat(e, (SELECT s FROM s WHERE i=2)) FROM e } } do_faultsim_test 1.$enc.3 -faults oom-t* -prep { set ::STMT [sqlite3_prepare db {SELECT group_concat(e, ?) FROM e} -1 dummy] sqlite3_bind_text $::STMT 1 ",0123456789," 12 } -body { while { "SQLITE_ROW"==[sqlite3_step $::STMT] } { } } -test { sqlite3_finalize $::STMT } } finish_test |
Added test/having.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 | # 2017 April 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. # #*********************************************************************** # # Test the HAVING->WHERE optimization. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix having do_execsql_test 1.0 { CREATE TABLE t2(c, d); CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 1); INSERT INTO t1 VALUES(2, 2); INSERT INTO t1 VALUES(1, 3); INSERT INTO t1 VALUES(2, 4); INSERT INTO t1 VALUES(1, 5); INSERT INTO t1 VALUES(2, 6); } {} foreach {tn sql res} { 1 "SELECT a, sum(b) FROM t1 GROUP BY a HAVING a=2" {2 12} 2 "SELECT a, sum(b) FROM t1 GROUP BY a HAVING a=2 AND sum(b)>10" {2 12} 3 "SELECT a, sum(b) FROM t1 GROUP BY a HAVING sum(b)>12" {} } { do_execsql_test 1.$tn $sql $res } # Run an EXPLAIN command for both SQL statements. Return true if # the outputs are identical, or false otherwise. # proc compare_vdbe {sql1 sql2} { set r1 [list] set r2 [list] db eval "explain $sql1" { lappend r1 $opcode $p1 $p2 $p3 $p4 $p5} db eval "explain $sql2" { lappend r2 $opcode $p1 $p2 $p3 $p4 $p5} return [expr {$r1==$r2}] } proc do_compare_vdbe_test {tn sql1 sql2 res} { uplevel [list do_test $tn [list compare_vdbe $sql1 $sql2] $res] } #------------------------------------------------------------------------- # Test that various statements that are eligible for the optimization # produce the same VDBE code as optimizing by hand does. # foreach {tn sql1 sql2} { 1 "SELECT a, sum(b) FROM t1 GROUP BY a HAVING a=2" "SELECT a, sum(b) FROM t1 WHERE a=2 GROUP BY a" 2 "SELECT a, sum(b) FROM t1 GROUP BY a HAVING sum(b)>5 AND a=2" "SELECT a, sum(b) FROM t1 WHERE a=2 GROUP BY a HAVING sum(b)>5" 3 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE binary HAVING a=2" "SELECT a, sum(b) FROM t1 WHERE a=2 GROUP BY a COLLATE binary" 4 { SELECT x,y FROM ( SELECT a AS x, sum(b) AS y FROM t1 GROUP BY a ) WHERE x BETWEEN 8888 AND 9999 } { SELECT x,y FROM ( SELECT a AS x, sum(b) AS y FROM t1 WHERE x BETWEEN 8888 AND 9999 GROUP BY a ) } 5 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE binary HAVING 0" "SELECT a, sum(b) FROM t1 WHERE 0 GROUP BY a COLLATE binary" 6 "SELECT count(*) FROM t1,t2 WHERE a=c GROUP BY b, d HAVING b=d" "SELECT count(*) FROM t1,t2 WHERE a=c AND b=d GROUP BY b, d" 7 { SELECT count(*) FROM t1,t2 WHERE a=c GROUP BY b, d HAVING b=d COLLATE nocase } { SELECT count(*) FROM t1,t2 WHERE a=c AND b=d COLLATE nocase GROUP BY b, d } 8 "SELECT a, sum(b) FROM t1 GROUP BY a||b HAVING substr(a||b, 1, 1)='a'" "SELECT a, sum(b) FROM t1 WHERE substr(a||b, 1, 1)='a' GROUP BY a||b" } { do_compare_vdbe_test 2.$tn $sql1 $sql2 1 } #------------------------------------------------------------------------- # 1: Test that the optimization is only applied if the GROUP BY term # uses BINARY collation. # # 2: Not applied if there is a non-deterministic function in the HAVING # term. # foreach {tn sql1 sql2} { 1 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE nocase HAVING a=2" "SELECT a, sum(b) FROM t1 WHERE a=2 GROUP BY a COLLATE nocase" 2 "SELECT a, sum(b) FROM t1 GROUP BY a HAVING randomblob(a)<X'88'" "SELECT a, sum(b) FROM t1 WHERE randomblob(a)<X'88' GROUP BY a" } { do_compare_vdbe_test 3.$tn $sql1 $sql2 0 } #------------------------------------------------------------------------- # Test that non-deterministic functions disqualify a term from being # moved from the HAVING to WHERE clause. # do_execsql_test 4.1 { CREATE TABLE t3(a, b); INSERT INTO t3 VALUES(1, 1); INSERT INTO t3 VALUES(1, 2); INSERT INTO t3 VALUES(1, 3); INSERT INTO t3 VALUES(2, 1); INSERT INTO t3 VALUES(2, 2); INSERT INTO t3 VALUES(2, 3); } proc nondeter {args} { incr ::nondeter_ret expr {$::nondeter_ret % 2} } db func nondeter nondeter set ::nondeter_ret 0 do_execsql_test 4.2 { SELECT a, sum(b) FROM t3 GROUP BY a HAVING nondeter(a) } {1 6} # If the term where moved, the query above would return the same # result as the following. But it does not. # set ::nondeter_ret 0 do_execsql_test 4.3 { SELECT a, sum(b) FROM t3 WHERE nondeter(a) GROUP BY a } {1 4 2 2} finish_test |
Changes to test/hexlit.test.
︙ | ︙ | |||
108 109 110 111 112 113 114 115 116 117 118 119 120 121 | # do_catchsql_test hexlist-400 { SELECT 0x10000000000000000; } {1 {hex literal too big: 0x10000000000000000}} do_catchsql_test hexlist-401 { SELECT DISTINCT 0x10000000000000000; } {1 {hex literal too big: 0x10000000000000000}} do_catchsql_test hexlist-410 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x); INSERT INTO t1 VALUES(1+0x10000000000000000); } {1 {hex literal too big: 0x10000000000000000}} | > > > | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | # do_catchsql_test hexlist-400 { SELECT 0x10000000000000000; } {1 {hex literal too big: 0x10000000000000000}} do_catchsql_test hexlist-401 { SELECT DISTINCT 0x10000000000000000; } {1 {hex literal too big: 0x10000000000000000}} do_catchsql_test hexlist-402 { SELECT DISTINCT -0x08000000000000000; } {1 {hex literal too big: -0x08000000000000000}} do_catchsql_test hexlist-410 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x); INSERT INTO t1 VALUES(1+0x10000000000000000); } {1 {hex literal too big: 0x10000000000000000}} |
︙ | ︙ |
Changes to test/hook.test.
︙ | ︙ | |||
845 846 847 848 849 850 851 | DELETE main t4 1 1 3 abc DELETE main t3 1 1 2 abc DELETE main t2 1 1 1 abc DELETE main t1 1 1 0 abc } # No preupdate callbacks for modifying sqlite_master. | | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | DELETE main t4 1 1 3 abc DELETE main t3 1 1 2 abc DELETE main t2 1 1 1 abc DELETE main t1 1 1 0 abc } # No preupdate callbacks for modifying sqlite_master. do_preupdate_test 8.1 { CREATE TABLE x1(x, y); } { } do_preupdate_test 8.2 { ALTER TABLE x1 ADD COLUMN z } { } do_preupdate_test 8.3 { ALTER TABLE x1 RENAME TO y1 } { } do_preupdate_test 8.4 { CREATE INDEX y1x ON y1(x) } { } do_preupdate_test 8.5 { CREATE VIEW v1 AS SELECT * FROM y1 } { } do_preupdate_test 8.6 { DROP TABLE y1 } { } #------------------------------------------------------------------------- reset_db db preupdate hook preupdate_hook do_execsql_test 9.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); CREATE TABLE t2(a, b INTEGER PRIMARY KEY); } do_preupdate_test 9.1 { INSERT INTO t1 VALUES(456, NULL, NULL); } { INSERT main t1 456 456 0 456 {} {} } do_execsql_test 9.2 { ALTER TABLE t1 ADD COLUMN d; } do_preupdate_test 9.3 { INSERT INTO t1(a, b, c) VALUES(457, NULL, NULL); } { INSERT main t1 457 457 0 457 {} {} {} } do_preupdate_test 9.4 { DELETE FROM t1 WHERE a=456 } { DELETE main t1 456 456 0 456 {} {} {} } do_preupdate_test 9.5 { INSERT INTO t2 DEFAULT VALUES; } { INSERT main t2 1 1 0 {} 1 } do_preupdate_test 9.6 { INSERT INTO t1 DEFAULT VALUES; } { INSERT main t1 458 458 0 458 {} {} {} } do_execsql_test 10.0 { CREATE TABLE t3(a, b INTEGER PRIMARY KEY); } do_preupdate_test 10.1 { INSERT INTO t3 DEFAULT VALUES } { INSERT main t3 1 1 0 {} 1 } do_execsql_test 10.2 { SELECT * FROM t3 } {{} 1} do_preupdate_test 10.3 { DELETE FROM t3 WHERE b=1 } {DELETE main t3 1 1 0 {} 1} finish_test |
Added test/hook2.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 | # 2017 Jan 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. # #*********************************************************************** # The tests in this file focus on the pre-update hook. # set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix hook2 ifcapable !preupdate { finish_test return } #------------------------------------------------------------------------- proc do_preupdate_test {tn sql x} { set X [list] foreach elem $x {lappend X $elem} uplevel do_test $tn [list " set ::preupdate \[list\] execsql { $sql } set ::preupdate "] [list $X] } proc preupdate_hook {args} { set type [lindex $args 0] eval lappend ::preupdate $args if {$type != "INSERT"} { for {set i 0} {$i < [db preupdate count]} {incr i} { lappend ::preupdate [db preupdate old $i] } } if {$type != "DELETE"} { for {set i 0} {$i < [db preupdate count]} {incr i} { set rc [catch { db preupdate new $i } v] lappend ::preupdate $v } } } #------------------------------------------------------------------------- # Simple tests - INSERT, UPDATE and DELETE on a WITHOUT ROWID table. # db preupdate hook preupdate_hook do_execsql_test 1.0 { CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT ROWID; } do_preupdate_test 1.1 { INSERT INTO t1 VALUES('one', 1); } { INSERT main t1 0 0 one 1 } do_preupdate_test 1.2 { UPDATE t1 SET b=2 WHERE a='one'; } { UPDATE main t1 0 0 one 1 one 2 } do_preupdate_test 1.3 { DELETE FROM t1 WHERE a='one'; } { DELETE main t1 0 0 one 2 } #------------------------------------------------------------------------- # Some more complex tests for the pre-update callback on WITHOUT ROWID # tables. # # 2.1.1 - INSERT statement. # 2.1.2 - INSERT INTO ... SELECT statement. # 2.1.3 - REPLACE INTO ... (PK conflict) # 2.1.4 - REPLACE INTO ... (other index conflicts) # 2.1.5 - REPLACE INTO ... (both PK and other index conflicts) # # 2.2.1 - DELETE statement. # 2.2.2 - DELETE statement that uses the truncate optimization. # # 2.3.1 - UPDATE statement. # 2.3.2 - UPDATE statement that modifies the PK. # 2.3.3 - UPDATE OR REPLACE ... (PK conflict). # 2.3.4 - UPDATE OR REPLACE ... (other index conflicts) # 2.3.4 - UPDATE OR REPLACE ... (both PK and other index conflicts) # do_execsql_test 2.0 { CREATE TABLE t2(a DEFAULT 4, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID; CREATE UNIQUE INDEX t2a ON t2(a); } do_preupdate_test 2.1.1 { INSERT INTO t2(b, c) VALUES(1, 1); } { INSERT main t2 0 0 4 1 1 } do_execsql_test 2.1.2.0 { CREATE TABLE d1(a DEFAULT 4, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID; CREATE UNIQUE INDEX d1a ON d1(a); INSERT INTO d1 VALUES(1, 2, 3); INSERT INTO d1 VALUES(11, 12, 13); } do_preupdate_test 2.1.2.1 { INSERT INTO t2 SELECT * FROM d1; } { INSERT main t2 0 0 1 2 3 INSERT main t2 0 0 11 12 13 } do_preupdate_test 2.1.2.2 { INSERT INTO t2 SELECT a+20, b+20, c+20 FROM d1; } { INSERT main t2 0 0 21 22 23 INSERT main t2 0 0 31 32 33 } do_execsql_test 2.1.2.3 { SELECT * FROM t2 ORDER BY b, c; } { 4 1 1 1 2 3 11 12 13 21 22 23 31 32 33 } do_preupdate_test 2.1.3 { REPLACE INTO t2 VALUES(45, 22, 23); } { DELETE main t2 0 0 21 22 23 INSERT main t2 0 0 45 22 23 } do_preupdate_test 2.1.4 { REPLACE INTO t2 VALUES(11, 100, 100); } { DELETE main t2 0 0 11 12 13 INSERT main t2 0 0 11 100 100 } do_preupdate_test 2.1.5 { REPLACE INTO t2(c, b) VALUES(33, 32) } { DELETE main t2 0 0 4 1 1 DELETE main t2 0 0 31 32 33 INSERT main t2 0 0 4 32 33 } do_execsql_test 2.2.0 { SELECT * FROM t2 ORDER BY b,c; } { 1 2 3 45 22 23 4 32 33 11 100 100 } do_preupdate_test 2.2.1 { DELETE FROM t2 WHERE b=22; } { DELETE main t2 0 0 45 22 23 } do_preupdate_test 2.2.2 { DELETE FROM t2; } { DELETE main t2 0 0 1 2 3 DELETE main t2 0 0 4 32 33 DELETE main t2 0 0 11 100 100 } do_execsql_test 2.3.0 { CREATE TABLE t3(x, y PRIMARY KEY, z UNIQUE) WITHOUT ROWID; INSERT INTO t3 VALUES('a', 'b', 'c'); INSERT INTO t3 VALUES('d', 'e', 'f'); INSERT INTO t3 VALUES(1, 1, 1); INSERT INTO t3 VALUES(2, 2, 2); INSERT INTO t3 VALUES(3, 3, 3); } do_preupdate_test 2.3.1 { UPDATE t3 SET x=4 WHERE y IN ('b', 'e', 'x'); } { UPDATE main t3 0 0 a b c 4 b c UPDATE main t3 0 0 d e f 4 e f } do_preupdate_test 2.3.2 { UPDATE t3 SET y=y||y WHERE z IN('c', 'f'); } { UPDATE main t3 0 0 4 b c 4 bb c UPDATE main t3 0 0 4 e f 4 ee f } do_preupdate_test 2.3.3 { UPDATE OR REPLACE t3 SET y='bb' WHERE z='f' } { DELETE main t3 0 0 4 bb c UPDATE main t3 0 0 4 ee f 4 bb f } do_preupdate_test 2.3.4 { UPDATE OR REPLACE t3 SET z=2 WHERE y=1; } { DELETE main t3 0 0 2 2 2 UPDATE main t3 0 0 1 1 1 1 1 2 } do_preupdate_test 2.3.5 { UPDATE OR REPLACE t3 SET z=2, y='bb' WHERE y=3; } { DELETE main t3 0 0 1 1 2 DELETE main t3 0 0 4 bb f UPDATE main t3 0 0 3 3 3 3 bb 2 } finish_test |
Changes to test/in.test.
︙ | ︙ | |||
625 626 627 628 629 630 631 | do_test in-13.14 { execsql { CREATE INDEX i5 ON b(id); SELECT * FROM a WHERE id NOT IN (SELECT id FROM b); } } {} | < | 625 626 627 628 629 630 631 632 633 634 635 636 637 638 | do_test in-13.14 { execsql { CREATE INDEX i5 ON b(id); SELECT * FROM a WHERE id NOT IN (SELECT id FROM b); } } {} do_test in-13.15 { catchsql { SELECT 0 WHERE (SELECT 0,0) OR (0 IN (1,2)); } } {1 {sub-select returns 2 columns - expected 1}} |
︙ | ︙ |
Changes to test/in5.test.
︙ | ︙ | |||
232 233 234 235 236 237 238 239 240 | do_execsql_test 8.3 { INSERT INTO n1 VALUES(1, NULL), (2, NULL), (3, NULL); SELECT count(*) FROM n1 WHERE a IN (1, 2, 3) } 3 do_execsql_test 8.4 { SELECT count(*) FROM n1 WHERE a IN (SELECT +a FROM n1) } 3 finish_test | > > > > > > > > > > > > | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | do_execsql_test 8.3 { INSERT INTO n1 VALUES(1, NULL), (2, NULL), (3, NULL); SELECT count(*) FROM n1 WHERE a IN (1, 2, 3) } 3 do_execsql_test 8.4 { SELECT count(*) FROM n1 WHERE a IN (SELECT +a FROM n1) } 3 #------------------------------------------------------------------------- # Test that ticket 61fe97454c is fixed. # do_execsql_test 9.0 { CREATE TABLE t9(a INTEGER PRIMARY KEY); INSERT INTO t9 VALUES (44), (45); } do_execsql_test 9.1 { SELECT * FROM t9 WHERE a IN (44, 45, 44, 45) } {44 45} finish_test |
Changes to test/incrblob2.test.
︙ | ︙ | |||
327 328 329 330 331 332 333 334 335 336 337 338 339 340 | if {$::tcl_platform(pointerSize)>=8} { do_test incrblob2-6.2b { set rc [catch { # Prior to 2015-02-07, the following caused a segfault due to # integer overflow. sqlite3_blob_read $rdHandle 2147483647 2147483647 } errmsg] lappend rc $errmsg } {1 SQLITE_ERROR} } do_test incrblob2-6.2c { set rc [catch { # Prior to 2015-02-07, the following caused a segfault due to # integer overflow. | > > > | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | if {$::tcl_platform(pointerSize)>=8} { do_test incrblob2-6.2b { set rc [catch { # Prior to 2015-02-07, the following caused a segfault due to # integer overflow. sqlite3_blob_read $rdHandle 2147483647 2147483647 } errmsg] if {[string match {out of memory in *test_blob.c} $errmsg]} { set errmsg SQLITE_ERROR } lappend rc $errmsg } {1 SQLITE_ERROR} } do_test incrblob2-6.2c { set rc [catch { # Prior to 2015-02-07, the following caused a segfault due to # integer overflow. |
︙ | ︙ |
Changes to test/incrblob4.test.
︙ | ︙ | |||
81 82 83 84 85 86 87 88 89 | do_test 3.3 { set new [string repeat % 900] execsql { UPDATE t1 SET v = $new WHERE k = 20 } execsql { DELETE FROM t1 WHERE k=19 } execsql { INSERT INTO t1(v) VALUES($new) } } {} finish_test | > > > > > > > > > > > > > > > > > > > > | 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 | do_test 3.3 { set new [string repeat % 900] execsql { UPDATE t1 SET v = $new WHERE k = 20 } execsql { DELETE FROM t1 WHERE k=19 } execsql { INSERT INTO t1(v) VALUES($new) } } {} #------------------------------------------------------------------------- # Test that it is not possible to DROP a table with an incremental blob # cursor open on it. # do_execsql_test 4.1 { CREATE TABLE t2(a INTEGER PRIMARY KEY, b); INSERT INTO t2 VALUES(456, '0123456789'); } do_test 4.2 { set blob [db incrblob -readonly t2 b 456] read $blob 5 } {01234} do_catchsql_test 4.3 { DROP TABLE t2 } {1 {database table is locked}} do_test 4.4 { sqlite3_extended_errcode db } {SQLITE_LOCKED} close $blob finish_test |
Changes to test/incrblobfault.test.
︙ | ︙ | |||
48 49 50 51 52 53 54 | sqlite3 db test.db set ::blob [db incrblob blob v 1] } -body { if {[catch {sqlite3_blob_reopen $::blob -1}]} { error [sqlite3_errmsg db] } } -test { | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | sqlite3 db test.db set ::blob [db incrblob blob v 1] } -body { if {[catch {sqlite3_blob_reopen $::blob -1}]} { error [sqlite3_errmsg db] } } -test { faultsim_test_result {1 {no such rowid: -1}} {1 {disk I/O error}} close $::blob } do_faultsim_test 3 -prep { sqlite3 db test.db } -body { set ::blob [db incrblob blob v 1] |
︙ | ︙ |
Changes to test/incrvacuum.test.
︙ | ︙ | |||
732 733 734 735 736 737 738 | set out [open invalid.db w] puts $out "This is not an SQLite database file" close $out sqlite3 db3 invalid.db catchsql { PRAGMA incremental_vacuum(10); } db3 | | | 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 | set out [open invalid.db w] puts $out "This is not an SQLite database file" close $out sqlite3 db3 invalid.db catchsql { PRAGMA incremental_vacuum(10); } db3 } {1 {file is not a database}} db3 close } do_test incrvacuum-15.1 { db close db2 close forcedelete test.db |
︙ | ︙ |
Added test/index9.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 | # 2017 Jun 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. # #*********************************************************************** # # Test that partial indexes work with bound variables. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix index9 proc sqluses {sql} { array unset ::T uplevel [list db eval "EXPLAIN $sql" a { if {$a(opcode)=="OpenRead"} { set ::T($a(p2)) 1 } }] set in [join [array names ::T] ,] db eval "SELECT name FROM sqlite_master WHERE rootpage IN ($in) ORDER BY 1" } proc do_sqluses_test {tn sql objects} { uplevel [list do_test $tn [list sqluses $sql] $objects] } do_execsql_test 1.0 { CREATE TABLE t1(x, y); CREATE INDEX t1x ON t1(x) WHERE y=45; } set y [expr 45] do_sqluses_test 1.1 { SELECT * FROM t1 WHERE x=? AND y=$y } {t1 t1x} set y [expr 45.1] do_sqluses_test 1.2 { SELECT * FROM t1 WHERE x=? AND y=$y } {t1} set y [expr 44] do_sqluses_test 1.3 { SELECT * FROM t1 WHERE x=? AND y=$y } {t1} unset -nocomplain y do_sqluses_test 1.4 { SELECT * FROM t1 WHERE x=? AND y=$y } {t1} set y [string range "45" 0 end] do_sqluses_test 1.5 { SELECT * FROM t1 WHERE x=? AND y=$y } {t1} do_execsql_test 2.0 { CREATE INDEX t1x2 ON t1(x) WHERE y=-20111000111 } do_sqluses_test 2.1 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} set y [expr -20111000111] do_sqluses_test 2.2 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1 t1x2} set y [expr -20111000110] do_sqluses_test 2.3 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} set y [expr -20111000112] do_sqluses_test 2.4 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} do_execsql_test 3.0 { CREATE INDEX t1x3 ON t1(x) WHERE y=9223372036854775807 } set y [expr 9223372036854775807] do_sqluses_test 3.1 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1 t1x3} set y [expr 9223372036854775808] do_sqluses_test 3.2 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} set y [expr 9223372036854775806] do_sqluses_test 3.3 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} db cache flush sqlite3_db_config db QPSG 1 set y [expr 9223372036854775807] do_sqluses_test 3.4 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} set y [expr 9223372036854775808] do_sqluses_test 3.5 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} sqlite3_db_config db QPSG 0 db cache flush do_execsql_test 4.0 { CREATE INDEX t1x4 ON t1(x) WHERE y=-9223372036854775808 } set y [expr -9223372036854775808] do_sqluses_test 4.1 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1 t1x4} set y [expr -9223372036854775807] do_sqluses_test 4.2 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} set y [expr -9223372036854775809] do_sqluses_test 4.3 { SELECT * FROM t1 WHERE y=$y ORDER BY x } {t1} set y [expr -9223372036854775808] do_sqluses_test 4.4 { SELECT * FROM t1 WHERE $y=y ORDER BY x } {t1 t1x4} db cache flush sqlite3_db_config db QPSG 1 do_sqluses_test 4.5 { SELECT * FROM t1 WHERE $y=y ORDER BY x } {t1} sqlite3_db_config db QPSG 0 db cache flush finish_test |
Changes to test/indexexpr1.test.
︙ | ︙ | |||
177 178 179 180 181 182 183 | EXPLAIN QUERY PLAN SELECT id, b, c FROM t1 WHERE substr(a,27,3)=='ord' AND d>=29; } {/USING INDEX t1a2/} do_catchsql_test indexexpr1-300 { | | | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | EXPLAIN QUERY PLAN SELECT id, b, c FROM t1 WHERE substr(a,27,3)=='ord' AND d>=29; } {/USING INDEX t1a2/} do_catchsql_test indexexpr1-300 { CREATE TABLE t2(a,b,c); INSERT INTO t2 VALUES(1,2,3); CREATE INDEX t2x1 ON t2(a,b+random()); } {1 {non-deterministic functions prohibited in index expressions}} do_catchsql_test indexexpr1-301 { CREATE INDEX t2x1 ON t2(julianday('now',a)); } {1 {non-deterministic function in index expression or CHECK constraint}} do_catchsql_test indexexpr1-310 { CREATE INDEX t2x2 ON t2(a,b+(SELECT 15)); } {1 {subqueries prohibited in index expressions}} do_catchsql_test indexexpr1-320 { CREATE TABLE e1(x,y,UNIQUE(y,substr(x,1,5))); } {1 {expressions prohibited in PRIMARY KEY and UNIQUE constraints}} do_catchsql_test indexexpr1-330 { |
︙ | ︙ | |||
365 366 367 368 369 370 371 372 373 | CREATE INDEX t10_abcd ON t10(a+b,c+d); } do_execsql_test indexexpr1-1200.4 { SELECT a+b, c+d FROM t10 ORDER BY a+b, c+d; } { 0 0 0 2 0 4 2 0 2 2 4 0 } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | CREATE INDEX t10_abcd ON t10(a+b,c+d); } do_execsql_test indexexpr1-1200.4 { SELECT a+b, c+d FROM t10 ORDER BY a+b, c+d; } { 0 0 0 2 0 4 2 0 2 2 4 0 } # Ticket https://www.sqlite.org/src/tktview/eb703ba7b50c1a # Incorrect result using an index on an expression with a collating function # do_execsql_test indexexpr1-1300.1 { CREATE TABLE t1300(a INTEGER PRIMARY KEY, b); INSERT INTO t1300 VALUES(1,'coffee'),(2,'COFFEE'),(3,'stress'),(4,'STRESS'); CREATE INDEX t1300bexpr ON t1300( substr(b,4) ); SELECT a FROM t1300 WHERE substr(b,4)='ess' COLLATE nocase ORDER BY +a; } {3 4} # Ticket https://sqlite.org/src/tktview/aa98619a # Assertion fault using an index on a constant # do_execsql_test indexexpr1-1400 { CREATE TABLE t1400(x TEXT); CREATE INDEX t1400x ON t1400(1); -- Index on a constant SELECT 1 IN (SELECT 2) FROM t1400; } {} do_execsql_test indexexpr1-1410 { INSERT INTO t1400 VALUES('a'),('b'); SELECT 1 IN (SELECT 2) FROM t1400; } {0 0} do_execsql_test indexexpr1-1420 { SELECT 1 IN (SELECT 2 UNION ALL SELECT 1) FROM t1400; } {1 1} do_execsql_test indexexpr1-1430 { DROP INDEX t1400x; CREATE INDEX t1400x ON t1400(abs(15+3)); SELECT abs(15+3) IN (SELECT 17 UNION ALL SELECT 18) FROM t1; } {1 1} finish_test |
Added test/indexexpr2.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 | # 2017 April 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. # #*********************************************************************** # This file implements regression tests for SQLite library. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix indexexpr2 do_execsql_test 1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); INSERT INTO t1 VALUES(3, 'three'); CREATE INDEX i1 ON t1(b || 'x'); } do_execsql_test 1.1 { SELECT 'TWOX' == (b || 'x') FROM t1 WHERE (b || 'x')>'onex' } {0 0} do_execsql_test 1.2 { SELECT 'TWOX' == (b || 'x') COLLATE nocase FROM t1 WHERE (b || 'x')>'onex' } {0 1} do_execsql_test 2.0 { CREATE INDEX i2 ON t1(a+1); } do_execsql_test 2.1 { SELECT a+1, quote(a+1) FROM t1 ORDER BY 1; } {2 2 3 3 4 4} #------------------------------------------------------------------------- # At one point SQLite was incorrectly using indexes on expressions to # optimize ORDER BY and GROUP BY clauses even when the collation # sequences of the query and index did not match (ticket [e20dd54ab0e4]). # The following tests - 3.* - attempt to verify that this has been fixed. # reset_db do_execsql_test 3.1.0 { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); } {} do_eqp_test 3.1.1 { SELECT b FROM t1 WHERE b IS NOT NULL AND a IS NULL GROUP BY b COLLATE nocase ORDER BY b COLLATE nocase; } {/USE TEMP B-TREE FOR GROUP BY/} do_execsql_test 3.2.0 { CREATE TABLE t2(x); INSERT INTO t2 VALUES('.ABC'); INSERT INTO t2 VALUES('.abcd'); INSERT INTO t2 VALUES('.defg'); INSERT INTO t2 VALUES('.DEF'); } {} do_execsql_test 3.2.1 { SELECT x FROM t2 ORDER BY substr(x, 2) COLLATE nocase; } { .ABC .abcd .DEF .defg } do_execsql_test 3.2.2 { CREATE INDEX i2 ON t2( substr(x, 2) ); SELECT x FROM t2 ORDER BY substr(x, 2) COLLATE nocase; } { .ABC .abcd .DEF .defg } do_execsql_test 3.3.0 { CREATE TABLE t3(x); } ifcapable json1 { do_eqp_test 3.3.1 { SELECT json_extract(x, '$.b') FROM t2 WHERE json_extract(x, '$.b') IS NOT NULL AND json_extract(x, '$.a') IS NULL GROUP BY json_extract(x, '$.b') COLLATE nocase ORDER BY json_extract(x, '$.b') COLLATE nocase; } { 0 0 0 {SCAN TABLE t2} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} } do_execsql_test 3.3.2 { CREATE INDEX i3 ON t3(json_extract(x, '$.a'), json_extract(x, '$.b')); } {} do_eqp_test 3.3.3 { SELECT json_extract(x, '$.b') FROM t3 WHERE json_extract(x, '$.b') IS NOT NULL AND json_extract(x, '$.a') IS NULL GROUP BY json_extract(x, '$.b') COLLATE nocase ORDER BY json_extract(x, '$.b') COLLATE nocase; } { 0 0 0 {SEARCH TABLE t3 USING INDEX i3 (<expr>=?)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} } } do_execsql_test 3.4.0 { CREATE TABLE t4(a, b); INSERT INTO t4 VALUES('.ABC', 1); INSERT INTO t4 VALUES('.abc', 2); INSERT INTO t4 VALUES('.ABC', 3); INSERT INTO t4 VALUES('.abc', 4); } do_execsql_test 3.4.1 { SELECT * FROM t4 WHERE substr(a, 2) = 'abc' COLLATE NOCASE ORDER BY substr(a, 2), b; } { .ABC 1 .ABC 3 .abc 2 .abc 4 } do_execsql_test 3.4.2 { CREATE INDEX i4 ON t4( substr(a, 2) COLLATE NOCASE, b ); SELECT * FROM t4 WHERE substr(a, 2) = 'abc' COLLATE NOCASE ORDER BY substr(a, 2), b; } { .ABC 1 .ABC 3 .abc 2 .abc 4 } finish_test |
Changes to test/insert4.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # This file implements regression tests for SQLite library. The # focus of this file is testing the INSERT transfer optimization. # # $Id: insert4.test,v 1.10 2008/01/21 16:22:46 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !view||!subquery { finish_test return } # The sqlite3_xferopt_count variable is incremented whenever the | > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # This file implements regression tests for SQLite library. The # focus of this file is testing the INSERT transfer optimization. # # $Id: insert4.test,v 1.10 2008/01/21 16:22:46 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix insert4 ifcapable !view||!subquery { finish_test return } # The sqlite3_xferopt_count variable is incremented whenever the |
︙ | ︙ | |||
561 562 563 564 565 566 567 568 569 | } {1 3} do_catchsql_test insert4-9.1 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x); INSERT INTO t1(x) VALUES(5 COLLATE xyzzy) UNION SELECT 0; } {1 {no such collation sequence: xyzzy}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {1 3} do_catchsql_test insert4-9.1 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x); INSERT INTO t1(x) VALUES(5 COLLATE xyzzy) UNION SELECT 0; } {1 {no such collation sequence: xyzzy}} #------------------------------------------------------------------------- # Check that running an integrity-check does not disable the xfer # optimization for tables with CHECK constraints. # do_execsql_test 10.1 { CREATE TABLE t8( rid INTEGER, pid INTEGER, mid INTEGER, px INTEGER DEFAULT(0) CHECK(px IN(0, 1)) ); CREATE TEMP TABLE x( rid INTEGER, pid INTEGER, mid INTEGER, px INTEGER DEFAULT(0) CHECK(px IN(0, 1)) ); } do_test 10.2 { set sqlite3_xferopt_count 0 execsql { INSERT INTO x SELECT * FROM t8 } set sqlite3_xferopt_count } {1} do_test 10.3 { execsql { PRAGMA integrity_check } set sqlite3_xferopt_count 0 execsql { INSERT INTO x SELECT * FROM t8 } set sqlite3_xferopt_count } {1} finish_test |
Changes to test/instr.test.
︙ | ︙ | |||
243 244 245 246 247 248 249 250 251 | } {999} do_execsql_test instr-1.61 { SELECT coalesce(instr('abc',NULL), 999); } {999} do_execsql_test instr-1.62 { SELECT coalesce(instr(NULL,NULL), 999); } {999} finish_test | > > > > > > > > > | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | } {999} do_execsql_test instr-1.61 { SELECT coalesce(instr('abc',NULL), 999); } {999} do_execsql_test instr-1.62 { SELECT coalesce(instr(NULL,NULL), 999); } {999} do_execsql_test instr-1.63 { SELECT instr(X'', 'abc') } 0 do_execsql_test instr-1.64 { CREATE TABLE x1(a, b); INSERT INTO x1 VALUES(X'', 'abc'); SELECT instr(a, b) FROM x1; } 0 finish_test |
Added test/instrfault.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 | # 2016 November 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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing OOM error handling within the built-in # INSTR() function. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix instrfault # Use big NEEDLE and HAYSTACK strings. Strings so large they cannot # use lookaside buffers. # set ::NEEDLE [string repeat "abcdefghijklmnopqrstuvwxyz" 10] set ::HAYSTACK "[string repeat 123 10]$NEEDLE[string repeat 456 10]" foreach {enc} { utf8 utf16 } { reset_db sqlite3_db_config_lookaside db 0 0 0 execsql "PRAGMA encoding = $enc" do_execsql_test 1.$enc.1 { CREATE TABLE t1(n, h); INSERT INTO t1 VALUES($::NEEDLE, $::HAYSTACK); } {} do_faultsim_test 1.$enc.1 -faults oom-t* -prep { execsql { SELECT instr(h, n) FROM t1 } } -body { execsql { SELECT instr(h, n) FROM t1 } } -test { faultsim_test_result {0 31} } do_faultsim_test 1.$enc.2 -faults oom-t* -prep { execsql { SELECT instr($::HAYSTACK, $::NEEDLE) FROM t1 } } -body { execsql { SELECT instr($::HAYSTACK, $::NEEDLE) FROM t1 } } -test { faultsim_test_result {0 31} } do_faultsim_test 1.$enc.3 -faults oom-t* -prep { set ::stmt [sqlite3_prepare_v2 db "SELECT instr(?, ?)" -1 dummy] sqlite3_bind_text $::stmt 1 $::HAYSTACK [string length $::HAYSTACK] sqlite3_bind_text $::stmt 2 $::NEEDLE [string length $::NEEDLE] } -body { set rc [sqlite3_step $::stmt] if {$rc=="SQLITE_NOMEM"} { error "out of memory" } sqlite3_column_int $::stmt 0 } -test { faultsim_test_result {0 31} sqlite3_finalize $::stmt } do_faultsim_test 1.$enc.4 -faults oom-t* -prep { set ::stmt [sqlite3_prepare_v2 db "SELECT instr(?, ?)" -1 dummy] sqlite3_bind_blob $::stmt 1 $::HAYSTACK [string length $::HAYSTACK] sqlite3_bind_text $::stmt 2 $::NEEDLE [string length $::NEEDLE] } -body { set rc [sqlite3_step $::stmt] if {$rc=="SQLITE_NOMEM"} { error "out of memory" } sqlite3_column_int $::stmt 0 } -test { faultsim_test_result {0 31} sqlite3_finalize $::stmt } do_execsql_test 1.$enc.5.0 { CREATE TABLE h1(a, b); INSERT INTO h1 VALUES('abcdefg%200hijkl', randomblob(200)); INSERT INTO h1 SELECT b, a FROM h1; } do_faultsim_test 1.$enc.5 -faults oom-t* -body { execsql { SELECT rowid FROM h1 WHERE instr(a,b) } } -test {} } finish_test |
Changes to test/intarray.test.
︙ | ︙ | |||
38 39 40 41 42 43 44 | do_test intarray-1.1 { set ia1 [sqlite3_intarray_create db ia1] set ia2 [sqlite3_intarray_create db ia2] set ia3 [sqlite3_intarray_create db ia3] set ia4 [sqlite3_intarray_create db ia4] db eval { | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | do_test intarray-1.1 { set ia1 [sqlite3_intarray_create db ia1] set ia2 [sqlite3_intarray_create db ia2] set ia3 [sqlite3_intarray_create db ia3] set ia4 [sqlite3_intarray_create db ia4] db eval { SELECT type, name FROM temp.sqlite_master ORDER BY name } } {table ia1 table ia2 table ia3 table ia4} do_test intarray-1.2 { db eval { SELECT b FROM t1 WHERE a IN ia3 ORDER BY a |
︙ | ︙ |
Changes to test/interrupt.test.
︙ | ︙ | |||
124 125 126 127 128 129 130 | set ::sqlite_interrupt_count $::i catchsql { INSERT INTO t2 SELECT * FROM t1; } } {1 interrupted} do_test interrupt-3.$i.3 { execsql { | | | | 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 | set ::sqlite_interrupt_count $::i catchsql { INSERT INTO t2 SELECT * FROM t1; } } {1 interrupted} do_test interrupt-3.$i.3 { execsql { SELECT name FROM temp.sqlite_master; } } {} do_test interrupt-3.$i.4 { catchsql { ROLLBACK } } {1 {cannot rollback - no transaction is active}} do_test interrupt-3.$i.5 { catchsql {SELECT name FROM sqlite_temp_master}; execsql { SELECT name FROM temp.sqlite_master; } } {} } } # There are reports of a memory leak if an interrupt occurs during # the beginning of a complex query - before the first callback. We |
︙ | ︙ |
Changes to test/interrupt2.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # interrupt WAL checkpoint operations. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/wal_common.tcl set testprefix interrupt2 db close testvfs tvfs -default 1 tvfs filter xWrite tvfs script write_cb | > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # interrupt WAL checkpoint operations. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/wal_common.tcl set testprefix interrupt2 if {[permutation]=="journaltest" || [permutation]=="inmemory_journal"} { finish_test return } db close testvfs tvfs -default 1 tvfs filter xWrite tvfs script write_cb |
︙ | ︙ |
Changes to test/join.test.
|
| | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 2002-05-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. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for joins, including outer joins. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test join-1.1 { execsql { CREATE TABLE t1(a,b,c); |
︙ | ︙ | |||
708 709 710 711 712 713 714 | # In the following, the order of [cc] and [bb] must not be exchanged, even # though this would be helpful if the query used an inner join. do_execsql_test join-13.2 { CREATE INDEX ccc ON cc(c); SELECT * FROM aa LEFT JOIN bb, cc WHERE cc.c=aa.a; } {45 {} 45 45 {} 45} | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # In the following, the order of [cc] and [bb] must not be exchanged, even # though this would be helpful if the query used an inner join. do_execsql_test join-13.2 { CREATE INDEX ccc ON cc(c); SELECT * FROM aa LEFT JOIN bb, cc WHERE cc.c=aa.a; } {45 {} 45 45 {} 45} # Verify that that iTable attributes the TK_IF_NULL_ROW operators in the # expression tree are correctly updated by the query flattener. This was # a bug discovered on 2017-05-22 by Mark Brand. # do_execsql_test join-14.1 { SELECT * FROM (SELECT 1 a) AS x LEFT JOIN (SELECT 1, * FROM (SELECT * FROM (SELECT 1))); } {1 1 1} do_execsql_test join-14.2 { SELECT * FROM (SELECT 1 a) AS x LEFT JOIN (SELECT 1, * FROM (SELECT * FROM (SELECT * FROM (SELECT 1)))) AS y JOIN (SELECT * FROM (SELECT 9)) AS z; } {1 1 1 9} do_execsql_test join-14.3 { SELECT * FROM (SELECT 111) LEFT JOIN (SELECT cc+222, * FROM (SELECT * FROM (SELECT 333 cc))); } {111 555 333} do_execsql_test join-14.4 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(c PRIMARY KEY, a TEXT(10000), b TEXT(10000)); SELECT * FROM (SELECT 111) LEFT JOIN (SELECT c+222 FROM t1) GROUP BY 1; } {111 {}} do_execsql_test join-14.5 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(c PRIMARY KEY) WITHOUT ROWID; SELECT * FROM (SELECT 111) LEFT JOIN (SELECT c+222 FROM t1) GROUP BY 1; } {111 {}} # Verify the fix to ticket # https://www.sqlite.org/src/tktview/7fde638e94287d2c948cd9389 # db close sqlite3 db :memory: do_execsql_test join-14.10 { CREATE TABLE t1(a); INSERT INTO t1 VALUES(1),(2),(3); CREATE VIEW v2 AS SELECT a, 1 AS b FROM t1; CREATE TABLE t3(x); INSERT INTO t3 VALUES(2),(4); SELECT *, '|' FROM t3 LEFT JOIN v2 ON a=x WHERE b=1; } {2 2 1 |} do_execsql_test join-14.11 { SELECT *, '|' FROM t3 LEFT JOIN v2 ON a=x WHERE b+1=x; } {2 2 1 |} do_execsql_test join-14.12 { SELECT *, '|' FROM t3 LEFT JOIN v2 ON a=x ORDER BY b; } {4 {} {} | 2 2 1 |} # Verify the fix for ticket # https://www.sqlite.org/src/info/892fc34f173e99d8 # db close sqlite3 db :memory: do_execsql_test join-14.20 { CREATE TABLE t1(id INTEGER PRIMARY KEY); CREATE TABLE t2(id INTEGER PRIMARY KEY, c2 INTEGER); CREATE TABLE t3(id INTEGER PRIMARY KEY, c3 INTEGER); INSERT INTO t1(id) VALUES(456); INSERT INTO t3(id) VALUES(1),(2); SELECT t1.id, x2.id, x3.id FROM t1 LEFT JOIN (SELECT * FROM t2) AS x2 ON t1.id=x2.c2 LEFT JOIN t3 AS x3 ON x2.id=x3.c3; } {456 {} {}} finish_test |
Changes to test/join2.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. # # This file implements tests for joins, including outer joins. # | < > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for joins, including outer joins. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix join2 do_test join2-1.1 { execsql { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,11); INSERT INTO t1 VALUES(2,22); INSERT INTO t1 VALUES(3,33); |
︙ | ︙ | |||
67 68 69 70 71 72 73 74 75 | do_test join2-1.7 { execsql { SELECT * FROM t1 NATURAL LEFT OUTER JOIN (t2 NATURAL JOIN t3) } } {1 11 111 1111 2 22 {} {} 3 33 {} {}} } finish_test | > > > > > > > > > > > > > > > > > > > > | 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 | do_test join2-1.7 { execsql { SELECT * FROM t1 NATURAL LEFT OUTER JOIN (t2 NATURAL JOIN t3) } } {1 11 111 1111 2 22 {} {} 3 33 {} {}} } #------------------------------------------------------------------------- # Check that ticket [25e335f802ddc] has been resolved. It should be an # error for the ON clause of a LEFT JOIN to refer to a table to its right. # do_execsql_test 2.0 { CREATE TABLE aa(a); CREATE TABLE bb(b); CREATE TABLE cc(c); INSERT INTO aa VALUES('one'); INSERT INTO bb VALUES('one'); INSERT INTO cc VALUES('one'); } do_catchsql_test 2.1 { SELECT * FROM aa LEFT JOIN cc ON (a=b) JOIN bb ON (b=c); } {1 {ON clause references tables to its right}} do_catchsql_test 2.2 { SELECT * FROM aa JOIN cc ON (a=b) JOIN bb ON (b=c); } {0 {one one one}} finish_test |
Changes to test/join5.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for left outer joins containing ON # clauses that restrict the scope of the left term of the join. # | < > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for left outer joins containing ON # clauses that restrict the scope of the left term of the join. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix join5 do_test join5-1.1 { execsql { BEGIN; CREATE TABLE t1(a integer primary key, b integer, c integer); CREATE TABLE t2(x integer primary key, y); |
︙ | ︙ | |||
179 180 181 182 183 184 185 186 187 | ) c ON b.fruit='banana'; } {apple apple {} banana banana 1} do_execsql_test join6-4.2 { SELECT * FROM (SELECT 'apple' fruit UNION ALL SELECT 'banana') LEFT JOIN (SELECT 1) ON fruit='banana'; } {apple {} banana 1} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ) c ON b.fruit='banana'; } {apple apple {} banana banana 1} do_execsql_test join6-4.2 { SELECT * FROM (SELECT 'apple' fruit UNION ALL SELECT 'banana') LEFT JOIN (SELECT 1) ON fruit='banana'; } {apple {} banana 1} #------------------------------------------------------------------------- do_execsql_test 5.0 { CREATE TABLE y1(x, y, z); INSERT INTO y1 VALUES(0, 0, 1); CREATE TABLE y2(a); } do_execsql_test 5.1 { SELECT count(z) FROM y1 LEFT JOIN y2 ON x GROUP BY y; } 1 do_execsql_test 5.2 { SELECT count(z) FROM ( SELECT * FROM y1 ) LEFT JOIN y2 ON x GROUP BY y; } 1 do_execsql_test 5.3 { CREATE VIEW v1 AS SELECT x, y, z FROM y1; SELECT count(z) FROM v1 LEFT JOIN y2 ON x GROUP BY y; } 1 do_execsql_test 5.4 { SELECT count(z) FROM ( SELECT * FROM y1 ) LEFT JOIN y2 ON x } 1 do_execsql_test 5.5 { SELECT * FROM ( SELECT * FROM y1 ) LEFT JOIN y2 ON x } {0 0 1 {}} finish_test |
Changes to test/json101.test.
︙ | ︙ | |||
351 352 353 354 355 356 357 358 359 360 361 362 363 364 | 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 json-8.2 { SELECT a=json_extract(b,'$[0]') FROM t8; } {1} # The json_quote() function transforms an SQL value into a JSON value. # String values are quoted and interior quotes are escaped. NULL values # are rendered as the unquoted string "null". # do_execsql_test json-9.1 { SELECT json_quote('abc"xyz'); | > > > > > > > > > | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | 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 json-8.2 { SELECT a=json_extract(b,'$[0]') FROM t8; } {1} # 2017-04-12. Regression reported on the mailing list by Rolf Ade # do_execsql_test json-8.3 { SELECT json_valid(char(0x22,0xe4,0x22)); } {1} do_execsql_test json-8.4 { SELECT unicode(json_extract(char(0x22,228,0x22),'$')); } {228} # The json_quote() function transforms an SQL value into a JSON value. # String values are quoted and interior quotes are escaped. NULL values # are rendered as the unquoted string "null". # do_execsql_test json-9.1 { SELECT json_quote('abc"xyz'); |
︙ | ︙ | |||
378 379 380 381 382 383 384 | do_catchsql_test json-9.6 { SELECT json_quote(123,456) } {1 {wrong number of arguments to function json_quote()}} do_catchsql_test json-9.7 { SELECT json_quote() } {1 {wrong number of arguments to function json_quote()}} | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | 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 | do_catchsql_test json-9.6 { SELECT json_quote(123,456) } {1 {wrong number of arguments to function json_quote()}} do_catchsql_test json-9.7 { SELECT json_quote() } {1 {wrong number of arguments to function json_quote()}} # Make sure only valid backslash-escapes are accepted. # do_execsql_test json-10.1 { SELECT json_valid('" \ "'); } {0} do_execsql_test json-10.2 { SELECT json_valid('" \! "'); } {0} do_execsql_test json-10.3 { SELECT json_valid('" \" "'); } {1} do_execsql_test json-10.4 { SELECT json_valid('" \# "'); } {0} do_execsql_test json-10.5 { SELECT json_valid('" \$ "'); } {0} do_execsql_test json-10.6 { SELECT json_valid('" \% "'); } {0} do_execsql_test json-10.7 { SELECT json_valid('" \& "'); } {0} do_execsql_test json-10.8 { SELECT json_valid('" \'' "'); } {0} do_execsql_test json-10.9 { SELECT json_valid('" \( "'); } {0} do_execsql_test json-10.10 { SELECT json_valid('" \) "'); } {0} do_execsql_test json-10.11 { SELECT json_valid('" \* "'); } {0} do_execsql_test json-10.12 { SELECT json_valid('" \+ "'); } {0} do_execsql_test json-10.13 { SELECT json_valid('" \, "'); } {0} do_execsql_test json-10.14 { SELECT json_valid('" \- "'); } {0} do_execsql_test json-10.15 { SELECT json_valid('" \. "'); } {0} do_execsql_test json-10.16 { SELECT json_valid('" \/ "'); } {1} do_execsql_test json-10.17 { SELECT json_valid('" \0 "'); } {0} do_execsql_test json-10.18 { SELECT json_valid('" \1 "'); } {0} do_execsql_test json-10.19 { SELECT json_valid('" \2 "'); } {0} do_execsql_test json-10.20 { SELECT json_valid('" \3 "'); } {0} do_execsql_test json-10.21 { SELECT json_valid('" \4 "'); } {0} do_execsql_test json-10.22 { SELECT json_valid('" \5 "'); } {0} do_execsql_test json-10.23 { SELECT json_valid('" \6 "'); } {0} do_execsql_test json-10.24 { SELECT json_valid('" \7 "'); } {0} do_execsql_test json-10.25 { SELECT json_valid('" \8 "'); } {0} do_execsql_test json-10.26 { SELECT json_valid('" \9 "'); } {0} do_execsql_test json-10.27 { SELECT json_valid('" \: "'); } {0} do_execsql_test json-10.28 { SELECT json_valid('" \; "'); } {0} do_execsql_test json-10.29 { SELECT json_valid('" \< "'); } {0} do_execsql_test json-10.30 { SELECT json_valid('" \= "'); } {0} do_execsql_test json-10.31 { SELECT json_valid('" \> "'); } {0} do_execsql_test json-10.32 { SELECT json_valid('" \? "'); } {0} do_execsql_test json-10.33 { SELECT json_valid('" \@ "'); } {0} do_execsql_test json-10.34 { SELECT json_valid('" \A "'); } {0} do_execsql_test json-10.35 { SELECT json_valid('" \B "'); } {0} do_execsql_test json-10.36 { SELECT json_valid('" \C "'); } {0} do_execsql_test json-10.37 { SELECT json_valid('" \D "'); } {0} do_execsql_test json-10.38 { SELECT json_valid('" \E "'); } {0} do_execsql_test json-10.39 { SELECT json_valid('" \F "'); } {0} do_execsql_test json-10.40 { SELECT json_valid('" \G "'); } {0} do_execsql_test json-10.41 { SELECT json_valid('" \H "'); } {0} do_execsql_test json-10.42 { SELECT json_valid('" \I "'); } {0} do_execsql_test json-10.43 { SELECT json_valid('" \J "'); } {0} do_execsql_test json-10.44 { SELECT json_valid('" \K "'); } {0} do_execsql_test json-10.45 { SELECT json_valid('" \L "'); } {0} do_execsql_test json-10.46 { SELECT json_valid('" \M "'); } {0} do_execsql_test json-10.47 { SELECT json_valid('" \N "'); } {0} do_execsql_test json-10.48 { SELECT json_valid('" \O "'); } {0} do_execsql_test json-10.49 { SELECT json_valid('" \P "'); } {0} do_execsql_test json-10.50 { SELECT json_valid('" \Q "'); } {0} do_execsql_test json-10.51 { SELECT json_valid('" \R "'); } {0} do_execsql_test json-10.52 { SELECT json_valid('" \S "'); } {0} do_execsql_test json-10.53 { SELECT json_valid('" \T "'); } {0} do_execsql_test json-10.54 { SELECT json_valid('" \U "'); } {0} do_execsql_test json-10.55 { SELECT json_valid('" \V "'); } {0} do_execsql_test json-10.56 { SELECT json_valid('" \W "'); } {0} do_execsql_test json-10.57 { SELECT json_valid('" \X "'); } {0} do_execsql_test json-10.58 { SELECT json_valid('" \Y "'); } {0} do_execsql_test json-10.59 { SELECT json_valid('" \Z "'); } {0} do_execsql_test json-10.60 { SELECT json_valid('" \[ "'); } {0} do_execsql_test json-10.61 { SELECT json_valid('" \\ "'); } {1} do_execsql_test json-10.62 { SELECT json_valid('" \] "'); } {0} do_execsql_test json-10.63 { SELECT json_valid('" \^ "'); } {0} do_execsql_test json-10.64 { SELECT json_valid('" \_ "'); } {0} do_execsql_test json-10.65 { SELECT json_valid('" \` "'); } {0} do_execsql_test json-10.66 { SELECT json_valid('" \a "'); } {0} do_execsql_test json-10.67 { SELECT json_valid('" \b "'); } {1} do_execsql_test json-10.68 { SELECT json_valid('" \c "'); } {0} do_execsql_test json-10.69 { SELECT json_valid('" \d "'); } {0} do_execsql_test json-10.70 { SELECT json_valid('" \e "'); } {0} do_execsql_test json-10.71 { SELECT json_valid('" \f "'); } {1} do_execsql_test json-10.72 { SELECT json_valid('" \g "'); } {0} do_execsql_test json-10.73 { SELECT json_valid('" \h "'); } {0} do_execsql_test json-10.74 { SELECT json_valid('" \i "'); } {0} do_execsql_test json-10.75 { SELECT json_valid('" \j "'); } {0} do_execsql_test json-10.76 { SELECT json_valid('" \k "'); } {0} do_execsql_test json-10.77 { SELECT json_valid('" \l "'); } {0} do_execsql_test json-10.78 { SELECT json_valid('" \m "'); } {0} do_execsql_test json-10.79 { SELECT json_valid('" \n "'); } {1} do_execsql_test json-10.80 { SELECT json_valid('" \o "'); } {0} do_execsql_test json-10.81 { SELECT json_valid('" \p "'); } {0} do_execsql_test json-10.82 { SELECT json_valid('" \q "'); } {0} do_execsql_test json-10.83 { SELECT json_valid('" \r "'); } {1} do_execsql_test json-10.84 { SELECT json_valid('" \s "'); } {0} do_execsql_test json-10.85 { SELECT json_valid('" \t "'); } {1} do_execsql_test json-10.86.0 { SELECT json_valid('" \u "'); } {0} do_execsql_test json-10.86.1 { SELECT json_valid('" \ua "'); } {0} do_execsql_test json-10.86.2 { SELECT json_valid('" \uab "'); } {0} do_execsql_test json-10.86.3 { SELECT json_valid('" \uabc "'); } {0} do_execsql_test json-10.86.4 { SELECT json_valid('" \uabcd "'); } {1} do_execsql_test json-10.86.5 { SELECT json_valid('" \uFEDC "'); } {1} do_execsql_test json-10.86.6 { SELECT json_valid('" \u1234 "'); } {1} do_execsql_test json-10.87 { SELECT json_valid('" \v "'); } {0} do_execsql_test json-10.88 { SELECT json_valid('" \w "'); } {0} do_execsql_test json-10.89 { SELECT json_valid('" \x "'); } {0} do_execsql_test json-10.90 { SELECT json_valid('" \y "'); } {0} do_execsql_test json-10.91 { SELECT json_valid('" \z "'); } {0} do_execsql_test json-10.92 { SELECT json_valid('" \{ "'); } {0} do_execsql_test json-10.93 { SELECT json_valid('" \| "'); } {0} do_execsql_test json-10.94 { SELECT json_valid('" \} "'); } {0} do_execsql_test json-10.95 { SELECT json_valid('" \~ "'); } {0} #-------------------------------------------------------------------------- # 2017-04-11. https://www.sqlite.org/src/info/981329adeef51011 # Stack overflow on deeply nested JSON. # # The following tests confirm that deeply nested JSON is considered invalid. # do_execsql_test json-11.0 { /* Shallow enough to be parsed */ SELECT json_valid(printf('%.2000c0%.2000c','[',']')); } {1} do_execsql_test json-11.1 { /* Too deep by one */ SELECT json_valid(printf('%.2001c0%.2001c','[',']')); } {0} do_execsql_test json-11.2 { /* Shallow enough to be parsed { */ SELECT json_valid(replace(printf('%.2000c0%.2000c','[','}'),'[','{"a":')); /* } */ } {1} do_execsql_test json-11.3 { /* Too deep by one { */ SELECT json_valid(replace(printf('%.2001c0%.2001c','[','}'),'[','{"a":')); /* } */ } {0} finish_test |
Changes to test/json102.test.
︙ | ︙ | |||
293 294 295 296 297 298 299 300 301 | # for {set i 0} {$i<100} {incr i} { set str abcdef[string repeat \" [expr {$i+50}]]uvwxyz do_test json102-[format %d [expr {$i+1300}]] { db eval {SELECT json_extract(json_array($::str),'$[0]')==$::str} } {1} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # for {set i 0} {$i<100} {incr i} { set str abcdef[string repeat \" [expr {$i+50}]]uvwxyz do_test json102-[format %d [expr {$i+1300}]] { db eval {SELECT json_extract(json_array($::str),'$[0]')==$::str} } {1} } #------------------------------------------------------------------------- # 2017-04-08 ticket b93be8729a895a528e2849fca99f7 # JSON extension accepts invalid numeric values # # JSON does not allow leading zeros. But the JSON extension was # allowing them. The following tests verify that the problem is now # fixed. # do_execsql_test json102-1401 { SELECT json_valid('{"x":01}') } 0 do_execsql_test json102-1402 { SELECT json_valid('{"x":-01}') } 0 do_execsql_test json102-1403 { SELECT json_valid('{"x":0}') } 1 do_execsql_test json102-1404 { SELECT json_valid('{"x":-0}') } 1 do_execsql_test json102-1405 { SELECT json_valid('{"x":0.1}') } 1 do_execsql_test json102-1406 { SELECT json_valid('{"x":-0.1}') } 1 do_execsql_test json102-1407 { SELECT json_valid('{"x":0.0000}') } 1 do_execsql_test json102-1408 { SELECT json_valid('{"x":-0.0000}') } 1 do_execsql_test json102-1409 { SELECT json_valid('{"x":01.5}') } 0 do_execsql_test json102-1410 { SELECT json_valid('{"x":-01.5}') } 0 do_execsql_test json102-1411 { SELECT json_valid('{"x":00}') } 0 do_execsql_test json102-1412 { SELECT json_valid('{"x":-00}') } 0 #------------------------------------------------------------------------ # 2017-04-10 ticket 6c9b5514077fed34551f98e64c09a10dc2fc8e16 # JSON extension accepts strings containing control characters. # # The JSON spec requires that all control characters be escaped. # do_execsql_test json102-1500 { WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<0x20) SELECT x FROM c WHERE json_valid(printf('{"a":"x%sz"}', char(x))) ORDER BY x; } {32} # All control characters are escaped # do_execsql_test json102-1501 { WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<0x1f) SELECT sum(json_valid(json_quote('a'||char(x)||'z'))) FROM c ORDER BY x; } {31} finish_test |
Added test/json104.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 | # 2017-03-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. # #*********************************************************************** # This file implements tests for json_patch(A,B) SQL function. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !json1 { finish_test return } # This is the example from pages 2 and 3 of RFC-7396 do_execsql_test json104-100 { SELECT json_patch('{ "a": "b", "c": { "d": "e", "f": "g" } }','{ "a":"z", "c": { "f": null } }'); } {{{"a":"z","c":{"d":"e"}}}} # This is the example from pages 4 and 5 of RFC-7396 do_execsql_test json104-110 { SELECT json_patch('{ "title": "Goodbye!", "author" : { "givenName" : "John", "familyName" : "Doe" }, "tags":[ "example", "sample" ], "content": "This will be unchanged" }','{ "title": "Hello!", "phoneNumber": "+01-123-456-7890", "author": { "familyName": null }, "tags": [ "example" ] }'); } {{{"title":"Hello!","author":{"givenName":"John"},"tags":["example"],"content":"This will be unchanged","phoneNumber":"+01-123-456-7890"}}} do_execsql_test json104-200 { SELECT json_patch('[1,2,3]','{"x":null}'); } {{{}}} do_execsql_test json104-210 { SELECT json_patch('[1,2,3]','{"x":null,"y":1,"z":null}'); } {{{"y":1}}} do_execsql_test json104-220 { SELECT json_patch('{}','{"a":{"bb":{"ccc":null}}}'); } {{{"a":{"bb":{}}}}} do_execsql_test json104-221 { SELECT json_patch('{}','{"a":{"bb":{"ccc":[1,null,3]}}}'); } {{{"a":{"bb":{"ccc":[1,null,3]}}}}} do_execsql_test json104-222 { SELECT json_patch('{}','{"a":{"bb":{"ccc":[1,{"dddd":null},3]}}}'); } {{{"a":{"bb":{"ccc":[1,{"dddd":null},3]}}}}} # Example test cases at the end of the RFC-7396 document do_execsql_test json104-300 { SELECT json_patch('{"a":"b"}','{"a":"c"}'); } {{{"a":"c"}}} do_execsql_test json104-300a { SELECT coalesce(json_patch(null,'{"a":"c"}'), 'real-null'); } {{real-null}} do_execsql_test json104-301 { SELECT json_patch('{"a":"b"}','{"b":"c"}'); } {{{"a":"b","b":"c"}}} do_execsql_test json104-302 { SELECT json_patch('{"a":"b"}','{"a":null}'); } {{{}}} do_execsql_test json104-303 { SELECT json_patch('{"a":"b","b":"c"}','{"a":null}'); } {{{"b":"c"}}} do_execsql_test json104-304 { SELECT json_patch('{"a":["b"]}','{"a":"c"}'); } {{{"a":"c"}}} do_execsql_test json104-305 { SELECT json_patch('{"a":"c"}','{"a":["b"]}'); } {{{"a":["b"]}}} do_execsql_test json104-306 { SELECT json_patch('{"a":{"b":"c"}}','{"a":{"b":"d","c":null}}'); } {{{"a":{"b":"d"}}}} do_execsql_test json104-307 { SELECT json_patch('{"a":[{"b":"c"}]}','{"a":[1]}'); } {{{"a":[1]}}} do_execsql_test json104-308 { SELECT json_patch('["a","b"]','["c","d"]'); } {{["c","d"]}} do_execsql_test json104-309 { SELECT json_patch('{"a":"b"}','["c"]'); } {{["c"]}} do_execsql_test json104-310 { SELECT json_patch('{"a":"foo"}','null'); } {{null}} do_execsql_test json104-310a { SELECT coalesce(json_patch('{"a":"foo"}',null), 'real-null'); } {{real-null}} do_execsql_test json104-311 { SELECT json_patch('{"a":"foo"}','"bar"'); } {{"bar"}} do_execsql_test json104-312 { SELECT json_patch('{"e":null}','{"a":1}'); } {{{"e":null,"a":1}}} do_execsql_test json104-313 { SELECT json_patch('[1,2]','{"a":"b","c":null}'); } {{{"a":"b"}}} do_execsql_test json104-314 { SELECT json_patch('{}','{"a":{"bb":{"ccc":null}}}'); } {{{"a":{"bb":{}}}}} finish_test |
Added test/kvtest.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 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 | /* ** 2016-12-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. ** ************************************************************************* ** ** This file implements "key-value" performance test for SQLite. The ** purpose is to compare the speed of SQLite for accessing large BLOBs ** versus reading those same BLOB values out of individual files in the ** filesystem. ** ** Run "kvtest" with no arguments for on-line help, or see comments below. ** ** HOW TO COMPILE: ** ** (1) Gather this source file and a recent SQLite3 amalgamation with its ** header into the working directory. You should have: ** ** kvtest.c >--- this file ** sqlite3.c \___ SQLite ** sqlite3.h / amlagamation & header ** ** (2) Run you compiler against the two C source code files. ** ** (a) On linux or mac: ** ** OPTS="-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION" ** gcc -Os -I. $OPTS kvtest.c sqlite3.c -o kvtest ** ** The $OPTS options can be omitted. The $OPTS merely omit ** the need to link against -ldl and -lpthread, or whatever ** the equivalent libraries are called on your system. ** ** (b) Windows with MSVC: ** ** cl -I. kvtest.c sqlite3.c ** ** USAGE: ** ** (1) Create a test database by running "kvtest init" with appropriate ** options. See the help message for available options. ** ** (2) Construct the corresponding pile-of-files database on disk using ** the "kvtest export" command. ** ** (3) Run tests using "kvtest run" against either the SQLite database or ** the pile-of-files database and with appropriate options. ** ** For example: ** ** ./kvtest init x1.db --count 100000 --size 10000 ** mkdir x1 ** ./kvtest export x1.db x1 ** ./kvtest run x1.db --count 10000 --max-id 1000000 ** ./kvtest run x1 --count 10000 --max-id 1000000 */ static const char zHelp[] = "Usage: kvtest COMMAND ARGS...\n" "\n" " kvtest init DBFILE --count N --size M --pagesize X\n" "\n" " Generate a new test database file named DBFILE containing N\n" " BLOBs each of size M bytes. The page size of the new database\n" " file will be X. Additional options:\n" "\n" " --variance V Randomly vary M by plus or minus V\n" "\n" " kvtest export DBFILE DIRECTORY [--tree]\n" "\n" " Export all the blobs in the kv table of DBFILE into separate\n" " files in DIRECTORY. DIRECTORY is created if it does not previously\n" " exist. If the --tree option is used, then the blobs are written\n" " into a hierarchy of directories, using names like 00/00/00,\n" " 00/00/01, 00/00/02, and so forth. Without the --tree option, all\n" " files are in the top-level directory with names like 000000, 000001,\n" " 000002, and so forth.\n" "\n" " kvtest stat DBFILE [options]\n" "\n" " Display summary information about DBFILE. Options:\n" "\n" " --vacuum Run VACUUM on the database file\n" "\n" " kvtest run DBFILE [options]\n" "\n" " Run a performance test. DBFILE can be either the name of a\n" " database or a directory containing sample files. Options:\n" "\n" " --asc Read blobs in ascending order\n" " --blob-api Use the BLOB API\n" " --cache-size N Database cache size\n" " --count N Read N blobs\n" " --desc Read blobs in descending order\n" " --fsync Synchronous file writes\n" " --integrity-check Run \"PRAGMA integrity_check\" after test\n" " --max-id N Maximum blob key to use\n" " --mmap N Mmap as much as N bytes of DBFILE\n" " --multitrans Each read or write in its own transaction\n" " --nocheckpoint Omit the checkpoint on WAL mode writes\n" " --nosync Set \"PRAGMA synchronous=OFF\"\n" " --jmode MODE Set MODE journal mode prior to starting\n" " --random Read blobs in a random order\n" " --start N Start reading with this blob key\n" " --stats Output operating stats before exiting\n" " --update Do an overwrite test\n" ; /* Reference resources used */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <assert.h> #include <string.h> #include "sqlite3.h" #ifndef _WIN32 # include <unistd.h> #else /* Provide Windows equivalent for the needed parts of unistd.h */ # include <direct.h> # include <io.h> # define R_OK 2 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) # define access _access #endif #include <stdint.h> /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** ** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all compilers, or ** if it is available, it requires an #include of specific headers ** that vary from one machine to the next. ** ** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on ** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)). ** So we have to define the macros in different ways depending on the ** compiler. */ #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ # define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define SQLITE_PTR_TO_INT(X) ((sqlite3_int64)(__PTRDIFF_TYPE__)(X)) #else # define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) # define SQLITE_PTR_TO_INT(X) ((sqlite3_int64)(intptr_t)(X)) #endif /* ** Show thqe help text and quit. */ static void showHelp(void){ fprintf(stdout, "%s", zHelp); exit(1); } /* ** Show an error message an quit. */ static void fatalError(const char *zFormat, ...){ va_list ap; fprintf(stdout, "ERROR: "); va_start(ap, zFormat); vfprintf(stdout, zFormat, ap); va_end(ap); fprintf(stdout, "\n"); exit(1); } /* ** Return the value of a hexadecimal digit. Return -1 if the input ** is not a hex digit. */ static int hexDigitValue(char c){ if( c>='0' && c<='9' ) return c - '0'; if( c>='a' && c<='f' ) return c - 'a' + 10; if( c>='A' && c<='F' ) return c - 'A' + 10; return -1; } /* ** Interpret zArg as an integer value, possibly with suffixes. */ static int integerValue(const char *zArg){ int v = 0; static const struct { char *zSuffix; int iMult; } aMult[] = { { "KiB", 1024 }, { "MiB", 1024*1024 }, { "GiB", 1024*1024*1024 }, { "KB", 1000 }, { "MB", 1000000 }, { "GB", 1000000000 }, { "K", 1000 }, { "M", 1000000 }, { "G", 1000000000 }, }; int i; int isNeg = 0; if( zArg[0]=='-' ){ isNeg = 1; zArg++; }else if( zArg[0]=='+' ){ zArg++; } if( zArg[0]=='0' && zArg[1]=='x' ){ int x; zArg += 2; while( (x = hexDigitValue(zArg[0]))>=0 ){ v = (v<<4) + x; zArg++; } }else{ while( zArg[0]>='0' && zArg[0]<='9' ){ v = v*10 + zArg[0] - '0'; zArg++; } } for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){ if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ v *= aMult[i].iMult; break; } } return isNeg? -v : v; } /* ** Check the filesystem object zPath. Determine what it is: ** ** PATH_DIR A single directory holding many files ** PATH_TREE A directory hierarchy with files at the leaves ** PATH_DB An SQLite database ** PATH_NEXIST Does not exist ** PATH_OTHER Something else ** ** PATH_DIR means all of the separate files are grouped together ** into a single directory with names like 000000, 000001, 000002, and ** so forth. PATH_TREE means there is a hierarchy of directories so ** that no single directory has too many entries. The files have names ** like 00/00/00, 00/00/01, 00/00/02 and so forth. The decision between ** PATH_DIR and PATH_TREE is determined by the presence of a subdirectory ** named "00" at the top-level. */ #define PATH_DIR 1 #define PATH_TREE 2 #define PATH_DB 3 #define PATH_NEXIST 0 #define PATH_OTHER 99 static int pathType(const char *zPath){ struct stat x; int rc; if( access(zPath,R_OK) ) return PATH_NEXIST; memset(&x, 0, sizeof(x)); rc = stat(zPath, &x); if( rc<0 ) return PATH_OTHER; if( S_ISDIR(x.st_mode) ){ char *zLayer1 = sqlite3_mprintf("%s/00", zPath); memset(&x, 0, sizeof(x)); rc = stat(zLayer1, &x); sqlite3_free(zLayer1); if( rc<0 ) return PATH_DIR; if( S_ISDIR(x.st_mode) ) return PATH_TREE; return PATH_DIR; } if( (x.st_size%512)==0 ) return PATH_DB; return PATH_OTHER; } /* ** Return the size of a file in bytes. Or return -1 if the ** named object is not a regular file or does not exist. */ static sqlite3_int64 fileSize(const char *zPath){ struct stat x; int rc; memset(&x, 0, sizeof(x)); rc = stat(zPath, &x); if( rc<0 ) return -1; if( !S_ISREG(x.st_mode) ) return -1; return x.st_size; } /* ** A Pseudo-random number generator with a fixed seed. Use this so ** that the same sequence of "random" numbers are generated on each ** run, for repeatability. */ static unsigned int randInt(void){ static unsigned int x = 0x333a13cd; static unsigned int y = 0xecb2adea; x = (x>>1) ^ ((1+~(x&1)) & 0xd0000001); y = y*1103515245 + 12345; return x^y; } /* ** Do database initialization. */ static int initMain(int argc, char **argv){ char *zDb; int i, rc; int nCount = 1000; int sz = 10000; int iVariance = 0; int pgsz = 4096; sqlite3 *db; char *zSql; char *zErrMsg = 0; assert( strcmp(argv[1],"init")==0 ); assert( argc>=3 ); zDb = argv[2]; for(i=3; i<argc; i++){ char *z = argv[i]; if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z); if( z[1]=='-' ) z++; if( strcmp(z, "-count")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); nCount = integerValue(argv[++i]); if( nCount<1 ) fatalError("the --count must be positive"); continue; } if( strcmp(z, "-size")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); sz = integerValue(argv[++i]); if( sz<1 ) fatalError("the --size must be positive"); continue; } if( strcmp(z, "-variance")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); iVariance = integerValue(argv[++i]); continue; } if( strcmp(z, "-pagesize")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); pgsz = integerValue(argv[++i]); if( pgsz<512 || pgsz>65536 || ((pgsz-1)&pgsz)!=0 ){ fatalError("the --pagesize must be power of 2 between 512 and 65536"); } continue; } fatalError("unknown option: \"%s\"", argv[i]); } rc = sqlite3_open(zDb, &db); if( rc ){ fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db)); } zSql = sqlite3_mprintf( "DROP TABLE IF EXISTS kv;\n" "PRAGMA page_size=%d;\n" "VACUUM;\n" "BEGIN;\n" "CREATE TABLE kv(k INTEGER PRIMARY KEY, v BLOB);\n" "WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<%d)" " INSERT INTO kv(k,v) SELECT x, randomblob(%d+(random()%%(%d))) FROM c;\n" "COMMIT;\n", pgsz, nCount, sz, iVariance+1 ); rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg); if( rc ) fatalError("database create failed: %s", zErrMsg); sqlite3_free(zSql); sqlite3_close(db); return 0; } /* ** Analyze an existing database file. Report its content. */ static int statMain(int argc, char **argv){ char *zDb; int i, rc; sqlite3 *db; char *zSql; sqlite3_stmt *pStmt; int doVacuum = 0; assert( strcmp(argv[1],"stat")==0 ); assert( argc>=3 ); zDb = argv[2]; for(i=3; i<argc; i++){ char *z = argv[i]; if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z); if( z[1]=='-' ) z++; if( strcmp(z, "-vacuum")==0 ){ doVacuum = 1; continue; } fatalError("unknown option: \"%s\"", argv[i]); } rc = sqlite3_open(zDb, &db); if( rc ){ fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db)); } if( doVacuum ){ printf("Vacuuming...."); fflush(stdout); sqlite3_exec(db, "VACUUM", 0, 0, 0); printf(" done\n"); } zSql = sqlite3_mprintf( "SELECT count(*), min(length(v)), max(length(v)), avg(length(v))" " FROM kv" ); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db)); sqlite3_free(zSql); if( sqlite3_step(pStmt)==SQLITE_ROW ){ printf("Number of entries: %8d\n", sqlite3_column_int(pStmt, 0)); printf("Average value size: %8d\n", sqlite3_column_int(pStmt, 3)); printf("Minimum value size: %8d\n", sqlite3_column_int(pStmt, 1)); printf("Maximum value size: %8d\n", sqlite3_column_int(pStmt, 2)); }else{ printf("No rows\n"); } sqlite3_finalize(pStmt); zSql = sqlite3_mprintf("PRAGMA page_size"); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db)); sqlite3_free(zSql); if( sqlite3_step(pStmt)==SQLITE_ROW ){ printf("Page-size: %8d\n", sqlite3_column_int(pStmt, 0)); } sqlite3_finalize(pStmt); zSql = sqlite3_mprintf("PRAGMA page_count"); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db)); sqlite3_free(zSql); if( sqlite3_step(pStmt)==SQLITE_ROW ){ printf("Page-count: %8d\n", sqlite3_column_int(pStmt, 0)); } sqlite3_finalize(pStmt); zSql = sqlite3_mprintf("PRAGMA freelist_count"); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db)); sqlite3_free(zSql); if( sqlite3_step(pStmt)==SQLITE_ROW ){ printf("Freelist-count: %8d\n", sqlite3_column_int(pStmt, 0)); } sqlite3_finalize(pStmt); rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check(10)", -1, &pStmt, 0); if( rc ) fatalError("cannot prepare integrity check: %s", sqlite3_errmsg(db)); while( sqlite3_step(pStmt)==SQLITE_ROW ){ printf("Integrity-check: %s\n", sqlite3_column_text(pStmt, 0)); } sqlite3_finalize(pStmt); sqlite3_close(db); return 0; } /* ** remember(V,PTR) ** ** Return the integer value V. Also save the value of V in a ** C-language variable whose address is PTR. */ static void rememberFunc( sqlite3_context *pCtx, int argc, sqlite3_value **argv ){ sqlite3_int64 v; sqlite3_int64 ptr; assert( argc==2 ); v = sqlite3_value_int64(argv[0]); ptr = sqlite3_value_int64(argv[1]); *(sqlite3_int64*)SQLITE_INT_TO_PTR(ptr) = v; sqlite3_result_int64(pCtx, v); } /* ** Make sure a directory named zDir exists. */ static void kvtest_mkdir(const char *zDir){ #if defined(_WIN32) (void)mkdir(zDir); #else (void)mkdir(zDir, 0755); #endif } /* ** Export the kv table to individual files in the filesystem */ static int exportMain(int argc, char **argv){ char *zDb; char *zDir; sqlite3 *db; sqlite3_stmt *pStmt; int rc; int ePathType; int nFN; char *zFN; char *zTail; size_t nWrote; int i; assert( strcmp(argv[1],"export")==0 ); assert( argc>=3 ); if( argc<4 ) fatalError("Usage: kvtest export DATABASE DIRECTORY [OPTIONS]"); zDb = argv[2]; zDir = argv[3]; kvtest_mkdir(zDir); for(i=4; i<argc; i++){ const char *z = argv[i]; if( z[0]=='-' && z[1]=='-' ) z++; if( strcmp(z,"-tree")==0 ){ zFN = sqlite3_mprintf("%s/00", zDir); kvtest_mkdir(zFN); sqlite3_free(zFN); continue; } fatalError("unknown argument: \"%s\"\n", argv[i]); } ePathType = pathType(zDir); if( ePathType!=PATH_DIR && ePathType!=PATH_TREE ){ fatalError("object \"%s\" is not a directory", zDir); } rc = sqlite3_open(zDb, &db); if( rc ){ fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db)); } rc = sqlite3_prepare_v2(db, "SELECT k, v FROM kv ORDER BY k", -1, &pStmt, 0); if( rc ){ fatalError("prepare_v2 failed: %s\n", sqlite3_errmsg(db)); } nFN = (int)strlen(zDir); zFN = sqlite3_mprintf("%s/00/00/00.extra---------------------", zDir); if( zFN==0 ){ fatalError("malloc failed\n"); } zTail = zFN + nFN + 1; while( sqlite3_step(pStmt)==SQLITE_ROW ){ int iKey = sqlite3_column_int(pStmt, 0); sqlite3_int64 nData = sqlite3_column_bytes(pStmt, 1); const void *pData = sqlite3_column_blob(pStmt, 1); FILE *out; if( ePathType==PATH_DIR ){ sqlite3_snprintf(20, zTail, "%06d", iKey); }else{ sqlite3_snprintf(20, zTail, "%02d", iKey/10000); kvtest_mkdir(zFN); sqlite3_snprintf(20, zTail, "%02d/%02d", iKey/10000, (iKey/100)%100); kvtest_mkdir(zFN); sqlite3_snprintf(20, zTail, "%02d/%02d/%02d", iKey/10000, (iKey/100)%100, iKey%100); } out = fopen(zFN, "wb"); nWrote = fwrite(pData, 1, nData, out); fclose(out); printf("\r%s ", zTail); fflush(stdout); if( nWrote!=nData ){ fatalError("Wrote only %d of %d bytes to %s\n", (int)nWrote, nData, zFN); } } sqlite3_finalize(pStmt); sqlite3_close(db); sqlite3_free(zFN); printf("\n"); return 0; } /* ** Read the content of file zName into memory obtained from sqlite3_malloc64() ** and return a pointer to the buffer. The caller is responsible for freeing ** the memory. ** ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes ** read. ** ** For convenience, a nul-terminator byte is always appended to the data read ** from the file before the buffer is returned. This byte is not included in ** the final value of (*pnByte), if applicable. ** ** NULL is returned if any error is encountered. The final value of *pnByte ** is undefined in this case. */ static unsigned char *readFile(const char *zName, sqlite3_int64 *pnByte){ FILE *in; /* FILE from which to read content of zName */ sqlite3_int64 nIn; /* Size of zName in bytes */ size_t nRead; /* Number of bytes actually read */ unsigned char *pBuf; /* Content read from disk */ nIn = fileSize(zName); if( nIn<0 ) return 0; in = fopen(zName, "rb"); if( in==0 ) return 0; pBuf = sqlite3_malloc64( nIn ); if( pBuf==0 ) return 0; nRead = fread(pBuf, (size_t)nIn, 1, in); fclose(in); if( nRead!=1 ){ sqlite3_free(pBuf); return 0; } if( pnByte ) *pnByte = nIn; return pBuf; } /* ** Overwrite a file with randomness. Do not change the size of the ** file. */ static void updateFile(const char *zName, sqlite3_int64 *pnByte, int doFsync){ FILE *out; /* FILE from which to read content of zName */ sqlite3_int64 sz; /* Size of zName in bytes */ size_t nWritten; /* Number of bytes actually read */ unsigned char *pBuf; /* Content to store on disk */ const char *zMode = "wb"; /* Mode for fopen() */ sz = fileSize(zName); if( sz<0 ){ fatalError("No such file: \"%s\"", zName); } *pnByte = sz; if( sz==0 ) return; pBuf = sqlite3_malloc64( sz ); if( pBuf==0 ){ fatalError("Cannot allocate %lld bytes\n", sz); } sqlite3_randomness((int)sz, pBuf); #if defined(_WIN32) if( doFsync ) zMode = "wbc"; #endif out = fopen(zName, zMode); if( out==0 ){ fatalError("Cannot open \"%s\" for writing\n", zName); } nWritten = fwrite(pBuf, 1, (size_t)sz, out); if( doFsync ){ #if defined(_WIN32) fflush(out); #else fsync(fileno(out)); #endif } fclose(out); if( nWritten!=(size_t)sz ){ fatalError("Wrote only %d of %d bytes to \"%s\"\n", (int)nWritten, (int)sz, zName); } sqlite3_free(pBuf); } /* ** Return the current time in milliseconds since the beginning of ** the Julian epoch. */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; sqlite3_int64 t; if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ clockVfs->xCurrentTimeInt64(clockVfs, &t); }else{ double r; clockVfs->xCurrentTime(clockVfs, &r); t = (sqlite3_int64)(r*86400000.0); } return t; } #ifdef __linux__ /* ** Attempt to display I/O stats on Linux using /proc/PID/io */ static void displayLinuxIoStats(FILE *out){ FILE *in; char z[200]; sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); in = fopen(z, "rb"); if( in==0 ) return; while( fgets(z, sizeof(z), in)!=0 ){ static const struct { const char *zPattern; const char *zDesc; } aTrans[] = { { "rchar: ", "Bytes received by read():" }, { "wchar: ", "Bytes sent to write():" }, { "syscr: ", "Read() system calls:" }, { "syscw: ", "Write() system calls:" }, { "read_bytes: ", "Bytes read from storage:" }, { "write_bytes: ", "Bytes written to storage:" }, { "cancelled_write_bytes: ", "Cancelled write bytes:" }, }; int i; for(i=0; i<sizeof(aTrans)/sizeof(aTrans[0]); i++){ int n = (int)strlen(aTrans[i].zPattern); if( strncmp(aTrans[i].zPattern, z, n)==0 ){ fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]); break; } } } fclose(in); } #endif /* ** Display memory stats. */ static int display_stats( sqlite3 *db, /* Database to query */ int bReset /* True to reset SQLite stats */ ){ int iCur; int iHiwtr; FILE *out = stdout; fprintf(out, "\n"); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset); fprintf(out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset); fprintf(out, "Number of Outstanding Allocations: %d (max %d)\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset); fprintf(out, "Number of Pcache Pages Used: %d (max %d) pages\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset); fprintf(out, "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset); fprintf(out, "Largest Allocation: %d bytes\n", iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset); fprintf(out, "Largest Pcache Allocation: %d bytes\n", iHiwtr); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); fprintf(out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); fprintf(out, "Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); fprintf(out, "Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); fprintf(out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; #ifdef __linux__ displayLinuxIoStats(out); #endif return 0; } /* Blob access order */ #define ORDER_ASC 1 #define ORDER_DESC 2 #define ORDER_RANDOM 3 /* ** Run a performance test */ static int runMain(int argc, char **argv){ int eType; /* Is zDb a database or a directory? */ char *zDb; /* Database or directory name */ int i; /* Loop counter */ int rc; /* Return code from SQLite calls */ int nCount = 1000; /* Number of blob fetch operations */ int nExtra = 0; /* Extra cycles */ int iKey = 1; /* Next blob key */ int iMax = 0; /* Largest allowed key */ int iPagesize = 0; /* Database page size */ int iCache = 1000; /* Database cache size in kibibytes */ int bBlobApi = 0; /* Use the incremental blob I/O API */ int bStats = 0; /* Print stats before exiting */ int eOrder = ORDER_ASC; /* Access order */ int isUpdateTest = 0; /* Do in-place updates rather than reads */ int doIntegrityCk = 0; /* Run PRAGMA integrity_check after the test */ int noSync = 0; /* Disable synchronous mode */ int doFsync = 0; /* Update disk files synchronously */ int doMultiTrans = 0; /* Each operation in its own transaction */ int noCheckpoint = 0; /* Omit the checkpoint in WAL mode */ sqlite3 *db = 0; /* Database connection */ sqlite3_stmt *pStmt = 0; /* Prepared statement for SQL access */ sqlite3_blob *pBlob = 0; /* Handle for incremental Blob I/O */ sqlite3_int64 tmStart; /* Start time */ sqlite3_int64 tmElapsed; /* Elapsed time */ int mmapSize = 0; /* --mmap N argument */ sqlite3_int64 nData = 0; /* Bytes of data */ sqlite3_int64 nTotal = 0; /* Total data read */ unsigned char *pData = 0; /* Content of the blob */ sqlite3_int64 nAlloc = 0; /* Space allocated for pData[] */ const char *zJMode = 0; /* Journal mode */ assert( strcmp(argv[1],"run")==0 ); assert( argc>=3 ); zDb = argv[2]; eType = pathType(zDb); if( eType==PATH_OTHER ) fatalError("unknown object type: \"%s\"", zDb); if( eType==PATH_NEXIST ) fatalError("object does not exist: \"%s\"", zDb); for(i=3; i<argc; i++){ char *z = argv[i]; if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z); if( z[1]=='-' ) z++; if( strcmp(z, "-asc")==0 ){ eOrder = ORDER_ASC; continue; } if( strcmp(z, "-blob-api")==0 ){ bBlobApi = 1; continue; } if( strcmp(z, "-cache-size")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); iCache = integerValue(argv[++i]); continue; } if( strcmp(z, "-count")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); nCount = integerValue(argv[++i]); if( nCount<1 ) fatalError("the --count must be positive"); continue; } if( strcmp(z, "-desc")==0 ){ eOrder = ORDER_DESC; continue; } if( strcmp(z, "-fsync")==0 ){ doFsync = 1; continue; } if( strcmp(z, "-integrity-check")==0 ){ doIntegrityCk = 1; continue; } if( strcmp(z, "-jmode")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); zJMode = argv[++i]; continue; } if( strcmp(z, "-mmap")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); mmapSize = integerValue(argv[++i]); if( nCount<0 ) fatalError("the --mmap must be non-negative"); continue; } if( strcmp(z, "-max-id")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); iMax = integerValue(argv[++i]); continue; } if( strcmp(z, "-multitrans")==0 ){ doMultiTrans = 1; continue; } if( strcmp(z, "-nocheckpoint")==0 ){ noCheckpoint = 1; continue; } if( strcmp(z, "-nosync")==0 ){ noSync = 1; continue; } if( strcmp(z, "-random")==0 ){ eOrder = ORDER_RANDOM; continue; } if( strcmp(z, "-start")==0 ){ if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]); iKey = integerValue(argv[++i]); if( iKey<1 ) fatalError("the --start must be positive"); continue; } if( strcmp(z, "-stats")==0 ){ bStats = 1; continue; } if( strcmp(z, "-update")==0 ){ isUpdateTest = 1; continue; } fatalError("unknown option: \"%s\"", argv[i]); } if( eType==PATH_DB ){ /* Recover any prior crashes prior to starting the timer */ sqlite3_open(zDb, &db); sqlite3_exec(db, "SELECT rowid FROM sqlite_master LIMIT 1", 0, 0, 0); sqlite3_close(db); db = 0; } tmStart = timeOfDay(); if( eType==PATH_DB ){ char *zSql; rc = sqlite3_open(zDb, &db); if( rc ){ fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db)); } zSql = sqlite3_mprintf("PRAGMA mmap_size=%d", mmapSize); sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); zSql = sqlite3_mprintf("PRAGMA cache_size=%d", iCache); sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( noSync ){ sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0); } pStmt = 0; sqlite3_prepare_v2(db, "PRAGMA page_size", -1, &pStmt, 0); if( sqlite3_step(pStmt)==SQLITE_ROW ){ iPagesize = sqlite3_column_int(pStmt, 0); } sqlite3_finalize(pStmt); sqlite3_prepare_v2(db, "PRAGMA cache_size", -1, &pStmt, 0); if( sqlite3_step(pStmt)==SQLITE_ROW ){ iCache = sqlite3_column_int(pStmt, 0); }else{ iCache = 0; } sqlite3_finalize(pStmt); pStmt = 0; if( zJMode ){ zSql = sqlite3_mprintf("PRAGMA journal_mode=%Q", zJMode); sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( noCheckpoint ){ sqlite3_exec(db, "PRAGMA wal_autocheckpoint=0", 0, 0, 0); } } sqlite3_prepare_v2(db, "PRAGMA journal_mode", -1, &pStmt, 0); if( sqlite3_step(pStmt)==SQLITE_ROW ){ zJMode = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); }else{ zJMode = "???"; } sqlite3_finalize(pStmt); if( iMax<=0 ){ sqlite3_prepare_v2(db, "SELECT max(k) FROM kv", -1, &pStmt, 0); if( sqlite3_step(pStmt)==SQLITE_ROW ){ iMax = sqlite3_column_int(pStmt, 0); } sqlite3_finalize(pStmt); } pStmt = 0; if( !doMultiTrans ) sqlite3_exec(db, "BEGIN", 0, 0, 0); } if( iMax<=0 ) iMax = 1000; for(i=0; i<nCount; i++){ if( eType==PATH_DIR || eType==PATH_TREE ){ /* CASE 1: Reading or writing blobs out of separate files */ char *zKey; if( eType==PATH_DIR ){ zKey = sqlite3_mprintf("%s/%06d", zDb, iKey); }else{ zKey = sqlite3_mprintf("%s/%02d/%02d/%02d", zDb, iKey/10000, (iKey/100)%100, iKey%100); } nData = 0; if( isUpdateTest ){ updateFile(zKey, &nData, doFsync); }else{ pData = readFile(zKey, &nData); sqlite3_free(pData); } sqlite3_free(zKey); }else if( bBlobApi ){ /* CASE 2: Reading from database using the incremental BLOB I/O API */ if( pBlob==0 ){ rc = sqlite3_blob_open(db, "main", "kv", "v", iKey, isUpdateTest, &pBlob); if( rc ){ fatalError("could not open sqlite3_blob handle: %s", sqlite3_errmsg(db)); } }else{ rc = sqlite3_blob_reopen(pBlob, iKey); } if( rc==SQLITE_OK ){ nData = sqlite3_blob_bytes(pBlob); if( nAlloc<nData+1 ){ nAlloc = nData+100; pData = sqlite3_realloc64(pData, nAlloc); } if( pData==0 ) fatalError("cannot allocate %d bytes", nData+1); if( isUpdateTest ){ sqlite3_randomness((int)nData, pData); rc = sqlite3_blob_write(pBlob, pData, (int)nData, 0); if( rc!=SQLITE_OK ){ fatalError("could not write the blob at %d: %s", iKey, sqlite3_errmsg(db)); } }else{ rc = sqlite3_blob_read(pBlob, pData, (int)nData, 0); if( rc!=SQLITE_OK ){ fatalError("could not read the blob at %d: %s", iKey, sqlite3_errmsg(db)); } } } }else{ /* CASE 3: Reading from database using SQL */ if( pStmt==0 ){ if( isUpdateTest ){ sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0, rememberFunc, 0, 0); rc = sqlite3_prepare_v2(db, "UPDATE kv SET v=randomblob(remember(length(v),?2))" " WHERE k=?1", -1, &pStmt, 0); sqlite3_bind_int64(pStmt, 2, SQLITE_PTR_TO_INT(&nData)); }else{ rc = sqlite3_prepare_v2(db, "SELECT v FROM kv WHERE k=?1", -1, &pStmt, 0); } if( rc ){ fatalError("cannot prepare query: %s", sqlite3_errmsg(db)); } }else{ sqlite3_reset(pStmt); } sqlite3_bind_int(pStmt, 1, iKey); nData = 0; rc = sqlite3_step(pStmt); if( rc==SQLITE_ROW ){ nData = sqlite3_column_bytes(pStmt, 0); pData = (unsigned char*)sqlite3_column_blob(pStmt, 0); } } if( eOrder==ORDER_ASC ){ iKey++; if( iKey>iMax ) iKey = 1; }else if( eOrder==ORDER_DESC ){ iKey--; if( iKey<=0 ) iKey = iMax; }else{ iKey = (randInt()%iMax)+1; } nTotal += nData; if( nData==0 ){ nCount++; nExtra++; } } if( nAlloc ) sqlite3_free(pData); if( pStmt ) sqlite3_finalize(pStmt); if( pBlob ) sqlite3_blob_close(pBlob); if( bStats ){ display_stats(db, 0); } if( db ){ if( !doMultiTrans ) sqlite3_exec(db, "COMMIT", 0, 0, 0); if( !noCheckpoint ){ sqlite3_close(db); db = 0; } } tmElapsed = timeOfDay() - tmStart; if( db && noCheckpoint ){ sqlite3_close(db); db = 0; } if( nExtra ){ printf("%d cycles due to %d misses\n", nCount, nExtra); } if( eType==PATH_DB ){ printf("SQLite version: %s\n", sqlite3_libversion()); if( doIntegrityCk ){ sqlite3_open(zDb, &db); sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &pStmt, 0); while( sqlite3_step(pStmt)==SQLITE_ROW ){ printf("integrity-check: %s\n", sqlite3_column_text(pStmt, 0)); } sqlite3_finalize(pStmt); sqlite3_close(db); db = 0; } } printf("--count %d --max-id %d", nCount-nExtra, iMax); switch( eOrder ){ case ORDER_RANDOM: printf(" --random\n"); break; case ORDER_DESC: printf(" --desc\n"); break; default: printf(" --asc\n"); break; } if( eType==PATH_DB ){ printf("--cache-size %d --jmode %s\n", iCache, zJMode); printf("--mmap %d%s\n", mmapSize, bBlobApi ? " --blob-api" : ""); if( noSync ) printf("--nosync\n"); } if( iPagesize ) printf("Database page size: %d\n", iPagesize); printf("Total elapsed time: %.3f\n", tmElapsed/1000.0); if( isUpdateTest ){ printf("Microseconds per BLOB write: %.3f\n", tmElapsed*1000.0/nCount); printf("Content write rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed)); }else{ printf("Microseconds per BLOB read: %.3f\n", tmElapsed*1000.0/nCount); printf("Content read rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed)); } return 0; } int main(int argc, char **argv){ if( argc<3 ) showHelp(); if( strcmp(argv[1],"init")==0 ){ return initMain(argc, argv); } if( strcmp(argv[1],"export")==0 ){ return exportMain(argc, argv); } if( strcmp(argv[1],"run")==0 ){ return runMain(argc, argv); } if( strcmp(argv[1],"stat")==0 ){ return statMain(argc, argv); } showHelp(); return 0; } |
Changes to test/like.test.
︙ | ︙ | |||
156 157 158 159 160 161 162 163 164 165 166 167 168 169 | # This procedure executes the SQL. Then it appends to the result the # "sort" or "nosort" keyword (as in the cksort procedure above) then # it appends the names of the table and index used. # proc queryplan {sql} { set ::sqlite_sort_count 0 set data [execsql $sql] if {$::sqlite_sort_count} {set x sort} {set x nosort} lappend data $x set eqp [execsql "EXPLAIN QUERY PLAN $sql"] # puts eqp=$eqp foreach {a b c x} $eqp { if {[regexp { TABLE (\w+ AS )?(\w+) USING COVERING INDEX (\w+)\y} \ | > | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | # This procedure executes the SQL. Then it appends to the result the # "sort" or "nosort" keyword (as in the cksort procedure above) then # it appends the names of the table and index used. # proc queryplan {sql} { set ::sqlite_sort_count 0 db cache flush set data [execsql $sql] if {$::sqlite_sort_count} {set x sort} {set x nosort} lappend data $x set eqp [execsql "EXPLAIN QUERY PLAN $sql"] # puts eqp=$eqp foreach {a b c x} $eqp { if {[regexp { TABLE (\w+ AS )?(\w+) USING COVERING INDEX (\w+)\y} \ |
︙ | ︙ | |||
192 193 194 195 196 197 198 | } {ABC {ABC abc xyz} abc abcd sort t1 *} do_test like-3.2 { set sqlite_like_count } {12} # With an index on t1.x and case sensitivity on, optimize completely. # | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {ABC {ABC abc xyz} abc abcd sort t1 *} do_test like-3.2 { set sqlite_like_count } {12} # With an index on t1.x and case sensitivity on, optimize completely. # do_test like-3.3.100 { set sqlite_like_count 0 execsql { PRAGMA case_sensitive_like=on; CREATE INDEX i1 ON t1(x); } queryplan { SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1; } } {abc abcd nosort {} i1} do_test like-3.3.100.cnt { set sqlite_like_count } 0 # The like optimization works even when the pattern is a bound parameter # # Exception: It does not work if sqlite3_prepare() is used instead of # sqlite3_prepare_v2(), as in that case the statement cannot be reprepared # after the parameter is bound. # unset -nocomplain ::likepat set ::likepat abc% if {[permutation]!="prepare"} { do_test like-3.3.102 { set sqlite_like_count 0 queryplan { SELECT x FROM t1 WHERE x LIKE $::likepat ORDER BY 1; } } {abc abcd nosort {} i1} do_test like-3.3.103 { set sqlite_like_count } 0 } # Except, the like optimization does not work for bound parameters if # the query planner stability guarantee is active. # do_test like-3.3.104 { set sqlite_like_count 0 sqlite3_db_config db QPSG 1 queryplan { SELECT x FROM t1 WHERE x LIKE $::likepat ORDER BY 1; } } {abc abcd nosort {} i1} do_test like-3.3.105 { set sqlite_like_count } 12 # The query planner stability guarantee does not disrupt explicit patterns # do_test like-3.3.105 { set sqlite_like_count 0 queryplan { SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1; } } {abc abcd nosort {} i1} do_test like-3.3.106 { set sqlite_like_count } 0 sqlite3_db_config db QPSG 0 # The LIKE optimization still works when the RHS is a string with no # wildcard. Ticket [e090183531fc2747] # do_test like-3.4.2 { queryplan { SELECT x FROM t1 WHERE x LIKE 'a' ORDER BY 1; |
︙ | ︙ | |||
975 976 977 978 979 980 981 982 | } {0} do_execsql_test like-13.3 { SELECT char(0x304d) LIKE char(0x6d); } {0} do_execsql_test like-13.4 { SELECT char(0x4d) LIKE char(0x6d); } {1} | > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {0} do_execsql_test like-13.3 { SELECT char(0x304d) LIKE char(0x6d); } {0} do_execsql_test like-13.4 { SELECT char(0x4d) LIKE char(0x6d); } {1} # Performance testing for patterns with many wildcards. These LIKE and GLOB # patterns were quite slow with SQLite 3.15.2 and earlier. # do_test like-14.1 { set x [lindex [time { db one {SELECT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz'GLOB'*a*a*a*a*a*a*a*a*y'} }] 0] puts -nonewline " ($x ms - want less than 1000) " expr {$x<1000} } {1} ifcapable !icu { do_test like-14.2 { set x [lindex [time { db one {SELECT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz'LIKE'%a%a%a%a%a%a%a%a%y'} }] 0] puts -nonewline " ($x ms - want less than 1000) " expr {$x<1000} } {1} } # As of 2017-07-27 (3.21.0) the LIKE optimization works with ESCAPE as # long as the ESCAPE is a single-byte literal. # db close sqlite3 db :memory: do_execsql_test like-15.100 { CREATE TABLE t15(x TEXT COLLATE nocase, y, PRIMARY KEY(x)); INSERT INTO t15(x,y) VALUES ('abcde',1), ('ab%de',2), ('a_cde',3), ('uvwxy',11),('uvwx%',12),('uvwx_',13), ('_bcde',21),('%bcde',22), ('abcd_',31),('abcd%',32), ('ab%xy',41); SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/'; } {2} do_execsql_test like-15.101 { EXPLAIN QUERY PLAN SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/'; } {/SEARCH/} do_execsql_test like-15.102 { EXPLAIN QUERY PLAN SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '//'; } {/SCAN/} do_execsql_test like-15.103 { EXPLAIN QUERY PLAN SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE ''; } {/SCAN/} do_execsql_test like-15.110 { SELECT y FROM t15 WHERE x LIKE 'abcdx%%' ESCAPE 'x'; } {32} do_execsql_test like-15.111 { SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y } {2 41} do_execsql_test like-15.112 { EXPLAIN QUERY PLAN SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y } {/SEARCH/} do_execsql_test like-15.120 { SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/'; } {22} do_execsql_test like-15.121 { EXPLAIN QUERY PLAN SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/'; } {/SEARCH/} finish_test |
Changes to test/lookaside.test.
︙ | ︙ | |||
29 30 31 32 33 34 35 | return } test_set_config_pagecache 0 0 catch {db close} sqlite3_shutdown | < | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | return } test_set_config_pagecache 0 0 catch {db close} sqlite3_shutdown sqlite3_initialize autoinstall_test_functions sqlite3 db test.db # Make sure sqlite3_db_config() and sqlite3_db_status are working. # do_test lookaside-1.1 { |
︙ | ︙ |
Changes to test/malloc5.test.
︙ | ︙ | |||
35 36 37 38 39 40 41 42 43 44 45 | # Skip these tests if OMIT_MEMORY_MANAGEMENT was defined at compile time. ifcapable !memorymanage { finish_test return } test_set_config_pagecache 0 100 sqlite3_soft_heap_limit 0 sqlite3 db test.db | > > > > > > > > > > > > > > > > | | 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 | # Skip these tests if OMIT_MEMORY_MANAGEMENT was defined at compile time. ifcapable !memorymanage { finish_test return } # The sizes of memory allocations from system malloc() might vary, # depending on the memory allocator algorithms used. The following # routine is designed to support answers that fall within a range # of values while also supplying easy-to-understand "expected" values # when errors occur. # proc value_in_range {target x args} { set v [lindex $args 0] if {$v!=""} { if {$v<$target*$x} {return $v} if {$v>$target/$x} {return $v} } return "number between [expr {int($target*$x)}] and [expr {int($target/$x)}]" } set mrange 0.98 ;# plus or minus 2% test_set_config_pagecache 0 100 sqlite3_soft_heap_limit 0 sqlite3 db test.db # db eval {PRAGMA cache_size=1} do_test malloc5-1.1 { # Simplest possible test. Call sqlite3_release_memory when there is exactly # one unused page in a single pager cache. The page cannot be freed, as # it is dirty. So sqlite3_release_memory() returns 0. # execsql { |
︙ | ︙ | |||
67 68 69 70 71 72 73 | execsql {PRAGMA cache_size=2; SELECT * FROM sqlite_master } db2 } {} do_test malloc5-1.3 { # Call [sqlite3_release_memory] when there is exactly one unused page # in the cache belonging to db2. # set ::pgalloc [sqlite3_release_memory] | < < < < < < < < | < < < < < < < < | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | execsql {PRAGMA cache_size=2; SELECT * FROM sqlite_master } db2 } {} do_test malloc5-1.3 { # Call [sqlite3_release_memory] when there is exactly one unused page # in the cache belonging to db2. # set ::pgalloc [sqlite3_release_memory] value_in_range 1288 0.75 } [value_in_range 1288 0.75] do_test malloc5-1.4 { # Commit the transaction and open a new one. Read 1 page into the cache. # Because the page is not dirty, it is eligible for collection even # before the transaction is concluded. # execsql { |
︙ | ︙ | |||
113 114 115 116 117 118 119 | do_test malloc5-1.6 { # Manipulate the cache so that it contains two unused pages. One requires # a journal-sync to free, the other does not. db2 close execsql { BEGIN; | < > < | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | do_test malloc5-1.6 { # Manipulate the cache so that it contains two unused pages. One requires # a journal-sync to free, the other does not. db2 close execsql { BEGIN; CREATE TABLE def(d, e, f); SELECT * FROM abc; } value_in_range $::pgalloc $::mrange [sqlite3_release_memory 500] } [value_in_range $::pgalloc $::mrange] do_test malloc5-1.7 { # Database should not be locked this time. sqlite3 db2 test.db catchsql { SELECT * FROM abc } db2 } {0 {}} do_test malloc5-1.8 { # Try to release another block of memory. This will fail as the only |
︙ | ︙ | |||
232 233 234 235 236 237 238 | puts -nonewline " (Highwater mark: $nMaxBytes) " expr $nMaxBytes > 1000000 } {1} do_test malloc5-4.2 { db eval {PRAGMA cache_size=1} db cache flush sqlite3_release_memory | | | | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | puts -nonewline " (Highwater mark: $nMaxBytes) " expr $nMaxBytes > 1000000 } {1} do_test malloc5-4.2 { db eval {PRAGMA cache_size=1} db cache flush sqlite3_release_memory sqlite3_soft_heap_limit 200000 sqlite3_memory_highwater 1 execsql {SELECT * FROM abc} set nMaxBytes [sqlite3_memory_highwater 1] puts -nonewline " (Highwater mark: $nMaxBytes) " expr $nMaxBytes <= 210000 } {1} do_test malloc5-4.3 { # Check that the content of table abc is at least roughly as expected. execsql { SELECT count(*), sum(a), sum(b) FROM abc; } } [list 10000 [expr int(10000.0 * 4999.5)] [expr int(10000.0 * 4999.5)]] |
︙ | ︙ | |||
342 343 344 345 346 347 348 | expr [nPage db] + [nPage db2] } {4} do_test malloc5-6.2.2 { # If we now try to reclaim some memory, it should come from the db2 cache. sqlite3_release_memory 3000 expr [nPage db] + [nPage db2] | | | > > | | | | | | 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 | expr [nPage db] + [nPage db2] } {4} do_test malloc5-6.2.2 { # If we now try to reclaim some memory, it should come from the db2 cache. sqlite3_release_memory 3000 expr [nPage db] + [nPage db2] } {1} do_test malloc5-6.2.3 { # Access the db2 cache again, so that all the db2 pages have been used # more recently than all the db pages. Then try to reclaim 3000 bytes. # This time, 3 pages should be pulled from the db cache. execsql { SELECT * FROM abc } db2 sqlite3_release_memory 3000 expr [nPage db] + [nPage db2] } {0} do_test malloc5-6.3.1 { # Now open a transaction and update 2 pages in the db2 cache. Then # do a SELECT on the db cache so that all the db pages are more recently # used than the db2 pages. When we try to free memory, SQLite should # free the non-dirty db2 pages, then the db pages, then finally use # sync() to free up the dirty db2 pages. The only page that cannot be # freed is page1 of db2. Because there is an open transaction, the # btree layer holds a reference to page 1 in the db2 cache. # # UPDATE: No longer. As release_memory() does not cause a sync() execsql { BEGIN; UPDATE abc SET c = randstr(100,100) WHERE rowid = 1 OR rowid = (SELECT max(rowid) FROM abc); } db2 execsql { SELECT * FROM abc } db expr [nPage db] + [nPage db2] } {4} do_test malloc5-6.3.2 { # Try to release 7700 bytes. This should release all the # non-dirty pages held by db2. sqlite3_release_memory [expr 7*1132] list [nPage db] [nPage db2] } {0 3} do_test malloc5-6.3.3 { # Try to release another 1000 bytes. This should come fromt the db # cache, since all three pages held by db2 are either in-use or diry. sqlite3_release_memory 1000 list [nPage db] [nPage db2] } {0 3} do_test malloc5-6.3.4 { # Now release 9900 more (about 9 pages worth). This should expunge # the rest of the db cache. But the db2 cache remains intact, because # SQLite tries to avoid calling sync(). if {$::tcl_platform(wordSize)==8} { sqlite3_release_memory 10500 } else { sqlite3_release_memory 9900 } list [nPage db] [nPage db2] } {0 3} do_test malloc5-6.3.5 { # But if we are really insistent, SQLite will consent to call sync() # if there is no other option. UPDATE: As of 3.6.2, SQLite will not # call sync() in this scenario. So no further memory can be reclaimed. sqlite3_release_memory 1000 list [nPage db] [nPage db2] } {0 3} do_test malloc5-6.3.6 { # The referenced page (page 1 of the db2 cache) will not be freed no # matter how much memory we ask for: sqlite3_release_memory 31459 list [nPage db] [nPage db2] } {0 3} db2 close sqlite3_soft_heap_limit $::soft_limit test_restore_config_pagecache finish_test catch {db close} |
Added test/mallocM.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 | # 2017 March 13 # # 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. # #*********************************************************************** # Further OOM tests. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl set testprefix mallocM sqlite3_db_config_lookaside db 0 0 0 do_execsql_test 1.0 { CREATE TABLE t1(x); } do_faultsim_test 1 -faults oom* -body { execsql { SELECT 'abc' FROM ( SELECT 'xyz' FROM t1 WHERE (SELECT 1) ) } } -test { faultsim_test_result {0 {}} } do_execsql_test 2.0.1 { SELECT instr(x'', x'') } {1} do_execsql_test 2.0.2 { SELECT instr(x'12345678', x'') } {1} do_execsql_test 2.0.3 { SELECT instr(x'', x'1234') } {0} do_faultsim_test 2.1 -faults oom* -body { execsql { SELECT instr (x'00', zeroblob(1)) } } -test { faultsim_test_result {0 1} } do_faultsim_test 2.2 -faults oom* -body { execsql { SELECT instr (zeroblob(1), x'00') } } -test { faultsim_test_result {0 1} } finish_test |
Changes to test/memsubsys1.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # This file contains tests of the memory allocation subsystem # set testdir [file dirname $argv0] source $testdir/tester.tcl sqlite3_reset_auto_extension | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # This file contains tests of the memory allocation subsystem # set testdir [file dirname $argv0] source $testdir/tester.tcl sqlite3_reset_auto_extension # This test assumes that no page-cache buffers are installed # by default when a new database connection is opened. As a result, it # will not work with the "memsubsys1" permutation. # if {[permutation] == "memsubsys1"} { finish_test return } |
︙ | ︙ | |||
152 153 154 155 156 157 158 | do_test memsubsys1-3.2.4 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] } 20 do_test memsubsys1-3.2.5 { set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2] } 0 | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | do_test memsubsys1-3.2.4 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] } 20 do_test memsubsys1-3.2.5 { set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2] } 0 # Test 4: Activate PAGECACHE # db close sqlite3_shutdown sqlite3_config_pagecache [expr 1024+$xtra_size] 50 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-4 {PRAGMA page_size=1024} #show_memstats do_test memsubsys1-4.3 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] expr {$pg_used>=45 && $pg_used<=50} } 1 do_test memsubsys1-4.4 { set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2] } 0 do_test memsubsys1-4.5 { set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2] expr {$maxreq<7000} } 1 db close sqlite3_shutdown sqlite3_config_memstatus 1 sqlite3_config_lookaside 100 500 sqlite3_config serialized sqlite3_initialize autoinstall_test_functions test_restore_config_pagecache finish_test |
Changes to test/misc1.test.
︙ | ︙ | |||
475 476 477 478 479 480 481 | # The following tests can only work if the current SQLite VFS has the concept # of a current directory. # ifcapable curdir { # Make sure a database connection still works after changing the # working directory. # | > | | | | | | | | | | | | | | | | | | | | > | 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 | # The following tests can only work if the current SQLite VFS has the concept # of a current directory. # ifcapable curdir { # Make sure a database connection still works after changing the # working directory. # if {[atomic_batch_write test.db]==0} { do_test misc1-14.1 { file mkdir tempdir cd tempdir execsql {BEGIN} file exists ./test.db-journal } {0} do_test misc1-14.2a { execsql {UPDATE t1 SET a=a||'x' WHERE 0} file exists ../test.db-journal } {0} do_test misc1-14.2b { execsql {UPDATE t1 SET a=a||'y' WHERE 1} file exists ../test.db-journal } {1} do_test misc1-14.3 { cd .. forcedelete tempdir execsql {COMMIT} file exists ./test.db-journal } {0} } } # A failed create table should not leave the table in the internal # data structures. Ticket #238. # do_test misc1-15.1.1 { catchsql { |
︙ | ︙ | |||
705 706 707 708 709 710 711 | # minutes to prepare. This has been speeded up to about 250 milliseconds. # do_catchsql_test misc1-25.0 { SELECT-1 UNION SELECT 5 UNION SELECT 0 UNION SElECT*from(SELECT-5) UNION SELECT*from(SELECT-0) UNION SELECT:SELECT-0 UNION SELECT-1 UNION SELECT 1 UNION SELECT 1 ORDER BY S in(WITH K AS(WITH K AS(select'CREINDERcharREADEVIRTUL5TABLECONFLICT !1 USIN'' MFtOR(b38q,eWITH K AS(selectCREATe TABLE t0(a,b,c,d,e, PRIMARY KEY(a,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,b,c,d,c,a,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d'CEIl,k'',ab, g, a,b,o11b, i'nEX/charREDE IVT LR!VABLt5SG',N ,N in rement,l_vacuum,M&U,'te3(''5l' a,bB,b,l*e)SELECT:SELECT, *,*,*from(( SELECT $group,:conc ap0,1)fro,(select"",:PBAG,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d, foreign_keysc,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,a,b,d,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,bb,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,a,b,d,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,MAato_aecSELEC,+?b," "O,"i","a",""b ,5 ))KEY)SELECT*FROM((k()reaC,k,K) eA,k '' )t ,K M); } {1 {'k' is not a function}} | > | > > > > > > > > > | 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 | # minutes to prepare. This has been speeded up to about 250 milliseconds. # do_catchsql_test misc1-25.0 { SELECT-1 UNION SELECT 5 UNION SELECT 0 UNION SElECT*from(SELECT-5) UNION SELECT*from(SELECT-0) UNION SELECT:SELECT-0 UNION SELECT-1 UNION SELECT 1 UNION SELECT 1 ORDER BY S in(WITH K AS(WITH K AS(select'CREINDERcharREADEVIRTUL5TABLECONFLICT !1 USIN'' MFtOR(b38q,eWITH K AS(selectCREATe TABLE t0(a,b,c,d,e, PRIMARY KEY(a,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,b,c,d,c,a,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d'CEIl,k'',ab, g, a,b,o11b, i'nEX/charREDE IVT LR!VABLt5SG',N ,N in rement,l_vacuum,M&U,'te3(''5l' a,bB,b,l*e)SELECT:SELECT, *,*,*from(( SELECT $group,:conc ap0,1)fro,(select"",:PBAG,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d, foreign_keysc,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,a,b,d,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,bb,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,a,b,d,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,MAato_aecSELEC,+?b," "O,"i","a",""b ,5 ))KEY)SELECT*FROM((k()reaC,k,K) eA,k '' )t ,K M); } {1 {'k' is not a function}} # 2017-09-17 # # Sometimes sqlite3ExprListAppend() can be invoked on an ExprList that # was obtained from sqlite3ExprListDup(). # do_execsql_test misc1-26.0 { DROP TABLE IF EXISTS abc; CREATE TABLE abc(a, b, c); SELECT randomblob(min(max(coalesce(EXISTS (SELECT 1 FROM ( SELECT (SELECT 2147483647) NOT IN (SELECT 2147483649 UNION ALL SELECT DISTINCT -1) IN (SELECT 2147483649), 'fault', (SELECT ALL -1 INTERSECT SELECT 'experiments') IN (SELECT ALL 56.1 ORDER BY 'experiments' DESC) FROM (SELECT DISTINCT 2147483648, 'hardware' UNION ALL SELECT -2147483648, 'experiments' ORDER BY 2147483648 LIMIT 1 OFFSET 123456789.1234567899) GROUP BY (SELECT ALL 0 INTERSECT SELECT 'in') IN (SELECT DISTINCT 'experiments' ORDER BY zeroblob(1000) LIMIT 56.1 OFFSET -456) HAVING EXISTS (SELECT 'fault' EXCEPT SELECT DISTINCT 56.1) UNION SELECT 'The', 'The', 2147483649 UNION ALL SELECT DISTINCT 'hardware', 'first', 'experiments' ORDER BY 'hardware' LIMIT 123456789.1234567899 OFFSET -2147483647)) NOT IN (SELECT (SELECT DISTINCT (SELECT 'The') FROM abc ORDER BY EXISTS (SELECT -1 INTERSECT SELECT ALL NULL) ASC) IN (SELECT DISTINCT EXISTS (SELECT ALL 123456789.1234567899 ORDER BY 1 ASC, NULL DESC) FROM sqlite_master INTERSECT SELECT 456)), (SELECT ALL 'injection' UNION ALL SELECT ALL (SELECT DISTINCT 'first' UNION SELECT DISTINCT 'The') FROM (SELECT 456, 'in', 2147483649))),1), 500)), 'first', EXISTS (SELECT DISTINCT 456 FROM abc ORDER BY 'experiments' DESC) FROM abc; } {} finish_test |
Changes to test/misc5.test.
︙ | ︙ | |||
518 519 520 521 522 523 524 | set fd [open test.db w] puts $fd "This is not really a database" close $fd sqlite3 db test.db catchsql { CREATE TABLE t1(a,b,c); } | | | 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | set fd [open test.db w] puts $fd "This is not really a database" close $fd sqlite3 db test.db catchsql { CREATE TABLE t1(a,b,c); } } {1 {file is not a database}} } # Ticket #1371. Allow floating point numbers of the form .N or N. # do_test misc5-5.1 { execsql {SELECT .1 } } 0.1 |
︙ | ︙ |
Changes to test/misuse.test.
︙ | ︙ | |||
175 176 177 178 179 180 181 | if {[clang_sanitize_address]==0} { do_test misuse-4.4 { # Flush the TCL statement cache here, otherwise the sqlite3_close() will # fail because there are still un-finalized() VDBEs. db cache flush sqlite3_close $::DB catchsql2 {SELECT * FROM t1} | | | | | 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 | if {[clang_sanitize_address]==0} { do_test misuse-4.4 { # Flush the TCL statement cache here, otherwise the sqlite3_close() will # fail because there are still un-finalized() VDBEs. db cache flush sqlite3_close $::DB catchsql2 {SELECT * FROM t1} } {1 {bad parameter or other API misuse}} do_test misuse-4.5 { catchsql { SELECT * FROM t1 } } {1 {bad parameter or other API misuse}} # Attempt to use a database after it has been closed. # do_test misuse-5.1 { db close sqlite3 db test2.db; set ::DB [sqlite3_connection_pointer db] execsql { SELECT * FROM t1 } } {1 2} do_test misuse-5.2 { catchsql2 {SELECT * FROM t1} } {0 {a b 1 2}} do_test misuse-5.3 { db close set r [catch { sqlite3_prepare $::DB {SELECT * FROM t1} -1 TAIL } msg] lappend r $msg } {1 {(21) bad parameter or other API misuse}} } finish_test |
Added test/mjournal.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 | # 2017 September 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. # #*********************************************************************** # This file implements regression tests for SQLite library. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix mjournal # Test that nothing bad happens if a journal file contains a pointer to # a master journal file that does not have a "-" in the name. At one point # this was causing a segfault on unix. # do_execsql_test 1.0 { CREATE TABLE t1(a, b); } do_test 1.1 { forcedelete test.db2journal test.db-journal close [open test.db-journal w] hexio_write test.db-journal 0 746573742e6462326a6f75726e616c00 hexio_write test.db-journal 16 00000010 hexio_write test.db-journal 20 000005e1 hexio_write test.db-journal 24 d9d505f920a163d7 close [open test.db2journal w] hexio_write test.db2journal 0 abcd } {2} do_execsql_test 1.2 { SELECT * FROM t1; } do_test 1.3 { forcedelete test0db2journal test.db-journal close [open test.db-journal w] hexio_write test.db-journal 0 74657374306462326a6f75726e616c00 hexio_write test.db-journal 16 00000010 hexio_write test.db-journal 20 000005e3 hexio_write test.db-journal 24 d9d505f920a163d7 close [open test0db2journal w] hexio_write test0db2journal 0 abcd } {2} do_execsql_test 1.4 { SELECT * FROM t1; } # And now test that nothing bad happens if a master journal contains a # pointer to a journal file that does not have a "-" in the name. # do_test 1.5 { forcedelete test.db2-master test.db-journal test1 close [open test.db-journal w] hexio_write test.db-journal 0 746573742e6462322d6d617374657200 hexio_write test.db-journal 16 00000010 hexio_write test.db-journal 20 0000059f hexio_write test.db-journal 24 d9d505f920a163d7 close [open test.db2-master w] hexio_write test.db2-master 0 746573743100 close [open test1 w] hexio_write test1 0 abcd } {2} do_execsql_test 1.6 { SELECT * FROM t1; } finish_test |
Changes to test/multiplex.test.
︙ | ︙ | |||
191 192 193 194 195 196 197 | do_test multiplex-2.3.1 { sqlite3 db2 test2.x db2 close } {} unset -nocomplain ::log | | | | | | | > | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | do_test multiplex-2.3.1 { sqlite3 db2 test2.x db2 close } {} unset -nocomplain ::log #do_test multiplex-2.4.1 { # sqlite3_multiplex_shutdown #} {SQLITE_MISUSE} do_test multiplex-2.4.2 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} #do_test multiplex-2.4.3 { # set ::log #} {SQLITE_MISUSE {sqlite3_multiplex_shutdown() called while database connections are still open}} do_test multiplex-2.4.4 { file size [multiplex_name test.x 0] } {7168} do_test multiplex-2.4.5 { db close sqlite3 db test.x db eval vacuum db close glob test.x* |
︙ | ︙ | |||
441 442 443 444 445 446 447 | # sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 # Return a list of all currently defined multiplexs. proc multiplex_list {} { | | < < < < | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | # sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 # Return a list of all currently defined multiplexs. proc multiplex_list {} { glob -nocomplain test2.db* } do_test multiplex-4.1.6 { multiplex_delete test2.db sqlite3 db test2.db db eval {CREATE TABLE t2(x); INSERT INTO t2 VALUES('tab-t2');} set res [multiplex_list] |
︙ | ︙ | |||
490 491 492 493 494 495 496 | db2 close set res [multiplex_list] list [regexp {test2.db} $res] } {1} do_test multiplex-4.1.12 { db close multiplex_list | | | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | db2 close set res [multiplex_list] list [regexp {test2.db} $res] } {1} do_test multiplex-4.1.12 { db close multiplex_list } {test2.db} #------------------------------------------------------------------------- # The following tests test that the multiplex VFS handles malloc and IO # errors. # |
︙ | ︙ |
Changes to test/nan.test.
︙ | ︙ | |||
362 363 364 365 366 367 368 | db eval { DELETE FROM t1; INSERT INTO t1 VALUES('2.5e-2147483650'); SELECT x, typeof(x) FROM t1; } } {0.0 real} | | | > | > | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | db eval { DELETE FROM t1; INSERT INTO t1 VALUES('2.5e-2147483650'); SELECT x, typeof(x) FROM t1; } } {0.0 real} do_realnum_test nan-4.40 { db eval { SELECT cast('-1e999' AS real); } } {-inf} finish_test |
Added test/nockpt.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 | # 2016 October 31 # # 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 SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE # option. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl ifcapable !wal {finish_test ; return } if {[permutation]=="journaltest" || [permutation]=="inmemory_journal"} { finish_test return } set testprefix nockpt do_execsql_test 1.0 { PRAGMA auto_vacuum=OFF; PRAGMA page_size = 1024; PRAGMA journal_mode = wal; CREATE TABLE c1(x, y, z); INSERT INTO c1 VALUES(1, 2, 3); } {wal} do_test 1.1 { file exists test.db-wal } 1 do_test 1.2 { file size test.db-wal } [wal_file_size 3 1024] do_test 1.3 { db close } {} do_test 1.4 { file exists test.db-wal } 0 sqlite3 db test.db do_execsql_test 1.5 { INSERT INTO c1 VALUES(4, 5, 6); INSERT INTO c1 VALUES(7, 8, 9); } do_test 1.6 { file exists test.db-wal } 1 do_test 1.7 { sqlite3_db_config db NO_CKPT_ON_CLOSE 1 } {1} do_test 1.8 { file size test.db-wal } [wal_file_size 2 1024] do_test 1.9 { db close } {} do_test 1.10 { file exists test.db-wal } 1 do_test 1.11 { file size test.db-wal } [wal_file_size 2 1024] sqlite3 db test.db do_execsql_test 1.12 { SELECT * FROM c1 } {1 2 3 4 5 6 7 8 9} do_execsql_test 1.13 { PRAGMA main.journal_mode } {wal} do_test 1.14 { sqlite3_db_config db NO_CKPT_ON_CLOSE 1 } {1} do_execsql_test 1.14 { PRAGMA main.journal_mode = delete } {delete} do_test 1.15 { file exists test.db-wal } {0} finish_test |
Added test/ossfuzz.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 | /* ** This module interfaces SQLite to the Google OSS-Fuzz, fuzzer as a service. ** (https://github.com/google/oss-fuzz) */ #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include "sqlite3.h" /* Global debugging settings. OSS-Fuzz will have all debugging turned ** off. But if LLVMFuzzerTestOneInput() is called interactively from ** the ossshell utility program, then these flags might be set. */ static unsigned mDebug = 0; #define FUZZ_SQL_TRACE 0x0001 /* Set an sqlite3_trace() callback */ #define FUZZ_SHOW_MAX_DELAY 0x0002 /* Show maximum progress callback delay */ #define FUZZ_SHOW_ERRORS 0x0004 /* Print error messages from SQLite */ /* The ossshell utility program invokes this interface to see the ** debugging flags. Unused by OSS-Fuzz. */ void ossfuzz_set_debug_flags(unsigned x){ mDebug = x; } /* Return the current real-world time in milliseconds since the ** Julian epoch (-4714-11-24). */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; sqlite3_int64 t; if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ clockVfs->xCurrentTimeInt64(clockVfs, &t); }else{ double r; clockVfs->xCurrentTime(clockVfs, &r); t = (sqlite3_int64)(r*86400000.0); } return t; } /* An instance of the following object is passed by pointer as the ** client data to various callbacks. */ typedef struct FuzzCtx { sqlite3 *db; /* The database connection */ sqlite3_int64 iCutoffTime; /* Stop processing at this time. */ sqlite3_int64 iLastCb; /* Time recorded for previous progress callback */ sqlite3_int64 mxInterval; /* Longest interval between two progress calls */ unsigned nCb; /* Number of progress callbacks */ } FuzzCtx; #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* ** Progress handler callback. ** ** The argument is the cutoff-time after which all processing should ** stop. So return non-zero if the cut-off time is exceeded. */ static int progress_handler(void *pClientData) { FuzzCtx *p = (FuzzCtx*)pClientData; sqlite3_int64 iNow = timeOfDay(); int rc = iNow>=p->iCutoffTime; sqlite3_int64 iDiff = iNow - p->iLastCb; if( iDiff > p->mxInterval ) p->mxInterval = iDiff; p->nCb++; return rc; } #endif /* ** Disallow debugging pragmas such as "PRAGMA vdbe_debug" and ** "PRAGMA parser_trace" since they can dramatically increase the ** amount of output without actually testing anything useful. */ static int block_debug_pragmas( void *Notused, int eCode, const char *zArg1, const char *zArg2, const char *zArg3, const char *zArg4 ){ if( eCode==SQLITE_PRAGMA && (sqlite3_strnicmp("vdbe_", zArg1, 5)==0 || sqlite3_stricmp("parser_trace", zArg1)==0) ){ return SQLITE_DENY; } return SQLITE_OK; } /* ** Callback for sqlite3_exec(). */ static int exec_handler(void *pCnt, int argc, char **argv, char **namev){ int i; if( argv ){ for(i=0; i<argc; i++) sqlite3_free(sqlite3_mprintf("%s", argv[i])); } return ((*(int*)pCnt)--)<=0; } /* ** Main entry point. The fuzzer invokes this function with each ** fuzzed input. */ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { int execCnt = 0; /* Abort row callback when count reaches zero */ char *zErrMsg = 0; /* Error message returned by sqlite_exec() */ uint8_t uSelector; /* First byte of input data[] */ int rc; /* Return code from various interfaces */ char *zSql; /* Zero-terminated copy of data[] */ FuzzCtx cx; /* Fuzzing context */ memset(&cx, 0, sizeof(cx)); if( size<3 ) return 0; /* Early out if unsufficient data */ /* Extract the selector byte from the beginning of the input. But only ** do this if the second byte is a \n. If the second byte is not \n, ** then use a default selector */ if( data[1]=='\n' ){ uSelector = data[0]; data += 2; size -= 2; }else{ uSelector = 0xfd; } /* Open the database connection. Only use an in-memory database. */ rc = sqlite3_open_v2("fuzz.db", &cx.db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY, 0); if( rc ) return 0; #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* Invoke the progress handler frequently to check to see if we ** are taking too long. The progress handler will return true ** (which will block further processing) if more than 10 seconds have ** elapsed since the start of the test. */ cx.iLastCb = timeOfDay(); cx.iCutoffTime = cx.iLastCb + 10000; /* Now + 10 seconds */ sqlite3_progress_handler(cx.db, 10, progress_handler, (void*)&cx); #endif /* Set a limit on the maximum size of a prepared statement */ sqlite3_limit(cx.db, SQLITE_LIMIT_VDBE_OP, 25000); /* Bit 1 of the selector enables foreign key constraints */ sqlite3_db_config(cx.db, SQLITE_DBCONFIG_ENABLE_FKEY, uSelector&1, &rc); uSelector >>= 1; /* Do not allow debugging pragma statements that might cause excess output */ sqlite3_set_authorizer(cx.db, block_debug_pragmas, 0); /* Remaining bits of the selector determine a limit on the number of ** output rows */ execCnt = uSelector + 1; /* Run the SQL. The sqlite_exec() interface expects a zero-terminated ** string, so make a copy. */ zSql = sqlite3_mprintf("%.*s", (int)size, data); sqlite3_exec(cx.db, zSql, exec_handler, (void*)&execCnt, &zErrMsg); /* Show any errors */ if( (mDebug & FUZZ_SHOW_ERRORS)!=0 && zErrMsg ){ printf("Error: %s\n", zErrMsg); } /* Cleanup and return */ sqlite3_free(zErrMsg); sqlite3_free(zSql); sqlite3_exec(cx.db, "PRAGMA temp_store_directory=''", 0, 0, 0); sqlite3_close(cx.db); if( mDebug & FUZZ_SHOW_MAX_DELAY ){ printf("Progress callback count....... %d\n", cx.nCb); printf("Max time between callbacks.... %d ms\n", (int)cx.mxInterval); } return 0; } |
Added test/ossshell.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 | /* ** This is a test interface for the ossfuzz.c module. The ossfuzz.c module ** is an adaptor for OSS-FUZZ. (https://github.com/google/oss-fuzz) ** ** This program links against ossfuzz.c. It reads files named on the ** command line and passes them one by one into ossfuzz.c. */ #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sqlite3.h" /* ** The entry point in ossfuzz.c that this routine will be calling */ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); /* Must match equivalent #defines in ossfuzz.c */ #define FUZZ_SQL_TRACE 0x0001 /* Set an sqlite3_trace() callback */ #define FUZZ_SHOW_MAX_DELAY 0x0002 /* Show maximum progress callback delay */ #define FUZZ_SHOW_ERRORS 0x0004 /* Show SQL errors */ extern void ossfuzz_set_debug_flags(unsigned); /* ** Read files named on the command-line and invoke the fuzzer for ** each one. */ int main(int argc, char **argv){ FILE *in; int i; int nErr = 0; uint8_t *zBuf = 0; size_t sz; unsigned mDebug = 0; for(i=1; i<argc; i++){ const char *zFilename = argv[i]; if( zFilename[0]=='-' ){ if( zFilename[1]=='-' ) zFilename++; if( strcmp(zFilename, "-show-errors")==0 ){ mDebug |= FUZZ_SHOW_ERRORS; ossfuzz_set_debug_flags(mDebug); }else if( strcmp(zFilename, "-show-max-delay")==0 ){ mDebug |= FUZZ_SHOW_MAX_DELAY; ossfuzz_set_debug_flags(mDebug); }else if( strcmp(zFilename, "-sql-trace")==0 ){ mDebug |= FUZZ_SQL_TRACE; ossfuzz_set_debug_flags(mDebug); }else { printf("unknown option \"%s\"\n", argv[i]); printf("should be one of: --show-errors --show-max-delay" " --sql-trace\n"); exit(1); } continue; } in = fopen(zFilename, "rb"); if( in==0 ){ fprintf(stderr, "cannot open \"%s\"\n", zFilename); nErr++; continue; } fseek(in, 0, SEEK_END); sz = ftell(in); rewind(in); zBuf = realloc(zBuf, sz); if( zBuf==0 ){ fprintf(stderr, "cannot malloc() for %d bytes\n", (int)sz); exit(1); } if( fread(zBuf, sz, 1, in)!=1 ){ fprintf(stderr, "cannot read %d bytes from \"%s\"\n", (int)sz, zFilename); nErr++; }else{ printf("%s... ", zFilename); if( mDebug ) printf("\n"); fflush(stdout); (void)LLVMFuzzerTestOneInput(zBuf, sz); if( mDebug ) printf("%s: ", zFilename); printf("ok\n"); } fclose(in); } free(zBuf); return nErr; } |
Changes to test/pager1.test.
︙ | ︙ | |||
2714 2715 2716 2717 2718 2719 2720 | catch { db close } forcedelete test.db set fd [open test.db w] puts $fd "hello world" close $fd sqlite3 db test.db catchsql { CREATE TABLE t1(x) } | | | 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 | catch { db close } forcedelete test.db set fd [open test.db w] puts $fd "hello world" close $fd sqlite3 db test.db catchsql { CREATE TABLE t1(x) } } {1 {file is not a database}} do_test 38.2 { catch { db close } forcedelete test.db } {} do_test 39.1 { sqlite3 db test.db |
︙ | ︙ |
Changes to test/pagerfault.test.
︙ | ︙ | |||
672 673 674 675 676 677 678 | } {} do_faultsim_test pagerfault-14a -prep { faultsim_restore_and_reopen } -body { if {[catch {db backup test.db2} msg]} { error [regsub {.*: } $msg {}] } } -test { | | | 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 | } {} do_faultsim_test pagerfault-14a -prep { faultsim_restore_and_reopen } -body { if {[catch {db backup test.db2} msg]} { error [regsub {.*: } $msg {}] } } -test { faultsim_test_result {0 {}} {1 {}} {1 {SQL logic error}} } # If TEMP_STORE is 2 or greater, then the database [db2] will be created # as an in-memory database. This test will not work in that case, as it # is not possible to change the page-size of an in-memory database. Even # using the backup API. # |
︙ | ︙ |
Changes to test/permutations.test.
︙ | ︙ | |||
85 86 87 88 89 90 91 | # $allquicktests # set alltests [list] foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] } foreach f [glob -nocomplain \ $testdir/../ext/rtree/*.test \ $testdir/../ext/fts5/test/*.test \ | > | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | # $allquicktests # set alltests [list] foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] } foreach f [glob -nocomplain \ $testdir/../ext/rtree/*.test \ $testdir/../ext/fts5/test/*.test \ $testdir/../ext/lsm1/test/*.test \ ] { lappend alltests $f } foreach f [glob -nocomplain $testdir/../ext/session/*.test] { lappend alltests $f } if {$::tcl_platform(platform)!="unix"} { |
︙ | ︙ | |||
243 244 245 246 247 248 249 | notify2.test thread001.test thread002.test thread003.test thread004.test thread005.test walthread.test } test_suite "fts3" -prefix "" -description { All FTS3 tests except fts3rnd.test. } -files { | | | > | | > | > | | < | > | | | | > | | | > > > > | 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 | notify2.test thread001.test thread002.test thread003.test thread004.test thread005.test walthread.test } test_suite "fts3" -prefix "" -description { All FTS3 tests except fts3rnd.test. } -files { fts3aa.test fts3ab.test fts3ac.test fts3ad.test fts3ae.test fts3af.test fts3ag.test fts3ah.test fts3ai.test fts3aj.test fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3atoken.test fts3auto.test fts3aux1.test fts3aux2.test fts3b.test fts3comp1.test fts3conf.test fts3corrupt2.test fts3corrupt.test fts3cov.test fts3c.test fts3defer2.test fts3defer3.test fts3defer.test fts3drop.test fts3d.test fts3e.test fts3expr2.test fts3expr3.test fts3expr4.test fts3expr5.test fts3expr.test fts3fault2.test fts3fault.test fts3first.test fts3join.test fts3malloc.test fts3matchinfo.test fts3near.test fts3offsets.test fts3prefix2.test fts3prefix.test fts3query.test fts3shared.test fts3snippet.test fts3sort.test fts3tok1.test fts3tok_err.test fts3varint.test fts4aa.test fts4check.test fts4content.test fts4docid.test fts4growth2.test fts4growth.test fts4incr.test fts4langid.test fts4lastrowid.test fts4merge2.test fts4merge4.test fts4merge.test fts4noti.test fts4onepass.test fts4opt.test fts4unicode.test fts3corrupt3.test fts3misc.test } test_suite "fts5" -prefix "" -description { All FTS5 tests. } -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test] test_suite "fts5-light" -prefix "" -description { All FTS5 tests. } -files [ test_set \ [glob -nocomplain $::testdir/../ext/fts5/test/*.test] \ -exclude *corrupt* *fault* *big* *fts5aj* ] test_suite "lsm1" -prefix "" -description { All LSM1 tests. } -files [glob -nocomplain $::testdir/../ext/lsm1/test/*.test] test_suite "nofaultsim" -prefix "" -description { "Very" quick test suite. Runs in less than 5 minutes on a workstation. This test suite is the same as the "quick" tests, except that some files that test malloc and IO errors are omitted. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* *_err* ] -initialize { |
︙ | ︙ | |||
375 376 377 378 379 380 381 382 383 384 385 386 387 388 | which do not work with a VFS that uses the pVfs argument passed to sqlite3_vfs methods. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* oserror.test \ pager1.test syscall.test sysfault.test tkt3457.test quota* superlock* \ wal* mmap* ] lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the coverage related test suites: # # coverage-wal # | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | which do not work with a VFS that uses the pVfs argument passed to sqlite3_vfs methods. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* oserror.test \ pager1.test syscall.test sysfault.test tkt3457.test quota* superlock* \ wal* mmap* ] test_suite "atomic-batch-write" -prefix "" -description { Like veryquick.test, but must be run on a file-system that supports atomic-batch-writes. Tests that depend on the journal file being present are omitted. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* *bigfile* *_err* \ *fts5corrupt* *fts5big* *fts5aj* \ crash8.test delete_db.test \ exclusive.test journal3.test \ journal1.test \ jrnlmode.test jrnlmode2.test \ lock4.test pager1.test \ pager3.test sharedA.test \ symlink.test stmt.test \ sync.test sync2.test \ tempdb.test tkt3457.test \ vacuum5.test wal2.test \ walmode.test zerodamage.test ] -initialize { if {[atomic_batch_write test.db]==0} { error "File system does NOT support atomic-batch-write" } } lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the coverage related test suites: # # coverage-wal # |
︙ | ︙ | |||
418 419 420 421 422 423 424 | lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the permutation test suites: # | | | < < | | < < | 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 | lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the permutation test suites: # # Run some tests using pre-allocated page blocks. # # mmap1.test is excluded because a good number of its tests depend on # the page-cache being larger than the database. But this permutation # causes the effective limit on the page-cache to be just 24 pages. # test_suite "memsubsys1" -description { Tests using pre-allocated page blocks } -files [ test_set $::allquicktests -exclude ioerr5.test malloc5.test mmap1.test ] -initialize { test_set_config_pagecache 4096 24 catch {db close} sqlite3_shutdown sqlite3_initialize autoinstall_test_functions } -shutdown { test_restore_config_pagecache catch {db close} sqlite3_shutdown sqlite3_initialize autoinstall_test_functions } # Run some tests using pre-allocated page blocks. This time # the allocations are too small to use in most cases. # # Both ioerr5.test and malloc5.test are excluded because they test the # sqlite3_soft_heap_limit() and sqlite3_release_memory() functionality. # This functionality is disabled if a pre-allocated page block is provided. # test_suite "memsubsys2" -description { Tests using small pre-allocated page blocks } -files [ test_set $::allquicktests -exclude ioerr5.test malloc5.test ] -initialize { test_set_config_pagecache 512 5 catch {db close} sqlite3_shutdown sqlite3_initialize autoinstall_test_functions } -shutdown { test_restore_config_pagecache catch {db close} sqlite3_shutdown sqlite3_initialize autoinstall_test_functions } # Run all tests with the lookaside allocator disabled. # test_suite "nolookaside" -description { |
︙ | ︙ | |||
750 751 752 753 754 755 756 757 758 759 760 761 762 763 | # This test does not work as the "PRAGMA journal_mode = memory" # statement switches the database out of wal mode at inopportune # times. snapshot_fault.test # This test assumes a journal file is created on disk. delete_db.test }] ifcapable mem3 { test_suite "memsys3" -description { Run tests using the allocator in mem3.c. } -files [test_set $::allquicktests -exclude { autovacuum.test delete3.test manydb.test | > > > > | 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 | # This test does not work as the "PRAGMA journal_mode = memory" # statement switches the database out of wal mode at inopportune # times. snapshot_fault.test # This test assumes a journal file is created on disk. delete_db.test # This test depends on a successful recovery from the pager error # state. Which is not possible with an in-memory journal fts5fault1.test }] ifcapable mem3 { test_suite "memsys3" -description { Run tests using the allocator in mem3.c. } -files [test_set $::allquicktests -exclude { autovacuum.test delete3.test manydb.test |
︙ | ︙ | |||
1031 1032 1033 1034 1035 1036 1037 | test_suite "prepare" -description { Run tests with the db connection using sqlite3_prepare() instead of _v2(). } -dbconfig { db_use_legacy_prepare $::dbhandle 1 #$::dbhandle cache size 0 } -files [ | | > | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 | test_suite "prepare" -description { Run tests with the db connection using sqlite3_prepare() instead of _v2(). } -dbconfig { db_use_legacy_prepare $::dbhandle 1 #$::dbhandle cache size 0 } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* \ stmtvtab1.test index9.test ] # End of tests ############################################################################# # run_tests NAME OPTIONS # |
︙ | ︙ |
Changes to test/pragma.test.
︙ | ︙ | |||
1692 1693 1694 1695 1696 1697 1698 | # The SQLITE_FCNTL_PRAGMA logic, with error handling. # db close testvfs tvfs sqlite3 db test.db -vfs tvfs do_test pragma-19.1 { catchsql {PRAGMA error} | | | 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 | # The SQLITE_FCNTL_PRAGMA logic, with error handling. # db close testvfs tvfs sqlite3 db test.db -vfs tvfs do_test pragma-19.1 { catchsql {PRAGMA error} } {1 {SQL logic error}} do_test pragma-19.2 { catchsql {PRAGMA error='This is the error message'} } {1 {This is the error message}} do_test pragma-19.3 { catchsql {PRAGMA error='7 This is the error message'} } {1 {This is the error message}} do_test pragma-19.4 { |
︙ | ︙ | |||
1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 | DROP TABLE t2; CREATE TABLE t2(x, y INTEGER REFERENCES t1); } db2 eval { PRAGMA foreign_key_list(t2); } } {0 0 t1 y {} {NO ACTION} {NO ACTION} NONE} database_never_corrupt finish_test | > > > > > > > > > > > > > > > > > > > > > > | 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 | DROP TABLE t2; CREATE TABLE t2(x, y INTEGER REFERENCES t1); } db2 eval { PRAGMA foreign_key_list(t2); } } {0 0 t1 y {} {NO ACTION} {NO ACTION} NONE} db2 close reset_db do_execsql_test 24.0 { PRAGMA page_size = 1024; CREATE TABLE t1(a, b, c); CREATE INDEX i1 ON t1(b); INSERT INTO t1 VALUES('a', 'b', 'c'); PRAGMA integrity_check; } {ok} set r [db one {SELECT rootpage FROM sqlite_master WHERE name = 't1'}] db close hexio_write test.db [expr $r*1024 - 16] 000000000000000701040f0f1f616263 sqlite3 db test.db do_catchsql_test 24.1 { SELECT * FROM t1; } {1 {database disk image is malformed}} do_catchsql_test 24.2 { PRAGMA integrity_check; } {0 {{database disk image is malformed}}} database_never_corrupt finish_test |
Added test/pragma4.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 | # 2017 Jan 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. # #*********************************************************************** # This file implements regression tests for SQLite library. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix pragma4 proc do_pragma_ncol_test {tn sql nCol} { set ::stmt 0 set ::stmt [sqlite3_prepare_v2 db $sql -1 dummy] uplevel [list do_test $tn { sqlite3_column_count $::stmt } $nCol] sqlite3_finalize $::stmt } # If there is no RHS argument, the following PRAGMA statements operate as # queries, returning a single row containing a single column. # # Or, if there is RHS argument, they return zero rows of zero columns. # foreach {tn sql} { 1 "PRAGMA application_id = 10" 2 "PRAGMA automatic_index = 1" 3 "PRAGMA auto_vacuum = 1" 4 "PRAGMA cache_size = -100" 5 "PRAGMA cache_spill = 1" 6 "PRAGMA cell_size_check = 1" 7 "PRAGMA checkpoint_fullfsync = 1" 8 "PRAGMA count_changes = 1" 9 "PRAGMA default_cache_size = 100" 10 "PRAGMA defer_foreign_keys = 1" 11 "PRAGMA empty_result_callbacks = 1" 12 "PRAGMA encoding = 'utf-8'" 13 "PRAGMA foreign_keys = 1" 14 "PRAGMA full_column_names = 1" 15 "PRAGMA fullfsync = 1" 16 "PRAGMA ignore_check_constraints = 1" 17 "PRAGMA legacy_file_format = 1" 18 "PRAGMA page_size = 511" 19 "PRAGMA page_size = 512" 20 "PRAGMA query_only = false" 21 "PRAGMA read_uncommitted = true" 22 "PRAGMA recursive_triggers = false" 23 "PRAGMA reverse_unordered_selects = false" 24 "PRAGMA schema_version = 211" 25 "PRAGMA short_column_names = 1" 26 "PRAGMA synchronous = full" 29 "PRAGMA temp_store = memory" 30 "PRAGMA user_version = 405" 31 "PRAGMA writable_schema = 1" } { reset_db # Without RHS: do_pragma_ncol_test 1.$tn.1 [lindex [split $sql =] 0] 1 # With RHS: do_pragma_ncol_test 1.$tn.2 $sql 0 } # These pragmas should never return any values. # foreach {tn sql} { 1 "PRAGMA shrink_memory" 2 "PRAGMA shrink_memory = 10" 3 "PRAGMA case_sensitive_like = 0" 4 "PRAGMA case_sensitive_like = 1" 5 "PRAGMA case_sensitive_like" } { do_pragma_ncol_test 1.$tn.1 $sql 0 } # EXPLAIN on a PRAGMA integrity_check. # Verify that that P4_INTARRAY argument to OP_IntegrityCk is rendered # correctly. # db close forcedelete test.db sqlite3 db test.db do_test pragma4-2.100 { db eval { PRAGMA page_size=512; CREATE TABLE t1(x); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<10000) INSERT INTO t1(x) SELECT zeroblob(300) FROM c; CREATE TABLE t2(y); DROP TABLE t1; } string map {\[ x \] x \173 {} \175 {}} \ [db eval {EXPLAIN PRAGMA integrity_check}] } {/ IntegrityCk 2 2 1 x[0-9]+,1x /} finish_test |
Added test/pragma5.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 | # 2017 August 25 # # 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. # # This file implements tests for the PRAGMA command. Specifically, # those pragmas enabled at build time by setting: # # -DSQLITE_INTROSPECTION_PRAGMAS # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix pragma5 if { [catch {db one "SELECT count(*) FROM pragma_function_list"}] } { finish_test return } db function external external do_execsql_test 1.0 { PRAGMA table_info(pragma_function_list) } { 0 name {} 0 {} 0 1 builtin {} 0 {} 0 } do_execsql_test 1.1 { SELECT * FROM pragma_function_list WHERE name='upper' } {upper 1} do_execsql_test 1.2 { SELECT * FROM pragma_function_list WHERE name LIKE 'exter%'; } {external 0} ifcapable fts5 { do_execsql_test 2.0 { PRAGMA table_info(pragma_module_list) } { 0 name {} 0 {} 0 } do_execsql_test 2.1 { SELECT * FROM pragma_module_list WHERE name='fts5' } {fts5} } do_execsql_test 3.0 { PRAGMA table_info(pragma_pragma_list) } { 0 name {} 0 {} 0 } do_execsql_test 3.1 { SELECT * FROM pragma_pragma_list WHERE name='pragma_list' } {pragma_list} finish_test |
Added test/pragmafault.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 | # 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl set testprefix pragmafault db close sqlite3 db test.db sqlite3_db_config_lookaside db 0 0 0 do_execsql_test 1.0 { CREATE TABLE t1(a, b, CHECK(a!=b)); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } faultsim_save_and_close do_faultsim_test 1 -prep { faultsim_restore_and_reopen } -body { catchsql { PRAGMA integrity_check } set {} 0 } -test { faultsim_test_result {0 0} } finish_test |
Changes to test/printf2.test.
︙ | ︙ | |||
112 113 114 115 116 117 118 119 120 121 122 | do_execsql_test printf2-3.4 { SELECT printf('|%8.8c|%-8.8c|','*','*'); } {|********|********|} do_execsql_test printf2-3.5 { SELECT printf('|%7.8c|%-7.8c|','*','*'); } {|********|********|} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_execsql_test printf2-3.4 { SELECT printf('|%8.8c|%-8.8c|','*','*'); } {|********|********|} do_execsql_test printf2-3.5 { SELECT printf('|%7.8c|%-7.8c|','*','*'); } {|********|********|} # The "," separator do_execsql_test printf2-4.1 { SELECT printf('|%,d|%,d|',0,-1); } {|0|-1|} do_execsql_test printf2-4.2 { SELECT printf('|%,d|%,d|',12,-12); } {|12|-12|} do_execsql_test printf2-4.3 { SELECT printf('|%,d|%,d|',123,-123); } {|123|-123|} do_execsql_test printf2-4.4 { SELECT printf('|%,d|%,d|',1234,-1234); } {|1,234|-1,234|} do_execsql_test printf2-4.5 { SELECT printf('|%,d|%,d|',12345,-12345); } {|12,345|-12,345|} do_execsql_test printf2-4.6 { SELECT printf('|%,d|%,d|',123456,-123456); } {|123,456|-123,456|} do_execsql_test printf2-4.7 { SELECT printf('|%,d|%,d|',1234567,-1234567); } {|1,234,567|-1,234,567|} do_execsql_test printf2-4.8 { SELECT printf('|%,d|%,d|',12345678,-12345678); } {|12,345,678|-12,345,678|} do_execsql_test printf2-4.9 { SELECT printf('|%,d|%,d|',123456789,-123456789); } {|123,456,789|-123,456,789|} do_execsql_test printf2-4.10 { SELECT printf('|%,d|%,d|',1234567890,-1234567890); } {|1,234,567,890|-1,234,567,890|} finish_test |
Added test/pushdown.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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 | # 2017 April 29 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix pushdown do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES(1, 'b1', 'c1'); INSERT INTO t1 VALUES(2, 'b2', 'c2'); INSERT INTO t1 VALUES(3, 'b3', 'c3'); INSERT INTO t1 VALUES(4, 'b4', 'c4'); CREATE INDEX i1 ON t1(a, c); } proc f {val} { lappend ::L $val return 0 } db func f f do_test 1.1 { set L [list] execsql { SELECT * FROM t1 WHERE a=2 AND f(b) AND f(c) } set L } {c2} do_test 1.2 { set L [list] execsql { SELECT * FROM t1 WHERE a=3 AND f(c) AND f(b) } set L } {c3} do_execsql_test 1.3 { DROP INDEX i1; CREATE INDEX i1 ON t1(a, b); } do_test 1.4 { set L [list] execsql { SELECT * FROM t1 WHERE a=2 AND f(b) AND f(c) } set L } {b2} do_test 1.5 { set L [list] execsql { SELECT * FROM t1 WHERE a=3 AND f(c) AND f(b) } set L } {b3} #----------------------------------------------- do_execsql_test 2.0 { CREATE TABLE u1(a, b, c); CREATE TABLE u2(x, y, z); INSERT INTO u1 VALUES('a1', 'b1', 'c1'); INSERT INTO u2 VALUES('a1', 'b1', 'c1'); } do_test 2.1 { set L [list] execsql { SELECT * FROM u1 WHERE f('one')=123 AND 123=( SELECT x FROM u2 WHERE x=a AND f('two') ) } set L } {one} do_test 2.2 { set L [list] execsql { SELECT * FROM u1 WHERE 123=( SELECT x FROM u2 WHERE x=a AND f('two') ) AND f('three')=123 } set L } {three} finish_test |
Changes to test/regexp2.test.
︙ | ︙ | |||
118 119 120 121 122 123 124 | DELETE FROM t5; SELECT * FROM t6; } {eab dea} finish_test | < | 118 119 120 121 122 123 124 | DELETE FROM t5; SELECT * FROM t6; } {eab dea} finish_test |
Changes to test/releasetest.tcl.
︙ | ︙ | |||
110 111 112 113 114 115 116 | -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_STMT_SCANSTATUS --enable-json1 --enable-fts5 --enable-session } "Debug-One" { --disable-shared | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_STMT_SCANSTATUS --enable-json1 --enable-fts5 --enable-session } "Debug-One" { --disable-shared -O2 -funsigned-char -DSQLITE_DEBUG=1 -DSQLITE_MEMDEBUG=1 -DSQLITE_MUTEX_NOOP=1 -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_MEMSYS5=1 |
︙ | ︙ | |||
175 176 177 178 179 180 181 | --enable-json1 --enable-fts5 --enable-session } "Locking-Style" { -O2 -DSQLITE_ENABLE_LOCKING_STYLE=1 } "Apple" { | | | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | --enable-json1 --enable-fts5 --enable-session } "Locking-Style" { -O2 -DSQLITE_ENABLE_LOCKING_STYLE=1 } "Apple" { -Os -DHAVE_GMTIME_R=1 -DHAVE_ISNAN=1 -DHAVE_LOCALTIME_R=1 -DHAVE_PREAD=1 -DHAVE_PWRITE=1 -DHAVE_USLEEP=1 -DHAVE_USLEEP=1 |
︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 | && $target!="fuzzoomtest" && !$::BUILDONLY && $::QUICK<2} { set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] set xtarget $target regsub -all {fulltest[a-z]*} $xtarget test xtarget regsub -all {fuzzoomtest} $xtarget fuzztest xtarget if {$debug_idx < 0} { incr NTEST | | | 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 | && $target!="fuzzoomtest" && !$::BUILDONLY && $::QUICK<2} { set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] set xtarget $target regsub -all {fulltest[a-z]*} $xtarget test xtarget regsub -all {fuzzoomtest} $xtarget fuzztest xtarget if {$debug_idx < 0} { incr NTEST append config_options " -DSQLITE_DEBUG=1 -DSQLITE_EXTRA_IFNULLROW=1" add_test_suite all "${zConfig}_debug" $xtarget $config_options } else { incr NTEST regsub { *-DSQLITE_MEMDEBUG[^ ]* *} $config_options { } config_options regsub { *-DSQLITE_DEBUG[^ ]* *} $config_options { } config_options add_test_suite all "${zConfig}_ndebug" $xtarget $config_options } |
︙ | ︙ |
Changes to test/rollback.test.
︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | do_test rollback-1.9 { sqlite3_finalize $STMT } {SQLITE_OK} if {$tcl_platform(platform) == "unix" && [permutation] ne "onefile" && [permutation] ne "inmemory_journal" } { do_test rollback-2.1 { execsql { BEGIN; INSERT INTO t3 VALUES('hello world'); } forcecopy test.db testA.db | > | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | do_test rollback-1.9 { sqlite3_finalize $STMT } {SQLITE_OK} if {$tcl_platform(platform) == "unix" && [permutation] ne "onefile" && [permutation] ne "inmemory_journal" && [permutation] ne "atomic-batch-write" } { do_test rollback-2.1 { execsql { BEGIN; INSERT INTO t3 VALUES('hello world'); } forcecopy test.db testA.db |
︙ | ︙ |
Changes to test/rowvalue.test.
︙ | ︙ | |||
251 252 253 254 255 256 257 258 259 | do_catchsql_test 11.7 { SELECT * FROM t11 WHERE (a,a) IS 1; } {1 {row value misused}} do_catchsql_test 11.8 { SELECT * FROM t11 WHERE (a,a) IS NOT 1; } {1 {row value misused}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_catchsql_test 11.7 { SELECT * FROM t11 WHERE (a,a) IS 1; } {1 {row value misused}} do_catchsql_test 11.8 { SELECT * FROM t11 WHERE (a,a) IS NOT 1; } {1 {row value misused}} # 2016-10-27: https://www.sqlite.org/src/tktview/fef4bb4bd9185ec8f # Incorrect result from a LEFT JOIN with a row-value constraint # do_execsql_test 12.1 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); DROP TABLE IF EXISTS t2; CREATE TABLE t2(x,y); INSERT INTO t2 VALUES(3,4); SELECT *,'x' FROM t1 LEFT JOIN t2 ON (a,b)=(x,y); } {1 2 {} {} x} foreach {tn sql} { 0 "SELECT (1,2) AS x WHERE x=3" 1 "SELECT (1,2) BETWEEN 1 AND 2" 2 "SELECT 1 BETWEEN (1,2) AND 2" 3 "SELECT 2 BETWEEN 1 AND (1,2)" 4 "SELECT (1,2) FROM (SELECT 1) ORDER BY 1" 5 "SELECT (1,2) FROM (SELECT 1) GROUP BY 1" } { do_catchsql_test 13.$tn $sql {1 {row value misused}} } do_execsql_test 14.0 { CREATE TABLE t12(x); INSERT INTO t12 VALUES(2), (4); } do_execsql_test 14.1 "SELECT 1 WHERE (2,2) BETWEEN (1,1) AND (3,3)" 1 do_execsql_test 14.2 "SELECT CASE (2,2) WHEN (1, 1) THEN 2 ELSE 1 END" 1 do_execsql_test 14.3 "SELECT CASE (SELECT 2,2) WHEN (1, 1) THEN 2 ELSE 1 END" 1 do_execsql_test 14.4 "SELECT 1 WHERE (SELECT 2,2) BETWEEN (1,1) AND (3,3)" 1 do_execsql_test 14.5 "SELECT 1 FROM t12 WHERE (x,1) BETWEEN (1,1) AND (3,3)" 1 do_execsql_test 14.6 { SELECT 1 FROM t12 WHERE (1,x) BETWEEN (1,1) AND (3,3) } {1 1} #------------------------------------------------------------------------- # Test that errors are not concealed by the SELECT flattening or # WHERE-clause push-down optimizations. do_execsql_test 14.1 { CREATE TABLE x1(a PRIMARY KEY, b); CREATE TABLE x2(a INTEGER PRIMARY KEY, b); } foreach {tn n sql} { 1 0 "SELECT * FROM (SELECT (1, 1) AS c FROM x1) WHERE c=1" 2 2 "SELECT * FROM (SELECT 1 AS x, (SELECT 8,9) AS y) WHERE y<1" 3 3 "SELECT * FROM (SELECT 1 AS x, (SELECT 8,9,10) AS y) WHERE y<1" 4 0 "SELECT * FROM (SELECT (a, b) AS c FROM x1), x2 WHERE c=a" 5 0 "SELECT * FROM (SELECT a AS c, (1, 2, 3) FROM x1), x2 WHERE c=a" 6 0 "SELECT * FROM (SELECT 1 AS c, (1, 2, 3) FROM x1) WHERE c=1" } { if {$n==0} { set err "row value misused" } else { set err "sub-select returns $n columns - expected 1" } do_catchsql_test 14.2.$tn $sql [list 1 $err] } #-------------------------------------------------------------------------- # Test for vector size mismatches concealed by unexpanded subqueries. # do_catchsql_test 15.1 { DETACH (SELECT * FROM (SELECT 1,2))<3; } {1 {row value misused}} do_catchsql_test 15.2 { UPDATE x1 SET a=(SELECT * FROM (SELECT b,2))<3; } {1 {row value misused}} do_catchsql_test 15.3 { UPDATE x1 SET a=NULL WHERE a<(SELECT * FROM (SELECT b,2)); } {1 {sub-select returns 2 columns - expected 1}} do_catchsql_test 15.4 { DELETE FROM x1 WHERE a<(SELECT * FROM (SELECT b,2)); } {1 {sub-select returns 2 columns - expected 1}} do_catchsql_test 15.5 { INSERT INTO x1(a,b) VALUES(1,(SELECT * FROM (SELECT 1,2))<3); } {1 {row value misused}} #------------------------------------------------------------------------- # Row-values used in UPDATE statements within TRIGGERs # # Ticket https://www.sqlite.org/src/info/8c9458e703666e1a # do_execsql_test 16.1 { CREATE TABLE t16a(a,b,c); INSERT INTO t16a VALUES(1,2,3); CREATE TABLE t16b(x); INSERT INTO t16b(x) VALUES(1); CREATE TRIGGER t16r AFTER UPDATE ON t16b BEGIN UPDATE t16a SET (a,b,c)=(SELECT new.x,new.x+1,new.x+2); END; UPDATE t16b SET x=7; SELECT * FROM t16a; } {7 8 9} do_execsql_test 16.2 { UPDATE t16b SET x=97; SELECT * FROM t16a; } {97 98 99} do_execsql_test 16.3 { CREATE TABLE t16c(a, b, c, d, e); INSERT INTO t16c VALUES(1, 'a', 'b', 'c', 'd'); CREATE TRIGGER t16c1 AFTER INSERT ON t16c BEGIN UPDATE t16c SET (c, d) = (SELECT 'A', 'B'), (e, b) = (SELECT 'C', 'D') WHERE a = new.a-1; END; SELECT * FROM t16c; } {1 a b c d} do_execsql_test 16.4 { INSERT INTO t16c VALUES(2, 'w', 'x', 'y', 'z'); SELECT * FROM t16c; } { 1 D A B C 2 w x y z } do_execsql_test 16.5 { DROP TRIGGER t16c1; PRAGMA recursive_triggers = 1; INSERT INTO t16c VALUES(3, 'i', 'ii', 'iii', 'iv'); CREATE TRIGGER t16c1 AFTER UPDATE ON t16c WHEN new.a>1 BEGIN UPDATE t16c SET (e, d) = ( SELECT b, c FROM t16c WHERE a = new.a-1 ), (c, b) = ( SELECT d, e FROM t16c WHERE a = new.a-1 ) WHERE a = new.a-1; END; UPDATE t16c SET a=a WHERE a=3; SELECT * FROM t16c; } { 1 C B A D 2 z y x w 3 i ii iii iv } finish_test |
Changes to test/rowvalue3.test.
︙ | ︙ | |||
197 198 199 200 201 202 203 204 205 206 | do_execsql_test 4.$tn.$tn2 " SELECT c FROM hh WHERE (a, b) in (SELECT x, y FROM k1) ORDER BY $orderby " $res } } #------------------------------------------------------------------------- finish_test | > > > > > > > > > > > > > > | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | do_execsql_test 4.$tn.$tn2 " SELECT c FROM hh WHERE (a, b) in (SELECT x, y FROM k1) ORDER BY $orderby " $res } } #------------------------------------------------------------------------- # 2016-11-17. Query flattening in a vector SELECT on the RHS of an IN # operator. Ticket https://www.sqlite.org/src/info/da7841375186386c # do_execsql_test 5.0 { DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; CREATE TABLE T1(a TEXT); INSERT INTO T1(a) VALUES ('aaa'); CREATE TABLE T2(a TEXT PRIMARY KEY,n INT); INSERT INTO T2(a, n) VALUES('aaa',0); SELECT * FROM T2 WHERE (a,n) IN (SELECT T1.a, V.n FROM T1, (SELECT * FROM (SELECT 0 n)) V); } {aaa 0} finish_test |
Changes to test/rowvalue9.test.
︙ | ︙ | |||
295 296 297 298 299 300 301 | SELECT * FROM g2 WHERE (x, y) IN ( SELECT a, b FROM g1 ORDER BY 1, 2 LIMIT 10 ); } { 1 4 1 5 } finish_test | < | 295 296 297 298 299 300 301 | SELECT * FROM g2 WHERE (x, y) IN ( SELECT a, b FROM g1 ORDER BY 1, 2 LIMIT 10 ); } { 1 4 1 5 } finish_test |
Changes to test/rowvaluefault.test.
︙ | ︙ | |||
65 66 67 68 69 70 71 | SELECT fou FROM xyz WHERE (one, two, thr) BETWEEN ('B', 'B', 'B') AND ('C', 'C', 'C') } } -test { faultsim_test_result {0 {2 3}} } finish_test | < | 65 66 67 68 69 70 71 | SELECT fou FROM xyz WHERE (one, two, thr) BETWEEN ('B', 'B', 'B') AND ('C', 'C', 'C') } } -test { faultsim_test_result {0 {2 3}} } finish_test |
Changes to test/savepoint.test.
︙ | ︙ | |||
611 612 613 614 615 616 617 618 619 620 621 622 623 | #------------------------------------------------------------------------- # The following tests - savepoint-10.* - test the interaction of # savepoints and ATTACH statements. # # First make sure it is not possible to attach or detach a database while # a savepoint is open (it is not possible if any transaction is open). # do_test savepoint-10.1.1 { catchsql { SAVEPOINT one; ATTACH 'test2.db' AS aux; } | > > > > | > | | 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 | #------------------------------------------------------------------------- # The following tests - savepoint-10.* - test the interaction of # savepoints and ATTACH statements. # # First make sure it is not possible to attach or detach a database while # a savepoint is open (it is not possible if any transaction is open). # # UPDATE 2017-07-26: It is not possible to ATTACH and DETACH within a # a transaction. # do_test savepoint-10.1.1 { catchsql { SAVEPOINT one; ATTACH 'test2.db' AS aux; DETACH aux; } } {0 {}} do_test savepoint-10.1.2 { execsql { RELEASE one; ATTACH 'test2.db' AS aux; } catchsql { SAVEPOINT one; DETACH aux; ATTACH 'test2.db' AS aux; } } {0 {}} do_test savepoint-10.1.3 { execsql { RELEASE one; DETACH aux; } } {} |
︙ | ︙ |
Changes to test/schema4.test.
︙ | ︙ | |||
145 146 147 148 149 150 151 | END; CREATE TEMP TABLE x1(x); INSERT INTO x1 VALUES(123); } {} do_execsql_test schema4-2.8 { | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | END; CREATE TEMP TABLE x1(x); INSERT INTO x1 VALUES(123); } {} do_execsql_test schema4-2.8 { select sql from temp.sqlite_master WHERE type='table'; } {{CREATE TABLE x1(x)}} do_execsql_test schema4-2.7 { ALTER TABLE tbl RENAME TO tbl2 } {} do_execsql_test schema4-2.9 { select sql from sqlite_temp_master WHERE type='table'; } {{CREATE TABLE x1(x)}} |
︙ | ︙ |
Added test/schema6.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 | # 2017-07-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 implements tests to show that certain CREATE TABLE statements # generate identical database files. For example, changes in identifier # names, white-space, and formatting of the CREATE TABLE statement should # produce identical table content. # set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix schema6 # Command: check_same_database_content TESTNAME SQL1 SQL2 SQL3 ... # # This command creates fresh databases using SQL1 and subsequent arguments # and checks to make sure the content of all database files is byte-for-byte # identical. Page 1 of the database files is allowed to be different, since # page 1 contains the sqlite_master table which is expected to vary. # proc check_same_database_content {basename args} { set i 0 set hash {} foreach sql $args { catch {db close} forcedelete test.db sqlite3 db test.db db eval $sql set pgsz [db one {PRAGMA page_size}] db close set sz [file size test.db] set thishash [md5file test.db $pgsz [expr {$sz-$pgsz}]] if {$i==0} { set hash $thishash } else { do_test $basename-$i "set x $thishash" $hash } incr i } } # Command: check_different_database_content TESTNAME SQL1 SQL2 SQL3 ... # # This command creates fresh databases using SQL1 and subsequent arguments # and checks to make sure the content of all database files is different # in ways other than on page 1. # proc check_different_database_content {basename args} { set i 0 set hashes {} foreach sql $args { forcedelete test.db sqlite3 db test.db db eval $sql set pgsz [db one {PRAGMA page_size}] db close set sz [file size test.db] set thishash [md5file test.db $pgsz [expr {$sz-$pgsz}]] set j [lsearch $hashes $thishash] if {$j>=0} { do_test $basename-$i "set x {$i is the same as $j}" "All are different" } else { do_test $basename-$i "set x {All are different}" "All are different" } lappend hashes $thishash incr i } } check_same_database_content 100 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(xyz INTEGER, abc, PRIMARY KEY(xyz), UNIQUE(abc)); INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(xyz INTEGER, abc, UNIQUE(abc), PRIMARY KEY(xyz)); INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY ASC, b UNIQUE); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY, b); CREATE UNIQUE INDEX t1b ON t1(b); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY, b); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); CREATE UNIQUE INDEX t1b ON t1(b); } check_same_database_content 110 { CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE, UNIQUE(a)); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b); CREATE UNIQUE INDEX t1b ON t1(b); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); CREATE UNIQUE INDEX t1b ON t1(b); } check_same_database_content 120 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE) WITHOUT ROWID; INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(xyz INTEGER, abc, PRIMARY KEY(xyz), UNIQUE(abc))WITHOUT ROWID; INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(xyz INTEGER, abc, UNIQUE(abc), PRIMARY KEY(xyz))WITHOUT ROWID; INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY ASC, b UNIQUE) WITHOUT ROWID; INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE) WITHOUT ROWID; INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE) WITHOUT ROWID; INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE, UNIQUE(a)) WITHOUT ROWID; INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY, b) WITHOUT ROWID; CREATE UNIQUE INDEX t1b ON t1(b); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY, b) WITHOUT ROWID; INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); CREATE UNIQUE INDEX t1b ON t1(b); } check_different_database_content 130 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE); INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } { CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE) WITHOUT ROWID; INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...'); } finish_test |
Changes to test/securedel.test.
︙ | ︙ | |||
47 48 49 50 51 52 53 54 55 56 57 58 59 60 | } } {0 0} do_test securedel-1.4 { db eval { PRAGMA secure_delete=ON; PRAGMA db2.secure_delete; } } {1 1} do_test securedel-2.1 { db eval { DETACH db2; ATTACH 'test2.db' AS db2; PRAGMA db2.secure_delete; | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } } {0 0} do_test securedel-1.4 { db eval { PRAGMA secure_delete=ON; PRAGMA db2.secure_delete; } } {1 1} do_test securedel-1.5 { db eval { PRAGMA secure_delete=FAST; PRAGMA db2.secure_delete; } } {2 2} do_test securedel-1.6 { db eval { PRAGMA secure_delete=ON; PRAGMA db2.secure_delete; } } {1 1} do_test securedel-1.7 { db eval { PRAGMA main.secure_delete=FAST; PRAGMA db2.secure_delete; } } {2 1} do_test securedel-1.8 { db eval { PRAGMA main.secure_delete=ON; PRAGMA db2.secure_delete; } } {1 1} do_test securedel-2.1 { db eval { DETACH db2; ATTACH 'test2.db' AS db2; PRAGMA db2.secure_delete; |
︙ | ︙ |
Changes to test/selectC.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #*********************************************************************** # This file implements regression tests for SQLite library. # # $Id: selectC.test,v 1.5 2009/05/17 15:26:21 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Ticket # do_test selectC-1.1 { execsql { CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES(1,'aaa','bbb'); INSERT INTO t1 SELECT * FROM t1; | > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #*********************************************************************** # This file implements regression tests for SQLite library. # # $Id: selectC.test,v 1.5 2009/05/17 15:26:21 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix selectC # Ticket # do_test selectC-1.1 { execsql { CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES(1,'aaa','bbb'); INSERT INTO t1 SELECT * FROM t1; |
︙ | ︙ | |||
228 229 230 231 232 233 234 235 236 | do_execsql_test selectC-4.2 { select a from (select distinct a, b from t_distinct_bug) } {1 1 1} do_execsql_test selectC-4.3 { select a, udf() from (select distinct a, b from t_distinct_bug) } {1 1 1 2 1 3} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_execsql_test selectC-4.2 { select a from (select distinct a, b from t_distinct_bug) } {1 1 1} do_execsql_test selectC-4.3 { select a, udf() from (select distinct a, b from t_distinct_bug) } {1 1 1 2 1 3} #------------------------------------------------------------------------- # Test that the problem in ticket #190c2507 has been fixed. # do_execsql_test 5.0 { CREATE TABLE x1(a); CREATE TABLE x2(b); CREATE TABLE x3(c); CREATE VIEW vvv AS SELECT b FROM x2 ORDER BY 1; INSERT INTO x1 VALUES('a'), ('b'); INSERT INTO x2 VALUES(22), (23), (25), (24), (21); INSERT INTO x3 VALUES(302), (303), (301); } do_execsql_test 5.1 { CREATE TABLE x4 AS SELECT b FROM vvv UNION ALL SELECT c from x3; SELECT * FROM x4; } {21 22 23 24 25 302 303 301} do_execsql_test 5.2 { SELECT * FROM x1, x4 } { a 21 a 22 a 23 a 24 a 25 a 302 a 303 a 301 b 21 b 22 b 23 b 24 b 25 b 302 b 303 b 301 } do_execsql_test 5.3 { SELECT * FROM x1, (SELECT b FROM vvv UNION ALL SELECT c from x3); } { a 21 a 22 a 23 a 24 a 25 a 302 a 303 a 301 b 21 b 22 b 23 b 24 b 25 b 302 b 303 b 301 } finish_test |
Changes to test/shared_err.test.
︙ | ︙ | |||
374 375 376 377 378 379 380 | # any time and from any thread #do_test shared_err-misuse-7.1 { # sqlite3 db test.db # catch { # sqlite3_enable_shared_cache 0 # } msg # set msg | | | 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 | # any time and from any thread #do_test shared_err-misuse-7.1 { # sqlite3 db test.db # catch { # sqlite3_enable_shared_cache 0 # } msg # set msg #} {bad parameter or other API misuse} # Again provoke a malloc() failure when a cursor position is being saved, # this time during a ROLLBACK operation by some other handle. # # The library should return an SQLITE_NOMEM to the caller. The query that # owns the cursor (the one for which the position is not saved) should # be aborted. |
︙ | ︙ |
Changes to test/shell1.test.
︙ | ︙ | |||
195 196 197 198 199 200 201 | catchcmd "test.db" ".explain \"OFF" } {0 {}} do_test shell1-2.2.4 { catchcmd "test.db" ".explain \'OFF" } {0 {}} do_test shell1-2.2.5 { catchcmd "test.db" ".mode \"insert FOO" | | | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | catchcmd "test.db" ".explain \"OFF" } {0 {}} do_test shell1-2.2.4 { catchcmd "test.db" ".explain \'OFF" } {0 {}} do_test shell1-2.2.5 { catchcmd "test.db" ".mode \"insert FOO" } {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} do_test shell1-2.2.6 { catchcmd "test.db" ".mode \'insert FOO" } {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} # check multiple tokens, and quoted tokens do_test shell1-2.3.1 { catchcmd "test.db" ".explain 1" } {0 {}} do_test shell1-2.3.2 { catchcmd "test.db" ".explain on" |
︙ | ︙ | |||
226 227 228 229 230 231 232 | do_test shell1-2.3.7 { catchcmd "test.db" ".\'explain\' \'OFF\'" } {0 {}} # check quoted args are unquoted do_test shell1-2.4.1 { catchcmd "test.db" ".mode FOO" | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | do_test shell1-2.3.7 { catchcmd "test.db" ".\'explain\' \'OFF\'" } {0 {}} # check quoted args are unquoted do_test shell1-2.4.1 { catchcmd "test.db" ".mode FOO" } {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode csv" } {0 {}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode \"csv\"" } {0 {}} |
︙ | ︙ | |||
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 | catchcmd "test.db" ".bail OFF" } {0 {}} do_test shell1-3.2.4 { # too many arguments catchcmd "test.db" ".bail OFF BAD" } {1 {Usage: .bail on|off}} # .databases List names and files of attached databases do_test shell1-3.3.1 { catchcmd "-csv test.db" ".databases" } "/0.+main.+[string map {/ ".{1,2}"} [string range [get_pwd] 0 10]].*/" do_test shell1-3.3.2 { # extra arguments ignored catchcmd "test.db" ".databases BAD" } "/0.+main.+[string map {/ ".{1,2}"} [string range [get_pwd] 0 10]].*/" # .dump ?TABLE? ... Dump the database in an SQL text format # If TABLE specified, only dump tables matching # LIKE pattern TABLE. do_test shell1-3.4.1 { set res [catchcmd "test.db" ".dump"] list [regexp {BEGIN TRANSACTION;} $res] \ [regexp {COMMIT;} $res] } {1 1} do_test shell1-3.4.2 { set res [catchcmd "test.db" ".dump FOO"] list [regexp {BEGIN TRANSACTION;} $res] \ [regexp {COMMIT;} $res] } {1 1} do_test shell1-3.4.3 { # too many arguments catchcmd "test.db" ".dump FOO BAD" | > > | | 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 | catchcmd "test.db" ".bail OFF" } {0 {}} do_test shell1-3.2.4 { # too many arguments catchcmd "test.db" ".bail OFF BAD" } {1 {Usage: .bail on|off}} ifcapable vtab { # .databases List names and files of attached databases do_test shell1-3.3.1 { catchcmd "-csv test.db" ".databases" } "/0.+main.+[string map {/ ".{1,2}"} [string range [get_pwd] 0 10]].*/" do_test shell1-3.3.2 { # extra arguments ignored catchcmd "test.db" ".databases BAD" } "/0.+main.+[string map {/ ".{1,2}"} [string range [get_pwd] 0 10]].*/" } # .dump ?TABLE? ... Dump the database in an SQL text format # If TABLE specified, only dump tables matching # LIKE pattern TABLE. do_test shell1-3.4.1 { set res [catchcmd "test.db" ".dump"] list [regexp {BEGIN TRANSACTION;} $res] \ [regexp {COMMIT;} $res] } {1 1} do_test shell1-3.4.2 { set res [catchcmd "test.db" ".dump FOO"] list [regexp {BEGIN TRANSACTION;} $res] \ [regexp {COMMIT;} $res] } {1 1} do_test shell1-3.4.3 { # too many arguments catchcmd "test.db" ".dump FOO BAD" } {1 {Usage: .dump ?--preserve-rowids? ?--newlines? ?LIKE-PATTERN?}} # .echo ON|OFF Turn command echo on or off do_test shell1-3.5.1 { catchcmd "test.db" ".echo" } {1 {Usage: .echo on|off}} do_test shell1-3.5.2 { catchcmd "test.db" ".echo ON" |
︙ | ︙ | |||
423 424 425 426 427 428 429 | # insert SQL insert statements for TABLE # line One value per line # list Values delimited by .separator strings # tabs Tab-separated values # tcl TCL list elements do_test shell1-3.13.1 { catchcmd "test.db" ".mode" | | | | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | # insert SQL insert statements for TABLE # line One value per line # list Values delimited by .separator strings # tabs Tab-separated values # tcl TCL list elements do_test shell1-3.13.1 { catchcmd "test.db" ".mode" } {0 {current output mode: list}} do_test shell1-3.13.2 { catchcmd "test.db" ".mode FOO" } {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} do_test shell1-3.13.3 { catchcmd "test.db" ".mode csv" } {0 {}} do_test shell1-3.13.4 { catchcmd "test.db" ".mode column" } {0 {}} do_test shell1-3.13.5 { |
︙ | ︙ | |||
459 460 461 462 463 464 465 | # extra arguments ignored catchcmd "test.db" ".mode tcl BAD" } {0 {}} # don't allow partial mode type matches do_test shell1-3.13.12 { catchcmd "test.db" ".mode l" | | | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | # extra arguments ignored catchcmd "test.db" ".mode tcl BAD" } {0 {}} # don't allow partial mode type matches do_test shell1-3.13.12 { catchcmd "test.db" ".mode l" } {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} do_test shell1-3.13.13 { catchcmd "test.db" ".mode li" } {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} do_test shell1-3.13.14 { catchcmd "test.db" ".mode lin" } {0 {}} # .nullvalue STRING Print STRING in place of NULL values do_test shell1-3.14.1 { catchcmd "test.db" ".nullvalue" |
︙ | ︙ | |||
552 553 554 555 556 557 558 559 560 561 562 563 564 565 | catchcmd "test.db" ".restore FOO BAR" } {1 {Error: unknown database FOO}} do_test shell1-3.20.4 { # too many arguments catchcmd "test.db" ".restore FOO BAR BAD" } {1 {Usage: .restore ?DB? FILE}} # .schema ?TABLE? Show the CREATE statements # If TABLE specified, only show tables matching # LIKE pattern TABLE. do_test shell1-3.21.1 { catchcmd "test.db" ".schema" } {0 {}} do_test shell1-3.21.2 { | > | 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | catchcmd "test.db" ".restore FOO BAR" } {1 {Error: unknown database FOO}} do_test shell1-3.20.4 { # too many arguments catchcmd "test.db" ".restore FOO BAR BAD" } {1 {Usage: .restore ?DB? FILE}} ifcapable vtab { # .schema ?TABLE? Show the CREATE statements # If TABLE specified, only show tables matching # LIKE pattern TABLE. do_test shell1-3.21.1 { catchcmd "test.db" ".schema" } {0 {}} do_test shell1-3.21.2 { |
︙ | ︙ | |||
577 578 579 580 581 582 583 584 585 586 587 588 589 590 | CREATE VIEW v1 AS SELECT y+1 FROM v2; } catchcmd "test.db" ".schema" } {0 {CREATE TABLE t1(x); CREATE VIEW v2 AS SELECT x+1 AS y FROM t1; CREATE VIEW v1 AS SELECT y+1 FROM v2;}} db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;} # .separator STRING Change column separator used by output and .import do_test shell1-3.22.1 { catchcmd "test.db" ".separator" } {1 {Usage: .separator COL ?ROW?}} do_test shell1-3.22.2 { catchcmd "test.db" ".separator FOO" | > | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | CREATE VIEW v1 AS SELECT y+1 FROM v2; } catchcmd "test.db" ".schema" } {0 {CREATE TABLE t1(x); CREATE VIEW v2 AS SELECT x+1 AS y FROM t1; CREATE VIEW v1 AS SELECT y+1 FROM v2;}} db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;} } # .separator STRING Change column separator used by output and .import do_test shell1-3.22.1 { catchcmd "test.db" ".separator" } {1 {Usage: .separator COL ?ROW?}} do_test shell1-3.22.2 { catchcmd "test.db" ".separator FOO" |
︙ | ︙ | |||
734 735 736 737 738 739 740 | INSERT INTO t3 VALUES(1,null), (2,''), (3,1), (4,2.25), (5,'hello'), (6,x'807f'); } catchcmd test.db {.dump} } {0 {PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE t1(x); | | | | | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | INSERT INTO t3 VALUES(1,null), (2,''), (3,1), (4,2.25), (5,'hello'), (6,x'807f'); } catchcmd test.db {.dump} } {0 {PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE t1(x); INSERT INTO t1 VALUES(NULL); INSERT INTO t1 VALUES(''); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2.25); INSERT INTO t1 VALUES('hello'); INSERT INTO t1 VALUES(X'807f'); CREATE TABLE t3(x,y); INSERT INTO t3 VALUES(1,NULL); INSERT INTO t3 VALUES(2,''); INSERT INTO t3 VALUES(3,1); INSERT INTO t3 VALUES(4,2.25); INSERT INTO t3 VALUES(5,'hello'); INSERT INTO t3 VALUES(6,X'807f'); COMMIT;}} ifcapable vtab { # The --preserve-rowids option to .dump # do_test shell1-4.1.1 { catchcmd test.db {.dump --preserve-rowids} } {0 {PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE t1(x); INSERT INTO t1(rowid,x) VALUES(1,NULL); INSERT INTO t1(rowid,x) VALUES(2,''); INSERT INTO t1(rowid,x) VALUES(3,1); INSERT INTO t1(rowid,x) VALUES(4,2.25); INSERT INTO t1(rowid,x) VALUES(5,'hello'); INSERT INTO t1(rowid,x) VALUES(6,X'807f'); CREATE TABLE t3(x,y); INSERT INTO t3(rowid,x,y) VALUES(1,1,NULL); INSERT INTO t3(rowid,x,y) VALUES(2,2,''); INSERT INTO t3(rowid,x,y) VALUES(3,3,1); INSERT INTO t3(rowid,x,y) VALUES(4,4,2.25); INSERT INTO t3(rowid,x,y) VALUES(5,5,'hello'); INSERT INTO t3(rowid,x,y) VALUES(6,6,X'807f'); COMMIT;}} # If the table contains an INTEGER PRIMARY KEY, do not record a separate # rowid column in the output. # do_test shell1-4.1.2 { db close forcedelete test2.db sqlite3 db test2.db db eval { CREATE TABLE t1(x INTEGER PRIMARY KEY, y); INSERT INTO t1 VALUES(1,null), (2,''), (3,1), (4,2.25), (5,'hello'), (6,x'807f'); } catchcmd test2.db {.dump --preserve-rowids} } {0 {PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE t1(x INTEGER PRIMARY KEY, y); INSERT INTO t1 VALUES(1,NULL); INSERT INTO t1 VALUES(2,''); INSERT INTO t1 VALUES(3,1); INSERT INTO t1 VALUES(4,2.25); INSERT INTO t1 VALUES(5,'hello'); INSERT INTO t1 VALUES(6,X'807f'); COMMIT;}} # Verify that the table named [table] is correctly quoted and that # an INTEGER PRIMARY KEY DESC is not an alias for the rowid. # do_test shell1-4.1.3 { db close forcedelete test2.db sqlite3 db test2.db db eval { CREATE TABLE [table](x INTEGER PRIMARY KEY DESC, y); INSERT INTO [table] VALUES(1,null), (12,''), (23,1), (34,2.25), (45,'hello'), (56,x'807f'); } catchcmd test2.db {.dump --preserve-rowids} } {0 {PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE [table](x INTEGER PRIMARY KEY DESC, y); INSERT INTO "table"(rowid,x,y) VALUES(1,1,NULL); INSERT INTO "table"(rowid,x,y) VALUES(2,12,''); INSERT INTO "table"(rowid,x,y) VALUES(3,23,1); INSERT INTO "table"(rowid,x,y) VALUES(4,34,2.25); INSERT INTO "table"(rowid,x,y) VALUES(5,45,'hello'); INSERT INTO "table"(rowid,x,y) VALUES(6,56,X'807f'); COMMIT;}} # Do not record rowids for a WITHOUT ROWID table. Also check correct quoting # of table names that contain odd characters. # do_test shell1-4.1.4 { db close forcedelete test2.db sqlite3 db test2.db db eval { CREATE TABLE [ta<>ble](x INTEGER PRIMARY KEY, y) WITHOUT ROWID; INSERT INTO [ta<>ble] VALUES(1,null), (12,''), (23,1), (34,2.25), (45,'hello'), (56,x'807f'); } catchcmd test2.db {.dump --preserve-rowids} } {0 {PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE [ta<>ble](x INTEGER PRIMARY KEY, y) WITHOUT ROWID; INSERT INTO "ta<>ble" VALUES(1,NULL); INSERT INTO "ta<>ble" VALUES(12,''); INSERT INTO "ta<>ble" VALUES(23,1); INSERT INTO "ta<>ble" VALUES(34,2.25); INSERT INTO "ta<>ble" VALUES(45,'hello'); INSERT INTO "ta<>ble" VALUES(56,X'807f'); COMMIT;}} # Do not record rowids if the rowid is inaccessible # do_test shell1-4.1.5 { db close forcedelete test2.db sqlite3 db test2.db db eval { CREATE TABLE t1(_ROWID_,rowid,oid); INSERT INTO t1 VALUES(1,null,'alpha'), (12,'',99), (23,1,x'b0b1b2'); } catchcmd test2.db {.dump --preserve-rowids} } {0 {PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE t1(_ROWID_,rowid,oid); INSERT INTO t1 VALUES(1,NULL,'alpha'); INSERT INTO t1 VALUES(12,'',99); INSERT INTO t1 VALUES(23,1,X'b0b1b2'); COMMIT;}} } else { do_test shell1-4.1.6 { db close forcedelete test2.db sqlite3 db test2.db db eval { CREATE TABLE t1(x INTEGER PRIMARY KEY, y); INSERT INTO t1 VALUES(1,null), (2,''), (3,1), (4,2.25), (5,'hello'), (6,x'807f'); } catchcmd test2.db {.dump --preserve-rowids} } {1 {The --preserve-rowids option is not compatible with SQLITE_OMIT_VIRTUALTABLE}} } # Test the output of ".mode insert" # do_test shell1-4.2.1 { catchcmd test.db ".mode insert t1\nselect * from t1;" } {0 {INSERT INTO t1 VALUES(NULL); INSERT INTO t1 VALUES(''); |
︙ | ︙ |
Changes to test/shell3.test.
︙ | ︙ | |||
92 93 94 95 96 97 98 | catchcmd "foo.db" "CREATE TABLE t1(a); DROP TABLE t1;" } {0 {}} do_test shell3-2.6 { catchcmd "foo.db" ".tables" } {0 {}} do_test shell3-2.7 { catchcmd "foo.db" "CREATE TABLE" | | | 92 93 94 95 96 97 98 99 100 101 | catchcmd "foo.db" "CREATE TABLE t1(a); DROP TABLE t1;" } {0 {}} do_test shell3-2.6 { catchcmd "foo.db" ".tables" } {0 {}} do_test shell3-2.7 { catchcmd "foo.db" "CREATE TABLE" } {1 {Error: near line 1: near "TABLE": syntax error}} finish_test |
Changes to test/shell5.test.
︙ | ︙ | |||
179 180 181 182 183 184 185 186 187 188 189 190 191 192 | set res [catchcmd "test.db" {.import shell5.csv t1 SELECT COUNT(*) FROM t1;}] } {0 7} do_test shell5-1.4.10.2 { catchcmd "test.db" {SELECT b FROM t1 WHERE a='7';} } {0 {Now is the time for all good men to come to the aid of their country.}} # check importing very long field do_test shell5-1.5.1 { set str [string repeat X 999] set in [open shell5.csv w] puts $in "8|$str" close $in | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | set res [catchcmd "test.db" {.import shell5.csv t1 SELECT COUNT(*) FROM t1;}] } {0 7} do_test shell5-1.4.10.2 { catchcmd "test.db" {SELECT b FROM t1 WHERE a='7';} } {0 {Now is the time for all good men to come to the aid of their country.}} # import file with 2 rows, 2 columns and an initial BOM # do_test shell5-1.4.11 { set in [open shell5.csv wb] puts -nonewline $in "\xef\xbb\xbf" puts $in "2|3" puts $in "4|5" close $in set res [catchcmd "test.db" {CREATE TABLE t2(x INT, y INT); .import shell5.csv t2 .mode quote .header on SELECT * FROM t2;}] string map {\n | \n\r |} $res } {0 {'x','y'|2,3|4,5}} # import file with 2 rows, 2 columns or text with an initial BOM # do_test shell5-1.4.12 { set in [open shell5.csv wb] puts $in "\xef\xbb\xbf\"two\"|3" puts $in "4|5" close $in set res [catchcmd "test.db" {DELETE FROM t2; .import shell5.csv t2 .mode quote .header on SELECT * FROM t2;}] string map {\n | \n\r |} $res } {0 {'x','y'|'two',3|4,5}} # check importing very long field do_test shell5-1.5.1 { set str [string repeat X 999] set in [open shell5.csv w] puts $in "8|$str" close $in |
︙ | ︙ | |||
206 207 208 209 210 211 212 | for {set i 1} {$i<$cols} {incr i} { append data "$i|" } append data "$cols" set in [open shell5.csv w] puts $in $data close $in | | > | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | for {set i 1} {$i<$cols} {incr i} { append data "$i|" } append data "$cols" set in [open shell5.csv w] puts $in $data close $in set res [catchcmd "test.db" {DROP TABLE IF EXISTS t2; .import shell5.csv t2 SELECT COUNT(*) FROM t2;}] } {0 1} # try importing a large number of rows set rows 9999 do_test shell5-1.7.1 { set in [open shell5.csv w] |
︙ | ︙ |
Added test/shell6.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 | # 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} set testprefix shell6 set CLI [test_find_cli] db close forcedelete test.db test.db-journal test.db-wal foreach {tn schema output} { 1 { CREATE TABLE p1(a PRIMARY KEY, b); CREATE TABLE c1(x, y REFERENCES p1); } { CREATE INDEX 'c1_y' ON 'c1'('y'); --> p1(a) } 2 { CREATE TABLE p1(a PRIMARY KEY, b); CREATE TABLE c2(x REFERENCES p1, y REFERENCES p1); } { CREATE INDEX 'c2_y' ON 'c2'('y'); --> p1(a) CREATE INDEX 'c2_x' ON 'c2'('x'); --> p1(a) } 3 { CREATE TABLE 'p 1'(a, b, c, PRIMARY KEY(c, b)); CREATE TABLE 'c 1'(x, y, z, FOREIGN KEY (z, y) REFERENCES 'p 1'); } { CREATE INDEX 'c 1_z_y' ON 'c 1'('z', 'y'); --> p 1(c,b) } 4 { CREATE TABLE p1(a, 'b b b' PRIMARY KEY); CREATE TABLE c1('x y z' REFERENCES p1); CREATE INDEX i1 ON c1('x y z') WHERE "x y z" IS NOT NULL; } { } 5 { CREATE TABLE p1(a, 'b b b' PRIMARY KEY); CREATE TABLE c1('x y z' REFERENCES p1); CREATE INDEX i1 ON c1('x y z') WHERE "x y z" IS NOT 12; } { CREATE INDEX 'c1_x y z' ON 'c1'('x y z'); --> p1(b b b) } 6 { CREATE TABLE x1(a, b, c, UNIQUE(a, b)); CREATE TABLE y1(a, b, c, FOREIGN KEY(b, a) REFERENCES x1(a, b)); CREATE INDEX y1i ON y1(a, c, b); } { CREATE INDEX 'y1_b_a' ON 'y1'('b', 'a'); --> x1(a,b) } 6 { CREATE TABLE x1(a COLLATE nocase, b, UNIQUE(a)); CREATE TABLE y1(a COLLATE rtrim REFERENCES x1(a)); } { CREATE INDEX 'y1_a' ON 'y1'('a' COLLATE nocase); --> x1(a) } 7 { CREATE TABLE x1(a PRIMARY KEY COLLATE nocase, b); CREATE TABLE y1(a REFERENCES x1); } { CREATE INDEX 'y1_a' ON 'y1'('a' COLLATE nocase); --> x1(a) } 8 { CREATE TABLE x1(a, b COLLATE nocase, c COLLATE rtrim, PRIMARY KEY(c, b, a)); CREATE TABLE y1(d, e, f, FOREIGN KEY(d, e, f) REFERENCES x1); } { CREATE INDEX 'y1_d_e_f' ON 'y1'('d' COLLATE rtrim, 'e' COLLATE nocase, 'f'); --> x1(c,b,a) } 9 { CREATE TABLE p1(a, b UNIQUE); CREATE TABLE c1(x INTEGER PRIMARY KEY REFERENCES p1(b)); } { } } { forcedelete test.db sqlite3 db test.db execsql $schema set expected "" foreach line [split $output "\n"] { set line [string trim $line] if {$line!=""} { append expected "$line\n" } } do_test 1.$tn.1 { set RES [catchcmd test.db [list .lint fkey-indexes]] } [list 0 [string trim $expected]] do_test 1.$tn.2 { execsql [lindex $RES 1] catchcmd test.db [list .lint fkey-indexes] } {0 {}} db close } finish_test |
Added test/shell7.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 | # 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] source $testdir/tester.tcl set testprefix shell7 set CLI [test_find_cli] do_execsql_test 1.0 { CREATE TABLE f1(tn INTEGER PRIMARY KEY, x BLOB); CREATE TABLE f2(tn INTEGER PRIMARY KEY, x BLOB); INSERT INTO f1 VALUES(1, X'01020304'); INSERT INTO f1 VALUES(2, X'01000304'); INSERT INTO f1 VALUES(3, randomblob(200)); } foreach {tn l x} [db eval { SELECT tn, length(x) AS l, x FROM f1 }] { forcedelete shell7_test.bin set fd [open shell7_test.bin w] fconfigure $fd -encoding binary fconfigure $fd -translation binary puts -nonewline $fd $x close $fd do_test 1.$tn.1 { file size shell7_test.bin } $l do_test 1.$tn.2 { catchcmd test.db "INSERT INTO f2 VALUES($tn, readfile('shell7_test.bin'));" } {0 {}} do_execsql_test 1.$tn.3 { SELECT (SELECT x FROM f1 WHERE tn=1)==(SELECT x FROM f2 WHERE tn=1) } {1} } finish_test |
Changes to test/snapshot.test.
︙ | ︙ | |||
22 23 24 25 26 27 28 | # "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 } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > | | | | | | | | | | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 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 | # "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 } foreach {tn tcl} { 1 { proc snapshot_get {DB DBNAME} { uplevel [list sqlite3_snapshot_get $DB $DBNAME] } proc snapshot_open {DB DBNAME SNAPSHOT} { uplevel [list sqlite3_snapshot_open $DB $DBNAME $SNAPSHOT] } proc snapshot_free {SNAPSHOT} { uplevel [list sqlite3_snapshot_free $SNAPSHOT] } proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} { uplevel [list sqlite3_snapshot_cmp $SNAPSHOT1 $SNAPSHOT2] } } 2 { proc snapshot_get {DB DBNAME} { uplevel [list sqlite3_snapshot_get_blob $DB $DBNAME] } proc snapshot_open {DB DBNAME SNAPSHOT} { uplevel [list sqlite3_snapshot_open_blob $DB $DBNAME $SNAPSHOT] } proc snapshot_free {SNAPSHOT} { } proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} { uplevel [list sqlite3_snapshot_cmp_blob $SNAPSHOT1 $SNAPSHOT2] } } } { reset_db eval $tcl #------------------------------------------------------------------------- # Check some error conditions in snapshot_get(). It is an error if: # # 1) snapshot_get() is called on a non-WAL database, or # 2) there is an open write transaction on the database. # 3) the database handle is in auto-commit mode # do_execsql_test $tn.1.0 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } do_test $tn.1.1.1 { execsql { BEGIN; SELECT * FROM t1; } list [catch { snapshot_get db main } msg] $msg } {1 SQLITE_ERROR} do_execsql_test $tn.1.1.2 COMMIT do_test $tn.1.2.1 { execsql { PRAGMA journal_mode = WAL; BEGIN; INSERT INTO t1 VALUES(5, 6); INSERT INTO t1 VALUES(7, 8); } list [catch { snapshot_get db main } msg] $msg } {1 SQLITE_ERROR} do_execsql_test $tn.1.2.2 COMMIT do_test $tn.1.3.1 { list [catch { snapshot_get db main } msg] $msg } {1 SQLITE_ERROR} do_test $tn.1.3.2 { db trans { set snap [snapshot_get db main] } snapshot_free $snap } {} #------------------------------------------------------------------------- # Check that a simple case works. Reuse the database created by the # block of tests above. # do_execsql_test $tn.2.1.0 { BEGIN; SELECT * FROM t1; } {1 2 3 4 5 6 7 8} do_test $tn.2.1.1 { set snapshot [snapshot_get db main] execsql { COMMIT; INSERT INTO t1 VALUES(9, 10); SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10} do_test $tn.2.1.2 { execsql BEGIN snapshot_open db main $snapshot execsql { SELECT * FROM t1; } } {1 2 3 4 5 6 7 8} do_test $tn.2.1.3 { snapshot_free $snapshot execsql COMMIT } {} do_test $tn.2.2.0 { sqlite3 db2 test.db execsql { BEGIN; SELECT * FROM t1; } db2 } {1 2 3 4 5 6 7 8 9 10} do_test $tn.2.2.1 { set snapshot [snapshot_get db2 main] execsql { INSERT INTO t1 VALUES(11, 12); SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test $tn.2.2.2 { execsql BEGIN snapshot_open db main $snapshot execsql { SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10} do_test $tn.2.2.3 { snapshot_free $snapshot execsql COMMIT execsql COMMIT db2 db2 close } {} do_test $tn.2.3.1 { execsql { DELETE FROM t1 WHERE a>6 } db trans { set snapshot [snapshot_get db main] } execsql { INSERT INTO t1 VALUES('a', 'b'); INSERT INTO t1 VALUES('c', 'd'); SELECT * FROM t1; } } {1 2 3 4 5 6 a b c d} do_test $tn.2.3.2 { execsql BEGIN snapshot_open db main $snapshot execsql { SELECT * FROM t1 } } {1 2 3 4 5 6} do_test $tn.2.3.3 { catchsql { INSERT INTO t1 VALUES('x','y') } } {1 {database is locked}} do_test $tn.2.3.4 { execsql COMMIT snapshot_free $snapshot } {} #------------------------------------------------------------------------- # Check some errors in snapshot_open(). It is an error if: # # 1) the db is in auto-commit mode, # 2) the db has an open (read or write) transaction, # 3) the db is not a wal database, # # Reuse the database created by earlier tests. # do_execsql_test $tn.3.0.0 { CREATE TABLE t2(x, y); INSERT INTO t2 VALUES('a', 'b'); INSERT INTO t2 VALUES('c', 'd'); BEGIN; SELECT * FROM t2; } {a b c d} do_test $tn.3.0.1 { set snapshot [snapshot_get db main] execsql { COMMIT } execsql { INSERT INTO t2 VALUES('e', 'f'); } } {} do_test $tn.3.1 { list [catch {snapshot_open db main $snapshot } msg] $msg } {1 SQLITE_ERROR} do_test $tn.3.2.1 { execsql { BEGIN; SELECT * FROM t2; } } {a b c d e f} do_test $tn.3.2.2 { list [catch {snapshot_open db main $snapshot } msg] $msg } {1 SQLITE_ERROR} do_test $tn.3.2.3 { execsql { COMMIT; BEGIN; INSERT INTO t2 VALUES('g', 'h'); } list [catch {snapshot_open db main $snapshot } msg] $msg } {1 SQLITE_ERROR} do_execsql_test $tn.3.2.4 COMMIT do_test $tn.3.3.1 { execsql { PRAGMA journal_mode = DELETE } execsql { BEGIN } list [catch {snapshot_open db main $snapshot } msg] $msg } {1 SQLITE_ERROR} do_test $tn.$tn.3.3.2 { snapshot_free $snapshot execsql COMMIT } {} #------------------------------------------------------------------------- # Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot # no longer exists because the wal file has been checkpointed. # # 1. Reading a snapshot from the middle of a wal file is not possible # after the wal file has been checkpointed. # # 2. That a snapshot from the end of a wal file can not be read once # the wal file has been wrapped. # do_execsql_test $tn.4.1.0 { PRAGMA journal_mode = wal; CREATE TABLE t3(i, j); INSERT INTO t3 VALUES('o', 't'); INSERT INTO t3 VALUES('t', 'f'); BEGIN; SELECT * FROM t3; } {wal o t t f} do_test $tn.4.1.1 { set snapshot [snapshot_get db main] execsql COMMIT } {} do_test $tn.4.1.2 { execsql { INSERT INTO t3 VALUES('f', 's'); BEGIN; } snapshot_open db main $snapshot execsql { SELECT * FROM t3 } } {o t t f} do_test $tn.4.1.3 { execsql { COMMIT; PRAGMA wal_checkpoint; BEGIN; } list [catch {snapshot_open db main $snapshot} msg] $msg } {1 SQLITE_BUSY_SNAPSHOT} do_test $tn.4.1.4 { snapshot_free $snapshot execsql COMMIT } {} do_test $tn.4.2.1 { execsql { INSERT INTO t3 VALUES('s', 'e'); INSERT INTO t3 VALUES('n', 't'); BEGIN; SELECT * FROM t3; } } {o t t f f s s e n t} do_test $tn.4.2.2 { set snapshot [snapshot_get db main] execsql { COMMIT; PRAGMA wal_checkpoint; BEGIN; } snapshot_open db main $snapshot execsql { SELECT * FROM t3 } } {o t t f f s s e n t} do_test $tn.4.2.3 { execsql { COMMIT; INSERT INTO t3 VALUES('e', 't'); BEGIN; } list [catch {snapshot_open db main $snapshot} msg] $msg } {1 SQLITE_BUSY_SNAPSHOT} do_test $tn.4.2.4 { snapshot_free $snapshot } {} #------------------------------------------------------------------------- # Check that SQLITE_BUSY is returned if a checkpoint is running when # sqlite3_snapshot_open() is called. # reset_db db close testvfs tvfs sqlite3 db test.db -vfs tvfs do_execsql_test $tn.5.1 { PRAGMA journal_mode = wal; CREATE TABLE x1(x, xx, xxx); INSERT INTO x1 VALUES('z', 'zz', 'zzz'); BEGIN; SELECT * FROM x1; } {wal z zz zzz} do_test $tn.5.2 { set ::snapshot [snapshot_get db main] sqlite3 db2 test.db -vfs tvfs execsql { INSERT INTO x1 VALUES('a', 'aa', 'aaa'); COMMIT; } } {} set t53 0 proc write_callback {args} { do_test $tn.5.3.[incr ::t53] { execsql BEGIN list [catch { snapshot_open db main $::snapshot } msg] $msg } {1 SQLITE_BUSY} catchsql COMMIT } tvfs filter xWrite tvfs script write_callback db2 eval { PRAGMA wal_checkpoint } db close db2 close tvfs delete snapshot_free $snapshot #------------------------------------------------------------------------- # Test that sqlite3_snapshot_get() may be called immediately after # "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may # be called after opening the db handle and running the script # "PRAGMA user_version; BEGIN". reset_db do_execsql_test $tn.6.1 { PRAGMA journal_mode = wal; CREATE TABLE x1(x, xx, xxx); INSERT INTO x1 VALUES('z', 'zz', 'zzz'); BEGIN; PRAGMA user_version; } {wal 0} do_test $tn.6.2 { set ::snapshot [snapshot_get db main] execsql { INSERT INTO x1 VALUES('a', 'aa', 'aaa'); COMMIT; } } {} do_test $tn.6.3 { sqlite3 db2 test.db db2 eval "PRAGMA user_version ; BEGIN" snapshot_open db2 main $::snapshot db2 eval { SELECT * FROM x1 } } {z zz zzz} do_test $tn.6.4 { db2 close sqlite3 db2 test.db db2 eval "PRAGMA application_id" db2 eval "BEGIN" snapshot_open db2 main $::snapshot db2 eval { SELECT * FROM x1 } } {z zz zzz} do_test $tn.6.5 { db2 close sqlite3 db2 test.db db2 eval "BEGIN" list [catch {snapshot_open db2 main $::snapshot} msg] $msg } {1 SQLITE_ERROR} snapshot_free $snapshot #------------------------------------------------------------------------- # The following tests investigate the sqlite3_snapshot_cmp() API. # # Compare snapshots $p1 and $p2, checking that the result is $r. # proc do_snapshot_cmp_test {tn p1 p2 r} { uplevel [list do_test $tn.1 [list snapshot_cmp $p1 $p2] $r] uplevel [list do_test $tn.2 [list snapshot_cmp $p2 $p1] [expr $r*-1]] uplevel [list do_test $tn.3 [list snapshot_cmp $p1 $p1] 0] uplevel [list do_test $tn.4 [list snapshot_cmp $p2 $p2] 0] } catch { db2 close } reset_db do_execsql_test $tn.7.1 { PRAGMA journal_mode = wal; CREATE TABLE t1(x); } wal do_test $tn.7.1.2 { execsql { BEGIN ; PRAGMA application_id } set p1 [snapshot_get db main] execsql { INSERT INTO t1 VALUES(10); COMMIT; } execsql { BEGIN ; PRAGMA application_id } set p2 [snapshot_get db main] execsql COMMIT } {} do_snapshot_cmp_test $tn.7.1.3 $p1 $p2 -1 snapshot_free $p1 snapshot_free $p2 do_execsql_test $tn.7.2.1 { INSERT INTO t1 VALUES(11); INSERT INTO t1 VALUES(12); INSERT INTO t1 VALUES(13); BEGIN; PRAGMA application_id; } {0} do_test $tn.7.2.2 { set p1 [snapshot_get db main] execsql { COMMIT; INSERT INTO t1 VALUES(14); PRAGMA wal_checkpoint; BEGIN; PRAGMA application_id; } set p2 [snapshot_get db main] execsql COMMIT } {} do_snapshot_cmp_test $tn.7.2.3 $p1 $p2 -1 snapshot_free $p2 do_test $tn.7.3.1 { execsql { INSERT INTO t1 VALUES(14); BEGIN; PRAGMA application_id; } set p2 [snapshot_get db main] execsql COMMIT } {} do_snapshot_cmp_test $tn.7.3.2 $p1 $p2 -1 snapshot_free $p1 snapshot_free $p2 } finish_test |
Added test/snapshot2.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 | # 2016 November 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. # #*********************************************************************** # This file implements regression tests for SQLite library. The focus # of this file is the sqlite3_snapshot_xxx() APIs. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !snapshot {finish_test; return} set testprefix snapshot2 # This test does not work with the inmemory_journal permutation. The reason # 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 } #------------------------------------------------------------------------- # Check that it is not possible to obtain a snapshot immediately after # a wal mode database with an empty wal file is opened. But it is after # the file has been written, even by some other connection. # do_execsql_test 1.0 { PRAGMA journal_mode = wal; CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); } {wal} db close do_test 1.1.1 { list [file exists test.db] [file exists test.db-wal] } {1 0} sqlite3 db test.db do_execsql_test 1.1.2 { SELECT * FROM t1 } {1 2 3 4 5 6} do_test 1.1.3 { execsql BEGIN list [catch { sqlite3_snapshot_get_blob db main } msg] $msg } {1 SQLITE_ERROR} execsql COMMIT do_test 1.1.4 { execsql { INSERT INTO t1 VALUES(7, 8, 9) } execsql BEGIN string length [sqlite3_snapshot_get_blob db main] } 48 execsql COMMIT db close do_test 1.2.1 { list [file exists test.db] [file exists test.db-wal] } {1 0} sqlite3 db test.db do_execsql_test 1.2.2 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8 9} do_test 1.2.3 { execsql BEGIN list [catch { sqlite3_snapshot_get_blob db main } msg] $msg } {1 SQLITE_ERROR} execsql COMMIT do_test 1.2.4 { sqlite3 db2 test.db execsql { INSERT INTO t1 VALUES(10, 11, 12) } db2 execsql BEGIN string length [sqlite3_snapshot_get_blob db main] } 48 execsql COMMIT db2 close #------------------------------------------------------------------------- # Simple tests for sqlite3_snapshot_recover(). # reset_db do_execsql_test 2.0 { CREATE TABLE t1(x); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); } {wal} do_test 2.1 { db trans { set snap [sqlite3_snapshot_get_blob db main] } sqlite3_db_config db NO_CKPT_ON_CLOSE 1 db close sqlite3 db test.db execsql {SELECT * FROM sqlite_master} execsql BEGIN sqlite3_snapshot_open_blob db main $snap execsql COMMIT; execsql { INSERT INTO t1 VALUES(3); } } {} do_test 2.2 { sqlite3_db_config db NO_CKPT_ON_CLOSE 1 db close sqlite3 db test.db execsql {SELECT * FROM sqlite_master} execsql BEGIN list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg } {1 SQLITE_BUSY_SNAPSHOT} do_test 2.3 { execsql COMMIT sqlite3_snapshot_recover db main execsql BEGIN sqlite3_snapshot_open_blob db main $snap execsql { SELECT * FROM t1 } } {1 2} do_test 2.4 { execsql COMMIT execsql { SELECT * FROM t1 } } {1 2 3} do_test 2.5 { execsql { PRAGMA wal_checkpoint } sqlite3_db_config db NO_CKPT_ON_CLOSE 1 db close sqlite3 db test.db sqlite3_snapshot_recover db main execsql BEGIN list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg } {1 SQLITE_BUSY_SNAPSHOT} #------------------------------------------------------------------------- # Check that calling sqlite3_snapshot_recover() does not confuse the # pager cache. reset_db do_execsql_test 3.0 { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES('a', 'b'); INSERT INTO t1 VALUES('c', 'd'); } {wal} do_test 3.1 { sqlite3 db2 test.db execsql { INSERT INTO t1 VALUES('e', 'f') } db2 db2 close sqlite3_snapshot_recover db main } {} do_execsql_test 3.2 { SELECT * FROM t1; } {a b c d e f} #------------------------------------------------------------------------- # Check that sqlite3_snapshot_recover() returns an error if it is called # with an open read-transaction. Or on a database that does not exist. Or # on the temp database. Or on a db that is not in wal mode. # do_test 4.1 { sqlite3_snapshot_recover db main } {} do_test 4.2 { execsql { BEGIN; SELECT * FROM sqlite_master; } list [catch { sqlite3_snapshot_recover db main } msg] $msg } {1 SQLITE_ERROR} do_test 4.3 { execsql COMMIT sqlite3_snapshot_recover db main } {} do_test 4.4 { list [catch { sqlite3_snapshot_recover db aux } msg] $msg } {1 SQLITE_ERROR} do_test 4.5 { forcedelete test.db2 execsql { ATTACH 'test.db2' AS aux; PRAGMA aux.journal_mode = wal; CREATE TABLE aux.t2(x, y); } list [catch { sqlite3_snapshot_recover db aux } msg] $msg } {0 {}} do_test 4.6 { list [catch { sqlite3_snapshot_recover db temp } msg] $msg } {1 SQLITE_ERROR} do_test 4.7 { execsql { PRAGMA aux.journal_mode = delete; } list [catch { sqlite3_snapshot_recover db aux } msg] $msg } {1 SQLITE_ERROR} finish_test |
Changes to test/snapshot_fault.test.
︙ | ︙ | |||
154 155 156 157 158 159 160 161 162 163 164 | PRAGMA integrity_check; }] if {$res != "1 2 3 ok"} { error "res is $res" } } sqlite3_snapshot_free $::snapshot } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | PRAGMA integrity_check; }] if {$res != "1 2 3 ok"} { error "res is $res" } } sqlite3_snapshot_free $::snapshot } #------------------------------------------------------------------------- # Test the handling of faults that occur within sqlite3_snapshot_recover(). # reset_db do_execsql_test 4.0 { PRAGMA journal_mode = wal; CREATE TABLE t1(zzz); INSERT INTO t1 VALUES('abc'); INSERT INTO t1 VALUES('def'); } {wal} faultsim_save_and_close do_test 4.0.1 { faultsim_restore_and_reopen db eval { SELECT * FROM sqlite_master } sqlite3_snapshot_recover db main } {} db close do_faultsim_test 4.0 -faults oom* -prep { faultsim_restore_and_reopen db eval { SELECT * FROM sqlite_master } } -body { sqlite3_snapshot_recover db main } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM} } # The following test cases contrive to call sqlite3_snapshot_recover() # before all pages of the *-shm file have been mapped. This tests an # extra branch of error handling logic in snapshot_recover(). # reset_db do_execsql_test 4.1.0 { PRAGMA page_size = 512; PRAGMA journal_mode = wal; PRAGMA wal_autocheckpoint = 0; CREATE TABLE t1(zzz); INSERT INTO t1 VALUES(randomblob( 500 * 9500 )); PRAGMA user_version = 211; } {wal 0} do_test 4.1.1 { list [file size test.db-shm] [file size test.db] } {98304 512} faultsim_save_and_close do_faultsim_test 4.1 -faults shm* -prep { catch { db2 close } catch { db close } faultsim_restore_and_reopen sqlite3 db2 test.db db2 eval { SELECT * FROM sqlite_master } db eval BEGIN sqlite3_snapshot_get_blob db main db eval COMMIT } -body { sqlite3_snapshot_recover db main } -test { faultsim_test_result {0 {}} {1 SQLITE_IOERR} } finish_test |
Changes to test/speedtest1.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | " --exclusive Enable locking_mode=EXCLUSIVE\n" " --explain Like --sqlonly but with added EXPLAIN keywords\n" " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n" " --incrvacuum Enable incremenatal vacuum mode\n" " --journal M Set the journal_mode to M\n" " --key KEY Set the encryption key to KEY\n" " --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n" " --multithread Set multithreaded mode\n" " --nomemstat Disable memory statistics\n" " --nosync Set PRAGMA synchronous=OFF\n" " --notnull Add NOT NULL constraints to table columns\n" " --pagesize N Set the page size to N\n" " --pcache N SZ Configure N pages of pagecache each of size SZ bytes\n" " --primarykey Use PRIMARY KEY instead of UNIQUE where appropriate\n" " --repeat N Repeat each SELECT N times (default: 1)\n" " --reprepare Reprepare each statement upon every invocation\n" | > < | > > > > > < < < | 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 | " --exclusive Enable locking_mode=EXCLUSIVE\n" " --explain Like --sqlonly but with added EXPLAIN keywords\n" " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n" " --incrvacuum Enable incremenatal vacuum mode\n" " --journal M Set the journal_mode to M\n" " --key KEY Set the encryption key to KEY\n" " --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n" " --mmap SZ MMAP the first SZ bytes of the database file\n" " --multithread Set multithreaded mode\n" " --nomemstat Disable memory statistics\n" " --nosync Set PRAGMA synchronous=OFF\n" " --notnull Add NOT NULL constraints to table columns\n" " --pagesize N Set the page size to N\n" " --pcache N SZ Configure N pages of pagecache each of size SZ bytes\n" " --primarykey Use PRIMARY KEY instead of UNIQUE where appropriate\n" " --repeat N Repeat each SELECT N times (default: 1)\n" " --reprepare Reprepare each statement upon every invocation\n" " --serialized Set serialized threading mode\n" " --singlethread Set single-threaded mode - disables all mutexing\n" " --sqlonly No-op. Only show the SQL that would have been run.\n" " --shrink-memory Invoke sqlite3_db_release_memory() frequently.\n" " --size N Relative test size. Default=100\n" " --stats Show statistics at the end\n" " --temp N N from 0 to 9. 0: no temp table. 9: all temp tables\n" " --testset T Run test-set T (main, cte, rtree, orm, debug)\n" " --trace Turn on SQL tracing\n" " --threads N Use up to N threads for sorting\n" " --utf16be Set text encoding to UTF-16BE\n" " --utf16le Set text encoding to UTF-16LE\n" " --verify Run additional verification steps.\n" " --without-rowid Use WITHOUT ROWID where appropriate\n" ; #include "sqlite3.h" #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <ctype.h> #ifndef _WIN32 # include <unistd.h> #else # include <io.h> #endif #define ISSPACE(X) isspace((unsigned char)(X)) #define ISDIGIT(X) isdigit((unsigned char)(X)) #if SQLITE_VERSION_NUMBER<3005000 # define sqlite3_int64 sqlite_int64 #endif /* All global state is held in this structure */ static struct Global { sqlite3 *db; /* The open database connection */ sqlite3_stmt *pStmt; /* Current SQL statement */ sqlite3_int64 iStart; /* Start-time for the current test */ sqlite3_int64 iTotal; /* Total time */ |
︙ | ︙ | |||
451 452 453 454 455 456 457 458 459 460 461 462 463 464 | y1 = (y0 + x/y0)/2; if( y1==y0 ) break; y0 = y1; } return y0; } /* ** The main and default testset */ void testset_main(void){ int i; /* Loop counter */ int n; /* iteration count */ int sz; /* Size of the tables */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | y1 = (y0 + x/y0)/2; if( y1==y0 ) break; y0 = y1; } return y0; } #if SQLITE_VERSION_NUMBER<3005004 /* ** An implementation of group_concat(). Used only when testing older ** versions of SQLite that lack the built-in group_concat(). */ struct groupConcat { char *z; int nAlloc; int nUsed; }; static void groupAppend(struct groupConcat *p, const char *z, int n){ if( p->nUsed+n >= p->nAlloc ){ int n2 = (p->nAlloc+n+1)*2; char *z2 = sqlite3_realloc(p->z, n2); if( z2==0 ) return; p->z = z2; p->nAlloc = n2; } memcpy(p->z+p->nUsed, z, n); p->nUsed += n; } static void groupStep( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zVal; struct groupConcat *p; const char *zSep; int nVal, nSep; assert( argc==1 || argc==2 ); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; p= (struct groupConcat*)sqlite3_aggregate_context(context, sizeof(*p)); if( p ){ int firstTerm = p->nUsed==0; if( !firstTerm ){ if( argc==2 ){ zSep = (char*)sqlite3_value_text(argv[1]); nSep = sqlite3_value_bytes(argv[1]); }else{ zSep = ","; nSep = 1; } if( nSep ) groupAppend(p, zSep, nSep); } zVal = (char*)sqlite3_value_text(argv[0]); nVal = sqlite3_value_bytes(argv[0]); if( zVal ) groupAppend(p, zVal, nVal); } } static void groupFinal(sqlite3_context *context){ struct groupConcat *p; p = sqlite3_aggregate_context(context, 0); if( p && p->z ){ p->z[p->nUsed] = 0; sqlite3_result_text(context, p->z, p->nUsed, sqlite3_free); } } #endif /* ** The main and default testset */ void testset_main(void){ int i; /* Loop counter */ int n; /* iteration count */ int sz; /* Size of the tables */ |
︙ | ︙ | |||
520 521 522 523 524 525 526 527 528 529 530 531 | sqlite3_bind_int64(g.pStmt, 1, (sqlite3_int64)x1); sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); n = 25; speedtest1_begin_test(130, "%d SELECTS, numeric BETWEEN, unindexed", n); speedtest1_exec("BEGIN"); speedtest1_prepare( | > > > > | | | | 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 | sqlite3_bind_int64(g.pStmt, 1, (sqlite3_int64)x1); sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); #if SQLITE_VERSION_NUMBER<3005004 sqlite3_create_function(g.db, "group_concat", 1, SQLITE_UTF8, 0, 0, groupStep, groupFinal); #endif n = 25; speedtest1_begin_test(130, "%d SELECTS, numeric BETWEEN, unindexed", n); speedtest1_exec("BEGIN"); speedtest1_prepare( "SELECT count(*), avg(b), sum(length(c)), group_concat(c) FROM t1\n" " WHERE b BETWEEN ?1 AND ?2; -- %d times", n ); for(i=1; i<=n; i++){ if( (i-1)%g.nRepeat==0 ){ x1 = speedtest1_random()%maxb; x2 = speedtest1_random()%10 + sz/5000 + x1; } sqlite3_bind_int(g.pStmt, 1, x1); sqlite3_bind_int(g.pStmt, 2, x2); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); n = 10; speedtest1_begin_test(140, "%d SELECTS, LIKE, unindexed", n); speedtest1_exec("BEGIN"); speedtest1_prepare( "SELECT count(*), avg(b), sum(length(c)), group_concat(c) FROM t1\n" " WHERE c LIKE ?1; -- %d times", n ); for(i=1; i<=n; i++){ if( (i-1)%g.nRepeat==0 ){ x1 = speedtest1_random()%maxb; zNum[0] = '%'; len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2); zNum[len] = '%'; zNum[len+1] = 0; } sqlite3_bind_text(g.pStmt, 1, zNum, len+1, SQLITE_STATIC); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); n = 10; |
︙ | ︙ | |||
578 579 580 581 582 583 584 | if( (i-1)%g.nRepeat==0 ){ x1 = speedtest1_random()%maxb; zNum[0] = '%'; len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2); zNum[len] = '%'; zNum[len+1] = 0; } | | | | 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 | if( (i-1)%g.nRepeat==0 ){ x1 = speedtest1_random()%maxb; zNum[0] = '%'; len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2); zNum[len] = '%'; zNum[len+1] = 0; } sqlite3_bind_text(g.pStmt, 1, zNum, len+1, SQLITE_STATIC); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); n = 10; /* g.szTest/5; */ speedtest1_begin_test(145, "%d SELECTS w/ORDER BY and LIMIT, unindexed", n); speedtest1_exec("BEGIN"); speedtest1_prepare( "SELECT a, b, c FROM t1 WHERE c LIKE ?1\n" " ORDER BY a LIMIT 10; -- %d times", n ); for(i=1; i<=n; i++){ if( (i-1)%g.nRepeat==0 ){ x1 = speedtest1_random()%maxb; zNum[0] = '%'; len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2); zNum[len] = '%'; zNum[len+1] = 0; } sqlite3_bind_text(g.pStmt, 1, zNum, len+1, SQLITE_STATIC); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); speedtest1_begin_test(150, "CREATE INDEX five times"); |
︙ | ︙ | |||
621 622 623 624 625 626 627 | speedtest1_end_test(); n = sz/5; speedtest1_begin_test(160, "%d SELECTS, numeric BETWEEN, indexed", n); speedtest1_exec("BEGIN"); speedtest1_prepare( | | | | | 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 | speedtest1_end_test(); n = sz/5; speedtest1_begin_test(160, "%d SELECTS, numeric BETWEEN, indexed", n); speedtest1_exec("BEGIN"); speedtest1_prepare( "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM t1\n" " WHERE b BETWEEN ?1 AND ?2; -- %d times", n ); for(i=1; i<=n; i++){ if( (i-1)%g.nRepeat==0 ){ x1 = speedtest1_random()%maxb; x2 = speedtest1_random()%10 + sz/5000 + x1; } sqlite3_bind_int(g.pStmt, 1, x1); sqlite3_bind_int(g.pStmt, 2, x2); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); n = sz/5; speedtest1_begin_test(161, "%d SELECTS, numeric BETWEEN, PK", n); speedtest1_exec("BEGIN"); speedtest1_prepare( "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM t2\n" " WHERE a BETWEEN ?1 AND ?2; -- %d times", n ); for(i=1; i<=n; i++){ if( (i-1)%g.nRepeat==0 ){ x1 = speedtest1_random()%maxb; x2 = speedtest1_random()%10 + sz/5000 + x1; } sqlite3_bind_int(g.pStmt, 1, x1); sqlite3_bind_int(g.pStmt, 2, x2); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); n = sz/5; speedtest1_begin_test(170, "%d SELECTS, text BETWEEN, indexed", n); speedtest1_exec("BEGIN"); speedtest1_prepare( "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM t1\n" " WHERE c BETWEEN ?1 AND (?1||'~'); -- %d times", n ); for(i=1; i<=n; i++){ if( (i-1)%g.nRepeat==0 ){ x1 = swizzle(i, maxb); len = speedtest1_numbername(x1, zNum, sizeof(zNum)-1); } |
︙ | ︙ | |||
826 827 828 829 830 831 832 833 834 835 836 837 838 839 | "SELECT sum(a), max(c),\n" " avg((SELECT a FROM t2 WHERE 5+t2.b=t1.b) AND rowid<?1), max(c)\n" " FROM t1 WHERE rowid<?1;" ); sqlite3_bind_int(g.pStmt, 1, est_square_root(g.szTest)*50); speedtest1_run(); speedtest1_end_test(); speedtest1_begin_test(980, "PRAGMA integrity_check"); speedtest1_exec("PRAGMA integrity_check"); speedtest1_end_test(); speedtest1_begin_test(990, "ANALYZE"); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | "SELECT sum(a), max(c),\n" " avg((SELECT a FROM t2 WHERE 5+t2.b=t1.b) AND rowid<?1), max(c)\n" " FROM t1 WHERE rowid<?1;" ); sqlite3_bind_int(g.pStmt, 1, est_square_root(g.szTest)*50); speedtest1_run(); speedtest1_end_test(); sz = n = g.szTest*700; zNum[0] = 0; maxb = roundup_allones(sz/3); speedtest1_begin_test(400, "%d REPLACE ops on an IPK", n); speedtest1_exec("BEGIN"); speedtest1_exec("CREATE%s TABLE t5(a INTEGER PRIMARY KEY, b %s);", isTemp(9), g.zNN); speedtest1_prepare("REPLACE INTO t5 VALUES(?1,?2); -- %d times",n); for(i=1; i<=n; i++){ x1 = swizzle(i,maxb); speedtest1_numbername(i, zNum, sizeof(zNum)); sqlite3_bind_int(g.pStmt, 1, (sqlite3_int64)x1); sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); speedtest1_begin_test(410, "%d SELECTS on an IPK", n); speedtest1_prepare("SELECT b FROM t5 WHERE a=?1; -- %d times",n); for(i=1; i<=n; i++){ x1 = swizzle(i,maxb); sqlite3_bind_int(g.pStmt, 1, (sqlite3_int64)x1); speedtest1_run(); } speedtest1_end_test(); sz = n = g.szTest*700; zNum[0] = 0; maxb = roundup_allones(sz/3); speedtest1_begin_test(500, "%d REPLACE on TEXT PK", n); speedtest1_exec("BEGIN"); speedtest1_exec("CREATE%s TABLE t6(a TEXT PRIMARY KEY, b %s)%s;", isTemp(9), g.zNN, sqlite3_libversion_number()>=3008002 ? "WITHOUT ROWID" : ""); speedtest1_prepare("REPLACE INTO t6 VALUES(?1,?2); -- %d times",n); for(i=1; i<=n; i++){ x1 = swizzle(i,maxb); speedtest1_numbername(x1, zNum, sizeof(zNum)); sqlite3_bind_int(g.pStmt, 2, i); sqlite3_bind_text(g.pStmt, 1, zNum, -1, SQLITE_STATIC); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); speedtest1_begin_test(510, "%d SELECTS on a TEXT PK", n); speedtest1_prepare("SELECT b FROM t6 WHERE a=?1; -- %d times",n); for(i=1; i<=n; i++){ x1 = swizzle(i,maxb); speedtest1_numbername(x1, zNum, sizeof(zNum)); sqlite3_bind_text(g.pStmt, 1, zNum, -1, SQLITE_STATIC); speedtest1_run(); } speedtest1_end_test(); speedtest1_begin_test(520, "%d SELECT DISTINCT", n); speedtest1_exec("SELECT DISTINCT b FROM t5;"); speedtest1_exec("SELECT DISTINCT b FROM t6;"); speedtest1_end_test(); speedtest1_begin_test(980, "PRAGMA integrity_check"); speedtest1_exec("PRAGMA integrity_check"); speedtest1_end_test(); speedtest1_begin_test(990, "ANALYZE"); |
︙ | ︙ | |||
1046 1047 1048 1049 1050 1051 1052 | ** A testset for the R-Tree virtual table */ void testset_rtree(int p1, int p2){ unsigned i, n; unsigned mxCoord; unsigned x0, x1, y0, y1, z0, z1; unsigned iStep; | | | | 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 | ** A testset for the R-Tree virtual table */ void testset_rtree(int p1, int p2){ unsigned i, n; unsigned mxCoord; unsigned x0, x1, y0, y1, z0, z1; unsigned iStep; int *aCheck = sqlite3_malloc( sizeof(int)*g.szTest*500 ); mxCoord = 15000; n = g.szTest*500; speedtest1_begin_test(100, "%d INSERTs into an r-tree", n); speedtest1_exec("BEGIN"); speedtest1_exec("CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1,z0,z1)"); speedtest1_prepare("INSERT INTO rt1(id,x0,x1,y0,y1,z0,z1)" "VALUES(?1,?2,?3,?4,?5,?6,?7)"); for(i=1; i<=n; i++){ twoCoords(p1, p2, mxCoord, &x0, &x1); |
︙ | ︙ | |||
1072 1073 1074 1075 1076 1077 1078 | sqlite3_bind_int(g.pStmt, 7, z1); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); speedtest1_begin_test(101, "Copy from rtree to a regular table"); | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | sqlite3_bind_int(g.pStmt, 7, z1); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); speedtest1_begin_test(101, "Copy from rtree to a regular table"); speedtest1_exec("CREATE TABLE t1(id INTEGER PRIMARY KEY,x0,x1,y0,y1,z0,z1)"); speedtest1_exec("INSERT INTO t1 SELECT * FROM rt1"); speedtest1_end_test(); n = g.szTest*100; speedtest1_begin_test(110, "%d one-dimensional intersect slice queries", n); speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x0>=?1 AND x1<=?2"); iStep = mxCoord/n; for(i=0; i<n; i++){ sqlite3_bind_int(g.pStmt, 1, i*iStep); sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep); speedtest1_run(); aCheck[i] = atoi(g.zResult); } speedtest1_end_test(); if( g.bVerify ){ n = g.szTest*100; speedtest1_begin_test(111, "Verify result from 1-D intersect slice queries"); speedtest1_prepare("SELECT count(*) FROM t1 WHERE x0>=?1 AND x1<=?2"); iStep = mxCoord/n; for(i=0; i<n; i++){ sqlite3_bind_int(g.pStmt, 1, i*iStep); sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep); speedtest1_run(); if( aCheck[i]!=atoi(g.zResult) ){ fatal_error("Count disagree step %d: %d..%d. %d vs %d", i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult)); } } speedtest1_end_test(); } n = g.szTest*100; speedtest1_begin_test(120, "%d one-dimensional overlap slice queries", n); speedtest1_prepare("SELECT count(*) FROM rt1 WHERE y1>=?1 AND y0<=?2"); iStep = mxCoord/n; for(i=0; i<n; i++){ sqlite3_bind_int(g.pStmt, 1, i*iStep); sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep); speedtest1_run(); aCheck[i] = atoi(g.zResult); } speedtest1_end_test(); if( g.bVerify ){ n = g.szTest*100; speedtest1_begin_test(121, "Verify result from 1-D overlap slice queries"); speedtest1_prepare("SELECT count(*) FROM t1 WHERE y1>=?1 AND y0<=?2"); iStep = mxCoord/n; for(i=0; i<n; i++){ sqlite3_bind_int(g.pStmt, 1, i*iStep); sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep); speedtest1_run(); if( aCheck[i]!=atoi(g.zResult) ){ fatal_error("Count disagree step %d: %d..%d. %d vs %d", i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult)); } } speedtest1_end_test(); } n = g.szTest*100; speedtest1_begin_test(125, "%d custom geometry callback queries", n); sqlite3_rtree_geometry_callback(g.db, "xslice", xsliceGeometryCallback, 0); speedtest1_prepare("SELECT count(*) FROM rt1 WHERE id MATCH xslice(?1,?2)"); iStep = mxCoord/n; for(i=0; i<n; i++){ sqlite3_bind_int(g.pStmt, 1, i*iStep); sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep); speedtest1_run(); if( aCheck[i]!=atoi(g.zResult) ){ fatal_error("Count disagree step %d: %d..%d. %d vs %d", i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult)); } } speedtest1_end_test(); n = g.szTest*400; speedtest1_begin_test(130, "%d three-dimensional intersect box queries", n); speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x1>=?1 AND x0<=?2" " AND y1>=?1 AND y0<=?2 AND z1>=?1 AND z0<=?2"); iStep = mxCoord/n; for(i=0; i<n; i++){ sqlite3_bind_int(g.pStmt, 1, i*iStep); sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep); speedtest1_run(); aCheck[i] = atoi(g.zResult); } speedtest1_end_test(); n = g.szTest*500; speedtest1_begin_test(140, "%d rowid queries", n); speedtest1_prepare("SELECT * FROM rt1 WHERE id=?1"); for(i=1; i<=n; i++){ sqlite3_bind_int(g.pStmt, 1, i); speedtest1_run(); } speedtest1_end_test(); } #endif /* SQLITE_ENABLE_RTREE */ /* ** A testset that does key/value storage on tables with many columns. ** This is the kind of workload generated by ORMs such as CoreData. */ void testset_orm(void){ unsigned i, j, n; unsigned nRow; unsigned x1, len; char zNum[2000]; /* A number name */ static const char zType[] = /* Types for all non-PK columns, in order */ "IBBIIITIVVITBTBFBFITTFBTBVBVIFTBBFITFFVBIFIVBVVVBTVTIBBFFIVIBTB" "TVTTFTVTVFFIITIFBITFTTFFFVBIIBTTITFTFFVVVFIIITVBBVFFTVVB"; nRow = n = g.szTest*250; speedtest1_begin_test(100, "Fill %d rows", n); speedtest1_exec( "BEGIN;" "CREATE TABLE ZLOOKSLIKECOREDATA (" " ZPK INTEGER PRIMARY KEY," " ZTERMFITTINGHOUSINGCOMMAND INTEGER," " ZBRIEFGOBYDODGERHEIGHT BLOB," " ZCAPABLETRIPDOORALMOND BLOB," " ZDEPOSITPAIRCOLLEGECOMET INTEGER," " ZFRAMEENTERSIMPLEMOUTH INTEGER," " ZHOPEFULGATEHOLECHALK INTEGER," " ZSLEEPYUSERGRANDBOWL TIMESTAMP," " ZDEWPEACHCAREERCELERY INTEGER," " ZHANGERLITHIUMDINNERMEET VARCHAR," " ZCLUBRELEASELIZARDADVICE VARCHAR," " ZCHARGECLICKHUMANEHIRE INTEGER," " ZFINGERDUEPIZZAOPTION TIMESTAMP," " ZFLYINGDOCTORTABLEMELODY BLOB," " ZLONGFINLEAVEIMAGEOIL TIMESTAMP," " ZFAMILYVISUALOWNERMATTER BLOB," " ZGOLDYOUNGINITIALNOSE FLOAT," " ZCAUSESALAMITERMCYAN BLOB," " ZSPREADMOTORBISCUITBACON FLOAT," " ZGIFTICEFISHGLUEHAIR INTEGER," " ZNOTICEPEARPOLICYJUICE TIMESTAMP," " ZBANKBUFFALORECOVERORBIT TIMESTAMP," " ZLONGDIETESSAYNATURE FLOAT," " ZACTIONRANGEELEGANTNEUTRON BLOB," " ZCADETBRIGHTPLANETBANK TIMESTAMP," " ZAIRFORGIVEHEADFROG BLOB," " ZSHARKJUSTFRUITMOVIE VARCHAR," " ZFARMERMORNINGMIRRORCONCERN BLOB," " ZWOODPOETRYCOBBLERBENCH VARCHAR," " ZHAFNIUMSCRIPTSALADMOTOR INTEGER," " ZPROBLEMCLUBPOPOVERJELLY FLOAT," " ZEIGHTLEADERWORKERMOST TIMESTAMP," " ZGLASSRESERVEBARIUMMEAL BLOB," " ZCLAMBITARUGULAFAJITA BLOB," " ZDECADEJOYOUSWAVEHABIT FLOAT," " ZCOMPANYSUMMERFIBERELF INTEGER," " ZTREATTESTQUILLCHARGE TIMESTAMP," " ZBROWBALANCEKEYCHOWDER FLOAT," " ZPEACHCOPPERDINNERLAKE FLOAT," " ZDRYWALLBEYONDBROWNBOWL VARCHAR," " ZBELLYCRASHITEMLACK BLOB," " ZTENNISCYCLEBILLOFFICER INTEGER," " ZMALLEQUIPTHANKSGLUE FLOAT," " ZMISSREPLYHUMANLIVING INTEGER," " ZKIWIVISUALPRIDEAPPLE VARCHAR," " ZWISHHITSKINMOTOR BLOB," " ZCALMRACCOONPROGRAMDEBIT VARCHAR," " ZSHINYASSISTLIVINGCRAB VARCHAR," " ZRESOLVEWRISTWRAPAPPLE VARCHAR," " ZAPPEALSIMPLESECONDHOUSING BLOB," " ZCORNERANCHORTAPEDIVER TIMESTAMP," " ZMEMORYREQUESTSOURCEBIG VARCHAR," " ZTRYFACTKEEPMILK TIMESTAMP," " ZDIVERPAINTLEATHEREASY INTEGER," " ZSORTMISTYQUOTECABBAGE BLOB," " ZTUNEGASBUFFALOCAPITAL BLOB," " ZFILLSTOPLAWJOYFUL FLOAT," " ZSTEELCAREFULPLATENUMBER FLOAT," " ZGIVEVIVIDDIVINEMEANING INTEGER," " ZTREATPACKFUTURECONVERT VARCHAR," " ZCALMLYGEMFINISHEFFECT INTEGER," " ZCABBAGESOCKEASEMINUTE BLOB," " ZPLANETFAMILYPUREMEMORY TIMESTAMP," " ZMERRYCRACKTRAINLEADER BLOB," " ZMINORWAYPAPERCLASSY TIMESTAMP," " ZEAGLELINEMINEMAIL VARCHAR," " ZRESORTYARDGREENLET TIMESTAMP," " ZYARDOREGANOVIVIDJEWEL TIMESTAMP," " ZPURECAKEVIVIDNEATLY FLOAT," " ZASKCONTACTMONITORFUN TIMESTAMP," " ZMOVEWHOGAMMAINCH VARCHAR," " ZLETTUCEBIRDMEETDEBATE TIMESTAMP," " ZGENENATURALHEARINGKITE VARCHAR," " ZMUFFINDRYERDRAWFORTUNE FLOAT," " ZGRAYSURVEYWIRELOVE FLOAT," " ZPLIERSPRINTASKOREGANO INTEGER," " ZTRAVELDRIVERCONTESTLILY INTEGER," " ZHUMORSPICESANDKIDNEY TIMESTAMP," " ZARSENICSAMPLEWAITMUON INTEGER," " ZLACEADDRESSGROUNDCAREFUL FLOAT," " ZBAMBOOMESSWASABIEVENING BLOB," " ZONERELEASEAVERAGENURSE INTEGER," " ZRADIANTWHENTRYCARD TIMESTAMP," " ZREWARDINSIDEMANGOINTENSE FLOAT," " ZNEATSTEWPARTIRON TIMESTAMP," " ZOUTSIDEPEAHENCOUNTICE TIMESTAMP," " ZCREAMEVENINGLIPBRANCH FLOAT," " ZWHALEMATHAVOCADOCOPPER FLOAT," " ZLIFEUSELEAFYBELL FLOAT," " ZWEALTHLINENGLEEFULDAY VARCHAR," " ZFACEINVITETALKGOLD BLOB," " ZWESTAMOUNTAFFECTHEARING INTEGER," " ZDELAYOUTCOMEHORNAGENCY INTEGER," " ZBIGTHINKCONVERTECONOMY BLOB," " ZBASEGOUDAREGULARFORGIVE TIMESTAMP," " ZPATTERNCLORINEGRANDCOLBY TIMESTAMP," " ZCYANBASEFEEDADROIT INTEGER," " ZCARRYFLOORMINNOWDRAGON TIMESTAMP," " ZIMAGEPENCILOTHERBOTTOM FLOAT," " ZXENONFLIGHTPALEAPPLE TIMESTAMP," " ZHERRINGJOKEFEATUREHOPEFUL FLOAT," " ZCAPYEARLYRIVETBRUSH FLOAT," " ZAGEREEDFROGBASKET VARCHAR," " ZUSUALBODYHALIBUTDIAMOND VARCHAR," " ZFOOTTAPWORDENTRY VARCHAR," " ZDISHKEEPBLESTMONITOR FLOAT," " ZBROADABLESOLIDCASUAL INTEGER," " ZSQUAREGLEEFULCHILDLIGHT INTEGER," " ZHOLIDAYHEADPONYDETAIL INTEGER," " ZGENERALRESORTSKYOPEN TIMESTAMP," " ZGLADSPRAYKIDNEYGUPPY VARCHAR," " ZSWIMHEAVYMENTIONKIND BLOB," " ZMESSYSULFURDREAMFESTIVE BLOB," " ZSKYSKYCLASSICBRIEF VARCHAR," " ZDILLASKHOKILEMON FLOAT," " ZJUNIORSHOWPRESSNOVA FLOAT," " ZSIZETOEAWARDFRESH TIMESTAMP," " ZKEYFAILAPRICOTMETAL VARCHAR," " ZHANDYREPAIRPROTONAIRPORT VARCHAR," " ZPOSTPROTEINHANDLEACTOR BLOB" ");" ); speedtest1_prepare( "INSERT INTO ZLOOKSLIKECOREDATA(ZPK,ZAIRFORGIVEHEADFROG," "ZGIFTICEFISHGLUEHAIR,ZDELAYOUTCOMEHORNAGENCY,ZSLEEPYUSERGRANDBOWL," "ZGLASSRESERVEBARIUMMEAL,ZBRIEFGOBYDODGERHEIGHT," "ZBAMBOOMESSWASABIEVENING,ZFARMERMORNINGMIRRORCONCERN," "ZTREATPACKFUTURECONVERT,ZCAUSESALAMITERMCYAN,ZCALMRACCOONPROGRAMDEBIT," "ZHOLIDAYHEADPONYDETAIL,ZWOODPOETRYCOBBLERBENCH,ZHAFNIUMSCRIPTSALADMOTOR," "ZUSUALBODYHALIBUTDIAMOND,ZOUTSIDEPEAHENCOUNTICE,ZDIVERPAINTLEATHEREASY," "ZWESTAMOUNTAFFECTHEARING,ZSIZETOEAWARDFRESH,ZDEWPEACHCAREERCELERY," "ZSTEELCAREFULPLATENUMBER,ZCYANBASEFEEDADROIT,ZCALMLYGEMFINISHEFFECT," "ZHANDYREPAIRPROTONAIRPORT,ZGENENATURALHEARINGKITE,ZBROADABLESOLIDCASUAL," "ZPOSTPROTEINHANDLEACTOR,ZLACEADDRESSGROUNDCAREFUL,ZIMAGEPENCILOTHERBOTTOM," "ZPROBLEMCLUBPOPOVERJELLY,ZPATTERNCLORINEGRANDCOLBY,ZNEATSTEWPARTIRON," "ZAPPEALSIMPLESECONDHOUSING,ZMOVEWHOGAMMAINCH,ZTENNISCYCLEBILLOFFICER," "ZSHARKJUSTFRUITMOVIE,ZKEYFAILAPRICOTMETAL,ZCOMPANYSUMMERFIBERELF," "ZTERMFITTINGHOUSINGCOMMAND,ZRESORTYARDGREENLET,ZCABBAGESOCKEASEMINUTE," "ZSQUAREGLEEFULCHILDLIGHT,ZONERELEASEAVERAGENURSE,ZBIGTHINKCONVERTECONOMY," "ZPLIERSPRINTASKOREGANO,ZDECADEJOYOUSWAVEHABIT,ZDRYWALLBEYONDBROWNBOWL," "ZCLUBRELEASELIZARDADVICE,ZWHALEMATHAVOCADOCOPPER,ZBELLYCRASHITEMLACK," "ZLETTUCEBIRDMEETDEBATE,ZCAPABLETRIPDOORALMOND,ZRADIANTWHENTRYCARD," "ZCAPYEARLYRIVETBRUSH,ZAGEREEDFROGBASKET,ZSWIMHEAVYMENTIONKIND," "ZTRAVELDRIVERCONTESTLILY,ZGLADSPRAYKIDNEYGUPPY,ZBANKBUFFALORECOVERORBIT," "ZFINGERDUEPIZZAOPTION,ZCLAMBITARUGULAFAJITA,ZLONGFINLEAVEIMAGEOIL," "ZLONGDIETESSAYNATURE,ZJUNIORSHOWPRESSNOVA,ZHOPEFULGATEHOLECHALK," "ZDEPOSITPAIRCOLLEGECOMET,ZWEALTHLINENGLEEFULDAY,ZFILLSTOPLAWJOYFUL," "ZTUNEGASBUFFALOCAPITAL,ZGRAYSURVEYWIRELOVE,ZCORNERANCHORTAPEDIVER," "ZREWARDINSIDEMANGOINTENSE,ZCADETBRIGHTPLANETBANK,ZPLANETFAMILYPUREMEMORY," "ZTREATTESTQUILLCHARGE,ZCREAMEVENINGLIPBRANCH,ZSKYSKYCLASSICBRIEF," "ZARSENICSAMPLEWAITMUON,ZBROWBALANCEKEYCHOWDER,ZFLYINGDOCTORTABLEMELODY," "ZHANGERLITHIUMDINNERMEET,ZNOTICEPEARPOLICYJUICE,ZSHINYASSISTLIVINGCRAB," "ZLIFEUSELEAFYBELL,ZFACEINVITETALKGOLD,ZGENERALRESORTSKYOPEN," "ZPURECAKEVIVIDNEATLY,ZKIWIVISUALPRIDEAPPLE,ZMESSYSULFURDREAMFESTIVE," "ZCHARGECLICKHUMANEHIRE,ZHERRINGJOKEFEATUREHOPEFUL,ZYARDOREGANOVIVIDJEWEL," "ZFOOTTAPWORDENTRY,ZWISHHITSKINMOTOR,ZBASEGOUDAREGULARFORGIVE," "ZMUFFINDRYERDRAWFORTUNE,ZACTIONRANGEELEGANTNEUTRON,ZTRYFACTKEEPMILK," "ZPEACHCOPPERDINNERLAKE,ZFRAMEENTERSIMPLEMOUTH,ZMERRYCRACKTRAINLEADER," "ZMEMORYREQUESTSOURCEBIG,ZCARRYFLOORMINNOWDRAGON,ZMINORWAYPAPERCLASSY," "ZDILLASKHOKILEMON,ZRESOLVEWRISTWRAPAPPLE,ZASKCONTACTMONITORFUN," "ZGIVEVIVIDDIVINEMEANING,ZEIGHTLEADERWORKERMOST,ZMISSREPLYHUMANLIVING," "ZXENONFLIGHTPALEAPPLE,ZSORTMISTYQUOTECABBAGE,ZEAGLELINEMINEMAIL," "ZFAMILYVISUALOWNERMATTER,ZSPREADMOTORBISCUITBACON,ZDISHKEEPBLESTMONITOR," "ZMALLEQUIPTHANKSGLUE,ZGOLDYOUNGINITIALNOSE,ZHUMORSPICESANDKIDNEY)" "VALUES(?1,?26,?20,?93,?8,?33,?3,?81,?28,?60,?18,?47,?109,?29,?30,?104,?86," "?54,?92,?117,?9,?58,?97,?61,?119,?73,?107,?120,?80,?99,?31,?96,?85,?50,?71," "?42,?27,?118,?36,?2,?67,?62,?108,?82,?94,?76,?35,?40,?11,?88,?41,?72,?4," "?83,?102,?103,?112,?77,?111,?22,?13,?34,?15,?23,?116,?7,?5,?90,?57,?56," "?75,?51,?84,?25,?63,?37,?87,?114,?79,?38,?14,?10,?21,?48,?89,?91,?110," "?69,?45,?113,?12,?101,?68,?105,?46,?95,?74,?24,?53,?39,?6,?64,?52,?98," "?65,?115,?49,?70,?59,?32,?44,?100,?55,?66,?16,?19,?106,?43,?17,?78);" ); for(i=0; i<n; i++){ x1 = speedtest1_random(); speedtest1_numbername(x1%1000, zNum, sizeof(zNum)); len = (int)strlen(zNum); sqlite3_bind_int(g.pStmt, 1, i^0xf); for(j=0; zType[j]; j++){ switch( zType[j] ){ case 'I': case 'T': sqlite3_bind_int64(g.pStmt, j+2, x1); break; case 'F': sqlite3_bind_double(g.pStmt, j+2, (double)x1); break; case 'V': case 'B': sqlite3_bind_text64(g.pStmt, j+2, zNum, len, SQLITE_STATIC, SQLITE_UTF8); break; } } speedtest1_run(); } speedtest1_exec("COMMIT;"); speedtest1_end_test(); n = g.szTest*250; speedtest1_begin_test(110, "Query %d rows by rowid", n); speedtest1_prepare( "SELECT ZCYANBASEFEEDADROIT,ZJUNIORSHOWPRESSNOVA,ZCAUSESALAMITERMCYAN," "ZHOPEFULGATEHOLECHALK,ZHUMORSPICESANDKIDNEY,ZSWIMHEAVYMENTIONKIND," "ZMOVEWHOGAMMAINCH,ZAPPEALSIMPLESECONDHOUSING,ZHAFNIUMSCRIPTSALADMOTOR," "ZNEATSTEWPARTIRON,ZLONGFINLEAVEIMAGEOIL,ZDEWPEACHCAREERCELERY," "ZXENONFLIGHTPALEAPPLE,ZCALMRACCOONPROGRAMDEBIT,ZUSUALBODYHALIBUTDIAMOND," "ZTRYFACTKEEPMILK,ZWEALTHLINENGLEEFULDAY,ZLONGDIETESSAYNATURE," "ZLIFEUSELEAFYBELL,ZTREATPACKFUTURECONVERT,ZMEMORYREQUESTSOURCEBIG," "ZYARDOREGANOVIVIDJEWEL,ZDEPOSITPAIRCOLLEGECOMET,ZSLEEPYUSERGRANDBOWL," "ZBRIEFGOBYDODGERHEIGHT,ZCLUBRELEASELIZARDADVICE,ZCAPABLETRIPDOORALMOND," "ZDRYWALLBEYONDBROWNBOWL,ZASKCONTACTMONITORFUN,ZKIWIVISUALPRIDEAPPLE," "ZNOTICEPEARPOLICYJUICE,ZPEACHCOPPERDINNERLAKE,ZSTEELCAREFULPLATENUMBER," "ZGLADSPRAYKIDNEYGUPPY,ZCOMPANYSUMMERFIBERELF,ZTENNISCYCLEBILLOFFICER," "ZIMAGEPENCILOTHERBOTTOM,ZWESTAMOUNTAFFECTHEARING,ZDIVERPAINTLEATHEREASY," "ZSKYSKYCLASSICBRIEF,ZMESSYSULFURDREAMFESTIVE,ZMERRYCRACKTRAINLEADER," "ZBROADABLESOLIDCASUAL,ZGLASSRESERVEBARIUMMEAL,ZTUNEGASBUFFALOCAPITAL," "ZBANKBUFFALORECOVERORBIT,ZTREATTESTQUILLCHARGE,ZBAMBOOMESSWASABIEVENING," "ZREWARDINSIDEMANGOINTENSE,ZEAGLELINEMINEMAIL,ZCALMLYGEMFINISHEFFECT," "ZKEYFAILAPRICOTMETAL,ZFINGERDUEPIZZAOPTION,ZCADETBRIGHTPLANETBANK," "ZGOLDYOUNGINITIALNOSE,ZMISSREPLYHUMANLIVING,ZEIGHTLEADERWORKERMOST," "ZFRAMEENTERSIMPLEMOUTH,ZBIGTHINKCONVERTECONOMY,ZFACEINVITETALKGOLD," "ZPOSTPROTEINHANDLEACTOR,ZHERRINGJOKEFEATUREHOPEFUL,ZCABBAGESOCKEASEMINUTE," "ZMUFFINDRYERDRAWFORTUNE,ZPROBLEMCLUBPOPOVERJELLY,ZGIVEVIVIDDIVINEMEANING," "ZGENENATURALHEARINGKITE,ZGENERALRESORTSKYOPEN,ZLETTUCEBIRDMEETDEBATE," "ZBASEGOUDAREGULARFORGIVE,ZCHARGECLICKHUMANEHIRE,ZPLANETFAMILYPUREMEMORY," "ZMINORWAYPAPERCLASSY,ZCAPYEARLYRIVETBRUSH,ZSIZETOEAWARDFRESH," "ZARSENICSAMPLEWAITMUON,ZSQUAREGLEEFULCHILDLIGHT,ZSHINYASSISTLIVINGCRAB," "ZCORNERANCHORTAPEDIVER,ZDECADEJOYOUSWAVEHABIT,ZTRAVELDRIVERCONTESTLILY," "ZFLYINGDOCTORTABLEMELODY,ZSHARKJUSTFRUITMOVIE,ZFAMILYVISUALOWNERMATTER," "ZFARMERMORNINGMIRRORCONCERN,ZGIFTICEFISHGLUEHAIR,ZOUTSIDEPEAHENCOUNTICE," "ZSPREADMOTORBISCUITBACON,ZWISHHITSKINMOTOR,ZHOLIDAYHEADPONYDETAIL," "ZWOODPOETRYCOBBLERBENCH,ZAIRFORGIVEHEADFROG,ZBROWBALANCEKEYCHOWDER," "ZDISHKEEPBLESTMONITOR,ZCLAMBITARUGULAFAJITA,ZPLIERSPRINTASKOREGANO," "ZRADIANTWHENTRYCARD,ZDELAYOUTCOMEHORNAGENCY,ZPURECAKEVIVIDNEATLY," "ZPATTERNCLORINEGRANDCOLBY,ZHANDYREPAIRPROTONAIRPORT,ZAGEREEDFROGBASKET," "ZSORTMISTYQUOTECABBAGE,ZFOOTTAPWORDENTRY,ZRESOLVEWRISTWRAPAPPLE," "ZDILLASKHOKILEMON,ZFILLSTOPLAWJOYFUL,ZACTIONRANGEELEGANTNEUTRON," "ZRESORTYARDGREENLET,ZCREAMEVENINGLIPBRANCH,ZWHALEMATHAVOCADOCOPPER," "ZGRAYSURVEYWIRELOVE,ZBELLYCRASHITEMLACK,ZHANGERLITHIUMDINNERMEET," "ZCARRYFLOORMINNOWDRAGON,ZMALLEQUIPTHANKSGLUE,ZTERMFITTINGHOUSINGCOMMAND," "ZONERELEASEAVERAGENURSE,ZLACEADDRESSGROUNDCAREFUL" " FROM ZLOOKSLIKECOREDATA WHERE ZPK=?1;" ); for(i=0; i<n; i++){ x1 = speedtest1_random()%nRow; sqlite3_bind_int(g.pStmt, 1, x1); speedtest1_run(); } speedtest1_end_test(); } /* ** A testset used for debugging speedtest1 itself. */ void testset_debug1(void){ unsigned i, n; unsigned x1, x2; |
︙ | ︙ | |||
1243 1244 1245 1246 1247 1248 1249 | int doAutovac = 0; /* True for --autovacuum */ int cacheSize = 0; /* Desired cache size. 0 means default */ int doExclusive = 0; /* True for --exclusive */ int nHeap = 0, mnHeap = 0; /* Heap size from --heap */ int doIncrvac = 0; /* True for --incrvacuum */ const char *zJMode = 0; /* Journal mode */ const char *zKey = 0; /* Encryption key */ | | < > < | 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 | int doAutovac = 0; /* True for --autovacuum */ int cacheSize = 0; /* Desired cache size. 0 means default */ int doExclusive = 0; /* True for --exclusive */ int nHeap = 0, mnHeap = 0; /* Heap size from --heap */ int doIncrvac = 0; /* True for --incrvacuum */ const char *zJMode = 0; /* Journal mode */ const char *zKey = 0; /* Encryption key */ int nLook = -1, szLook = 0; /* --lookaside configuration */ int noSync = 0; /* True for --nosync */ int pageSize = 0; /* Desired page size. 0 means default */ int nPCache = 0, szPCache = 0;/* --pcache configuration */ int doPCache = 0; /* True if --pcache is seen */ int showStats = 0; /* True for --stats */ int nThread = 0; /* --threads value */ int mmapSize = 0; /* How big of a memory map to use */ const char *zTSet = "main"; /* Which --testset torun */ int doTrace = 0; /* True for --trace */ const char *zEncoding = 0; /* --utf16be or --utf16le */ const char *zDbName = 0; /* Name of the test database */ void *pHeap = 0; /* Allocated heap space */ void *pLook = 0; /* Allocated lookaside space */ void *pPCache = 0; /* Allocated storage for pcache */ int iCur, iHi; /* Stats values, current and "highwater" */ int i; /* Loop counter */ int rc; /* API return code */ /* Display the version of SQLite being tested */ printf("-- Speedtest1 for SQLite %s %.50s\n", sqlite3_libversion(), sqlite3_sourceid()); |
︙ | ︙ | |||
1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 | if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); zKey = argv[++i]; }else if( strcmp(z,"lookaside")==0 ){ if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); nLook = integerValue(argv[i+1]); szLook = integerValue(argv[i+2]); i += 2; }else if( strcmp(z,"multithread")==0 ){ sqlite3_config(SQLITE_CONFIG_MULTITHREAD); }else if( strcmp(z,"nomemstat")==0 ){ sqlite3_config(SQLITE_CONFIG_MEMSTATUS, 0); }else if( strcmp(z,"nosync")==0 ){ noSync = 1; }else if( strcmp(z,"notnull")==0 ){ g.zNN = "NOT NULL"; | > > > > > > > < < < < < | < < < < > | 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 | if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); zKey = argv[++i]; }else if( strcmp(z,"lookaside")==0 ){ if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); nLook = integerValue(argv[i+1]); szLook = integerValue(argv[i+2]); i += 2; #if SQLITE_VERSION_NUMBER>=3006000 }else if( strcmp(z,"multithread")==0 ){ sqlite3_config(SQLITE_CONFIG_MULTITHREAD); }else if( strcmp(z,"nomemstat")==0 ){ sqlite3_config(SQLITE_CONFIG_MEMSTATUS, 0); #endif #if SQLITE_VERSION_NUMBER>=3007017 }else if( strcmp(z, "mmap")==0 ){ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); mmapSize = integerValue(argv[++i]); #endif }else if( strcmp(z,"nosync")==0 ){ noSync = 1; }else if( strcmp(z,"notnull")==0 ){ g.zNN = "NOT NULL"; }else if( strcmp(z,"pagesize")==0 ){ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); pageSize = integerValue(argv[++i]); }else if( strcmp(z,"pcache")==0 ){ if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); nPCache = integerValue(argv[i+1]); szPCache = integerValue(argv[i+2]); doPCache = 1; i += 2; }else if( strcmp(z,"primarykey")==0 ){ g.zPK = "PRIMARY KEY"; }else if( strcmp(z,"repeat")==0 ){ if( i>=argc-1 ) fatal_error("missing arguments on %s\n", argv[i]); g.nRepeat = integerValue(argv[i+1]); i += 1; }else if( strcmp(z,"reprepare")==0 ){ g.bReprepare = 1; #if SQLITE_VERSION_NUMBER>=3006000 }else if( strcmp(z,"serialized")==0 ){ sqlite3_config(SQLITE_CONFIG_SERIALIZED); }else if( strcmp(z,"singlethread")==0 ){ sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); #endif }else if( strcmp(z,"sqlonly")==0 ){ g.bSqlOnly = 1; }else if( strcmp(z,"shrink-memory")==0 ){ g.bMemShrink = 1; }else if( strcmp(z,"size")==0 ){ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); g.szTest = integerValue(argv[++i]); |
︙ | ︙ | |||
1410 1411 1412 1413 1414 1415 1416 | pPCache = malloc( nPCache*(sqlite3_int64)szPCache ); if( pPCache==0 ) fatal_error("cannot allocate %lld-byte pcache\n", nPCache*(sqlite3_int64)szPCache); } rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, pPCache, szPCache, nPCache); if( rc ) fatal_error("pcache configuration failed: %d\n", rc); } | < < < < < < < | | 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 | pPCache = malloc( nPCache*(sqlite3_int64)szPCache ); if( pPCache==0 ) fatal_error("cannot allocate %lld-byte pcache\n", nPCache*(sqlite3_int64)szPCache); } rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, pPCache, szPCache, nPCache); if( rc ) fatal_error("pcache configuration failed: %d\n", rc); } if( nLook>=0 ){ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0); } #endif /* Open the database and the input file */ if( sqlite3_open(zDbName, &g.db) ){ fatal_error("Cannot open database file: %s\n", zDbName); |
︙ | ︙ | |||
1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 | #endif /* Set database connection options */ sqlite3_create_function(g.db, "random", 0, SQLITE_UTF8, 0, randomFunc, 0, 0); #ifndef SQLITE_OMIT_DEPRECATED if( doTrace ) sqlite3_trace(g.db, traceCallback, 0); #endif speedtest1_exec("PRAGMA threads=%d", nThread); if( zKey ){ speedtest1_exec("PRAGMA key('%s')", zKey); } if( zEncoding ){ speedtest1_exec("PRAGMA encoding=%s", zEncoding); } | > > > | 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 | #endif /* Set database connection options */ sqlite3_create_function(g.db, "random", 0, SQLITE_UTF8, 0, randomFunc, 0, 0); #ifndef SQLITE_OMIT_DEPRECATED if( doTrace ) sqlite3_trace(g.db, traceCallback, 0); #endif if( mmapSize>0 ){ speedtest1_exec("PRAGMA mmap_size=%d", mmapSize); } speedtest1_exec("PRAGMA threads=%d", nThread); if( zKey ){ speedtest1_exec("PRAGMA key('%s')", zKey); } if( zEncoding ){ speedtest1_exec("PRAGMA encoding=%s", zEncoding); } |
︙ | ︙ | |||
1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 | } if( g.bExplain ) printf(".explain\n.echo on\n"); if( strcmp(zTSet,"main")==0 ){ testset_main(); }else if( strcmp(zTSet,"debug1")==0 ){ testset_debug1(); }else if( strcmp(zTSet,"cte")==0 ){ testset_cte(); }else if( strcmp(zTSet,"rtree")==0 ){ #ifdef SQLITE_ENABLE_RTREE testset_rtree(6, 147); #else fatal_error("compile with -DSQLITE_ENABLE_RTREE to enable " | > > | 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 | } if( g.bExplain ) printf(".explain\n.echo on\n"); if( strcmp(zTSet,"main")==0 ){ testset_main(); }else if( strcmp(zTSet,"debug1")==0 ){ testset_debug1(); }else if( strcmp(zTSet,"orm")==0 ){ testset_orm(); }else if( strcmp(zTSet,"cte")==0 ){ testset_cte(); }else if( strcmp(zTSet,"rtree")==0 ){ #ifdef SQLITE_ENABLE_RTREE testset_rtree(6, 147); #else fatal_error("compile with -DSQLITE_ENABLE_RTREE to enable " |
︙ | ︙ | |||
1528 1529 1530 1531 1532 1533 1534 | printf("-- Memory Used (bytes): %d (max %d)\n", iCur,iHi); #if SQLITE_VERSION_NUMBER>=3007000 sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHi, 0); printf("-- Outstanding Allocations: %d (max %d)\n", iCur,iHi); #endif sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHi, 0); printf("-- Pcache Overflow Bytes: %d (max %d)\n", iCur,iHi); | < < < < < | 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 | printf("-- Memory Used (bytes): %d (max %d)\n", iCur,iHi); #if SQLITE_VERSION_NUMBER>=3007000 sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHi, 0); printf("-- Outstanding Allocations: %d (max %d)\n", iCur,iHi); #endif sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHi, 0); printf("-- Pcache Overflow Bytes: %d (max %d)\n", iCur,iHi); sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHi, 0); printf("-- Largest Allocation: %d bytes\n",iHi); sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHi, 0); printf("-- Largest Pcache Allocation: %d bytes\n",iHi); } #endif #ifdef __linux__ if( showStats ){ displayLinuxIoStats(stdout); } #endif /* Release memory */ free( pLook ); free( pPCache ); free( pHeap ); return 0; } |
Added test/stmtvtab1.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 | # 2017-06-29 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Testing of the STMT virtual table. # # This also validates the SQLITE_STMTSTATUS_REPREPARE and # SQLITE_STMTSTATUS_RUN values for sqlite3_stmt_status(). # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !stmtvtab { finish_test return } db cache flush db cache size 20 unset -nocomplain x y z set x giraffe set y mango set z alabama do_execsql_test stmtvtab1-100 { CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES($a,$b,$c); CREATE INDEX t1a ON t1(a); SELECT run, sql FROM sqlite_stmt ORDER BY 1; } {1 {SELECT run, sql FROM sqlite_stmt ORDER BY 1;} 1 {CREATE INDEX t1a ON t1(a);} 1 {INSERT INTO t1 VALUES($a,$b,$c);} 1 {CREATE TABLE t1(a,b,c);}} set x neon set y event set z future do_execsql_test stmtvtab1-110 { INSERT INTO t1 VALUES($a,$b,$c); SELECT reprep,run,SQL FROM sqlite_stmt WHERE sql LIKE '%INSERT%' AND NOT busy; } {1 2 {INSERT INTO t1 VALUES($a,$b,$c);}} set x network set y fit set z metal do_execsql_test stmtvtab1-120 { INSERT INTO t1 VALUES($a,$b,$c); SELECT reprep,run,SQL FROM sqlite_stmt WHERE sql LIKE '%INSERT%' AND NOT busy; } {1 3 {INSERT INTO t1 VALUES($a,$b,$c);}} set x history set y detail set z grace do_execsql_test stmtvtab1-130 { CREATE INDEX t1b ON t1(b); INSERT INTO t1 VALUES($a,$b,$c); SELECT reprep,run,SQL FROM sqlite_stmt WHERE sql LIKE '%INSERT%' AND NOT busy; } {2 4 {INSERT INTO t1 VALUES($a,$b,$c);}} # All statements are still in cache # do_execsql_test stmtvtab1-140 { SELECT count(*) FROM sqlite_stmt WHERE NOT busy; } {6} # None of the prepared statements should use more than a couple thousand # bytes of memory # #db eval {SELECT mem, sql FROM sqlite_stmt} {puts [format {%5d %s} $mem $sql]} do_execsql_test stmtvtab1-150 { SELECT count(*) FROM sqlite_stmt WHERE mem>5000; } {0} # Flushing the cache clears all of the prepared statements. # db cache flush do_execsql_test stmtvtab1-160 { SELECT * FROM sqlite_stmt WHERE NOT busy; } {} |
Added test/subjournal.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 | # 2017 May 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix subjournal do_execsql_test 1.0 { PRAGMA temp_store = memory; CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(1, 2, 3); } {} do_execsql_test 1.1 { BEGIN; INSERT INTO t1 VALUES(4, 5, 6); SAVEPOINT one; INSERT INTO t1 VALUES(7, 8, 9); ROLLBACK TO one; SELECT * FROM t1; } {1 2 3 4 5 6} do_execsql_test 1.2 { COMMIT; } do_execsql_test 2.0 { PRAGMA cache_size = 5; CREATE TABLE t2(a BLOB); CREATE INDEX i2 ON t2(a); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 ) INSERT INTO t2 SELECT randomblob(500) FROM s; } do_test 2.1 { forcedelete test.db2 sqlite3 db2 test2.db sqlite3_backup B db2 main db main set nPage [db one {PRAGMA page_count}] B step [expr $nPage-10] } {SQLITE_OK} do_execsql_test 2.2 { BEGIN; UPDATE t2 SET a=randomblob(499); SAVEPOINT two; UPDATE t2 SET a=randomblob(498); ROLLBACK TO two; COMMIT; PRAGMA integrity_check; } {ok} do_test 2.3 { B step 1000 } {SQLITE_DONE} do_test 2.4 { B finish execsql { PRAGMA integrity_check } db2 } {ok} finish_test |
Added test/swarmvtab.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 | # 2017-07-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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is the "swarmvtab" extension # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix swarmvtab ifcapable !vtab { finish_test return } load_static_extension db unionvtab set nFile $sqlite_open_file_count do_execsql_test 1.0 { CREATE TABLE t0(a INTEGER PRIMARY KEY, b TEXT); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<400) INSERT INTO t0 SELECT i, hex(randomblob(50)) FROM s; CREATE TABLE dir(f, t, imin, imax); } do_test 1.1 { for {set i 0} {$i < 40} {incr i} { set iMin [expr $i*10 + 1] set iMax [expr $iMin+9] forcedelete "test.db$i" execsql [subst { ATTACH 'test.db$i' AS aux; CREATE TABLE aux.t$i (a INTEGER PRIMARY KEY, b TEXT); INSERT INTO aux.t$i SELECT * FROM t0 WHERE a BETWEEN $iMin AND $iMax; DETACH aux; INSERT INTO dir VALUES('test.db$i', 't$i', $iMin, $iMax); }] } execsql { CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir'); } } {} do_execsql_test 1.2 { DROP TABLE s1; } {} do_execsql_test 1.3 { CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir'); SELECT count(*) FROM s1 WHERE rowid<50; } {49} proc do_compare_test {tn where} { set sql [subst { SELECT (SELECT group_concat(a || ',' || b, ',') FROM t0 WHERE $where) IS (SELECT group_concat(a || ',' || b, ',') FROM s1 WHERE $where) }] uplevel [list do_execsql_test $tn $sql 1] } do_compare_test 1.4.1 "rowid = 700" do_compare_test 1.4.2 "rowid = -1" do_compare_test 1.4.3 "rowid = 0" do_compare_test 1.4.4 "rowid = 55" do_compare_test 1.4.5 "rowid BETWEEN 20 AND 100" do_compare_test 1.4.6 "rowid > 350" do_compare_test 1.4.7 "rowid >= 350" do_compare_test 1.4.8 "rowid >= 200" do_compare_test 1.4.9 "1" # Multiple simultaneous cursors. # do_execsql_test 1.5.1.(5-seconds-or-so) { SELECT count(*) FROM s1 a, s1 b WHERE b.rowid<=200; } {80000} do_execsql_test 1.5.2 { SELECT count(*) FROM s1 a, s1 b, s1 c WHERE a.rowid=b.rowid AND b.rowid=c.rowid; } {400} # Empty source tables. # do_test 1.6.0 { for {set i 0} {$i < 20} {incr i} { sqlite3 db2 test.db$i db2 eval " DELETE FROM t$i " db2 close } db eval { DELETE FROM t0 WHERE rowid<=200 } } {} do_compare_test 1.6.1 "rowid = 700" do_compare_test 1.6.2 "rowid = -1" do_compare_test 1.6.3 "rowid = 0" do_compare_test 1.6.4 "rowid = 55" do_compare_test 1.6.5 "rowid BETWEEN 20 AND 100" do_compare_test 1.6.6 "rowid > 350" do_compare_test 1.6.7 "rowid >= 350" do_compare_test 1.6.8 "rowid >= 200" do_compare_test 1.6.9 "1" do_compare_test 1.6.10 "rowid >= 5" do_test 1.x { set sqlite_open_file_count } [expr $nFile+9] do_test 1.y { db close } {} # Delete all the database files created above. # for {set i 0} {$i < 40} {incr i} { forcedelete "test.db$i" } #------------------------------------------------------------------------- # Test some error conditions: # # 2.1: Database file does not exist. # 2.2: Table does not exist. # 2.3: Table schema does not match. # 2.4: Syntax error in SELECT statement. # reset_db load_static_extension db unionvtab do_test 2.0.1 { db eval { CREATE TABLE t0(a INTEGER PRIMARY KEY, b TEXT); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<400) INSERT INTO t0 SELECT i, hex(randomblob(50)) FROM s; CREATE TABLE dir(f, t, imin, imax); } for {set i 0} {$i < 40} {incr i} { set iMin [expr $i*10 + 1] set iMax [expr $iMin+9] forcedelete "test.db$i" db eval [subst { ATTACH 'test.db$i' AS aux; CREATE TABLE aux.t$i (a INTEGER PRIMARY KEY, b TEXT); INSERT INTO aux.t$i SELECT * FROM t0 WHERE a BETWEEN $iMin AND $iMax; DETACH aux; INSERT INTO dir VALUES('test.db$i', 't$i', $iMin, $iMax); }] } execsql { CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir'); } } {} do_test 2.0.2 { forcedelete test.db5 sqlite3 db2 test.db15 db2 eval { DROP TABLE t15 } db2 close sqlite3 db2 test.db25 db2 eval { DROP TABLE t25; CREATE TABLE t25(x, y, z PRIMARY KEY); } db2 close } {} do_catchsql_test 2.1 { SELECT * FROM s1 WHERE rowid BETWEEN 1 AND 100; } {1 {unable to open database file}} do_catchsql_test 2.2 { SELECT * FROM s1 WHERE rowid BETWEEN 101 AND 200; } {1 {no such rowid table: t15}} do_catchsql_test 2.3 { SELECT * FROM s1 WHERE rowid BETWEEN 201 AND 300; } {1 {source table schema mismatch}} do_catchsql_test 2.4 { CREATE VIRTUAL TABLE temp.x1 USING swarmvtab('SELECT * FROMdir'); } {1 {sql error: near "FROMdir": syntax error}} do_catchsql_test 2.5 { CREATE VIRTUAL TABLE temp.x1 USING swarmvtab('SELECT * FROMdir', 'fetchdb'); } {1 {sql error: near "FROMdir": syntax error}} for {set i 0} {$i < 40} {incr i} { forcedelete "test.db$i" } #------------------------------------------------------------------------- # Test the outcome of the fetch function throwing an exception. # proc fetch_db {file} { error "fetch_db error!" } db func fetch_db fetch_db do_catchsql_test 3.1 { CREATE VIRTUAL TABLE temp.xyz USING swarmvtab( 'VALUES ("test.db1", "t1", 1, 10), ("test.db2", "t1", 11, 20) ', 'fetch_db_no_such_function' ); } {1 {no such function: fetch_db_no_such_function}} do_catchsql_test 3.2 { CREATE VIRTUAL TABLE temp.xyz USING swarmvtab( 'VALUES ("test.db1", "t1", 1, 10), ("test.db2", "t1", 11, 20) ', 'fetch_db' ); } {1 {fetch_db error!}} do_execsql_test 3.3.1 { ATTACH 'test.db1' AS aux; CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b); INSERT INTO aux.t1 VALUES(1, NULL); INSERT INTO aux.t1 VALUES(2, NULL); INSERT INTO aux.t1 VALUES(9, NULL); DETACH aux; CREATE VIRTUAL TABLE temp.xyz USING swarmvtab( 'VALUES ("test.db1", "t1", 1, 10), ("test.db2", "t1", 11, 20) ', 'fetch_db' ); } {} do_catchsql_test 3.3.2 { SELECT * FROM xyz } {1 {fetch_db error!}} finish_test |
Added test/swarmvtab2.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 | # 2017-07-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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is the "swarmvtab" extension # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix swarmvtab ifcapable !vtab { finish_test return } db close foreach name [glob -nocomplain test*.db] { forcedelete $name } sqlite3 db test.db load_static_extension db unionvtab proc create_database {filename} { sqlite3 dbx $filename set num [regsub -all {[^0-9]+} $filename {}] set num [string trimleft $num 0] set start [expr {$num*1000}] set end [expr {$start+999}] dbx eval { CREATE TABLE t2(a INTEGER PRIMARY KEY,b); WITH RECURSIVE c(x) AS ( VALUES($start) UNION ALL SELECT x+1 FROM c WHERE x<$end ) INSERT INTO t2(a,b) SELECT x, printf('**%05d**',x) FROM c; } dbx close } db func create_database create_database do_execsql_test 100 { CREATE TABLE t1(filename, tablename, istart, iend); WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<99) INSERT INTO t1 SELECT printf('test%03d.db',x),'t2',x*1000,x*1000+999 FROM c; CREATE VIRTUAL TABLE temp.v1 USING swarmvtab( 'SELECT * FROM t1', 'create_database' ); } {} do_execsql_test 110 { SELECT b FROM v1 WHERE a=3875; } {**03875**} do_test 120 { lsort [glob -nocomplain test?*.db] } {test001.db test003.db} do_execsql_test 130 { SELECT b FROM v1 WHERE a BETWEEN 3999 AND 4000 ORDER BY a; } {**03999** **04000**} do_test 140 { lsort [glob -nocomplain test?*.db] } {test001.db test003.db test004.db} do_execsql_test 150 { SELECT b FROM v1 WHERE a>=99998; } {**99998** **99999**} do_test 160 { lsort -dictionary [glob -nocomplain test?*.db] } {test001.db test003.db test004.db test099.db} finish_test |
Added test/swarmvtabfault.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 | # 2017-07-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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is error handling in the swarmvtab extension. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix swarmvtabfault ifcapable !vtab { finish_test return } proc fetch_db {file} { forcedelete $file sqlite3 dbX $file dbX eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b) } dbX close } forcedelete test.db1 do_execsql_test 1.0 { ATTACH 'test.db1' AS aux; CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b); INSERT INTO aux.t1 VALUES(1, NULL); INSERT INTO aux.t1 VALUES(2, NULL); INSERT INTO aux.t1 VALUES(9, NULL); DETACH aux; } {} faultsim_save_and_close do_faultsim_test 1.1 -faults oom* -prep { faultsim_restore_and_reopen db func fetch_db fetch_db load_static_extension db unionvtab db eval { CREATE VIRTUAL TABLE temp.xyz USING swarmvtab( 'VALUES ("test.db1", "t1", 1, 10), ("test.db2", "t1", 11, 20) ', 'fetch_db' ); } } -body { execsql { SELECT a FROM xyz } } -test { faultsim_test_result {0 {1 2 9}} {1 {sql error: out of memory}} } finish_test |
Added test/sync2.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 | # 2017 March 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 implements regression tests for SQLite library. # # Specificly, it tests that "PRAGMA synchronous" appears to work. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sync2 # # These tests are only applicable when pager pragma are # enabled. Also, since every test uses an ATTACHed database, they # are only run when ATTACH is enabled. # ifcapable !pager_pragmas||!attach||!dirsync { finish_test return } if {$::tcl_platform(platform)!="unix" || [permutation] == "journaltest" || [permutation] == "inmemory_journal" } { finish_test return } proc execsql_sync {sql} { set s $::sqlite_sync_count set res [execsql $sql] concat [expr $::sqlite_sync_count-$s] $res } proc do_execsql_sync_test {tn sql res} { uplevel [list do_test $tn [list execsql_sync $sql] [list {*}$res]] } #----------------------------------------------------------------------- # Tests for journal mode. # sqlite3 db test.db do_execsql_test 1.0 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } do_execsql_sync_test 1.1 { INSERT INTO t1 VALUES(3, 4) } 4 # synchronous=normal. So, 1 sync on the directory, 1 on the journal, 1 # on the db file. 3 in total. do_execsql_test 1.2.1 { PRAGMA main.synchronous = NORMAL } do_execsql_test 1.2.2 { PRAGMA main.synchronous } 1 do_execsql_sync_test 1.2.3 { INSERT INTO t1 VALUES(5, 6) } 3 # synchronous=off. No syncs. do_execsql_test 1.3.1 { PRAGMA main.synchronous = OFF } do_execsql_test 1.3.2 { PRAGMA main.synchronous } 0 do_execsql_sync_test 1.3.3 { INSERT INTO t1 VALUES(7, 8) } 0 # synchronous=full, journal_mode=delete. So, 1 sync on the directory, # 2 on the journal, 1 on the db file. 4 in total. do_execsql_test 1.4.1 { PRAGMA main.synchronous = FULL } do_execsql_test 1.4.2 { PRAGMA main.synchronous } 2 do_execsql_sync_test 1.4.3 { INSERT INTO t1 VALUES(9, 10) } 4 #----------------------------------------------------------------------- # Tests for wal mode. # do_execsql_test 1.5 { PRAGMA journal_mode = wal } {wal} # sync=full, journal_mode=wal. One sync on the directory, two on the # wal file. do_execsql_sync_test 1.6 { INSERT INTO t1 VALUES(11, 12) } 3 # One sync on the wal file. do_execsql_sync_test 1.7 { INSERT INTO t1 VALUES(13, 14) } 1 # No syncs. do_execsql_test 1.8.1 { PRAGMA main.synchronous = NORMAL } do_execsql_test 1.8.2 { PRAGMA main.synchronous } 1 do_execsql_sync_test 1.8.3 { INSERT INTO t1 VALUES(15, 16) } 0 # One sync on wal file, one on the db file. do_execsql_sync_test 1.9 { PRAGMA wal_checkpoint } {2 0 3 3} # No syncs. do_execsql_test 1.10.1 { PRAGMA main.synchronous = OFF } do_execsql_test 1.10.2 { PRAGMA main.synchronous } 0 do_execsql_sync_test 1.10.3 { INSERT INTO t1 VALUES(17, 18) } 0 #----------------------------------------------------------------------- # Tests for the compile time settings SQLITE_DEFAULT_SYNCHRONOUS and # SQLITE_DEFAULT_WAL_SYNCHRONOUS. These tests only run if the former # is set to "2" and the latter to "1". This is not the default, but # it is currently the recommended configuration. # # https://sqlite.org/compile.html#recommended_compile_time_options # if {$SQLITE_DEFAULT_SYNCHRONOUS==2 && $SQLITE_DEFAULT_WAL_SYNCHRONOUS==1} { db close sqlite3 db test.db # Wal mode, sync=normal. The first transaction does one sync on directory, # one on the wal file. The second does no syncs. do_execsql_sync_test 1.11.1 { INSERT INTO t1 VALUES(19, 20) } 2 do_execsql_sync_test 1.11.2 { INSERT INTO t1 VALUES(21, 22) } 0 do_execsql_test 1.11.3 { PRAGMA main.synchronous } 1 # One sync on wal file, one on the db file. do_execsql_sync_test 1.12 { PRAGMA wal_checkpoint } {2 0 2 2} # First transaction syncs the wal file once, the second not at all. # one on the wal file. The second does no syncs. do_execsql_sync_test 1.13.1 { INSERT INTO t1 VALUES(22, 23) } 1 do_execsql_sync_test 1.13.2 { INSERT INTO t1 VALUES(24, 25) } 0 do_execsql_test 1.14 { PRAGMA journal_mode = delete } {delete} # Delete mode, sync=full. The first transaction does one sync on # directory, two on the journal file, one on the db. The second does # the same. do_execsql_sync_test 1.15.1 { INSERT INTO t1 VALUES(26, 27) } 4 do_execsql_sync_test 1.15.2 { INSERT INTO t1 VALUES(28, 29) } 4 do_execsql_test 1.15.3 { PRAGMA main.synchronous } 2 # Switch back to wal mode. do_execsql_test 1.16 { PRAGMA journal_mode = wal } {wal} do_execsql_sync_test 1.17.1 { INSERT INTO t1 VALUES(30, 31) } 2 do_execsql_sync_test 1.17.2 { INSERT INTO t1 VALUES(32, 33) } 0 do_execsql_test 1.17.3 { PRAGMA main.synchronous } 1 # Now set synchronous=off, then switch back to delete mode. Check # that the db handle is still using synchronous=off. do_execsql_test 1.18.3 { PRAGMA main.synchronous=off } do_execsql_test 1.18 { PRAGMA journal_mode = delete } {delete} do_execsql_sync_test 1.19.1 { INSERT INTO t1 VALUES(34, 35) } 0 do_execsql_sync_test 1.19.2 { INSERT INTO t1 VALUES(36, 37) } 0 do_execsql_test 1.19.3 { PRAGMA main.synchronous } 0 # Close and reopen the db. Back to synchronous=normal. db close sqlite3 db test.db do_execsql_sync_test 1.20.1 { INSERT INTO t1 VALUES(38, 39) } 4 do_execsql_sync_test 1.20.2 { INSERT INTO t1 VALUES(40, 41) } 4 do_execsql_test 1.20.3 { PRAGMA main.synchronous } 2 } finish_test |
Changes to test/syscall.test.
︙ | ︙ | |||
57 58 59 60 61 62 63 | # Tests for the xNextSystemCall method. # foreach s { open close access getcwd stat fstat ftruncate fcntl read pread write pwrite fchmod fallocate pread64 pwrite64 unlink openDirectory mkdir rmdir statvfs fchown geteuid umask mmap munmap mremap | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | # Tests for the xNextSystemCall method. # foreach s { open close access getcwd stat fstat ftruncate fcntl read pread write pwrite fchmod fallocate pread64 pwrite64 unlink openDirectory mkdir rmdir statvfs fchown geteuid umask mmap munmap mremap getpagesize readlink lstat ioctl } { if {[test_syscall exists $s]} {lappend syscall_list $s} } do_test 3.1 { lsort [test_syscall list] } [lsort $syscall_list] #------------------------------------------------------------------------- # This test verifies that if a call to open() fails and errno is set to |
︙ | ︙ | |||
214 215 216 217 218 219 220 | fconfigure $fd -translation binary -encoding binary puts -nonewline $fd [string range "xSQLite" 1 $nByte] close $fd } foreach {nByte res} { 1 {0 {}} | | | | 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | fconfigure $fd -translation binary -encoding binary puts -nonewline $fd [string range "xSQLite" 1 $nByte] close $fd } foreach {nByte res} { 1 {0 {}} 2 {1 {file is not a database}} 3 {1 {file is not a database}} } { do_test 7.$nByte { create_db_file $nByte list [catch { sqlite3 db test.db execsql { CREATE TABLE t1(a, b) } } msg] $msg |
︙ | ︙ |
Changes to test/tabfunc01.test.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ifcapable !vtab { finish_test return } load_static_extension db series load_static_extension db carray do_execsql_test tabfunc01-1.1 { SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2; } {1 | 3 | 5 | 7 | 9 |} do_execsql_test tabfunc01-1.2 { SELECT *, '|' FROM generate_series LIMIT 5; } {0 | 1 | 2 | 3 | 4 |} | > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ifcapable !vtab { finish_test return } load_static_extension db series load_static_extension db carray load_static_extension db remember do_execsql_test tabfunc01-1.1 { SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2; } {1 | 3 | 5 | 7 | 9 |} do_execsql_test tabfunc01-1.2 { SELECT *, '|' FROM generate_series LIMIT 5; } {0 | 1 | 2 | 3 | 4 |} |
︙ | ︙ | |||
143 144 145 146 147 148 149 | WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) INSERT INTO t600(a,b) SELECT x, printf('(%03d)',x) FROM c; SELECT b FROM t600 WHERE a IN generate_series(2,52,10); } {(002) (012) (022) (032) (042) (052)} do_test tabfunc01-700 { | | | | | | | | > > > > | > > > > > > > > > | | | | > > > > > > | > > > | 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 | WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) INSERT INTO t600(a,b) SELECT x, printf('(%03d)',x) FROM c; SELECT b FROM t600 WHERE a IN generate_series(2,52,10); } {(002) (012) (022) (032) (042) (052)} do_test tabfunc01-700 { set PTR1 [intarray_addr 5 7 13 17 23] db eval { SELECT b FROM t600, carray(inttoptr($PTR1),5) WHERE a=value; } } {(005) (007) (013) (017) (023)} do_test tabfunc01-701 { db eval { SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),5,'int32'); } } {(005) (007) (013) (017) (023)} do_test tabfunc01-702 { db eval { SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),4,'int32'); } } {(005) (007) (013) (017)} do_catchsql_test tabfunc01-710 { SELECT b FROM t600 WHERE a IN carray(inttoptr($PTR1),5,'int33'); } {1 {unknown datatype: 'int33'}} do_test tabfunc01-720 { set PTR2 [int64array_addr 5 7 13 17 23] db eval { SELECT b FROM t600, carray(inttoptr($PTR2),5,'int64') WHERE a=value; } } {(005) (007) (013) (017) (023)} do_test tabfunc01-721 { db eval { SELECT remember(123,inttoptr($PTR2)); SELECT value FROM carray(inttoptr($PTR2),5,'int64'); } } {123 123 7 13 17 23} do_test tabfunc01-722 { set PTR3 [expr {$PTR2+16}] db eval { SELECT remember(987,inttoptr($PTR3)); SELECT value FROM carray(inttoptr($PTR2),5,'int64'); } } {987 123 7 987 17 23} do_test tabfunc01-730 { set PTR4 [doublearray_addr 5.0 7.0 13.0 17.0 23.0] db eval { SELECT b FROM t600, carray(inttoptr($PTR4),5,'double') WHERE a=value; } } {(005) (007) (013) (017) (023)} do_test tabfunc01-740 { set PTR5 [textarray_addr x5 x7 x13 x17 x23] db eval { SELECT b FROM t600, carray(inttoptr($PTR5),5,'char*') WHERE a=trim(value,'x'); } } {(005) (007) (013) (017) (023)} do_test tabfunc01-750 { db eval { SELECT aa.value, bb.value, '|' FROM carray(inttoptr($PTR4),5,'double') AS aa JOIN carray(inttoptr($PTR5),5,'char*') AS bb ON aa.rowid=bb.rowid; } } {5.0 x5 | 7.0 x7 | 13.0 x13 | 17.0 x17 | 23.0 x23 |} # Free up memory allocations intarray_addr int64array_addr doublearray_addr textarray_addr finish_test |
Changes to test/tclsqlite.test.
︙ | ︙ | |||
109 110 111 112 113 114 115 | set v [catch {db complete} msg] lappend v $msg } {1 {wrong # args: should be "db complete SQL"}} } do_test tcl-1.14 { set v [catch {db eval} msg] lappend v $msg | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | set v [catch {db complete} msg] lappend v $msg } {1 {wrong # args: should be "db complete SQL"}} } do_test tcl-1.14 { set v [catch {db eval} msg] lappend v $msg } {1 {wrong # args: should be "db eval ?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?"}} do_test tcl-1.15 { set v [catch {db function} msg] lappend v $msg } {1 {wrong # args: should be "db function NAME ?SWITCHES? SCRIPT"}} do_test tcl-1.16 { set v [catch {db last_insert_rowid xyz} msg] lappend v $msg |
︙ | ︙ | |||
661 662 663 664 665 666 667 668 669 670 671 672 673 | db exists {SELECT a FROM t1 WHERE a>2} } {1} do_test tcl-15.5 { db exists {SELECT a FROM t1 WHERE a>3} } {0} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | db exists {SELECT a FROM t1 WHERE a>2} } {1} do_test tcl-15.5 { db exists {SELECT a FROM t1 WHERE a>3} } {0} # 2017-06-26: The --withoutnulls flag to "db eval". # # In the "db eval --withoutnulls SQL ARRAY" form, NULL results cause the # corresponding array entry to be unset. The default behavior (without # the -withoutnulls flags) is for the corresponding array value to get # the [db nullvalue] string. # catch {db close} forcedelete test.db sqlite3 db test.db do_execsql_test tcl-16.100 { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2),(2,NULL),(3,'xyz'); } do_test tcl-16.101 { set res {} unset -nocomplain x db eval {SELECT * FROM t1} x { lappend res $x(a) [array names x] } set res } {1 {a b *} 2 {a b *} 3 {a b *}} do_test tcl-16.102 { set res [catch { db eval -unknown {SELECT * FROM t1} x { lappend res $x(a) [array names x] } } rc] lappend res $rc } {1 {unknown option: "-unknown"}} do_test tcl-16.103 { set res {} unset -nocomplain x db eval -withoutnulls {SELECT * FROM t1} x { lappend res $x(a) [array names x] } set res } {1 {a b *} 2 {a *} 3 {a b *}} finish_test |
Changes to test/tempdb2.test.
︙ | ︙ | |||
70 71 72 73 74 75 76 | } do_execsql_test 1.4 { SELECT b=int2str(2) FROM t1 } {1 1 1} finish_test | < | 70 71 72 73 74 75 76 | } do_execsql_test 1.4 { SELECT b=int2str(2) FROM t1 } {1 1 1} finish_test |
Changes to test/temptrigger.test.
︙ | ︙ | |||
232 233 234 235 236 237 238 | do_test 5.1 { sqlite3 db2 test.db execsql { DROP TABLE t1 } db2 } {} do_execsql_test 5.2 { SELECT * FROM sqlite_master; | | | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | do_test 5.1 { sqlite3 db2 test.db execsql { DROP TABLE t1 } db2 } {} do_execsql_test 5.2 { SELECT * FROM sqlite_master; SELECT * FROM temp.sqlite_master; } { trigger tr1 t1 0 {CREATE TRIGGER tr1 BEFORE INSERT ON t1 BEGIN SELECT 1,2,3; END} } db2 close #------------------------------------------------------------------------- |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
457 458 459 460 461 462 463 | foreach {dummy cmdlinearg(malloctrace)} [split $a =] break if {$cmdlinearg(malloctrace)} { sqlite3_memdebug_log start } } {^-+backtrace=.+$} { foreach {dummy cmdlinearg(backtrace)} [split $a =] break | | | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | foreach {dummy cmdlinearg(malloctrace)} [split $a =] break if {$cmdlinearg(malloctrace)} { sqlite3_memdebug_log start } } {^-+backtrace=.+$} { foreach {dummy cmdlinearg(backtrace)} [split $a =] break sqlite3_memdebug_backtrace $cmdlinearg(backtrace) } {^-+binarylog=.+$} { foreach {dummy cmdlinearg(binarylog)} [split $a =] break set cmdlinearg(binarylog) [file normalize $cmdlinearg(binarylog)] } {^-+soak=.+$} { foreach {dummy cmdlinearg(soak)} [split $a =] break |
︙ | ︙ | |||
915 916 917 918 919 920 921 | proc normalize_list {L} { set L2 [list] foreach l $L {lappend L2 $l} set L2 } | > > > > > | > > > > > > > > > > > > > > > > > > > > > | > | 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 | proc normalize_list {L} { set L2 [list] foreach l $L {lappend L2 $l} set L2 } # Either: # # do_execsql_test TESTNAME SQL ?RES? # do_execsql_test -db DB TESTNAME SQL ?RES? # proc do_execsql_test {args} { set db db if {[lindex $args 0]=="-db"} { set db [lindex $args 1] set args [lrange $args 2 end] } if {[llength $args]==2} { foreach {testname sql} $args {} set result "" } elseif {[llength $args]==3} { foreach {testname sql result} $args {} } else { error [string trim { wrong # args: should be "do_execsql_test ?-db DB? testname sql ?result?" }] } fix_testname testname uplevel do_test \ [list $testname] \ [list "execsql {$sql} $db"] \ [list [list {*}$result]] } proc do_catchsql_test {testname sql result} { fix_testname testname uplevel do_test [list $testname] [list "catchsql {$sql}"] [list $result] } proc do_timed_execsql_test {testname sql {result {}}} { fix_testname testname uplevel do_test [list $testname] [list "execsql_timed {$sql}"]\ |
︙ | ︙ | |||
1210 1211 1212 1213 1214 1215 1216 | set y [sqlite3_status SQLITE_STATUS_PAGECACHE_SIZE 0] set val [format {now %10d max %10d max-size %10d} \ [lindex $x 1] [lindex $x 2] [lindex $y 2]] output1 "Page-cache used: $val" set x [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] set val [format {now %10d max %10d} [lindex $x 1] [lindex $x 2]] output1 "Page-cache overflow: $val" | < < < < < < < < | 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 | set y [sqlite3_status SQLITE_STATUS_PAGECACHE_SIZE 0] set val [format {now %10d max %10d max-size %10d} \ [lindex $x 1] [lindex $x 2] [lindex $y 2]] output1 "Page-cache used: $val" set x [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] set val [format {now %10d max %10d} [lindex $x 1] [lindex $x 2]] output1 "Page-cache overflow: $val" ifcapable yytrackmaxstackdepth { set x [sqlite3_status SQLITE_STATUS_PARSER_STACK 0] set val [format { max %10d} [lindex $x 2]] output2 "Parser stack depth: $val" } } |
︙ | ︙ | |||
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 | set blocksize "" set crashdelay 1 set prngseed 0 set opendb { sqlite3 db test.db -vfs crash } set tclbody {} set crashfile "" set dc "" set sql [lindex $args end] for {set ii 0} {$ii < [llength $args]-1} {incr ii 2} { set z [lindex $args $ii] set n [string length $z] set z2 [lindex $args [expr $ii+1]] if {$n>1 && [string first $z -delay]==0} {set crashdelay $z2} \ elseif {$n>1 && [string first $z -opendb]==0} {set opendb $z2} \ elseif {$n>1 && [string first $z -seed]==0} {set prngseed $z2} \ elseif {$n>1 && [string first $z -file]==0} {set crashfile $z2} \ elseif {$n>1 && [string first $z -tclbody]==0} {set tclbody $z2} \ elseif {$n>1 && [string first $z -blocksize]==0} {set blocksize "-s $z2" } \ | > | > | | 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 | set blocksize "" set crashdelay 1 set prngseed 0 set opendb { sqlite3 db test.db -vfs crash } set tclbody {} set crashfile "" set dc "" set dfltvfs 0 set sql [lindex $args end] for {set ii 0} {$ii < [llength $args]-1} {incr ii 2} { set z [lindex $args $ii] set n [string length $z] set z2 [lindex $args [expr $ii+1]] if {$n>1 && [string first $z -delay]==0} {set crashdelay $z2} \ elseif {$n>1 && [string first $z -opendb]==0} {set opendb $z2} \ elseif {$n>1 && [string first $z -seed]==0} {set prngseed $z2} \ elseif {$n>1 && [string first $z -file]==0} {set crashfile $z2} \ elseif {$n>1 && [string first $z -tclbody]==0} {set tclbody $z2} \ elseif {$n>1 && [string first $z -blocksize]==0} {set blocksize "-s $z2" } \ elseif {$n>1 && [string first $z -characteristics]==0} {set dc "-c {$z2}" }\ elseif {$n>1 && [string first $z -dfltvfs]==0} {set dfltvfs $z2 }\ else { error "Unrecognized option: $z" } } if {$crashfile eq ""} { error "Compulsory option -file missing" } # $crashfile gets compared to the native filename in # cfSync(), which can be different then what TCL uses by # default, so here we force it to the "nativename" format. set cfile [string map {\\ \\\\} [file nativename [file join [get_pwd] $crashfile]]] set f [open crash.tcl w] puts $f "sqlite3_crash_enable 1 $dfltvfs" puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile" puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte" # This block sets the cache size of the main database to 10 # pages. This is done in case the build is configured to omit # "PRAGMA cache_size". if {$opendb!=""} { |
︙ | ︙ | |||
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 | puts $f $tclbody } if {[string length $sql]>0} { puts $f "db eval {" puts $f "$sql" puts $f "}" } close $f set r [catch { exec [info nameofexec] crash.tcl >@stdout } msg] # Windows/ActiveState TCL returns a slightly different # error message. We map that to the expected message | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | puts $f $tclbody } if {[string length $sql]>0} { puts $f "db eval {" puts $f "$sql" puts $f "}" } close $f set r [catch { exec [info nameofexec] crash.tcl >@stdout } msg] # Windows/ActiveState TCL returns a slightly different # error message. We map that to the expected message # so that we don't have to change all of the test # cases. if {$::tcl_platform(platform)=="windows"} { if {$msg=="child killed: unknown signal"} { set msg "child process exited abnormally" } } lappend r $msg } # crash_on_write ?-devchar DEVCHAR? CRASHDELAY SQL # proc crash_on_write {args} { set nArg [llength $args] if {$nArg<2 || $nArg%2} { error "bad args: $args" } set zSql [lindex $args end] set nDelay [lindex $args end-1] set devchar {} for {set ii 0} {$ii < $nArg-2} {incr ii 2} { set opt [lindex $args $ii] switch -- [lindex $args $ii] { -devchar { set devchar [lindex $args [expr $ii+1]] } default { error "unrecognized option: $opt" } } } set f [open crash.tcl w] puts $f "sqlite3_crash_on_write $nDelay" puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte" puts $f "sqlite3 db test.db -vfs writecrash" puts $f "db eval {$zSql}" puts $f "set {} {}" close $f set r [catch { exec [info nameofexec] crash.tcl >@stdout } msg] # Windows/ActiveState TCL returns a slightly different # error message. We map that to the expected message |
︙ | ︙ |
Changes to test/tkt-78e04e52ea.test.
︙ | ︙ | |||
38 39 40 41 42 43 44 | do_test tkt-78e04-1.3 { execsql { CREATE INDEX i1 ON ""("" COLLATE nocase); } } {} do_test tkt-78e04-1.4 { execsql { | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | do_test tkt-78e04-1.3 { execsql { CREATE INDEX i1 ON ""("" COLLATE nocase); } } {} do_test tkt-78e04-1.4 { execsql { EXPLAIN QUERY PLAN SELECT "" FROM "" WHERE "" LIKE '1abc%'; } } {0 0 0 {SCAN TABLE USING COVERING INDEX i1}} do_test tkt-78e04-1.5 { execsql { DROP TABLE ""; SELECT name FROM sqlite_master; } |
︙ | ︙ |
Changes to test/tkt-80e031a00f.test.
︙ | ︙ | |||
16 17 18 19 20 21 22 | # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl | < < < < | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl # EVIDENCE-OF: R-52275-55503 When the right operand is an empty set, the # result of IN is false and the result of NOT IN is true, regardless of # the left operand and even if the left operand is NULL. # # EVIDENCE-OF: R-13595-45863 Note that SQLite allows the parenthesized # list of scalar values on the right-hand side of an IN or NOT IN # operator to be an empty list but most other SQL database database |
︙ | ︙ |
Changes to test/tkt3630.test.
︙ | ︙ | |||
19 20 21 22 23 24 25 | set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt3630-1 { db eval { CREATE TEMP TABLE temp1(a,b,c); | | | | 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 | set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt3630-1 { db eval { CREATE TEMP TABLE temp1(a,b,c); SELECT * FROM temp.sqlite_master WHERE sql GLOB '*TEMP*'; } } {} do_test tkt3630-2 { db eval { CREATE TABLE main1(a,b,c); CREATE TEMP TABLE temp2 AS SELECT * FROM main1; SELECT * FROM sqlite_temp_master WHERE sql GLOB '*TEMP*'; } } {} ifcapable altertable { do_test tkt3630-3 { db eval { ALTER TABLE temp2 ADD COLUMN d; ALTER TABLE temp2 RENAME TO temp2rn; SELECT name FROM temp.sqlite_master WHERE name LIKE 'temp2%'; } } {temp2rn} } finish_test |
Changes to test/tkt3810.test.
︙ | ︙ | |||
58 59 60 61 62 63 64 | } } {0 {}} # Trigger still exists in the sqlite_temp_master table, but now it is # an orphan. # do_test tkt3810-4 { | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | } } {0 {}} # Trigger still exists in the sqlite_temp_master table, but now it is # an orphan. # do_test tkt3810-4 { execsql {SELECT name FROM temp.sqlite_master ORDER BY name} } {r1} # Because it is an orphan, it cannot be dropped. # do_test tkt3810-5 { catchsql {DROP TRIGGER r1} } {1 {no such trigger: r1}} |
︙ | ︙ |
Added test/triggerF.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 | # 2017 January 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix triggerF ifcapable {!trigger} { finish_test return } foreach {tn sql log} { 1 { } { } 2 { CREATE TRIGGER trd AFTER DELETE ON t1 BEGIN INSERT INTO log VALUES(old.a || old.b || (SELECT count(*) FROM t1)); END; } {1one2 2two1 3three1} 3 { CREATE TRIGGER trd BEFORE DELETE ON t1 BEGIN INSERT INTO log VALUES(old.a || old.b || (SELECT count(*) FROM t1)); END; } {1one3 2two2 3three2} 4 { CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN INSERT INTO log VALUES(old.a || old.b || (SELECT count(*) FROM t1)); END; CREATE TRIGGER tr2 BEFORE DELETE ON t1 BEGIN INSERT INTO log VALUES(old.a || old.b || (SELECT count(*) FROM t1)); END; } {1one3 1one2 2two2 2two1 3three2 3three1} } { reset_db do_execsql_test 1.$tn.0 { PRAGMA recursive_triggers = on; CREATE TABLE t1(a INT PRIMARY KEY, b) WITHOUT ROWID; CREATE TABLE log(t); } execsql $sql do_execsql_test 1.$tn.1 { INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); INSERT INTO t1 VALUES(3, 'three'); DELETE FROM t1 WHERE a=1; INSERT OR REPLACE INTO t1 VALUES(2, 'three'); UPDATE OR REPLACE t1 SET a=3 WHERE a=2; } do_execsql_test 1.$tn.2 { SELECT * FROM log ORDER BY rowid; } $log } finish_test |
Added test/triggerG.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 | # 2017-03-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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix triggerG ifcapable {!trigger} { finish_test return } # Test cases for ticket # https://www.sqlite.org/src/tktview/06796225f59c057cd120f # # The OP_Once opcode was not working correctly for recursive triggers. # do_execsql_test 100 { PRAGMA recursive_triggers = 1; CREATE TABLE t1(a); CREATE INDEX i1 ON t1(a); INSERT INTO t1(a) VALUES(0),(2),(3),(8),(9); CREATE TABLE t2(b); CREATE TABLE t3(c); CREATE TRIGGER tr AFTER INSERT ON t3 BEGIN INSERT INTO t3 SELECT new.c+1 WHERE new.c<5; INSERT INTO t2 SELECT new.c*100+a FROM t1 WHERE a IN (1, 2, 3, 4); END; INSERT INTO t3 VALUES(2); SELECT c FROM t3 ORDER BY c;; } {2 3 4 5} do_execsql_test 110 { SELECT b FROM t2 ORDER BY b; } {202 203 302 303 402 403 502 503} do_execsql_test 200 { DELETE FROM t1; INSERT INTO t1(a) VALUES(0),(2),(3),(8),(9); DELETE FROM t2; DELETE FROM t3; DROP TRIGGER tr; CREATE TRIGGER tr AFTER INSERT ON t3 BEGIN INSERT INTO t3 SELECT new.c+1 WHERE new.c<5; INSERT INTO t2 SELECT new.c*10000+xx.a*100+yy.a FROM t1 AS xx, t1 AS yy WHERE xx.a IN (1,2,3,4) AND yy.a IN (2,3,4,5); END; INSERT INTO t3 VALUES(2); SELECT b FROM t2 ORDER BY b; } {20202 20203 20302 20303 30202 30203 30302 30303 40202 40203 40302 40303 50202 50203 50302 50303} finish_test |
Added test/unionvtab.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 | # 2017-07-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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is percentile.c extension # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix unionvtab ifcapable !vtab { finish_test return } load_static_extension db unionvtab #------------------------------------------------------------------------- # Warm body tests. # forcedelete test.db2 do_execsql_test 1.0 { ATTACH 'test.db2' AS aux; CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT); CREATE TABLE aux.t3(a INTEGER PRIMARY KEY, b TEXT); INSERT INTO t1 VALUES(1, 'one'), (2, 'two'), (3, 'three'); INSERT INTO t2 VALUES(10, 'ten'), (11, 'eleven'), (12, 'twelve'); INSERT INTO t3 VALUES(20, 'twenty'), (21, 'twenty-one'), (22, 'twenty-two'); } do_execsql_test 1.1 { CREATE VIRTUAL TABLE temp.uuu USING unionvtab( "VALUES(NULL, 't1', 1, 9), ('main', 't2', 10, 19), ('aux', 't3', 20, 29)" ); SELECT * FROM uuu; } { 1 one 2 two 3 three 10 ten 11 eleven 12 twelve 20 twenty 21 twenty-one 22 twenty-two } do_execsql_test 1.2 { PRAGMA table_info(uuu); } { 0 a INTEGER 0 {} 0 1 b TEXT 0 {} 0 } do_execsql_test 1.3 { SELECT * FROM uuu WHERE rowid = 3; SELECT * FROM uuu WHERE rowid = 11; } {3 three 11 eleven} do_execsql_test 1.4 { SELECT * FROM uuu WHERE rowid IN (12, 10, 2); } {2 two 10 ten 12 twelve} do_execsql_test 1.5 { SELECT * FROM uuu WHERE rowid BETWEEN 3 AND 11; } {3 three 10 ten 11 eleven} do_execsql_test 1.6 { SELECT * FROM uuu WHERE rowid BETWEEN 11 AND 15; } {11 eleven 12 twelve} do_execsql_test 1.7 { SELECT * FROM uuu WHERE rowid BETWEEN -46 AND 1500; } { 1 one 2 two 3 three 10 ten 11 eleven 12 twelve 20 twenty 21 twenty-one 22 twenty-two } do_execsql_test 1.8 { CREATE TABLE src(db, tbl, min, max); INSERT INTO src VALUES(NULL, 't1', 1, 9); INSERT INTO src VALUES('main', 't2', 10, 19); INSERT INTO src VALUES('aux', 't3', 20, 29); CREATE VIRTUAL TABLE temp.opp USING unionvtab(src); SELECT * FROM opp; } { 1 one 2 two 3 three 10 ten 11 eleven 12 twelve 20 twenty 21 twenty-one 22 twenty-two } do_execsql_test 1.9 { CREATE VIRTUAL TABLE temp.qll USING unionvtab( 'SELECT * FROM src WHERE db!=''xyz''' ); SELECT * FROM qll WHERE rowid BETWEEN 10 AND 21; } { 10 ten 11 eleven 12 twelve 20 twenty 21 twenty-one } #------------------------------------------------------------------------- # Error conditions. # # 2.1.*: Attempt to create a unionvtab table outside of the TEMP schema. # 2.2.*: Tables that do not exist. # 2.3.*: Non rowid tables. # 2.4.*: Tables with mismatched schemas. # 2.5.*: A unionvtab table with zero source tables. # do_catchsql_test 2.1.1 { CREATE VIRTUAL TABLE u1 USING unionvtab("VALUES(NULL, 't1', 1, 100)"); } {1 {unionvtab tables must be created in TEMP schema}} do_catchsql_test 2.1.2 { CREATE VIRTUAL TABLE main.u1 USING unionvtab("VALUES('', 't1', 1, 100)"); } {1 {unionvtab tables must be created in TEMP schema}} do_catchsql_test 2.1.3 { CREATE VIRTUAL TABLE aux.u1 USING unionvtab("VALUES('', 't1', 1, 100)"); } {1 {unionvtab tables must be created in TEMP schema}} do_catchsql_test 2.2.1 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES(NULL, 't555', 1, 100)"); } {1 {no such rowid table: t555}} do_catchsql_test 2.2.2 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES('aux', 't555', 1, 100)"); } {1 {no such rowid table: aux.t555}} do_catchsql_test 2.2.3 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES('xua', 't555', 1, 100)"); } {1 {no such rowid table: xua.t555}} do_execsql_test 2.3.0 { CREATE TABLE wr1(a, b, c PRIMARY KEY) WITHOUT ROWID; CREATE VIEW v1 AS SELECT * FROM t1; CREATE VIEW v2 AS SELECT _rowid_, * FROM t1; CREATE TABLE wr2(a, _rowid_ INTEGER, c PRIMARY KEY) WITHOUT ROWID; CREATE TABLE wr3(a, b, _rowid_ PRIMARY KEY) WITHOUT ROWID; } do_catchsql_test 2.3.1 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES('main', 'wr1', 1, 2)"); } {1 {no such rowid table: main.wr1}} do_catchsql_test 2.3.2 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES(NULL, 'v1', 1, 2)"); } {1 {no such rowid table: v1}} do_catchsql_test 2.3.3 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES(NULL, 'v2', 1, 2)"); } {1 {no such rowid table: v2}} do_catchsql_test 2.3.4 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES(NULL, 'wr2', 1, 2)"); } {1 {no such rowid table: wr2}} do_catchsql_test 2.3.5 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES(NULL, 'wr3', 1, 2)"); } {1 {no such rowid table: wr3}} do_execsql_test 2.4.0 { CREATE TABLE x1(a BLOB, b); CREATE TABLE x2(a BLOB, b); CREATE TEMP TABLE x3(a BLOB, b); CREATE TABLE aux.y1(one, two, three INTEGER PRIMARY KEY); CREATE TEMP TABLE y2(one, two, three INTEGER PRIMARY KEY); CREATE TABLE y3(one, two, three INTEGER PRIMARY KEY); } foreach {tn dbs res} { 1 {x1 x2 x3} {0 {}} 2 {y1 y2 y3} {0 {}} 3 {x1 y2 y3} {1 {source table schema mismatch}} 4 {x1 y2 x3} {1 {source table schema mismatch}} 5 {x1 x2 y3} {1 {source table schema mismatch}} } { set L [list] set iMin 0 foreach e $dbs { set E [split $e .] if {[llength $E]>1} { lappend L "('[lindex $E 0]', '[lindex $E 1]', $iMin, $iMin)" } else { lappend L "(NULL, '$e', $iMin, $iMin)" } incr iMin } set sql "CREATE VIRTUAL TABLE temp.a1 USING unionvtab(\"VALUES [join $L ,]\")" do_catchsql_test 2.4.$tn " DROP TABLE IF EXISTS temp.a1; CREATE VIRTUAL TABLE temp.a1 USING unionvtab(\"VALUES [join $L ,]\"); " $res } do_catchsql_test 2.5 { CREATE VIRTUAL TABLE temp.b1 USING unionvtab( [SELECT 'main', 'b1', 0, 100 WHERE 0] ) } {1 {no source tables configured}} foreach {tn sql} { 1 { VALUES('main', 't1', 10, 20), ('main', 't2', 30, 29) } 2 { VALUES('main', 't1', 10, 20), ('main', 't2', 15, 30) } } { do_catchsql_test 2.6.$tn " CREATE VIRTUAL TABLE temp.a1 USING unionvtab(`$sql`) " {1 {rowid range mismatch error}} } do_catchsql_test 2.7.1 { CREATE VIRTUAL TABLE temp.b1 USING unionvtab(1, 2, 3, 4) } {1 {wrong number of arguments for unionvtab}} #------------------------------------------------------------------------- # reset_db load_static_extension db unionvtab do_execsql_test 3.0 { CREATE TABLE tbl1(a INTEGER PRIMARY KEY, b); CREATE TABLE tbl2(a INTEGER PRIMARY KEY, b); CREATE TABLE tbl3(a INTEGER PRIMARY KEY, b); WITH ss(ii) AS ( SELECT 1 UNION ALL SELECT ii+1 FROM ss WHERE ii<100 ) INSERT INTO tbl1 SELECT ii, '1.' || ii FROM ss; WITH ss(ii) AS ( SELECT 1 UNION ALL SELECT ii+1 FROM ss WHERE ii<100 ) INSERT INTO tbl2 SELECT ii, '2.' || ii FROM ss; WITH ss(ii) AS ( SELECT 1 UNION ALL SELECT ii+1 FROM ss WHERE ii<100 ) INSERT INTO tbl3 SELECT ii, '3.' || ii FROM ss; CREATE VIRTUAL TABLE temp.uu USING unionvtab( "VALUES(NULL,'tbl2', 26, 74), (NULL,'tbl3', 75, 100), (NULL,'tbl1', 1, 25)" ); } do_execsql_test 3.1 { SELECT * FROM uu WHERE rowid = 10; } {10 {1.10}} do_execsql_test 3.2 { SELECT * FROM uu WHERE rowid = 25; } {25 {1.25}} do_execsql_test 3.3 { SELECT count(*) FROM uu WHERE rowid <= 24 } {24} # The following queries get the "wrong" answers. This is because the # module assumes that each source table contains rowids from only within # the range specified. For example, (rowid <= 25) matches 100 rows. This # is because the module implements (rowid <= 25) as a full table scan # of tbl1 only. do_execsql_test 3.4.1 { SELECT count(*) FROM uu WHERE rowid <= 25 } {100} do_execsql_test 3.4.2 { SELECT count(*) FROM uu WHERE rowid <= 26 } {126} do_execsql_test 3.4.3 { SELECT count(*) FROM uu WHERE rowid <= 73 } {173} do_execsql_test 3.4.4 { SELECT count(*) FROM uu WHERE rowid <= 74 } {200} do_execsql_test 3.4.5 { SELECT count(*) FROM uu WHERE rowid <= 75 } {275} do_execsql_test 3.4.6 { SELECT count(*) FROM uu WHERE rowid <= 99 } {299} do_execsql_test 3.4.7 { SELECT count(*) FROM uu WHERE rowid <= 100 } {300} do_execsql_test 3.4.8 { SELECT count(*) FROM uu WHERE rowid <= 101 } {300} do_execsql_test 3.5.1 { SELECT count(*) FROM uu WHERE rowid < 25 } {24} do_execsql_test 3.5.2 { SELECT count(*) FROM uu WHERE rowid < 26 } {100} do_execsql_test 3.5.3 { SELECT count(*) FROM uu WHERE rowid < 27 } {126} do_execsql_test 3.5.4 { SELECT count(*) FROM uu WHERE rowid < 73 } {172} do_execsql_test 3.5.5 { SELECT count(*) FROM uu WHERE rowid < 74 } {173} do_execsql_test 3.5.6 { SELECT count(*) FROM uu WHERE rowid < 75 } {200} do_execsql_test 3.5.7 { SELECT count(*) FROM uu WHERE rowid < 76 } {275} do_execsql_test 3.5.8 { SELECT count(*) FROM uu WHERE rowid < 99 } {298} do_execsql_test 3.5.9 { SELECT count(*) FROM uu WHERE rowid < 100 } {299} do_execsql_test 3.5.10 { SELECT count(*) FROM uu WHERE rowid < 101 } {300} do_execsql_test 3.6.1 { SELECT count(*) FROM uu WHERE rowid > 24 } {276} do_execsql_test 3.6.1 { SELECT count(*) FROM uu WHERE rowid > 25 } {200} do_execsql_test 3.6.2 { SELECT count(*) FROM uu WHERE rowid > 26 } {174} do_execsql_test 3.6.3 { SELECT count(*) FROM uu WHERE rowid > 27 } {173} do_execsql_test 3.6.4 { SELECT count(*) FROM uu WHERE rowid > 73 } {127} do_execsql_test 3.6.5 { SELECT count(*) FROM uu WHERE rowid > 74 } {100} do_execsql_test 3.6.6 { SELECT count(*) FROM uu WHERE rowid > 75 } {25} do_execsql_test 3.6.7 { SELECT count(*) FROM uu WHERE rowid > 76 } {24} do_execsql_test 3.6.8 { SELECT count(*) FROM uu WHERE rowid > 99 } {1} do_execsql_test 3.6.9 { SELECT count(*) FROM uu WHERE rowid > 100 } {0} do_execsql_test 3.6.10 { SELECT count(*) FROM uu WHERE rowid > 101 } {0} do_execsql_test 3.7.1 { SELECT count(*) FROM uu WHERE rowid >= 24 } {277} do_execsql_test 3.7.1 { SELECT count(*) FROM uu WHERE rowid >= 25 } {276} do_execsql_test 3.7.2 { SELECT count(*) FROM uu WHERE rowid >= 26 } {200} do_execsql_test 3.7.3 { SELECT count(*) FROM uu WHERE rowid >= 27 } {174} do_execsql_test 3.7.4 { SELECT count(*) FROM uu WHERE rowid >= 73 } {128} do_execsql_test 3.7.5 { SELECT count(*) FROM uu WHERE rowid >= 74 } {127} do_execsql_test 3.7.6 { SELECT count(*) FROM uu WHERE rowid >= 75 } {100} do_execsql_test 3.7.7 { SELECT count(*) FROM uu WHERE rowid >= 76 } {25} do_execsql_test 3.7.8 { SELECT count(*) FROM uu WHERE rowid >= 99 } {2} do_execsql_test 3.7.9 { SELECT count(*) FROM uu WHERE rowid >= 100 } {1} do_execsql_test 3.7.10 { SELECT count(*) FROM uu WHERE rowid >= 101 } {0} set L [expr 9223372036854775807] set S [expr -9223372036854775808] do_execsql_test 3.8.1 { SELECT count(*) FROM uu WHERE rowid >= $S } {300} do_execsql_test 3.8.2 { SELECT count(*) FROM uu WHERE rowid > $S } {300} do_execsql_test 3.8.3 { SELECT count(*) FROM uu WHERE rowid <= $S } {0} do_execsql_test 3.8.4 { SELECT count(*) FROM uu WHERE rowid < $S } {0} do_execsql_test 3.9.1 { SELECT count(*) FROM uu WHERE rowid >= $L } {0} do_execsql_test 3.9.2 { SELECT count(*) FROM uu WHERE rowid > $L } {0} do_execsql_test 3.9.3 { SELECT count(*) FROM uu WHERE rowid <= $L } {300} do_execsql_test 3.9.4 { SELECT count(*) FROM uu WHERE rowid < $L } {300} do_execsql_test 3.10.1 { SELECT count(*) FROM uu WHERE a < 25 } {24} do_execsql_test 3.10.2 { SELECT count(*) FROM uu WHERE a < 26 } {100} do_execsql_test 3.10.3 { SELECT count(*) FROM uu WHERE a < 27 } {126} do_execsql_test 3.10.4 { SELECT count(*) FROM uu WHERE a < 73 } {172} do_execsql_test 3.10.5 { SELECT count(*) FROM uu WHERE a < 74 } {173} do_execsql_test 3.10.6 { SELECT count(*) FROM uu WHERE a < 75 } {200} do_execsql_test 3.10.7 { SELECT count(*) FROM uu WHERE a < 76 } {275} do_execsql_test 3.10.8 { SELECT count(*) FROM uu WHERE a < 99 } {298} do_execsql_test 3.10.9 { SELECT count(*) FROM uu WHERE a < 100 } {299} do_execsql_test 3.10.10 { SELECT count(*) FROM uu WHERE a < 101 } {300} #------------------------------------------------------------------------- # do_execsql_test 4.0 { CREATE TABLE s1(k INTEGER PRIMARY KEY, v); INSERT INTO s1 VALUES($S, 'one'); INSERT INTO s1 VALUES($S+1, 'two'); INSERT INTO s1 VALUES($S+2, 'three'); CREATE TABLE l1(k INTEGER PRIMARY KEY, v); INSERT INTO l1 VALUES($L, 'six'); INSERT INTO l1 VALUES($L-1, 'five'); INSERT INTO l1 VALUES($L-2, 'four'); CREATE VIRTUAL TABLE temp.sl USING unionvtab( "SELECT NULL, 'l1', 0, 9223372036854775807 UNION ALL SELECT NULL, 's1', -9223372036854775808, -1" ); } do_execsql_test 4.1 { SELECT * FROM sl; } { -9223372036854775808 one -9223372036854775807 two -9223372036854775806 three 9223372036854775805 four 9223372036854775806 five 9223372036854775807 six } foreach {k v} { -9223372036854775808 one -9223372036854775807 two -9223372036854775806 three 9223372036854775805 four 9223372036854775806 five 9223372036854775807 six } { do_execsql_test 4.2.$v { SELECT * FROM sl WHERE rowid=$k } [list $k $v] } do_execsql_test 4.3.1 { SELECT * FROM sl WHERE rowid>-9223372036854775808 } { -9223372036854775807 two -9223372036854775806 three 9223372036854775805 four 9223372036854775806 five 9223372036854775807 six } do_execsql_test 4.3.2 { SELECT * FROM sl WHERE rowid>=-9223372036854775808 } { -9223372036854775808 one -9223372036854775807 two -9223372036854775806 three 9223372036854775805 four 9223372036854775806 five 9223372036854775807 six } do_execsql_test 4.3.3 { SELECT * FROM sl WHERE rowid<=-9223372036854775808 } { -9223372036854775808 one } do_execsql_test 4.3.4 { SELECT * FROM sl WHERE rowid<-9223372036854775808 } { } do_execsql_test 4.4.1 { SELECT * FROM sl WHERE rowid<9223372036854775807 } { -9223372036854775808 one -9223372036854775807 two -9223372036854775806 three 9223372036854775805 four 9223372036854775806 five } do_execsql_test 4.4.2 { SELECT * FROM sl WHERE rowid<=9223372036854775807 } { -9223372036854775808 one -9223372036854775807 two -9223372036854775806 three 9223372036854775805 four 9223372036854775806 five 9223372036854775807 six } do_execsql_test 4.4.3 { SELECT * FROM sl WHERE rowid>=9223372036854775807 } { 9223372036854775807 six } do_execsql_test 4.4.4 { SELECT * FROM sl WHERE rowid>9223372036854775807 } { } #------------------------------------------------------------------------- # More than 8 source tables. # do_execsql_test 5.0 { CREATE TABLE c0(one, two INTEGER PRIMARY KEY); CREATE TABLE c1(one, two INTEGER PRIMARY KEY); CREATE TABLE c2(one, two INTEGER PRIMARY KEY); CREATE TABLE c3(one, two INTEGER PRIMARY KEY); CREATE TABLE c4(one, two INTEGER PRIMARY KEY); CREATE TABLE c5(one, two INTEGER PRIMARY KEY); CREATE TABLE c6(one, two INTEGER PRIMARY KEY); CREATE TABLE c7(one, two INTEGER PRIMARY KEY); CREATE TABLE c8(one, two INTEGER PRIMARY KEY); CREATE TABLE c9(one, two INTEGER PRIMARY KEY); INSERT INTO c0 VALUES('zero', 0); INSERT INTO c1 VALUES('one', 1); INSERT INTO c2 VALUES('two', 2); INSERT INTO c3 VALUES('three', 3); INSERT INTO c4 VALUES('four', 4); INSERT INTO c5 VALUES('five', 5); INSERT INTO c6 VALUES('six', 6); INSERT INTO c7 VALUES('seven', 7); INSERT INTO c8 VALUES('eight', 8); INSERT INTO c9 VALUES('nine', 9); CREATE VIRTUAL TABLE temp.cc USING unionvtab([ SELECT 'main', 'c9', 9, 9 UNION ALL SELECT 'main', 'c8', 8, 8 UNION ALL SELECT 'main', 'c7', 7, 7 UNION ALL SELECT 'main', 'c6', 6, 6 UNION ALL SELECT 'main', 'c5', 5, 5 UNION ALL SELECT 'main', 'c4', 4, 4 UNION ALL SELECT 'main', 'c3', 3, 3 UNION ALL SELECT 'main', 'c2', 2, 2 UNION ALL SELECT 'main', 'c1', 1, 1 UNION ALL SELECT 'main', 'c0', 0, 0 ]); SELECT sum(two) FROM cc; } {45} do_execsql_test 5.1 { SELECT one FROM cc WHERE one>='seven' } {zero two three six seven} do_execsql_test 5.2 { SELECT y.one FROM cc AS x, cc AS y WHERE x.one=y.one AND x.rowid>5 } {six seven eight nine} do_execsql_test 5.3 { SELECT cc.one FROM c4, cc WHERE cc.rowid>c4.rowid } {five six seven eight nine} do_execsql_test 5.4 { SELECT * FROM cc WHERE two LIKE '6' } {six 6} finish_test |
Added test/unionvtabfault.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 | # 2017-07-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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is percentile.c extension # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix unionvtabfault ifcapable !vtab { finish_test return } forcedelete test.db2 do_execsql_test 1.0 { ATTACH 'test.db2' AS aux; CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT); CREATE TABLE aux.t3(a INTEGER PRIMARY KEY, b TEXT); INSERT INTO t1 VALUES(1, 'one'), (2, 'two'), (3, 'three'); INSERT INTO t2 VALUES(10, 'ten'), (11, 'eleven'), (12, 'twelve'); INSERT INTO t3 VALUES(20, 'twenty'), (21, 'twenty-one'), (22, 'twenty-two'); } faultsim_save_and_close do_faultsim_test 1.1 -faults * -prep { faultsim_restore_and_reopen load_static_extension db unionvtab execsql { ATTACH 'test.db2' AS aux; } execsql { CREATE TEMP TABLE xyz(x); } } -body { execsql { CREATE VIRTUAL TABLE temp.uuu USING unionvtab( "VALUES(NULL, 't1', 1, 9), ('main', 't2', 10, 19), ('aux', 't3', 20, 29)" ); } } -test { faultsim_test_result {0 {}} \ {1 {vtable constructor failed: uuu}} \ {1 {sql error: interrupted}} } faultsim_restore_and_reopen load_static_extension db unionvtab execsql { ATTACH 'test.db2' AS aux; } execsql { CREATE TEMP TABLE xyz(x); } execsql { CREATE VIRTUAL TABLE temp.uuu USING unionvtab( "VALUES(NULL, 't1', 1, 9), ('main', 't2', 10, 19), ('aux', 't3', 20, 29)" ); } do_faultsim_test 1.2 -faults oom* -prep { } -body { execsql { SELECT * FROM uuu } } -test { faultsim_test_result {0 {1 one 2 two 3 three 10 ten 11 eleven 12 twelve 20 twenty 21 twenty-one 22 twenty-two}} } #------------------------------------------------------------------------- # Error while registering the two vtab modules. do_faultsim_test 2.0 -faults * -prep { catch { db close } sqlite3 db :memory: } -body { load_static_extension db unionvtab } -test { faultsim_test_result {0 {}} {1 {initialization of unionvtab failed: }} } finish_test |
Added test/update2.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 | # 2017 January 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix update2 db func repeat [list string repeat] #------------------------------------------------------------------------- # 1.1.* A one-pass UPDATE that does balance() operations on the IPK index # that it is scanning. # # 1.2.* Same again, but with a WITHOUT ROWID table. # set nrow [expr 10] do_execsql_test 1.1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b); CREATE TABLE t2(a INTEGER PRIMARY KEY, b); WITH s(i) AS ( SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<$nrow ) INSERT INTO t1(b) SELECT char((i % 26) + 65) FROM s; INSERT INTO t2 SELECT * FROM t1; } do_execsql_test 1.1.1 { UPDATE t1 SET b = repeat(b, 100) } do_execsql_test 1.1.2 { SELECT * FROM t1; } [db eval { SELECT a, repeat(b, 100) FROM t2 }] do_execsql_test 1.2.0 { DROP TABLE t1; CREATE TABLE t1(a INT PRIMARY KEY, b) WITHOUT ROWID; WITH s(i) AS ( SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<$nrow ) INSERT INTO t1(a, b) SELECT i+1, char((i % 26) + 65) FROM s; } #explain_i { UPDATE t1 SET b = repeat(b, 100) } do_execsql_test 1.2.1 { UPDATE t1 SET b = repeat(b, 100) } do_execsql_test 1.2.2 { SELECT * FROM t1; } [db eval { SELECT a, repeat(b, 100) FROM t2 }] #------------------------------------------------------------------------- # A one-pass UPDATE that does balance() operations on the IPK index # that it is scanning. # do_execsql_test 2.1 { CREATE TABLE t3(a PRIMARY KEY, b, c); CREATE INDEX t3i ON t3(b); } {} do_execsql_test 2.2 { UPDATE t3 SET c=1 WHERE b=? } {} do_execsql_test 2.3 { UPDATE t3 SET c=1 WHERE rowid=? } {} #------------------------------------------------------------------------- # do_execsql_test 3.0 { CREATE TABLE t4(a PRIMARY KEY, b, c) WITHOUT ROWID; CREATE INDEX t4c ON t4(c); INSERT INTO t4 VALUES(1, 2, 3); INSERT INTO t4 VALUES(2, 3, 4); } do_execsql_test 3.1 { UPDATE t4 SET c=c+2 WHERE c>2; SELECT a, c FROM t4 ORDER BY a; } {1 5 2 6} #------------------------------------------------------------------------- # foreach {tn sql} { 1 { CREATE TABLE b1(a INTEGER PRIMARY KEY, b, c); CREATE TABLE c1(a INTEGER PRIMARY KEY, b, c, d) } 2 { CREATE TABLE b1(a INT PRIMARY KEY, b, c) WITHOUT ROWID; CREATE TABLE c1(a INT PRIMARY KEY, b, c, d) WITHOUT ROWID; } } { execsql { DROP TABLE IF EXISTS b1; DROP TABLE IF EXISTS c1; } execsql $sql do_execsql_test 4.$tn.0 { CREATE UNIQUE INDEX b1c ON b1(c); INSERT INTO b1 VALUES(1, 'a', 1); INSERT INTO b1 VALUES(2, 'b', 15); INSERT INTO b1 VALUES(3, 'c', 3); INSERT INTO b1 VALUES(4, 'd', 4); INSERT INTO b1 VALUES(5, 'e', 5); INSERT INTO b1 VALUES(6, 'f', 6); INSERT INTO b1 VALUES(7, 'g', 7); } do_execsql_test 4.$tn.1 { UPDATE OR REPLACE b1 SET c=c+10 WHERE a BETWEEN 4 AND 7; SELECT * FROM b1 ORDER BY a; } { 1 a 1 3 c 3 4 d 14 5 e 15 6 f 16 7 g 17 } do_execsql_test 4.$tn.2 { CREATE INDEX c1d ON c1(d, b); CREATE UNIQUE INDEX c1c ON c1(c, b); INSERT INTO c1 VALUES(1, 'a', 1, 1); INSERT INTO c1 VALUES(2, 'a', 15, 2); INSERT INTO c1 VALUES(3, 'a', 3, 3); INSERT INTO c1 VALUES(4, 'a', 4, 4); INSERT INTO c1 VALUES(5, 'a', 5, 5); INSERT INTO c1 VALUES(6, 'a', 6, 6); INSERT INTO c1 VALUES(7, 'a', 7, 7); } do_execsql_test 4.$tn.3 { UPDATE OR REPLACE c1 SET c=c+10 WHERE d BETWEEN 4 AND 7; SELECT * FROM c1 ORDER BY a; } { 1 a 1 1 3 a 3 3 4 a 14 4 5 a 15 5 6 a 16 6 7 a 17 7 } do_execsql_test 4.$tn.4 { PRAGMA integrity_check } ok do_execsql_test 4.$tn.5 { DROP INDEX c1d; DROP INDEX c1c; DELETE FROM c1; INSERT INTO c1 VALUES(1, 'a', 1, 1); INSERT INTO c1 VALUES(2, 'a', 15, 2); INSERT INTO c1 VALUES(3, 'a', 3, 3); INSERT INTO c1 VALUES(4, 'a', 4, 4); INSERT INTO c1 VALUES(5, 'a', 5, 5); INSERT INTO c1 VALUES(6, 'a', 6, 6); INSERT INTO c1 VALUES(7, 'a', 7, 7); CREATE INDEX c1d ON c1(d); CREATE UNIQUE INDEX c1c ON c1(c); } do_execsql_test 4.$tn.6 { UPDATE OR REPLACE c1 SET c=c+10 WHERE d BETWEEN 4 AND 7; SELECT * FROM c1 ORDER BY a; } { 1 a 1 1 3 a 3 3 4 a 14 4 5 a 15 5 6 a 16 6 7 a 17 7 } } #------------------------------------------------------------------------- # do_execsql_test 5.0 { CREATE TABLE x1(a INTEGER PRIMARY KEY, b, c); CREATE INDEX x1c ON x1(b, c); INSERT INTO x1 VALUES(1, 'a', 1); INSERT INTO x1 VALUES(2, 'a', 2); INSERT INTO x1 VALUES(3, 'a', 3); } do_execsql_test 5.1.1 { UPDATE x1 SET c=c+1 WHERE b='a'; } do_execsql_test 5.1.2 { SELECT * FROM x1; } {1 a 2 2 a 3 3 a 4} do_test 5.2 { catch { array unset A } db eval { EXPLAIN UPDATE x1 SET c=c+1 WHERE b='a' } { incr A($opcode) } set A(NotExists) } {1} finish_test |
Changes to test/uri.test.
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 | 15 test.db?mork=1#boris test.db?mork=1#boris 16 file://localhostPWD/test.db%3Fhello test.db?hello } { ifcapable !curdir { if {$tn==3} break } if {$tcl_platform(platform)=="windows"} { # # NOTE: Due to limits on legal characters for file names imposed by # Windows, we must skip the final two tests here (i.e. the # question mark is illegal in a file name on Windows). # | > > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 15 test.db?mork=1#boris test.db?mork=1#boris 16 file://localhostPWD/test.db%3Fhello test.db?hello } { ifcapable !curdir { if {$tn==3} break } ifcapable uri_00_error { if {[string first %00 $uri]>=0} continue } if {$tcl_platform(platform)=="windows"} { # # NOTE: Due to limits on legal characters for file names imposed by # Windows, we must skip the final two tests here (i.e. the # question mark is illegal in a file name on Windows). # |
︙ | ︙ | |||
119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 10 file:test.db?hello=%00world&xyz= {hello {} xyz {}} 11 file:test.db?=#ravada {} 12 file:test.db?&&&&&&&&hello=world&&&&&&& {hello world} 13 test.db?&&&&&&&&hello=world&&&&&&& {} 14 http:test.db?hello&world {} } { if {$tcl_platform(platform) == "windows" && $tn>12} { continue } set ::arglist "" set DB [sqlite3_open $uri] | > > > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 10 file:test.db?hello=%00world&xyz= {hello {} xyz {}} 11 file:test.db?=#ravada {} 12 file:test.db?&&&&&&&&hello=world&&&&&&& {hello world} 13 test.db?&&&&&&&&hello=world&&&&&&& {} 14 http:test.db?hello&world {} } { ifcapable uri_00_error { if {[string first %00 $uri]>=0} continue } if {$tcl_platform(platform) == "windows" && $tn>12} { continue } set ::arglist "" set DB [sqlite3_open $uri] |
︙ | ︙ |
Added test/uri2.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 | # 2016 October 26 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Tests for SQLITE_ENABLE_URI_00_ERROR builds. set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !uri_00_error { finish_test return } set testprefix uri2 db close sqlite3_shutdown sqlite3_config_uri 1 foreach {tn uri} { 1 file:test.db%00trailing 2 file:test.db?%00trailing=1 3 file:test.db?trailing=1%00 4 file:test.db?trailing=1&abc%00def 5 file:test.db?trailing=1&abc%00def } { do_test 1.$tn.1 { set rc [catch { sqlite3 db $uri } msg] list $rc $msg } {1 {unexpected %00 in uri}} do_test 1.$tn.2 { set DB2 [sqlite3_open $uri] sqlite3_errcode $DB2 } {SQLITE_ERROR} catch { sqlite3_close $DB2 } do_test 1.$tn.2 { sqlite3 db "" catchsql { ATTACH $uri AS aux } } {1 {unexpected %00 in uri}} do_test 1.$tn.3 { sqlite3_errcode db } {SQLITE_ERROR} catch { db close } } reset_db do_test 2.0 { expr {[lsearch [execsql {PRAGMA compile_options}] ENABLE_URI_00_ERROR] >= 0} } 1 finish_test |
Changes to test/vacuum5.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #*********************************************************************** # # This file implements a test for VACUUM on attached databases. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If the VACUUM statement is disabled in the current build, skip all # the tests in this file. # ifcapable !vacuum { finish_test return | > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #*********************************************************************** # # This file implements a test for VACUUM on attached databases. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix vacuum5 # If the VACUUM statement is disabled in the current build, skip all # the tests in this file. # ifcapable !vacuum { finish_test return |
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 | do_execsql_test vacuum5-1.4.2 { PRAGMA temp.page_count; } $sizeTemp do_catchsql_test vacuum5-2.0 { VACUUM olaf; } {1 {unknown database olaf}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_execsql_test vacuum5-1.4.2 { PRAGMA temp.page_count; } $sizeTemp do_catchsql_test vacuum5-2.0 { VACUUM olaf; } {1 {unknown database olaf}} #------------------------------------------------------------------------- # Test that a temp file is opened as part of VACUUM. # if {$::TEMP_STORE<3 && [permutation]!="inmemory_journal"} { db close testvfs tvfs tvfs filter xOpen tvfs script open_cb forcedelete test.db set ::openfiles [list] proc open_cb {method args} { lappend ::openfiles [file tail [lindex $args 0]] } sqlite3 db test.db -vfs tvfs do_execsql_test 3.0 { PRAGMA temp_store = file; PRAGMA page_size = 1024; PRAGMA cache_size = 50; CREATE TABLE t1(i INTEGER PRIMARY KEY, j UNIQUE); WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1000 ) INSERT INTO t1 SELECT NULL, randomblob(100) FROM s; } do_execsql_test 3.1 { VACUUM } db close tvfs delete do_test 3.2 { lrange $::openfiles 0 4 } {test.db test.db-journal test.db-journal {} test.db-journal} } finish_test |
Changes to test/vtab1.test.
︙ | ︙ | |||
944 945 946 947 948 949 950 | } {1 {vtable constructor failed: e2}} do_test vtab1.10-2 { set rc [catch { set ptr [sqlite3_connection_pointer db] sqlite3_declare_vtab $ptr {CREATE TABLE abc(a, b, c)} } msg] list $rc $msg | | | | 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 | } {1 {vtable constructor failed: e2}} do_test vtab1.10-2 { set rc [catch { set ptr [sqlite3_connection_pointer db] sqlite3_declare_vtab $ptr {CREATE TABLE abc(a, b, c)} } msg] list $rc $msg } {1 {bad parameter or other API misuse}} do_test vtab1.10-3 { set ::echo_module_begin_fail r catchsql { INSERT INTO e VALUES(1, 2, 3); } } {1 {SQL logic error}} do_test vtab1.10-4 { catch {execsql { EXPLAIN SELECT * FROM e WHERE rowid = 2; EXPLAIN QUERY PLAN SELECT * FROM e WHERE rowid = 2 ORDER BY rowid; }} } {0} |
︙ | ︙ | |||
1291 1292 1293 1294 1295 1296 1297 | unset -nocomplain echo_module_begin_fail do_execsql_test 18.1.0 { CREATE TABLE t6(a, b TEXT); CREATE INDEX i6 ON t6(b, a); INSERT INTO t6 VALUES(1, 'Peter'); INSERT INTO t6 VALUES(2, 'Andrew'); | | | | | | | | | | | | | | | | 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 | unset -nocomplain echo_module_begin_fail do_execsql_test 18.1.0 { CREATE TABLE t6(a, b TEXT); CREATE INDEX i6 ON t6(b, a); INSERT INTO t6 VALUES(1, 'Peter'); INSERT INTO t6 VALUES(2, 'Andrew'); INSERT INTO t6 VALUES(3, '8James'); INSERT INTO t6 VALUES(4, '8John'); INSERT INTO t6 VALUES(5, 'Phillip'); INSERT INTO t6 VALUES(6, 'Bartholomew'); CREATE VIRTUAL TABLE e6 USING echo(t6); } foreach {tn sql res filter} { 1.1 "SELECT a FROM e6 WHERE b>'8James'" {4 2 6 1 5} {xFilter {SELECT rowid, a, b FROM 't6' WHERE b > ?} 8James} 1.2 "SELECT a FROM e6 WHERE b>='8' AND b<'9'" {3 4} {xFilter {SELECT rowid, a, b FROM 't6' WHERE b >= ? AND b < ?} 8 9} 1.3 "SELECT a FROM e6 WHERE b LIKE '8J%'" {3 4} {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8J%} 1.4 "SELECT a FROM e6 WHERE b LIKE '8j%'" {3 4} {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8j%} } { set echo_module {} do_execsql_test 18.$tn.1 $sql $res do_test 18.$tn.2 { lrange $::echo_module 2 end } $filter } do_execsql_test 18.2.0 { PRAGMA case_sensitive_like = ON } foreach {tn sql res filter} { 2.1 "SELECT a FROM e6 WHERE b LIKE '8J%'" {3 4} {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8J%} 2.2 "SELECT a FROM e6 WHERE b LIKE '8j%'" {} {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8j%} } { set echo_module {} do_execsql_test 18.$tn.1 $sql $res do_test 18.$tn.2 { lrange $::echo_module 2 end } $filter } do_execsql_test 18.2.x { PRAGMA case_sensitive_like = OFF } |
︙ | ︙ |
Changes to test/vtab2.test.
︙ | ︙ | |||
56 57 58 59 60 61 62 | } {6} register_tclvar_module [sqlite3_connection_pointer db] do_test vtab2-2.1 { set ::abc 123 execsql { CREATE VIRTUAL TABLE vars USING tclvar; | | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | } {6} register_tclvar_module [sqlite3_connection_pointer db] do_test vtab2-2.1 { set ::abc 123 execsql { CREATE VIRTUAL TABLE vars USING tclvar; SELECT name, arrayname, value FROM vars WHERE name='abc'; } } [list abc "" 123] do_test vtab2-2.2 { set A(1) 1 set A(2) 4 set A(3) 9 execsql { SELECT name, arrayname, value FROM vars WHERE name='A'; } } [list A 1 1 A 2 4 A 3 9] unset -nocomplain result unset -nocomplain var set result {} foreach var [lsort [info vars tcl_*]] { catch {lappend result $var [set $var]} |
︙ | ︙ |
Changes to test/vtab7.test.
︙ | ︙ | |||
158 159 160 161 162 163 164 | # unset -nocomplain ::callbacks(xSync,abc) # set ::callbacks(xCommit,abc) { # execsql { INSERT INTO log VALUES('hello') } # } # catchsql { # INSERT INTO abc2 VALUES(1, 2, 3); # } | | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | # unset -nocomplain ::callbacks(xSync,abc) # set ::callbacks(xCommit,abc) { # execsql { INSERT INTO log VALUES('hello') } # } # catchsql { # INSERT INTO abc2 VALUES(1, 2, 3); # } # } {1 {bad parameter or other API misuse}} # These tests, vtab7-4.*, test that an SQLITE_LOCKED error is returned # if an attempt to write to a virtual module table or create a new # virtual table from within an xSync() callback. do_test vtab7-4.1 { execsql { CREATE TABLE def(d, e, f); |
︙ | ︙ |
Changes to test/vtabE.test.
︙ | ︙ | |||
35 36 37 38 39 40 41 | set vtabE2(c) d do_test vtabE-1 { db eval { CREATE VIRTUAL TABLE t1 USING tclvar; CREATE VIRTUAL TABLE t2 USING tclvar; CREATE TABLE t3(a INTEGER PRIMARY KEY, b); | > > | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | set vtabE2(c) d do_test vtabE-1 { db eval { CREATE VIRTUAL TABLE t1 USING tclvar; CREATE VIRTUAL TABLE t2 USING tclvar; CREATE TABLE t3(a INTEGER PRIMARY KEY, b); SELECT t1.name, t1.arrayname, t1.value, t2.name, t2.arrayname, t2.value, abs(t3.b + abs(t2.value + abs(t1.value))) FROM t1 LEFT JOIN t2 ON t2.name = t1.arrayname LEFT JOIN t3 ON t3.a=t2.value WHERE t1.name = 'vtabE' ORDER BY t1.value, t2.value; } } {vtabE vtabE1 11 vtabE1 w x {} vtabE vtabE1 11 vtabE1 y z {} vtabE vtabE2 22 vtabE2 a b {} vtabE vtabE2 22 vtabE2 c d {}} |
︙ | ︙ |
Changes to test/vtabH.test.
︙ | ︙ | |||
27 28 29 30 31 32 33 | do_execsql_test 1.0 { CREATE TABLE t6(a, b TEXT); CREATE INDEX i6 ON t6(b, a); CREATE VIRTUAL TABLE e6 USING echo(t6); } foreach {tn sql expect} { | | | | | | | 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 | do_execsql_test 1.0 { CREATE TABLE t6(a, b TEXT); CREATE INDEX i6 ON t6(b, a); CREATE VIRTUAL TABLE e6 USING echo(t6); } foreach {tn sql expect} { 1 "SELECT * FROM e6 WHERE b LIKE '8abc'" { xBestIndex {SELECT rowid, a, b FROM 't6' WHERE b like ?} xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} 8abc } 2 "SELECT * FROM e6 WHERE b GLOB '8abc'" { xBestIndex {SELECT rowid, a, b FROM 't6' WHERE b glob ?} xFilter {SELECT rowid, a, b FROM 't6' WHERE b glob ?} 8abc } } { do_test 1.$tn { set echo_module {} execsql $sql set ::echo_module } [list {*}$expect] } #-------------------------------------------------------------------------- register_tclvar_module db set ::xyz 10 do_execsql_test 2.0 { CREATE VIRTUAL TABLE vars USING tclvar; SELECT name, arrayname, value FROM vars WHERE name = 'xyz'; } {xyz {} 10} set x1 aback set x2 abaft set x3 abandon set x4 abandonint set x5 babble |
︙ | ︙ | |||
116 117 118 119 120 121 122 123 124 125 | [regexp -nocase -- {^[A-Z]:} $drive]} { reset_db register_fs_module db do_execsql_test 3.0 { SELECT name FROM fsdir WHERE dir = '.' AND name = 'test.db'; SELECT name FROM fsdir WHERE dir = '.' AND name = '.' } {test.db .} proc list_root_files {} { if {$::tcl_platform(platform) eq "windows"} { | > > > > > > > > | > | > > | | | > | > > | | | < < | > | > | | > | > | | > | | | | | | > | | | | | | | | | | | | | > | 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 | [regexp -nocase -- {^[A-Z]:} $drive]} { reset_db register_fs_module db do_execsql_test 3.0 { SELECT name FROM fsdir WHERE dir = '.' AND name = 'test.db'; SELECT name FROM fsdir WHERE dir = '.' AND name = '.' } {test.db .} proc sort_files { names {nocase false} } { if {$nocase && $::tcl_platform(platform) eq "windows"} { return [lsort -nocase $names] } else { return [lsort $names] } } proc list_root_files {} { if {$::tcl_platform(platform) eq "windows"} { set res [list]; set dir $::env(fstreeDrive)/; set names [list] eval lappend names [glob -nocomplain -directory $dir -- *] foreach name $names { if {[string index [file tail $name] 0] eq "."} continue if {[file attributes $name -hidden]} continue if {[file attributes $name -system]} continue lappend res $name } return [sort_files $res true] } else { return [sort_files [string map {/ {}} [glob -nocomplain -- /*]]] } } proc list_files { pattern } { if {$::tcl_platform(platform) eq "windows"} { set res [list]; set names [list] eval lappend names [glob -nocomplain -- $pattern] foreach name $names { if {[string index [file tail $name] 0] eq "."} continue if {[file attributes $name -hidden]} continue if {[file attributes $name -system]} continue lappend res $name } return [sort_files $res] } else { return [sort_files [glob -nocomplain -- $pattern]] } } # Read the first 5 entries from the root directory. Except, ignore # files that contain the "$" character in their names as these are # special files on some Windows platforms. # set res [list] set root_files [list_root_files] foreach p $root_files { if {$::tcl_platform(platform) eq "windows"} { if {![regexp {\$} $p]} {lappend res $p} } else { lappend res "/$p" } } set num_root_files [llength $root_files] do_test 3.1 { sort_files [execsql { SELECT path FROM fstree WHERE path NOT GLOB '*\$*' LIMIT $num_root_files; }] true } [sort_files $res true] # Read all entries in the current directory. # proc contents {pattern} { set res [list] foreach f [list_files $pattern] { lappend res $f if {[file isdir $f]} { set res [concat $res [contents "$f/*"]] } } set res } set pwd "[pwd]/*" set res [contents $pwd] do_execsql_test 3.2 { SELECT path FROM fstree WHERE path GLOB $pwd ORDER BY 1 } [sort_files $res] # Add some sub-directories and files to the current directory. # do_test 3.3 { catch { file delete -force subdir } foreach {path sz} { subdir/x1.txt 143 subdir/x2.txt 153 } { set dir [file dirname $path] catch { file mkdir $dir } set fd [open $path w] puts -nonewline $fd [string repeat 1 $sz] close $fd } } {} set pwd [pwd] if {![string match {*[_%]*} $pwd]} { do_execsql_test 3.5 { SELECT path, size FROM fstree WHERE path GLOB $pwd || '/subdir/*' ORDER BY 1 } [list \ "$pwd/subdir/x1.txt" 143 \ "$pwd/subdir/x2.txt" 153 \ ] do_execsql_test 3.6 { SELECT path, size FROM fstree WHERE path LIKE $pwd || '/subdir/%' ORDER BY 1 } [list \ "$pwd/subdir/x1.txt" 143 \ "$pwd/subdir/x2.txt" 153 \ ] do_execsql_test 3.7 { SELECT sum(size) FROM fstree WHERE path LIKE $pwd || '/subdir/%' } 296 do_execsql_test 3.8 { SELECT size FROM fstree WHERE path = $pwd || '/subdir/x1.txt' } 143 } } finish_test |
Added test/vtabJ.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 | # 2017-08-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 tests of writing to WITHOUT ROWID virtual tables # using the tclvar eponymous virtual table. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix vtabJ ifcapable !vtab { finish_test return } register_tclvar_module db unset -nocomplain vtabJ do_test 100 { set vtabJ(1) this set vtabJ(two) is set vtabJ(3) {a test} db eval { SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname; } } {vtabJ(1) this vtabJ(3) {a test} vtabJ(two) is} do_execsql_test 110 { INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',4),('vtabJ(five)',555); SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname; } {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is} do_test 111 { set res {} foreach vname [lsort [array names vtabJ]] { lappend res vtabJ($vname) $vtabJ($vname) } set res } {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is} do_test 120 { db eval { INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',444); } set vtabJ(4) } {444} do_test 130 { db eval { INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',NULL); } info exists vtabJ(4) } {0} do_test 140 { db eval { UPDATE tclvar SET value=55 WHERE fullname='vtabJ(five)'; } set vtabJ(five) } {55} do_test 150 { db eval { UPDATE tclvar SET fullname='vtabJ(5)' WHERE fullname='vtabJ(five)'; } set vtabJ(5) } {55} do_test 151 { info exists vtabJ(five) } {0} do_test 152 { set res {} foreach vname [lsort [array names vtabJ]] { lappend res vtabJ($vname) $vtabJ($vname) } set res } {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55 vtabJ(two) is} do_execsql_test 160 { SELECT fullname FROM tclvar WHERE arrayname='two' } {vtabJ(two)} do_execsql_test 161 { DELETE FROM tclvar WHERE arrayname='two'; SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname; } {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55} do_test 162 { set res {} foreach vname [lsort [array names vtabJ]] { lappend res vtabJ($vname) $vtabJ($vname) } set res } {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55} # Try to trick the module into updating the same variable twice for a # single UPDATE statement. # do_execsql_test 171 { INSERT INTO tclvar(fullname, value) VALUES('xx', 'a'); SELECT name, value FROM tclvar where name = 'xx'; } {xx a} do_execsql_test 172 { UPDATE tclvar SET value = value || 't' WHERE name = 'xx' OR name = 'x'||'x'; SELECT name, value FROM tclvar where name = 'xx'; } {xx at} do_execsql_test 173 { UPDATE tclvar SET value = value || 't' WHERE name = 'xx' OR name BETWEEN 'xx' AND 'xx'; SELECT name, value FROM tclvar where name = 'xx'; } {xx att} do_execsql_test 181 { DELETE FROM tclvar WHERE name BETWEEN 'xx' AND 'xx' OR name='xx'; SELECT name, value FROM tclvar where name = 'xx'; } {} finish_test |
Changes to test/vtab_alter.test.
︙ | ︙ | |||
91 92 93 94 95 96 97 | # Cause an error to occur when the echo module renames its # backing store table. # do_test vtab_alter-3.1 { execsql { CREATE TABLE y_base(a, b, c) } catchsql { ALTER TABLE x RENAME TO y } | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 | # Cause an error to occur when the echo module renames its # backing store table. # do_test vtab_alter-3.1 { execsql { CREATE TABLE y_base(a, b, c) } catchsql { ALTER TABLE x RENAME TO y } } {1 {SQL logic error}} do_test vtab_alter-3.2 { execsql { SELECT * FROM x } } {1 2 3} finish_test |
Changes to test/wal2.test.
︙ | ︙ | |||
1187 1188 1189 1190 1191 1192 1193 | } #------------------------------------------------------------------------- # Test that "PRAGMA checkpoint_fullsync" appears to be working. # foreach {tn sql reslist} { 1 { } {10 0 4 0 6 0} | | | 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 | } #------------------------------------------------------------------------- # Test that "PRAGMA checkpoint_fullsync" appears to be working. # foreach {tn sql reslist} { 1 { } {10 0 4 0 6 0} 2 { PRAGMA checkpoint_fullfsync = 1 } {10 6 4 3 6 3} 3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0} } { ifcapable default_ckptfullfsync { if {[string trim $sql]==""} continue } faultsim_delete_and_reopen |
︙ | ︙ | |||
1257 1258 1259 1260 1261 1262 1263 | 3 {0 0 full} {2 0} {1 0} {2 0} 4 {0 1 off} {0 0} {0 0} {0 0} 5 {0 1 normal} {0 1} {0 0} {0 2} 6 {0 1 full} {0 2} {0 1} {0 2} 7 {1 0 off} {0 0} {0 0} {0 0} | | | | 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 | 3 {0 0 full} {2 0} {1 0} {2 0} 4 {0 1 off} {0 0} {0 0} {0 0} 5 {0 1 normal} {0 1} {0 0} {0 2} 6 {0 1 full} {0 2} {0 1} {0 2} 7 {1 0 off} {0 0} {0 0} {0 0} 8 {1 0 normal} {0 1} {0 0} {0 2} 9 {1 0 full} {1 1} {1 0} {0 2} 10 {1 1 off} {0 0} {0 0} {0 0} 11 {1 1 normal} {0 1} {0 0} {0 2} 12 {1 1 full} {0 2} {0 1} {0 2} } { forcedelete test.db |
︙ | ︙ |
Changes to test/wal6.test.
︙ | ︙ | |||
229 230 231 232 233 234 235 236 237 238 | do_test 4.4.1 { catchsql { SELECT * FROM t1 } db2 } {0 {1 2}} do_test 4.4.2 { catchsql { SELECT * FROM t2 } db2 } {1 {database disk image is malformed}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_test 4.4.1 { catchsql { SELECT * FROM t1 } db2 } {0 {1 2}} do_test 4.4.2 { catchsql { SELECT * FROM t2 } db2 } {1 {database disk image is malformed}} #------------------------------------------------------------------------- # Confirm that it is possible to get an SQLITE_BUSY_SNAPSHOT error from # "BEGIN EXCLUSIVE" if the connection already has an open read-transaction. # db close db2 close reset_db sqlite3 db2 test.db do_execsql_test 5.1 { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } {wal} do_test 5.2 { set res [list] db eval { SELECT * FROM t1 } { if {$x==1} { db2 eval { INSERT INTO t1 VALUES(5, 6) } } if {$x==3} { set res [catchsql {BEGIN EXCLUSIVE}] lappend res [sqlite3_extended_errcode db] } } set res } {1 {database is locked} SQLITE_BUSY_SNAPSHOT} finish_test |
Changes to test/whereA.test.
︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 | } } {1 2 1} do_test whereA-4.6 { count { SELECT x FROM t2 ORDER BY x DESC; } } {2 1 1} finish_test | > > > > > > > > > > > > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | } } {1 2 1} do_test whereA-4.6 { count { SELECT x FROM t2 ORDER BY x DESC; } } {2 1 1} # Ticket https://sqlite.org/src/tktview/cb91bf4290c211 2017-08-01 # Assertion fault following PRAGMA reverse_unordered_selects=ON. # do_execsql_test whereA-5.1 { PRAGMA reverse_unordered_selects=on; DROP TABLE IF EXISTS t1; CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); CREATE INDEX t1b ON t1(b); SELECT a FROM t1 WHERE b=-99 OR b>1; } {1} finish_test |
Changes to test/whereD.test.
︙ | ︙ | |||
332 333 334 335 336 337 338 339 340 | } {3 7 11 search 7} do_searchcount_test 6.6.3 { SELECT c FROM x1 WHERE c=11 OR a=1 OR b=6 } {11 3 7 search 7} do_searchcount_test 6.6.4 { SELECT c FROM x1 WHERE b=6 OR c=11 OR a=1 } {7 11 3 search 7} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {3 7 11 search 7} do_searchcount_test 6.6.3 { SELECT c FROM x1 WHERE c=11 OR a=1 OR b=6 } {11 3 7 search 7} do_searchcount_test 6.6.4 { SELECT c FROM x1 WHERE b=6 OR c=11 OR a=1 } {7 11 3 search 7} #------------------------------------------------------------------------- # do_execsql_test 7.0 { CREATE TABLE y1(a, b); CREATE TABLE y2(x, y); CREATE INDEX y2xy ON y2(x, y); INSERT INTO y1 VALUES(1, 1); INSERT INTO y2 VALUES(3, 3); } do_execsql_test 7.1 { SELECT * FROM y1 LEFT JOIN y2 ON ((x=1 AND y=b) OR (x=2 AND y=b)) } {1 1 {} {}} do_execsql_test 7.3 { CREATE TABLE foo (Id INTEGER PRIMARY KEY, fa INTEGER, fb INTEGER); CREATE TABLE bar (Id INTEGER PRIMARY KEY, ba INTEGER, bb INTEGER); INSERT INTO foo VALUES(1, 1, 1); INSERT INTO foo VALUES(2, 1, 2); INSERT INTO foo VALUES(3, 1, 3); INSERT INTO foo VALUES(4, 1, 4); INSERT INTO foo VALUES(5, 1, 5); INSERT INTO foo VALUES(6, 1, 6); INSERT INTO foo VALUES(7, 1, 7); INSERT INTO foo VALUES(8, 1, 8); INSERT INTO foo VALUES(9, 1, 9); INSERT INTO bar VALUES(NULL, 1, 1); INSERT INTO bar VALUES(NULL, 2, 2); INSERT INTO bar VALUES(NULL, 3, 3); INSERT INTO bar VALUES(NULL, 1, 4); INSERT INTO bar VALUES(NULL, 2, 5); INSERT INTO bar VALUES(NULL, 3, 6); INSERT INTO bar VALUES(NULL, 1, 7); INSERT INTO bar VALUES(NULL, 2, 8); INSERT INTO bar VALUES(NULL, 3, 9); } do_execsql_test 7.4 { SELECT bar.Id, bar.ba, bar.bb, foo.fb FROM foo LEFT JOIN bar ON (bar.ba = 1 AND bar.bb = foo.fb) OR (bar.ba = 5 AND bar.bb = foo.fb); } { 1 1 1 1 {} {} {} 2 {} {} {} 3 4 1 4 4 {} {} {} 5 {} {} {} 6 7 1 7 7 {} {} {} 8 {} {} {} 9 } do_execsql_test 7.5 { CREATE INDEX idx_bar ON bar(ba, bb); SELECT bar.Id, bar.ba, bar.bb, foo.fb FROM foo LEFT JOIN bar ON (bar.ba = 1 AND bar.bb = foo.fb) OR (bar.ba = 5 AND bar.bb = foo.fb); } { 1 1 1 1 {} {} {} 2 {} {} {} 3 4 1 4 4 {} {} {} 5 {} {} {} 6 7 1 7 7 {} {} {} 8 {} {} {} 9 } finish_test |
Changes to test/whereF.test.
︙ | ︙ | |||
114 115 116 117 118 119 120 121 122 | do_execsql_test 4.0 { CREATE TABLE t4(a,b,c,d,e, PRIMARY KEY(a,b,c)); CREATE INDEX t4adc ON t4(a,d,c); CREATE UNIQUE INDEX t4aebc ON t4(a,e,b,c); EXPLAIN QUERY PLAN SELECT rowid FROM t4 WHERE a=? AND b=?; } {/a=. AND b=./} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_execsql_test 4.0 { CREATE TABLE t4(a,b,c,d,e, PRIMARY KEY(a,b,c)); CREATE INDEX t4adc ON t4(a,d,c); CREATE UNIQUE INDEX t4aebc ON t4(a,e,b,c); EXPLAIN QUERY PLAN SELECT rowid FROM t4 WHERE a=? AND b=?; } {/a=. AND b=./} #------------------------------------------------------------------------- # Test the following case: # # ... FROM t1, t2 WHERE ( # t2.rowid = +t1.rowid OR (t2.f2 = t1.f1 AND t1.f1!=-1) # ) # # where there is an index on t2(f2). The planner should use "t1" as the # outer loop. The inner loop, on "t2", is an OR optimization. One pass # for: # # t2.rowid = $1 # # and another for: # # t2.f2=$1 AND $1!=-1 # # the test is to ensure that on the second pass, the ($1!=-1) condition # is tested before any seek operations are performed - i.e. outside of # the loop through the f2=$1 range of the t2(f2) index. # reset_db do_execsql_test 5.0 { CREATE TABLE t1(f1); CREATE TABLE t2(f2); CREATE INDEX t2f ON t2(f2); INSERT INTO t1 VALUES(-1); INSERT INTO t1 VALUES(-1); INSERT INTO t1 VALUES(-1); INSERT INTO t1 VALUES(-1); WITH w(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM w WHERE i<1000 ) INSERT INTO t2 SELECT -1 FROM w; } do_execsql_test 5.1 { SELECT count(*) FROM t1, t2 WHERE t2.rowid = +t1.rowid } {4} do_test 5.2 { expr [db status vmstep]<200 } 1 do_execsql_test 5.3 { SELECT count(*) FROM t1, t2 WHERE ( t2.rowid = +t1.rowid OR t2.f2 = t1.f1 ) } {4000} do_test 5.4 { expr [db status vmstep]>1000 } 1 do_execsql_test 5.5 { SELECT count(*) FROM t1, t2 WHERE ( t2.rowid = +t1.rowid OR (t2.f2 = t1.f1 AND t1.f1!=-1) ) } {4} do_test 5.6 { expr [db status vmstep]<200 } 1 # 2017-09-04 ticket b899b6042f97f52d # Segfault on correlated subquery... # ifcapable json1 { do_execsql_test 6.1 { CREATE TABLE t6(x); SELECT * FROM t6 WHERE 1 IN (SELECT value FROM json_each(x)); } {} do_execsql_test 6.2 { DROP TABLE t6; CREATE TABLE t6(a,b,c); INSERT INTO t6 VALUES (0,null,'{"a":0,"b":[3,4,5],"c":{"x":4.5,"y":7.8}}'), (1,null,'{"a":1,"b":[3,4,5],"c":{"x":4.5,"y":7.8}}'), (2,null,'{"a":9,"b":[3,4,5],"c":{"x":4.5,"y":7.8}}'); SELECT * FROM t6 WHERE (EXISTS (SELECT 1 FROM json_each(t6.c) AS x WHERE x.value=1)); } {1 {} {{"a":1,"b":[3,4,5],"c":{"x":4.5,"y":7.8}}}} } finish_test |
Changes to test/win32heap.test.
1 2 3 4 5 6 7 8 9 10 11 | # 2013 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. # #*********************************************************************** # This file implements regression tests for SQLite library. The | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # 2013 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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is the Win32 heap implementation. # if {$tcl_platform(platform)!="windows"} return set testdir [file dirname $argv0] source $testdir/tester.tcl |
︙ | ︙ |
Changes to test/with1.test.
︙ | ︙ | |||
985 986 987 988 989 990 991 992 993 994 | WITH xyz(x) AS ( SELECT printf('%d', 5) * NULL UNION SELECT round(1<1+x) FROM xyz ORDER BY 1 ) SELECT 1 FROM xyz; } 1 finish_test | > > > > > > > > > > > > > > | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 | WITH xyz(x) AS ( SELECT printf('%d', 5) * NULL UNION SELECT round(1<1+x) FROM xyz ORDER BY 1 ) SELECT 1 FROM xyz; } 1 # EXPLAIN QUERY PLAN on a self-join of a CTE # do_execsql_test 19.1 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x); EXPLAIN QUERY PLAN WITH x1(a) AS (values(100)) INSERT INTO t1(x) SELECT * FROM (WITH x2(y) AS (SELECT * FROM x1) SELECT y+a FROM x1, x2); SELECT * FROM t1; } {0 0 0 {SCAN SUBQUERY 1} 0 1 1 {SCAN SUBQUERY 1}} finish_test |
Changes to test/with3.test.
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 | CREATE TABLE t1(x); WITH x1(a) AS (values(100)) INSERT INTO t1(x) SELECT * FROM (WITH x2(y) AS (SELECT * FROM x1) SELECT y+a FROM x1, x2); SELECT * FROM t1; } {200} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | CREATE TABLE t1(x); WITH x1(a) AS (values(100)) INSERT INTO t1(x) SELECT * FROM (WITH x2(y) AS (SELECT * FROM x1) SELECT y+a FROM x1, x2); SELECT * FROM t1; } {200} #------------------------------------------------------------------------- # Test that the planner notices LIMIT clauses on recursive WITH queries. # ifcapable analyze { do_execsql_test 3.1.1 { CREATE TABLE y1(a, b); CREATE INDEX y1a ON y1(a); WITH cnt(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM cnt LIMIT 1000) INSERT INTO y1 SELECT i%10, i FROM cnt; ANALYZE; } do_eqp_test 3.1.2 { WITH cnt(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM cnt LIMIT 1) SELECT * FROM cnt, y1 WHERE i=a } { 3 0 0 {SCAN TABLE cnt} 1 0 0 {COMPOUND SUBQUERIES 0 AND 0 (UNION ALL)} 0 0 0 {SCAN SUBQUERY 1} 0 1 1 {SEARCH TABLE y1 USING INDEX y1a (a=?)} } do_eqp_test 3.1.3 { WITH cnt(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM cnt LIMIT 1000000) SELECT * FROM cnt, y1 WHERE i=a } { 3 0 0 {SCAN TABLE cnt} 1 0 0 {COMPOUND SUBQUERIES 0 AND 0 (UNION ALL)} 0 0 1 {SCAN TABLE y1} 0 1 0 {SEARCH SUBQUERY 1 USING AUTOMATIC COVERING INDEX (i=?)} } } do_execsql_test 3.2.1 { CREATE TABLE w1(pk INTEGER PRIMARY KEY, x INTEGER); CREATE TABLE w2(pk INTEGER PRIMARY KEY); } do_eqp_test 3.2.2 { WITH RECURSIVE c(w,id) AS (SELECT 0, (SELECT pk FROM w2 LIMIT 1) UNION ALL SELECT c.w + 1, x FROM w1, c LIMIT 1) SELECT * FROM c, w2, w1 WHERE c.id=w2.pk AND c.id=w1.pk; } { 2 0 0 {EXECUTE SCALAR SUBQUERY 3} 3 0 0 {SCAN TABLE w2} 4 0 0 {SCAN TABLE w1} 4 1 1 {SCAN TABLE c} 1 0 0 {COMPOUND SUBQUERIES 0 AND 0 (UNION ALL)} 0 0 0 {SCAN SUBQUERY 1} 0 1 1 {SEARCH TABLE w2 USING INTEGER PRIMARY KEY (rowid=?)} 0 2 2 {SEARCH TABLE w1 USING INTEGER PRIMARY KEY (rowid=?)} } finish_test |
Changes to test/without_rowid1.test.
︙ | ︙ | |||
324 325 326 327 328 329 330 | do_catchsql_test 7.3 { CREATE TABLE t70b( a INT CHECK( rowid!=33 ), b TEXT PRIMARY KEY ) WITHOUT ROWID; } {1 {no such column: rowid}} | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_catchsql_test 7.3 { CREATE TABLE t70b( a INT CHECK( rowid!=33 ), b TEXT PRIMARY KEY ) WITHOUT ROWID; } {1 {no such column: rowid}} # The PRAGMA index_info and index_xinfo pragmas work on # WITHOUT ROWID tables too, but not on rowid tables. # do_execsql_test 8.1 { CREATE TABLE t80a(a TEXT, b INT, c BLOB, PRIMARY KEY(c,b)); PRAGMA index_info(t80a); } {} do_execsql_test 8.2 { PRAGMA index_xinfo(t80a); } {} do_execsql_test 8.3 { CREATE TABLE t80b(a TEXT, b INT, c BLOB, PRIMARY KEY(c,b)) WITHOUT ROWID; PRAGMA index_info(t80b); } {0 2 c 1 1 b} do_execsql_test 8.4 { PRAGMA index_xinfo(t80b); } {0 2 c 0 BINARY 1 1 1 b 0 BINARY 1 2 0 a 0 BINARY 0} # 2017-07-30: OSSFuzz discovered that an extra entry was being # added in the sqlite_master table for an "INTEGER PRIMARY KEY UNIQUE" # WITHOUT ROWID table. Make sure this has now been fixed. # db close sqlite3 db :memory: do_execsql_test 9.1 { CREATE TABLE t1(x INTEGER PRIMARY KEY UNIQUE, b) WITHOUT ROWID; CREATE INDEX t1x ON t1(x); INSERT INTO t1(x,b) VALUES('funny','buffalo'); SELECT type, name, '|' FROM sqlite_master; } {table t1 | index t1x |} finish_test |
Changes to test/without_rowid3.test.
︙ | ︙ | |||
1027 1028 1029 1030 1031 1032 1033 | catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 } } {1 {Cannot add a REFERENCES column with non-NULL default value}} do_test without_rowid3-14.1tmp.6 { execsql { PRAGMA foreign_keys = off; ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; PRAGMA foreign_keys = on; | | | 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 } } {1 {Cannot add a REFERENCES column with non-NULL default value}} do_test without_rowid3-14.1tmp.6 { execsql { PRAGMA foreign_keys = off; ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; PRAGMA foreign_keys = on; SELECT sql FROM temp.sqlite_master WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} do_test without_rowid3-14.2tmp.1.1 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} do_test without_rowid3-14.2tmp.1.2 { |
︙ | ︙ | |||
1060 1061 1062 1063 1064 1065 1066 | {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2) WITHOUT rowid} \ {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \ ] do_test without_rowid3-14.2tmp.2.2 { execsql { ALTER TABLE t1 RENAME TO t4 } | | | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 | {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2) WITHOUT rowid} \ {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \ ] do_test without_rowid3-14.2tmp.2.2 { execsql { ALTER TABLE t1 RENAME TO t4 } execsql { SELECT sql FROM temp.sqlite_master WHERE type = 'table'} } [list \ {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4") WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2) WITHOUT rowid} \ {CREATE TABLE t3(a REFERENCES "t4", b REFERENCES t2, c REFERENCES "t4")} \ ] do_test without_rowid3-14.2tmp.2.3 { |
︙ | ︙ |
Changes to test/wordcount.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** wordcount DATABASE INPUTFILE ** ** The INPUTFILE name can be omitted, in which case input it taken from ** standard input. ** ** Option: ** | < < < < < < < < < < < < < < < < < < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** wordcount DATABASE INPUTFILE ** ** The INPUTFILE name can be omitted, in which case input it taken from ** standard input. ** ** Option: ** ** ** Modes: ** ** Insert mode means: ** (1) INSERT OR IGNORE INTO wordcount VALUES($new,1) ** (2) UPDATE wordcount SET cnt=cnt+1 WHERE word=$new -- if (1) is a noop ** |
︙ | ︙ | |||
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | */ #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <stdarg.h> #include "sqlite3.h" #define ISALPHA(X) isalpha((unsigned char)(X)) /* Output tag */ char *zTag = "--"; /* Return the current wall-clock time */ static sqlite3_int64 realTime(void){ static sqlite3_vfs *clockVfs = 0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | */ #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <stdarg.h> #include "sqlite3.h" #ifndef _WIN32 # include <unistd.h> #else # include <io.h> #endif #define ISALPHA(X) isalpha((unsigned char)(X)) const char zHelp[] = "Usage: wordcount [OPTIONS] DATABASE [INPUT]\n" " --all Repeat the test for all test modes\n" " --cachesize NNN Use a cache size of NNN\n" " --commit NNN Commit after every NNN operations\n" " --delete Use DELETE mode\n" " --insert Use INSERT mode (the default)\n" " --journal MMMM Use PRAGMA journal_mode=MMMM\n" " --nocase Add the NOCASE collating sequence to the words.\n" " --nosync Use PRAGMA synchronous=OFF\n" " --pagesize NNN Use a page size of NNN\n" " --query Use QUERY mode\n" " --replace Use REPLACE mode\n" " --select Use SELECT mode\n" " --stats Show sqlite3_status() results at the end.\n" " --summary Show summary information on the collected data.\n" " --tag NAME Tag all output using NAME. Use only stdout.\n" " --timer Time the operation of this program\n" " --trace Enable sqlite3_trace() output.\n" " --update Use UPDATE mode\n" " --without-rowid Use a WITHOUT ROWID table to store the words.\n" ; /* Output tag */ char *zTag = "--"; /* Return the current wall-clock time */ static sqlite3_int64 realTime(void){ static sqlite3_vfs *clockVfs = 0; |
︙ | ︙ | |||
105 106 107 108 109 110 111 112 113 114 115 116 117 118 | static void fatal_error(const char *zMsg, ...){ va_list ap; va_start(ap, zMsg); vfprintf(stderr, zMsg, ap); va_end(ap); exit(1); } /* The sqlite3_trace() callback function */ static void traceCallback(void *NotUsed, const char *zSql){ printf("%s;\n", zSql); } /* An sqlite3_exec() callback that prints results on standard output, | > > > > > > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | static void fatal_error(const char *zMsg, ...){ va_list ap; va_start(ap, zMsg); vfprintf(stderr, zMsg, ap); va_end(ap); exit(1); } /* Print a usage message and quit */ static void usage(void){ printf("%s",zHelp); exit(0); } /* The sqlite3_trace() callback function */ static void traceCallback(void *NotUsed, const char *zSql){ printf("%s;\n", zSql); } /* An sqlite3_exec() callback that prints results on standard output, |
︙ | ︙ | |||
185 186 187 188 189 190 191 | a = sqlite3_aggregate_context(context, 0); if( a ){ finalHash(a, zResult); sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT); } } | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | a = sqlite3_aggregate_context(context, 0); if( a ){ finalHash(a, zResult); sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT); } } /* Define operating modes */ #define MODE_INSERT 0 #define MODE_REPLACE 1 #define MODE_SELECT 2 #define MODE_UPDATE 3 #define MODE_DELETE 4 #define MODE_QUERY 5 #define MODE_COUNT 6 #define MODE_ALL (-1) /* Mode names */ static const char *azMode[] = { "--insert", "--replace", "--select", "--update", "--delete", "--query" }; /* ** Determine if another iteration of the test is required. Return true ** if so. Return zero if all iterations have finished. */ static int allLoop( int iMode, /* The selected test mode */ int *piLoopCnt, /* Iteration loop counter */ int *piMode2, /* The test mode to use on the next iteration */ int *pUseWithoutRowid /* Whether or not to use --without-rowid */ ){ int i; if( iMode!=MODE_ALL ){ if( *piLoopCnt ) return 0; *piMode2 = iMode; *piLoopCnt = 1; return 1; } if( (*piLoopCnt)>=MODE_COUNT*2 ) return 0; i = (*piLoopCnt)++; *pUseWithoutRowid = i&1; *piMode2 = i>>1; return 1; } int main(int argc, char **argv){ const char *zFileToRead = 0; /* Input file. NULL for stdin */ const char *zDbName = 0; /* Name of the database file to create */ int useWithoutRowid = 0; /* True for --without-rowid */ int iMode = MODE_INSERT; /* One of MODE_xxxxx */ int iMode2; /* Mode to use for current --all iteration */ int iLoopCnt = 0; /* Which iteration when running --all */ int useNocase = 0; /* True for --nocase */ int doTrace = 0; /* True for --trace */ int showStats = 0; /* True for --stats */ int showSummary = 0; /* True for --summary */ int showTimer = 0; /* True for --timer */ int cacheSize = 0; /* Desired cache size. 0 means default */ int pageSize = 0; /* Desired page size. 0 means default */ |
︙ | ︙ | |||
222 223 224 225 226 227 228 | sqlite3_stmt *pSelect = 0; /* The SELECT statement */ sqlite3_stmt *pDelete = 0; /* The DELETE statement */ FILE *in; /* The open input file */ int rc; /* Return code from an SQLite interface */ int iCur, iHiwtr; /* Statistics values, current and "highwater" */ FILE *pTimer = stderr; /* Output channel for the timer */ sqlite3_int64 sumCnt = 0; /* Sum in QUERY mode */ | | > | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | sqlite3_stmt *pSelect = 0; /* The SELECT statement */ sqlite3_stmt *pDelete = 0; /* The DELETE statement */ FILE *in; /* The open input file */ int rc; /* Return code from an SQLite interface */ int iCur, iHiwtr; /* Statistics values, current and "highwater" */ FILE *pTimer = stderr; /* Output channel for the timer */ sqlite3_int64 sumCnt = 0; /* Sum in QUERY mode */ sqlite3_int64 startTime; /* Time of start */ sqlite3_int64 totalTime = 0; /* Total time */ char zInput[2000]; /* A single line of input */ /* Process command-line arguments */ for(i=1; i<argc; i++){ const char *z = argv[i]; if( z[0]=='-' ){ do{ z++; }while( z[0]=='-' ); |
︙ | ︙ | |||
244 245 246 247 248 249 250 251 252 253 254 255 256 257 | iMode = MODE_INSERT; }else if( strcmp(z,"update")==0 ){ iMode = MODE_UPDATE; }else if( strcmp(z,"delete")==0 ){ iMode = MODE_DELETE; }else if( strcmp(z,"query")==0 ){ iMode = MODE_QUERY; }else if( strcmp(z,"nocase")==0 ){ useNocase = 1; }else if( strcmp(z,"trace")==0 ){ doTrace = 1; }else if( strcmp(z,"nosync")==0 ){ noSync = 1; }else if( strcmp(z,"stats")==0 ){ | > > > | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | iMode = MODE_INSERT; }else if( strcmp(z,"update")==0 ){ iMode = MODE_UPDATE; }else if( strcmp(z,"delete")==0 ){ iMode = MODE_DELETE; }else if( strcmp(z,"query")==0 ){ iMode = MODE_QUERY; }else if( strcmp(z,"all")==0 ){ iMode = MODE_ALL; showTimer = -99; }else if( strcmp(z,"nocase")==0 ){ useNocase = 1; }else if( strcmp(z,"trace")==0 ){ doTrace = 1; }else if( strcmp(z,"nosync")==0 ){ noSync = 1; }else if( strcmp(z,"stats")==0 ){ |
︙ | ︙ | |||
270 271 272 273 274 275 276 277 | i++; commitInterval = atoi(argv[i]); }else if( strcmp(z,"journal")==0 && i<argc-1 ){ zJMode = argv[++i]; }else if( strcmp(z,"tag")==0 && i<argc-1 ){ zTag = argv[++i]; pTimer = stdout; }else{ | > > | > > | | > > > > > > | 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 | i++; commitInterval = atoi(argv[i]); }else if( strcmp(z,"journal")==0 && i<argc-1 ){ zJMode = argv[++i]; }else if( strcmp(z,"tag")==0 && i<argc-1 ){ zTag = argv[++i]; pTimer = stdout; }else if( strcmp(z, "help")==0 || strcmp(z,"?")==0 ){ usage(); }else{ fatal_error("unknown option: \"%s\"\n" "Use --help for a list of options\n", argv[i]); } }else if( zDbName==0 ){ zDbName = argv[i]; }else if( zFileToRead==0 ){ zFileToRead = argv[i]; }else{ fatal_error("surplus argument: \"%s\"\n", argv[i]); } } if( zDbName==0 ){ usage(); } startTime = realTime(); /* Open the database and the input file */ if( zDbName[0] && strcmp(zDbName,":memory:")!=0 ){ unlink(zDbName); } if( sqlite3_open(zDbName, &db) ){ fatal_error("Cannot open database file: %s\n", zDbName); } if( zFileToRead ){ in = fopen(zFileToRead, "rb"); if( in==0 ){ fatal_error("Could not open input file \"%s\"\n", zFileToRead); } }else{ if( iMode==MODE_ALL ){ fatal_error("The --all mode cannot be used with stdin\n"); } in = stdin; } /* Set database connection options */ if( doTrace ) sqlite3_trace(db, traceCallback, 0); if( pageSize ){ zSql = sqlite3_mprintf("PRAGMA page_size=%d", pageSize); |
︙ | ︙ | |||
318 319 320 321 322 323 324 | if( noSync ) sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0); if( zJMode ){ zSql = sqlite3_mprintf("PRAGMA journal_mode=%s", zJMode); sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); } | > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | > | > > > > | | > | | | | | | | | | | | | | | | | | | > > > > > > > > > > | 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 | if( noSync ) sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0); if( zJMode ){ zSql = sqlite3_mprintf("PRAGMA journal_mode=%s", zJMode); sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); } iLoopCnt = 0; while( allLoop(iMode, &iLoopCnt, &iMode2, &useWithoutRowid) ){ /* Delete prior content in --all mode */ if( iMode==MODE_ALL ){ if( sqlite3_exec(db, "DROP TABLE IF EXISTS wordcount; VACUUM;",0,0,0) ){ fatal_error("Could not clean up prior iteration\n"); } startTime = realTime(); rewind(in); } /* Construct the "wordcount" table into which to put the words */ if( sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0) ){ fatal_error("Could not start a transaction\n"); } zSql = sqlite3_mprintf( "CREATE TABLE IF NOT EXISTS wordcount(\n" " word TEXT PRIMARY KEY COLLATE %s,\n" " cnt INTEGER\n" ")%s", useNocase ? "nocase" : "binary", useWithoutRowid ? " WITHOUT ROWID" : "" ); if( zSql==0 ) fatal_error("out of memory\n"); rc = sqlite3_exec(db, zSql, 0, 0, 0); if( rc ) fatal_error("Could not create the wordcount table: %s.\n", sqlite3_errmsg(db)); sqlite3_free(zSql); /* Prepare SQL statements that will be needed */ if( iMode2==MODE_QUERY ){ rc = sqlite3_prepare_v2(db, "SELECT cnt FROM wordcount WHERE word=?1", -1, &pSelect, 0); if( rc ) fatal_error("Could not prepare the SELECT statement: %s\n", sqlite3_errmsg(db)); } if( iMode2==MODE_SELECT ){ rc = sqlite3_prepare_v2(db, "SELECT 1 FROM wordcount WHERE word=?1", -1, &pSelect, 0); if( rc ) fatal_error("Could not prepare the SELECT statement: %s\n", sqlite3_errmsg(db)); rc = sqlite3_prepare_v2(db, "INSERT INTO wordcount(word,cnt) VALUES(?1,1)", -1, &pInsert, 0); if( rc ) fatal_error("Could not prepare the INSERT statement: %s\n", sqlite3_errmsg(db)); } if( iMode2==MODE_SELECT || iMode2==MODE_UPDATE || iMode2==MODE_INSERT ){ rc = sqlite3_prepare_v2(db, "UPDATE wordcount SET cnt=cnt+1 WHERE word=?1", -1, &pUpdate, 0); if( rc ) fatal_error("Could not prepare the UPDATE statement: %s\n", sqlite3_errmsg(db)); } if( iMode2==MODE_INSERT ){ rc = sqlite3_prepare_v2(db, "INSERT OR IGNORE INTO wordcount(word,cnt) VALUES(?1,1)", -1, &pInsert, 0); if( rc ) fatal_error("Could not prepare the INSERT statement: %s\n", sqlite3_errmsg(db)); } if( iMode2==MODE_UPDATE ){ rc = sqlite3_prepare_v2(db, "INSERT OR IGNORE INTO wordcount(word,cnt) VALUES(?1,0)", -1, &pInsert, 0); if( rc ) fatal_error("Could not prepare the INSERT statement: %s\n", sqlite3_errmsg(db)); } if( iMode2==MODE_REPLACE ){ rc = sqlite3_prepare_v2(db, "REPLACE INTO wordcount(word,cnt)" "VALUES(?1,coalesce((SELECT cnt FROM wordcount WHERE word=?1),0)+1)", -1, &pInsert, 0); if( rc ) fatal_error("Could not prepare the REPLACE statement: %s\n", sqlite3_errmsg(db)); } if( iMode2==MODE_DELETE ){ rc = sqlite3_prepare_v2(db, "DELETE FROM wordcount WHERE word=?1", -1, &pDelete, 0); if( rc ) fatal_error("Could not prepare the DELETE statement: %s\n", sqlite3_errmsg(db)); } /* Process the input file */ while( fgets(zInput, sizeof(zInput), in) ){ for(i=0; zInput[i]; i++){ if( !ISALPHA(zInput[i]) ) continue; for(j=i+1; ISALPHA(zInput[j]); j++){} /* Found a new word at zInput[i] that is j-i bytes long. ** Process it into the wordcount table. */ if( iMode2==MODE_DELETE ){ sqlite3_bind_text(pDelete, 1, zInput+i, j-i, SQLITE_STATIC); if( sqlite3_step(pDelete)!=SQLITE_DONE ){ fatal_error("DELETE failed: %s\n", sqlite3_errmsg(db)); } sqlite3_reset(pDelete); }else if( iMode2==MODE_SELECT ){ sqlite3_bind_text(pSelect, 1, zInput+i, j-i, SQLITE_STATIC); rc = sqlite3_step(pSelect); sqlite3_reset(pSelect); if( rc==SQLITE_ROW ){ sqlite3_bind_text(pUpdate, 1, zInput+i, j-i, SQLITE_STATIC); if( sqlite3_step(pUpdate)!=SQLITE_DONE ){ fatal_error("UPDATE failed: %s\n", sqlite3_errmsg(db)); } sqlite3_reset(pUpdate); }else if( rc==SQLITE_DONE ){ sqlite3_bind_text(pInsert, 1, zInput+i, j-i, SQLITE_STATIC); if( sqlite3_step(pInsert)!=SQLITE_DONE ){ fatal_error("Insert failed: %s\n", sqlite3_errmsg(db)); } sqlite3_reset(pInsert); }else{ fatal_error("SELECT failed: %s\n", sqlite3_errmsg(db)); } }else if( iMode2==MODE_QUERY ){ sqlite3_bind_text(pSelect, 1, zInput+i, j-i, SQLITE_STATIC); if( sqlite3_step(pSelect)==SQLITE_ROW ){ sumCnt += sqlite3_column_int64(pSelect, 0); } sqlite3_reset(pSelect); }else{ sqlite3_bind_text(pInsert, 1, zInput+i, j-i, SQLITE_STATIC); if( sqlite3_step(pInsert)!=SQLITE_DONE ){ fatal_error("INSERT failed: %s\n", sqlite3_errmsg(db)); } sqlite3_reset(pInsert); if( iMode2==MODE_UPDATE || (iMode2==MODE_INSERT && sqlite3_changes(db)==0) ){ sqlite3_bind_text(pUpdate, 1, zInput+i, j-i, SQLITE_STATIC); if( sqlite3_step(pUpdate)!=SQLITE_DONE ){ fatal_error("UPDATE failed: %s\n", sqlite3_errmsg(db)); } sqlite3_reset(pUpdate); } } i = j-1; /* Increment the operation counter. Do a COMMIT if it is time. */ nOp++; if( commitInterval>0 && (nOp%commitInterval)==0 ){ sqlite3_exec(db, "COMMIT; BEGIN IMMEDIATE", 0, 0, 0); } } } sqlite3_exec(db, "COMMIT", 0, 0, 0); sqlite3_finalize(pInsert); pInsert = 0; sqlite3_finalize(pUpdate); pUpdate = 0; sqlite3_finalize(pSelect); pSelect = 0; sqlite3_finalize(pDelete); pDelete = 0; if( iMode2==MODE_QUERY && iMode!=MODE_ALL ){ printf("%s sum of cnt: %lld\n", zTag, sumCnt); rc = sqlite3_prepare_v2(db,"SELECT sum(cnt*cnt) FROM wordcount", -1, &pSelect, 0); if( rc==SQLITE_OK && sqlite3_step(pSelect)==SQLITE_ROW ){ printf("%s double-check: %lld\n", zTag,sqlite3_column_int64(pSelect,0)); } sqlite3_finalize(pSelect); } if( showTimer ){ sqlite3_int64 elapseTime = realTime() - startTime; totalTime += elapseTime; fprintf(pTimer, "%3d.%03d wordcount", (int)(elapseTime/1000), (int)(elapseTime%1000)); if( iMode==MODE_ALL ){ fprintf(pTimer, " %s%s\n", azMode[iMode2], useWithoutRowid? " --without-rowid" : ""); }else{ for(i=1; i<argc; i++) if( i!=showTimer ) fprintf(pTimer," %s",argv[i]); fprintf(pTimer, "\n"); } } if( showSummary ){ sqlite3_create_function(db, "checksum", -1, SQLITE_UTF8, 0, 0, checksumStep, checksumFinalize); sqlite3_exec(db, "SELECT 'count(*): ', count(*) FROM wordcount;\n" "SELECT 'sum(cnt): ', sum(cnt) FROM wordcount;\n" "SELECT 'max(cnt): ', max(cnt) FROM wordcount;\n" "SELECT 'avg(cnt): ', avg(cnt) FROM wordcount;\n" "SELECT 'sum(cnt=1):', sum(cnt=1) FROM wordcount;\n" "SELECT 'top 10: ', group_concat(word, ', ') FROM " "(SELECT word FROM wordcount ORDER BY cnt DESC, word LIMIT 10);\n" "SELECT 'checksum: ', checksum(word, cnt) FROM " "(SELECT word, cnt FROM wordcount ORDER BY word);\n" "PRAGMA integrity_check;\n", printResult, 0, 0); } } /* End the --all loop */ /* Close the input file after the last read */ if( zFileToRead ) fclose(in); /* In --all mode, so the total time */ if( iMode==MODE_ALL && showTimer ){ fprintf(pTimer, "%3d.%03d wordcount --all\n", (int)(totalTime/1000), (int)(totalTime%1000)); } /* Database connection statistics printed after both prepared statements ** have been finalized */ if( showStats ){ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, 0); printf("%s Lookaside Slots Used: %d (max %d)\n", zTag, iCur,iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, 0); printf("%s Successful lookasides: %d\n", zTag, iHiwtr); |
︙ | ︙ | |||
537 538 539 540 541 542 543 | if( showStats ){ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, 0); printf("%s Memory Used (bytes): %d (max %d)\n", zTag,iCur,iHiwtr); sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, 0); printf("%s Outstanding Allocations: %d (max %d)\n",zTag,iCur,iHiwtr); sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, 0); printf("%s Pcache Overflow Bytes: %d (max %d)\n",zTag,iCur,iHiwtr); | < < < < | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 | if( showStats ){ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, 0); printf("%s Memory Used (bytes): %d (max %d)\n", zTag,iCur,iHiwtr); sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, 0); printf("%s Outstanding Allocations: %d (max %d)\n",zTag,iCur,iHiwtr); sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, 0); printf("%s Pcache Overflow Bytes: %d (max %d)\n",zTag,iCur,iHiwtr); sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, 0); printf("%s Largest Allocation: %d bytes\n",zTag,iHiwtr); sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, 0); printf("%s Largest Pcache Allocation: %d bytes\n",zTag,iHiwtr); } return 0; } |
Added test/writecrash.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 | # 2009 January 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. # #*********************************************************************** # # Test the outcome of a writer crashing within a call to the VFS # xWrite function. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix writecrash do_not_use_codec if {$tcl_platform(platform)=="windows"} { finish_test return } do_execsql_test 1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB UNIQUE); WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100 ) INSERT INTO t1 SELECT NULL, randomblob(900) FROM s; } {} set bGo 1 for {set tn 1} {$bGo} {incr tn} { db close sqlite3 db test.db do_test 1.$tn.1 { set res [crash_on_write $tn { UPDATE t1 SET b = randomblob(899) WHERE (a%3)==0 }] set bGo 0 if {[string match {1 {child killed:*}} $res]} { set res {0 {}} set bGo 1 } set res } {0 {}} #db close #sqlite3 db test.db do_execsql_test 1.$tn.2 { PRAGMA integrity_check } {ok} db close sqlite3 db test.db do_execsql_test 1.$tn.3 { PRAGMA integrity_check } {ok} } finish_test |
Changes to tool/GetTclKit.bat.
︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 34 35 36 37 38 | IF DEFINED PROCESSOR ( CALL :fn_UnquoteVariable PROCESSOR ) ELSE ( GOTO usage ) %_VECHO% Processor = '%PROCESSOR%' SET DUMMY2=%2 IF DEFINED DUMMY2 ( GOTO usage ) | > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | IF DEFINED PROCESSOR ( CALL :fn_UnquoteVariable PROCESSOR ) ELSE ( GOTO usage ) SET PROCESSOR=%PROCESSOR:AMD64=x64% %_VECHO% Processor = '%PROCESSOR%' SET DUMMY2=%2 IF DEFINED DUMMY2 ( GOTO usage ) |
︙ | ︙ | |||
120 121 122 123 124 125 126 | %_VECHO% FrameworkDir = '%FRAMEWORKDIR%' IF NOT EXIST "%FRAMEWORKDIR%\csc.exe" ( ECHO The file "%FRAMEWORKDIR%\csc.exe" is missing. GOTO errors ) | | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | %_VECHO% FrameworkDir = '%FRAMEWORKDIR%' IF NOT EXIST "%FRAMEWORKDIR%\csc.exe" ( ECHO The file "%FRAMEWORKDIR%\csc.exe" is missing. GOTO errors ) CALL :fn_PrependToPath FRAMEWORKDIR :skip_addToPath IF NOT EXIST "%TEMP%\GetFile.exe" ( %__ECHO% csc.exe "/out:%TEMP%\GetFile.exe" /target:exe "%TOOLS%\GetFile.cs" IF ERRORLEVEL 1 ( |
︙ | ︙ | |||
189 190 191 192 193 194 195 196 | ECHO. :skip_sdkEnvironment GOTO no_errors :fn_TclKitX86Variables IF NOT DEFINED TCLKIT_PATCHLEVEL ( | > > > > > > | | > < | | | | | | | < < | > > | 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 | ECHO. :skip_sdkEnvironment GOTO no_errors :fn_TclKitX86Variables REM REM NOTE: By default, use latest available version of the TclKit SDK REM for x86. However, the "default" TclKit executable for x86 REM is still used here because it is the only one "well-known" REM to be available for download. REM IF NOT DEFINED TCLKIT_PATCHLEVEL ( SET TCLKIT_PATCHLEVEL=8.6.6 ) SET TCLKIT_VERSION=%TCLKIT_PATCHLEVEL:.=% SET TCLKIT_VERSION=%TCLKIT_VERSION:~0,2% REM SET TCLKIT_EXE=tclkit-%TCLKIT_PATCHLEVEL%.exe SET TCLKIT_EXE=tclkit-8.6.4.exe SET TCLKIT_LIB=libtclkit%TCLKIT_PATCHLEVEL:.=%.lib SET TCLKIT_LIB_STUB=libtclstub%TCLKIT_VERSION:.=%.a SET TCLKIT_SDK=libtclkit-sdk-x86-%TCLKIT_PATCHLEVEL% SET TCLKIT_SDK_ZIP=%TCLKIT_SDK%.zip SET TCLKIT_FILES=%TCLKIT_EXE% IF NOT DEFINED TCLKIT_NOENV IF NOT DEFINED TCLKIT_NOSDK ( SET TCLKIT_FILES=%TCLKIT_FILES% unzip.exe %TCLKIT_SDK_ZIP% ) GOTO :EOF :fn_TclKitX64Variables REM REM NOTE: By default, use latest available version of the TclKit SDK REM for x64. However, the "default" TclKit executable for x86 REM is still used here because it is the only one "well-known" REM to be available for download. REM IF NOT DEFINED TCLKIT_PATCHLEVEL ( SET TCLKIT_PATCHLEVEL=8.6.6 ) SET TCLKIT_VERSION=%TCLKIT_PATCHLEVEL:.=% SET TCLKIT_VERSION=%TCLKIT_VERSION:~0,2% REM SET TCLKIT_EXE=tclkit-%TCLKIT_PATCHLEVEL%.exe SET TCLKIT_EXE=tclkit-8.6.4.exe SET TCLKIT_LIB=libtclkit%TCLKIT_PATCHLEVEL:.=%.lib SET TCLKIT_LIB_STUB=libtclstub%TCLKIT_VERSION:.=%.a SET TCLKIT_SDK=libtclkit-sdk-x64-%TCLKIT_PATCHLEVEL% SET TCLKIT_SDK_ZIP=%TCLKIT_SDK%.zip SET TCLKIT_FILES=%TCLKIT_EXE% IF NOT DEFINED TCLKIT_NOENV IF NOT DEFINED TCLKIT_NOSDK ( SET TCLKIT_FILES=%TCLKIT_FILES% unzip.exe %TCLKIT_SDK_ZIP% |
︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 | FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO ( SET VALUE=%%V ) SET VALUE=%VALUE:"=% REM " ENDLOCAL && SET %1=%VALUE% GOTO :EOF :fn_ResetErrorLevel VERIFY > NUL GOTO :EOF :fn_SetErrorLevel VERIFY MAYBE 2> NUL | > > > > > > > > > > > > | 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 | FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO ( SET VALUE=%%V ) SET VALUE=%VALUE:"=% REM " ENDLOCAL && SET %1=%VALUE% GOTO :EOF :fn_PrependToPath IF NOT DEFINED %1 GOTO :EOF SETLOCAL SET __ECHO_CMD=ECHO %%%1%% FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO ( SET VALUE=%%V ) SET VALUE=%VALUE:"=% REM " ENDLOCAL && SET PATH=%VALUE%;%PATH% GOTO :EOF :fn_ResetErrorLevel VERIFY > NUL GOTO :EOF :fn_SetErrorLevel VERIFY MAYBE 2> NUL |
︙ | ︙ |
Changes to tool/addopcodes.tcl.
︙ | ︙ | |||
18 19 20 21 22 23 24 | } close $in # The following are the extra token codes to be added. SPACE and # ILLEGAL *must* be the last two token codes and they must be in that order. # set extras { | < < < < < < < > > > | 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 | } close $in # The following are the extra token codes to be added. SPACE and # ILLEGAL *must* be the last two token codes and they must be in that order. # set extras { ISNOT FUNCTION COLUMN AGG_FUNCTION AGG_COLUMN UMINUS UPLUS REGISTER VECTOR SELECT_COLUMN IF_NULL_ROW ASTERISK SPAN END_OF_FILE UNCLOSED_STRING SPACE ILLEGAL } if {[lrange $extras end-1 end]!="SPACE ILLEGAL"} { error "SPACE and ILLEGAL must be the last two token codes and they\ must be in that order" } |
︙ | ︙ |
Changes to tool/build-all-msvc.bat.
︙ | ︙ | |||
264 265 266 267 268 269 270 | REM NOTE: Check for the external tools needed during the build process ^(i.e. REM those that do not get compiled as part of the build process itself^) REM along the PATH. REM IF DEFINED TCLSH_CMD ( SET TCLSH_FILE=%TCLSH_CMD% ) ELSE ( | | | 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | REM NOTE: Check for the external tools needed during the build process ^(i.e. REM those that do not get compiled as part of the build process itself^) REM along the PATH. REM IF DEFINED TCLSH_CMD ( SET TCLSH_FILE=%TCLSH_CMD% ) ELSE ( SET TCLSH_FILE=tclsh.exe ) FOR %%T IN (%TCLSH_FILE%) DO ( SET %%T_PATH=%%~dp$PATH:T ) REM |
︙ | ︙ | |||
456 457 458 459 460 461 462 | CALL :fn_UnsetVariable WindowsSDKVersion CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x86 CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x64 REM REM NOTE: Reset the PATH here to the absolute bare minimum required. REM | | | 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | CALL :fn_UnsetVariable WindowsSDKVersion CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x86 CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x64 REM REM NOTE: Reset the PATH here to the absolute bare minimum required. REM CALL :fn_ResetPath REM REM NOTE: This is the inner loop. There are normally two iterations, one REM for each supported build configuration, e.g. Debug or Retail. REM FOR %%B IN (%CONFIGURATIONS%) DO ( REM |
︙ | ︙ | |||
813 814 815 816 817 818 819 820 821 822 823 824 825 826 | ENDLOCAL SET %VALUE%= ) ELSE ( ENDLOCAL ) CALL :fn_ResetErrorLevel GOTO :EOF :fn_AppendVariable SET __ECHO_CMD=ECHO %%%1%% IF DEFINED %1 ( FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO ( SET %1=%%V%~2 ) | > > > > | 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 | ENDLOCAL SET %VALUE%= ) ELSE ( ENDLOCAL ) CALL :fn_ResetErrorLevel GOTO :EOF :fn_ResetPath SET PATH=%TOOLPATH%;%SystemRoot%\System32;%SystemRoot% GOTO :EOF :fn_AppendVariable SET __ECHO_CMD=ECHO %%%1%% IF DEFINED %1 ( FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO ( SET %1=%%V%~2 ) |
︙ | ︙ |
Changes to tool/cg_anno.tcl.
1 2 3 4 | #!/usr/bin/tclsh # # A wrapper around cg_annotate that sets appropriate command-line options # and rearranges the output so that annotated files occur in a consistent | | > > > > > > > > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/tclsh # # A wrapper around cg_annotate that sets appropriate command-line options # and rearranges the output so that annotated files occur in a consistent # sorted order. Used by the speed-check.tcl script. # set in [open "|cg_annotate --show=Ir --auto=yes --context=40 $argv" r] set dest ! set out(!) {} set linenum 0 set cntlines 0 ;# true to remember cycle counts on each line set seenSqlite3 0 ;# true if we have seen the sqlite3.c file while {![eof $in]} { set line [string map {\t { }} [gets $in]] if {[regexp {^-- Auto-annotated source: (.*)} $line all name]} { set dest $name if {[string match */sqlite3.c $dest]} { set cntlines 1 set seenSqlite3 1 } else { set cntlines 0 } } elseif {[regexp {^-- line (\d+) ------} $line all ln]} { set line [lreplace $line 2 2 {#}] set linenum [expr {$ln-1}] } elseif {[regexp {^The following files chosen for } $line]} { set dest ! } append out($dest) $line\n if {$cntlines} { incr linenum if {[regexp {^ *([0-9,]+) } $line all x]} { set x [string map {, {}} $x] set cycles($linenum) $x } } } foreach x [lsort [array names out]] { puts $out($x) } # If the sqlite3.c file has been seen, then output a summary of the # cycle counts for each file that went into making up sqlite3.c # if {$seenSqlite3} { close $in set in [open sqlite3.c] set linenum 0 set fn sqlite3.c set pattern1 {^/\*+ Begin file ([^ ]+) \*} set pattern2 {^/\*+ Continuing where we left off in ([^ ]+) \*} while {![eof $in]} { set line [gets $in] incr linenum if {[regexp $pattern1 $line all newfn]} { set fn $newfn } elseif {[regexp $pattern2 $line all newfn]} { set fn $newfn } elseif {[info exists cycles($linenum)]} { incr fcycles($fn) $cycles($linenum) } } close $in puts {**********************************************************************} set lx {} set sum 0 foreach {fn cnt} [array get fcycles] { lappend lx [list $cnt $fn] incr sum $cnt } puts [format {%20s %14d %8.3f%%} TOTAL $sum 100] foreach entry [lsort -index 0 -integer -decreasing $lx] { foreach {cnt fn} $entry break puts [format {%20s %14d %8.3f%%} $fn $cnt [expr {$cnt*100.0/$sum}]] } } |
Added tool/faststat1.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 | /* ** 2016-10-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. ** ************************************************************************* ** ** This is a utility program that uses the est_count and btree_sample ** pragmas to try to approximate the content of the sqlite_stat1 table ** without doing a full table scan. ** ** To compile, simply link against SQLite. ** ** 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" /* ** All global variables are gathered into the "g" singleton. */ struct GlobalVars { const char *zArgv0; /* Name of program */ unsigned fDebug; /* Debug flags */ sqlite3 *db; /* The database connection */ } g; /* ** Allowed values for g.fDebug */ #define DEBUG_NONE 0 /* ** Print an error resulting from faulting command-line arguments and ** abort the program. */ static void cmdlineError(const char *zFormat, ...){ va_list ap; fprintf(stderr, "%s: ", g.zArgv0); va_start(ap, zFormat); vfprintf(stderr, zFormat, ap); va_end(ap); fprintf(stderr, "\n\"%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, ...){ va_list ap; fprintf(stderr, "%s: ", g.zArgv0); va_start(ap, zFormat); vfprintf(stderr, zFormat, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } /* ** Return the current time in milliseconds since the julian epoch. */ static sqlite3_int64 currentTime(void){ sqlite3_vfs *pVfs = sqlite3_vfs_find(0); sqlite3_int64 x = 0; (void)pVfs->xCurrentTimeInt64(pVfs, &x); return x; } /* ** Prepare a new SQL statement. Print an error and abort if anything ** goes wrong. */ static sqlite3_stmt *db_vprepare(const char *zFormat, va_list ap){ char *zSql; int rc; sqlite3_stmt *pStmt; zSql = sqlite3_vmprintf(zFormat, ap); if( zSql==0 ) runtimeError("out of memory"); rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt, 0); if( rc ){ runtimeError("SQL statement error: %s\n\"%s\"", sqlite3_errmsg(g.db), zSql); } sqlite3_free(zSql); return pStmt; } static sqlite3_stmt *db_prepare(const char *zFormat, ...){ va_list ap; sqlite3_stmt *pStmt; va_start(ap, zFormat); pStmt = db_vprepare(zFormat, ap); va_end(ap); return pStmt; } /* ** Estimate the number of rows in the given table or index. */ static sqlite3_int64 estEntryCount(const char *zTabIdx){ double sum = 0.0; int i; int n = 0; sqlite3_stmt *pStmt; # define N_CNT_SAMPLE 10 for(i=0; i<=N_CNT_SAMPLE; i++){ pStmt = db_prepare("PRAGMA est_count(\"%w\",%g)", zTabIdx, ((double)i)/(double)(N_CNT_SAMPLE)); if( sqlite3_step(pStmt)==SQLITE_ROW ){ sum += sqlite3_column_double(pStmt, 0); n++; } sqlite3_finalize(pStmt); } return n==0 ? 0 : (sqlite3_int64)(sum/n); } /* ** Compare the i-th column of pStmt against pValue. Return true if they ** are different. */ static int columnNotEqual(sqlite3_stmt *pStmt, int i, sqlite3_value *pValue){ int n1, n2, n; if( sqlite3_column_type(pStmt,i)!=sqlite3_value_type(pValue) ) return 1; switch( sqlite3_column_type(pStmt,i) ){ case SQLITE_NULL: return 0; /* Nulls compare equal to one another in this context */ case SQLITE_INTEGER: return sqlite3_column_int64(pStmt,i)!=sqlite3_value_int64(pValue); case SQLITE_FLOAT: return sqlite3_column_double(pStmt,i)!=sqlite3_value_double(pValue); case SQLITE_BLOB: n1 = sqlite3_column_bytes(pStmt,i); n2 = sqlite3_value_bytes(pValue); n = n1<n2 ? n1 : n2; if( memcmp(sqlite3_column_blob(pStmt,i), sqlite3_value_blob(pValue),n) ){ return 1; } return n1!=n2; case SQLITE_TEXT: n1 = sqlite3_column_bytes(pStmt,i); n2 = sqlite3_value_bytes(pValue); n = n1<n2 ? n1 : n2; if( memcmp(sqlite3_column_text(pStmt,i), sqlite3_value_text(pValue),n) ){ return 1; } return n1!=n2; } return 1; } /* ** Stat1 for an index. Return non-zero if an entry was created. */ static int analyzeIndex(const char *zTab, const char *zIdx){ sqlite3_int64 n = estEntryCount(zIdx); sqlite3_stmt *pStmt; sqlite3_uint64 *aCnt; sqlite3_value **apValue; int nCol = 0; int nByte; int i, j, k; int iLimit; int nRow = 0; char *zRes; int szRes; int rc; # define N_SPAN 5 if( n==0 ) return 0; pStmt = db_prepare("PRAGMA index_xinfo=\"%w\"", zIdx); while( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zColl = (const char*)sqlite3_column_text(pStmt,4); if( sqlite3_stricmp(zColl,"binary")!=0 ){ printf("-- cannot analyze index \"%s\" because column \"%s\" uses" " collating sequence \"%s\".\n", zIdx, sqlite3_column_text(pStmt, 2), zColl); sqlite3_finalize(pStmt); return 0; } if( sqlite3_column_int(pStmt, 5)==0 ) break; nCol++; } sqlite3_finalize(pStmt); if( nCol==0 ) return 0; nByte = (sizeof(aCnt[0]) + sizeof(apValue[0]))*nCol + 30*(nCol+1); aCnt = sqlite3_malloc( nByte ); if( aCnt==0 ){ runtimeError("out of memory"); } memset(aCnt, 0, nByte); apValue = (sqlite3_value**)&aCnt[nCol]; zRes = (char*)&apValue[nCol]; szRes = 30*(nCol+1); iLimit = n>10000 ? 100 : 20000; pStmt = db_prepare("PRAGMA btree_sample(\"%w\",0.0,%lld)", zIdx, n*2); for(i=0; i<=N_SPAN; i++){ k = 0; while( k<iLimit && (rc = sqlite3_step(pStmt))==SQLITE_ROW ){ int iFirst; for(iFirst=0; iFirst<nCol; iFirst++){ if( apValue[iFirst]==0 ) break; if( columnNotEqual(pStmt, iFirst, apValue[iFirst]) ) break; } for(j=iFirst; j<nCol; j++){ aCnt[j]++; sqlite3_value_free(apValue[j]); apValue[j] = sqlite3_value_dup(sqlite3_column_value(pStmt,j)); } if( k==0 && iFirst==nCol ){ nRow += n/(N_SPAN+1) - iLimit; } nRow++; k++; } sqlite3_finalize(pStmt); if( rc!=SQLITE_ROW || i==N_SPAN-1 ) break; pStmt = db_prepare("PRAGMA btree_sample(\"%w\",%g,%lld)", zIdx, ((double)i)/(double)N_SPAN, n*2); } for(j=0; j<nCol; j++) sqlite3_value_free(apValue[j]); sqlite3_snprintf(szRes, zRes, "%lld", n); k = (int)strlen(zRes); for(j=0; j<nCol; j++){ sqlite3_snprintf(szRes-k, zRes+k, " %d", (nRow+aCnt[j]-1)/aCnt[j]); k += (int)strlen(zRes+k); } printf("INSERT INTO sqlite_stat1 VALUES('%s','%s','%s');\n", zTab, zIdx, zRes); return 1; } /* ** Stat1 for a table. */ static void analyzeTable(const char *zTab){ sqlite3_int64 n = estEntryCount(zTab); sqlite3_stmt *pStmt; int nIndex = 0; int isWithoutRowid = 0; if( n==0 ){ printf("-- empty table: %s\n", zTab); return; } if( analyzeIndex(zTab,zTab) ){ isWithoutRowid = 1; nIndex++; } pStmt = db_prepare("PRAGMA index_list(\"%w\")", zTab); while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( sqlite3_column_text(pStmt,3)[0]=='p' && isWithoutRowid ) continue; if( sqlite3_column_int(pStmt,4)==0 ) nIndex++; analyzeIndex(zTab, (const char*)sqlite3_column_text(pStmt,1)); } sqlite3_finalize(pStmt); if( nIndex==0 ){ printf("INSERT INTO sqlite_stat1 VALUES('%s',NULL,'%lld');\n", zTab, n); } } /* ** Print sketchy documentation for this utility program */ static void showHelp(void){ printf("Usage: %s [options] DBFILE\n", g.zArgv0); printf( "Generate an approximate sqlite_stat1 table for the database in the DBFILE\n" "file. Write the result to standard output.\n" "Options:\n" " (none yet....)\n" ); } int main(int argc, char **argv){ const char *zDb = 0; int i; int rc; char *zErrMsg = 0; sqlite3_stmt *pStmt; sqlite3_int64 iStart, iTotal; g.zArgv0 = argv[0]; sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); iStart = currentTime(); for(i=1; i<argc; i++){ const char *z = argv[i]; if( z[0]=='-' ){ z++; if( z[0]=='-' ) z++; if( strcmp(z,"debug")==0 ){ if( i==argc-1 ) cmdlineError("missing argument to %s", argv[i]); g.fDebug = strtol(argv[++i], 0, 0); }else if( strcmp(z,"help")==0 ){ showHelp(); return 0; }else { cmdlineError("unknown option: %s", argv[i]); } }else if( zDb==0 ){ zDb = argv[i]; }else{ cmdlineError("unknown argument: %s", argv[i]); } } if( zDb==0 ){ cmdlineError("database filename required"); } rc = sqlite3_open_v2(zDb, &g.db, SQLITE_OPEN_READONLY, 0); if( rc ){ cmdlineError("cannot open database file \"%s\"", zDb); } rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, &zErrMsg); if( rc || zErrMsg ){ cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb); } printf("ANALYZE sqlite_master;\nDELETE FROM sqlite_stat1;\n"); pStmt = db_prepare("SELECT name FROM sqlite_master" " WHERE type='table' AND rootpage>0" " AND name NOT LIKE 'sqlite_%%'" " ORDER BY name"); while( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zName = (const char*)sqlite3_column_text(pStmt, 0); analyzeTable(zName); } sqlite3_finalize(pStmt); printf("ANALYZE sqlite_master;\n"); sqlite3_close(g.db); iTotal = currentTime() - iStart; printf("-- elapsed time: %lld.%03lld seconds\n", iTotal/1000, iTotal%1000); return 0; } |
Changes to tool/fuzzershell.c.
︙ | ︙ | |||
191 192 193 194 195 196 197 | /* ** This callback is invoked by sqlite3_exec() to return query results. */ static int execCallback(void *NotUsed, int argc, char **argv, char **colv){ int i; static unsigned cnt = 0; printf("ROW #%u:\n", ++cnt); | > | | | | | | > | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | /* ** This callback is invoked by sqlite3_exec() to return query results. */ static int execCallback(void *NotUsed, int argc, char **argv, char **colv){ int i; static unsigned cnt = 0; printf("ROW #%u:\n", ++cnt); if( argv ){ for(i=0; i<argc; i++){ printf(" %s=", colv[i]); if( argv[i] ){ printf("[%s]\n", argv[i]); }else{ printf("NULL\n"); } } } fflush(stdout); return 0; } static int execNoop(void *NotUsed, int argc, char **argv, char **colv){ return 0; |
︙ | ︙ | |||
219 220 221 222 223 224 225 226 227 228 229 230 231 232 | printf("TRACE: %s\n", zMsg); fflush(stdout); } static void traceNoop(void *NotUsed, const char *zMsg){ return; } #endif /*************************************************************************** ** eval() implementation copied from ../ext/misc/eval.c */ /* ** Structure used to accumulate the output */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | printf("TRACE: %s\n", zMsg); fflush(stdout); } static void traceNoop(void *NotUsed, const char *zMsg){ return; } #endif /*************************************************************************** ** String accumulator object */ typedef struct Str Str; struct Str { char *z; /* The string. Memory from malloc() */ sqlite3_uint64 n; /* Bytes of input used */ sqlite3_uint64 nAlloc; /* Bytes allocated to z[] */ int oomErr; /* OOM error has been seen */ }; /* Initialize a Str object */ static void StrInit(Str *p){ memset(p, 0, sizeof(*p)); } /* Append text to the end of a Str object */ static void StrAppend(Str *p, const char *z){ sqlite3_uint64 n = strlen(z); if( p->n + n >= p->nAlloc ){ char *zNew; sqlite3_uint64 nNew; if( p->oomErr ) return; nNew = p->nAlloc*2 + 100 + n; zNew = sqlite3_realloc(p->z, (int)nNew); if( zNew==0 ){ sqlite3_free(p->z); memset(p, 0, sizeof(*p)); p->oomErr = 1; return; } p->z = zNew; p->nAlloc = nNew; } memcpy(p->z + p->n, z, (size_t)n); p->n += n; p->z[p->n] = 0; } /* Return the current string content */ static char *StrStr(Str *p){ return p->z; } /* Free the string */ static void StrFree(Str *p){ sqlite3_free(p->z); StrInit(p); } /*************************************************************************** ** eval() implementation copied from ../ext/misc/eval.c */ /* ** Structure used to accumulate the output */ |
︙ | ︙ | |||
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 | printf("Once.%d\n", oomCnt); fflush(stdout); } }else{ oomCnt = 0; } do{ if( zDbName ){ rc = sqlite3_open_v2(zDbName, &db, SQLITE_OPEN_READWRITE, 0); if( rc!=SQLITE_OK ){ abendError("Cannot open database file %s", zDbName); } }else{ rc = sqlite3_open_v2( | > > | 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 | printf("Once.%d\n", oomCnt); fflush(stdout); } }else{ oomCnt = 0; } do{ Str sql; StrInit(&sql); if( zDbName ){ rc = sqlite3_open_v2(zDbName, &db, SQLITE_OPEN_READWRITE, 0); if( rc!=SQLITE_OK ){ abendError("Cannot open database file %s", zDbName); } }else{ rc = sqlite3_open_v2( |
︙ | ︙ | |||
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 | sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0, sqlEvalFunc, 0, 0); sqlite3_create_module(db, "generate_series", &seriesModule, 0); sqlite3_limit(db, SQLITE_LIMIT_LENGTH, 1000000); if( zEncoding ) sqlexec(db, "PRAGMA encoding=%s", zEncoding); if( pageSize ) sqlexec(db, "PRAGMA pagesize=%d", pageSize); if( doAutovac ) sqlexec(db, "PRAGMA auto_vacuum=FULL"); iStart = timeOfDay(); g.bOomEnable = 1; if( verboseFlag ){ zErrMsg = 0; rc = sqlite3_exec(db, zSql, execCallback, 0, &zErrMsg); if( zErrMsg ){ sqlite3_snprintf(sizeof(zErrBuf),zErrBuf,"%z", zErrMsg); zErrMsg = 0; } }else { rc = sqlite3_exec(db, zSql, execNoop, 0, 0); } g.bOomEnable = 0; iEnd = timeOfDay(); rc = sqlite3_close(db); if( rc ){ abendError("sqlite3_close() failed with rc=%d", rc); } if( !zDataOut && sqlite3_memory_used()>0 ){ abendError("memory in use after close: %lld bytes",sqlite3_memory_used()); } | > > > > > > > > > > > > > > > > > > > > | 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 | sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0, sqlEvalFunc, 0, 0); sqlite3_create_module(db, "generate_series", &seriesModule, 0); sqlite3_limit(db, SQLITE_LIMIT_LENGTH, 1000000); if( zEncoding ) sqlexec(db, "PRAGMA encoding=%s", zEncoding); if( pageSize ) sqlexec(db, "PRAGMA pagesize=%d", pageSize); if( doAutovac ) sqlexec(db, "PRAGMA auto_vacuum=FULL"); iStart = timeOfDay(); /* If using an input database file and that database contains a table ** named "autoexec" with a column "sql", then replace the input SQL ** with the concatenated text of the autoexec table. In this way, ** if the database file is the input being fuzzed, the SQL text is ** fuzzed at the same time. */ if( sqlite3_table_column_metadata(db,0,"autoexec","sql",0,0,0,0,0)==0 ){ sqlite3_stmt *pStmt2; rc = sqlite3_prepare_v2(db,"SELECT sql FROM autoexec",-1,&pStmt2,0); if( rc==SQLITE_OK ){ while( sqlite3_step(pStmt2)==SQLITE_ROW ){ StrAppend(&sql, (const char*)sqlite3_column_text(pStmt2, 0)); StrAppend(&sql, "\n"); } } sqlite3_finalize(pStmt2); zSql = StrStr(&sql); } g.bOomEnable = 1; if( verboseFlag ){ zErrMsg = 0; rc = sqlite3_exec(db, zSql, execCallback, 0, &zErrMsg); if( zErrMsg ){ sqlite3_snprintf(sizeof(zErrBuf),zErrBuf,"%z", zErrMsg); zErrMsg = 0; } }else { rc = sqlite3_exec(db, zSql, execNoop, 0, 0); } g.bOomEnable = 0; iEnd = timeOfDay(); StrFree(&sql); rc = sqlite3_close(db); if( rc ){ abendError("sqlite3_close() failed with rc=%d", rc); } if( !zDataOut && sqlite3_memory_used()>0 ){ abendError("memory in use after close: %lld bytes",sqlite3_memory_used()); } |
︙ | ︙ |
Added tool/kvtest-speed.sh.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/bin/bash # # A script for running speed tests using kvtest. # # The test database must be set up first. Recommended # command-line: # # ./kvtest init kvtest.db --count 100K --size 12K --variance 5K if test "$1" = "" then echo "Usage: $0 OUTPUTFILE [OPTIONS]" exit fi NAME=$1 shift OPTS="-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DIRECT_OVERFLOW_READ -DUSE_PREAD" KVARGS="--count 100K --stats" gcc -g -Os -I. $OPTS $* kvtest.c sqlite3.c -o kvtest # First run using SQL rm cachegrind.out.[1-9][0-9]* valgrind --tool=cachegrind ./kvtest run kvtest.db $KVARGS 2>&1 | tee summary-kvtest-$NAME.txt mv cachegrind.out.[1-9][0-9]* cachegrind.out.sql-$NAME cg_anno.tcl cachegrind.out.sql-$NAME >cout-kvtest-sql-$NAME.txt # Second run using the sqlite3_blob object valgrind --tool=cachegrind ./kvtest run kvtest.db $KVARGS --blob-api 2>&1 | tee -a summary-kvtest-$NAME.txt mv cachegrind.out.[1-9][0-9]* cachegrind.out.$NAME cg_anno.tcl cachegrind.out.$NAME >cout-kvtest-$NAME.txt # Diff the sqlite3_blob API analysis for non-trunk runs. if test "$NAME" != "trunk"; then fossil test-diff --tk cout-kvtest-trunk.txt cout-kvtest-$NAME.txt & fi |
Changes to tool/lemon.c.
︙ | ︙ | |||
164 165 166 167 168 169 170 | struct lemon; struct action; static struct action *Action_new(void); static struct action *Action_sort(struct action *); /********** From the file "build.h" ************************************/ | | | | | | | | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | struct lemon; struct action; static struct action *Action_new(void); static struct action *Action_sort(struct action *); /********** From the file "build.h" ************************************/ void FindRulePrecedences(struct lemon*); void FindFirstSets(struct lemon*); void FindStates(struct lemon*); void FindLinks(struct lemon*); void FindFollowSets(struct lemon*); void FindActions(struct lemon*); /********* From the file "configlist.h" *********************************/ void Configlist_init(void); struct config *Configlist_add(struct rule *, int); struct config *Configlist_addbasis(struct rule *, int); void Configlist_closure(struct lemon *); void Configlist_sort(void); |
︙ | ︙ | |||
453 454 455 456 457 458 459 | /* Routines to manage the state table */ int Configcmp(const char *, const char *); struct state *State_new(void); void State_init(void); int State_insert(struct state *, struct config *); struct state *State_find(struct config *); | | | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | /* Routines to manage the state table */ int Configcmp(const char *, const char *); struct state *State_new(void); void State_init(void); int State_insert(struct state *, struct config *); struct state *State_find(struct config *); struct state **State_arrayof(void); /* Routines used for efficiency in Configlist_add */ void Configtable_init(void); int Configtable_insert(struct config *); struct config *Configtable_find(struct config *); void Configtable_clear(int(*)(struct config *)); |
︙ | ︙ | |||
557 558 559 560 561 562 563 | ** value is then added to this initial offset to get an index X into the ** yy_action array. If the aAction[X].lookahead equals the value of the ** of the lookahead input, then the value of the action_number output is ** aAction[X].action. If the lookaheads do not match then the ** default action for the state_number is returned. ** ** All actions associated with a single state_number are first entered | | | | 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | ** value is then added to this initial offset to get an index X into the ** yy_action array. If the aAction[X].lookahead equals the value of the ** of the lookahead input, then the value of the action_number output is ** aAction[X].action. If the lookaheads do not match then the ** default action for the state_number is returned. ** ** All actions associated with a single state_number are first entered ** into aLookahead[] using multiple calls to acttab_action(). Then the ** actions for that single state_number are placed into the aAction[] ** array with a single call to acttab_insert(). The acttab_insert() call ** also resets the aLookahead[] array in preparation for the next ** state number. */ struct lookahead_action { int lookahead; /* Value of the lookahead token */ int action; /* Action to take on the given lookahead */ |
︙ | ︙ | |||
608 609 610 611 612 613 614 | fprintf(stderr,"Unable to allocate memory for a new acttab."); exit(1); } memset(p, 0, sizeof(*p)); return p; } | | | 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | fprintf(stderr,"Unable to allocate memory for a new acttab."); exit(1); } memset(p, 0, sizeof(*p)); 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; |
︙ | ︙ | |||
670 671 672 673 674 675 676 | } for(i=oldAlloc; i<p->nActionAlloc; i++){ p->aAction[i].lookahead = -1; p->aAction[i].action = -1; } } | | | 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | } for(i=oldAlloc; i<p->nActionAlloc; i++){ p->aAction[i].lookahead = -1; p->aAction[i].action = -1; } } /* Scan the existing action table looking for an offset that is a ** duplicate of the current transaction set. Fall out of the loop ** if and when the duplicate is found. ** ** i is the index in p->aAction[] where p->mnLookahead is inserted. */ for(i=p->nAction-1; i>=0; i--){ if( p->aAction[i].lookahead==p->mnLookahead ){ |
︙ | ︙ | |||
748 749 750 751 752 753 754 | /********************** From the file "build.c" *****************************/ /* ** Routines to construction the finite state machine for the LEMON ** parser generator. */ /* Find a precedence symbol of every rule in the grammar. | | | 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 | /********************** From the file "build.c" *****************************/ /* ** Routines to construction the finite state machine for the LEMON ** parser generator. */ /* Find a precedence symbol of every rule in the grammar. ** ** Those rules which have a precedence symbol coded in the input ** grammar using the "[symbol]" construct will already have the ** rp->precsym field filled. Other rules take as their precedence ** symbol the first RHS symbol with a defined precedence. If there ** are not RHS symbols with a defined precedence, the precedence ** symbol field is left blank. */ |
︙ | ︙ | |||
1068 1069 1070 1071 1072 1073 1074 | int change; for(i=0; i<lemp->nstate; i++){ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ cfp->status = INCOMPLETE; } } | | | 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 | int change; for(i=0; i<lemp->nstate; i++){ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ cfp->status = INCOMPLETE; } } do{ progress = 0; for(i=0; i<lemp->nstate; i++){ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ if( cfp->status==COMPLETE ) continue; for(plp=cfp->fplp; plp; plp=plp->next){ change = SetUnion(plp->cfp->fws,cfp->fws); |
︙ | ︙ | |||
1099 1100 1101 1102 1103 1104 1105 | { int i,j; struct config *cfp; struct state *stp; struct symbol *sp; struct rule *rp; | | | 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 | { int i,j; struct config *cfp; struct state *stp; struct symbol *sp; struct rule *rp; /* Add all of the reduce actions ** A reduce action is added for each element of the followset of ** a configuration which has its dot at the extreme right. */ for(i=0; i<lemp->nstate; i++){ /* Loop over all states */ stp = lemp->sorted[i]; for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ |
︙ | ︙ | |||
1216 1217 1218 1219 1220 1221 1222 | errcnt++; }else if( spx->prec>spy->prec ){ apy->type = RD_RESOLVED; }else if( spx->prec<spy->prec ){ apx->type = RD_RESOLVED; } }else{ | | | 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 | errcnt++; }else if( spx->prec>spy->prec ){ apy->type = RD_RESOLVED; }else if( spx->prec<spy->prec ){ apx->type = RD_RESOLVED; } }else{ assert( apx->type==SH_RESOLVED || apx->type==RD_RESOLVED || apx->type==SSCONFLICT || apx->type==SRCONFLICT || apx->type==RRCONFLICT || apy->type==SH_RESOLVED || apy->type==RD_RESOLVED || |
︙ | ︙ | |||
1247 1248 1249 1250 1251 1252 1253 | static struct config *freelist = 0; /* List of free configurations */ 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 */ | | | 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 | static struct config *freelist = 0; /* List of free configurations */ 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){ struct config *newcfg; if( freelist==0 ){ int i; int amt = 3; freelist = (struct config *)calloc( amt, sizeof(struct config) ); if( freelist==0 ){ fprintf(stderr,"Unable to allocate memory for a new configuration."); |
︙ | ︙ | |||
1273 1274 1275 1276 1277 1278 1279 | PRIVATE void deleteconfig(struct config *old) { old->next = freelist; freelist = old; } /* Initialized the configuration list builder */ | | | | 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 | PRIVATE void deleteconfig(struct config *old) { old->next = freelist; freelist = old; } /* Initialized the configuration list builder */ void Configlist_init(void){ current = 0; currentend = ¤t; basis = 0; basisend = &basis; Configtable_init(); return; } /* Initialized the configuration list builder */ void Configlist_reset(void){ current = 0; currentend = ¤t; basis = 0; basisend = &basis; Configtable_clear(0); return; } |
︙ | ︙ | |||
1393 1394 1395 1396 1397 1398 1399 | } } } return; } /* Sort the configuration list */ | | | | | | 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 | } } } return; } /* Sort the configuration list */ void Configlist_sort(void){ current = (struct config*)msort((char*)current,(char**)&(current->next), Configcmp); currentend = 0; return; } /* Sort the basis configuration list */ void Configlist_sortbasis(void){ basis = (struct config*)msort((char*)current,(char**)&(current->bp), Configcmp); basisend = 0; return; } /* Return a pointer to the head of the configuration list and ** reset the list */ struct config *Configlist_return(void){ struct config *old; old = current; current = 0; currentend = 0; return old; } /* Return a pointer to the head of the configuration list and ** reset the list */ struct config *Configlist_basis(void){ struct config *old; old = basis; basis = 0; basisend = 0; return old; } |
︙ | ︙ | |||
1462 1463 1464 1465 1466 1467 1468 | /* ** Main program file for the LEMON parser generator. */ /* Report an out-of-memory condition and abort. This function ** is used mostly by the "MemoryCheck" macro in struct.h */ | | | 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 | /* ** Main program file for the LEMON parser generator. */ /* Report an out-of-memory condition and abort. This function ** is used mostly by the "MemoryCheck" macro in struct.h */ void memory_error(void){ fprintf(stderr,"Out of memory. Aborting...\n"); exit(1); } static int nDefine = 0; /* Number of -D options on the command line */ static char **azDefine = 0; /* Name of the -D macros */ |
︙ | ︙ | |||
1602 1603 1604 1605 1606 1607 1608 | int exitcode; struct lemon lem; struct rule *rp; OptInit(argv,options,stderr); if( version ){ printf("Lemon version 1.0\n"); | | | 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 | int exitcode; struct lemon lem; struct rule *rp; OptInit(argv,options,stderr); if( version ){ printf("Lemon version 1.0\n"); exit(0); } if( OptNArgs()!=1 ){ fprintf(stderr,"Exactly one filename argument is required.\n"); exit(1); } memset(&lem, 0, sizeof(lem)); lem.errorcnt = 0; |
︙ | ︙ | |||
2047 2048 2049 2050 2051 2052 2053 | fprintf(err,"Valid command line options for \"%s\" are:\n",*a); OptPrint(); exit(1); } return 0; } | | | 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 | fprintf(err,"Valid command line options for \"%s\" are:\n",*a); OptPrint(); exit(1); } return 0; } int OptNArgs(void){ int cnt = 0; int dashdash = 0; int i; if( argv!=0 && argv[0]!=0 ){ for(i=1; argv[i]; i++){ if( dashdash || !ISOPT(argv[i]) ) cnt++; if( strcmp(argv[i],"--")==0 ) dashdash = 1; |
︙ | ︙ | |||
2074 2075 2076 2077 2078 2079 2080 | void OptErr(int n) { int i; i = argindex(n); if( i>=0 ) errline(i,0,errstream); } | | | 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 | void OptErr(int n) { int i; i = argindex(n); if( i>=0 ) errline(i,0,errstream); } void OptPrint(void){ int i; int max, len; max = 0; for(i=0; op[i].label; i++){ len = lemonStrlen(op[i].label) + 1; switch( op[i].type ){ case OPT_FLAG: |
︙ | ︙ | |||
2151 2152 2153 2154 2155 2156 2157 | RESYNC_AFTER_RULE_ERROR, RESYNC_AFTER_DECL_ERROR, WAITING_FOR_DESTRUCTOR_SYMBOL, WAITING_FOR_DATATYPE_SYMBOL, WAITING_FOR_FALLBACK_ID, WAITING_FOR_WILDCARD_ID, WAITING_FOR_CLASS_ID, | | > | 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 | RESYNC_AFTER_RULE_ERROR, RESYNC_AFTER_DECL_ERROR, WAITING_FOR_DESTRUCTOR_SYMBOL, WAITING_FOR_DATATYPE_SYMBOL, WAITING_FOR_FALLBACK_ID, WAITING_FOR_WILDCARD_ID, WAITING_FOR_CLASS_ID, WAITING_FOR_CLASS_TOKEN, WAITING_FOR_TOKEN_NAME }; struct pstate { char *filename; /* Name of the input file */ int tokenlineno; /* Linenumber at which current token starts */ int errorcnt; /* Number of errors so far */ char *tokenstart; /* Text of current token */ struct lemon *gp; /* Global state vector */ |
︙ | ︙ | |||
2303 2304 2305 2306 2307 2308 2309 | psp->errorcnt++; psp->state = RESYNC_AFTER_RULE_ERROR; } break; case IN_RHS: if( x[0]=='.' ){ struct rule *rp; | | | 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 | psp->errorcnt++; psp->state = RESYNC_AFTER_RULE_ERROR; } break; case IN_RHS: if( x[0]=='.' ){ struct rule *rp; rp = (struct rule *)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{ |
︙ | ︙ | |||
2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 | }else if( strcmp(x,"destructor")==0 ){ psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; }else if( strcmp(x,"type")==0 ){ psp->state = WAITING_FOR_DATATYPE_SYMBOL; }else if( strcmp(x,"fallback")==0 ){ psp->fallback = 0; psp->state = WAITING_FOR_FALLBACK_ID; }else if( strcmp(x,"wildcard")==0 ){ psp->state = WAITING_FOR_WILDCARD_ID; }else if( strcmp(x,"token_class")==0 ){ psp->state = WAITING_FOR_CLASS_ID; }else{ ErrorMsg(psp->filename,psp->tokenlineno, "Unknown declaration keyword: \"%%%s\".",x); | > > | 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 | }else if( strcmp(x,"destructor")==0 ){ psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; }else if( strcmp(x,"type")==0 ){ psp->state = WAITING_FOR_DATATYPE_SYMBOL; }else if( strcmp(x,"fallback")==0 ){ psp->fallback = 0; psp->state = WAITING_FOR_FALLBACK_ID; }else if( strcmp(x,"token")==0 ){ psp->state = WAITING_FOR_TOKEN_NAME; }else if( strcmp(x,"wildcard")==0 ){ psp->state = WAITING_FOR_WILDCARD_ID; }else if( strcmp(x,"token_class")==0 ){ psp->state = WAITING_FOR_CLASS_ID; }else{ ErrorMsg(psp->filename,psp->tokenlineno, "Unknown declaration keyword: \"%%%s\".",x); |
︙ | ︙ | |||
2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 | "More than one fallback assigned to token %s", x); psp->errorcnt++; }else{ sp->fallback = psp->fallback; psp->gp->has_fallback = 1; } } break; case WAITING_FOR_WILDCARD_ID: if( x[0]=='.' ){ psp->state = WAITING_FOR_DECL_OR_RULE; }else if( !ISUPPER(x[0]) ){ ErrorMsg(psp->filename, psp->tokenlineno, "%%wildcard argument \"%s\" should be a token", x); | > > > > > > > > > > > > > > > > > > > > | 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 | "More than one fallback assigned to token %s", x); psp->errorcnt++; }else{ sp->fallback = psp->fallback; psp->gp->has_fallback = 1; } } break; case WAITING_FOR_TOKEN_NAME: /* Tokens do not have to be declared before use. But they can be ** in order to control their assigned integer number. The number for ** each token is assigned when it is first seen. So by including ** ** %token ONE TWO THREE ** ** early in the grammar file, that assigns small consecutive values ** to each of the tokens ONE TWO and THREE. */ if( x[0]=='.' ){ psp->state = WAITING_FOR_DECL_OR_RULE; }else if( !ISUPPER(x[0]) ){ ErrorMsg(psp->filename, psp->tokenlineno, "%%token argument \"%s\" should be a token", x); psp->errorcnt++; }else{ (void)Symbol_new(x); } break; case WAITING_FOR_WILDCARD_ID: if( x[0]=='.' ){ psp->state = WAITING_FOR_DECL_OR_RULE; }else if( !ISUPPER(x[0]) ){ ErrorMsg(psp->filename, psp->tokenlineno, "%%wildcard argument \"%s\" should be a token", x); |
︙ | ︙ | |||
2892 2893 2894 2895 2896 2897 2898 | /* ** Routines processing configuration follow-set propagation links ** in the LEMON parser generator. */ static struct plink *plink_freelist = 0; /* Allocate a new plink */ | | | 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 | /* ** 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 *)calloc( amt, sizeof(struct plink) ); if( plink_freelist==0 ){ |
︙ | ︙ | |||
2993 2994 2995 2996 2997 2998 2999 | fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); lemp->errorcnt++; return 0; } return fp; } | | | 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 | fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); lemp->errorcnt++; return 0; } return fp; } /* Duplicate the input file without comments and without actions ** on rules */ void Reprint(struct lemon *lemp) { struct rule *rp; struct symbol *sp; int i, j, maxlen, len, ncolumns, skip; printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); |
︙ | ︙ | |||
3144 3145 3146 3147 3148 3149 3150 | break; case SRCONFLICT: case RRCONFLICT: fprintf(fp,"%*s reduce %-7d ** Parsing conflict **", indent,ap->sp->name,ap->x.rp->iRule); break; case SSCONFLICT: | | | 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 | break; case SRCONFLICT: case RRCONFLICT: fprintf(fp,"%*s reduce %-7d ** Parsing conflict **", indent,ap->sp->name,ap->x.rp->iRule); break; case SSCONFLICT: fprintf(fp,"%*s shift %-7d ** Parsing conflict **", indent,ap->sp->name,ap->x.stp->statenum); break; case SH_RESOLVED: if( showPrecedenceConflict ){ fprintf(fp,"%*s shift %-7d -- dropped by precedence", indent,ap->sp->name,ap->x.stp->statenum); }else{ |
︙ | ︙ | |||
3293 3294 3295 3296 3297 3298 3299 | ** Return negative if no action should be generated. */ PRIVATE int compute_action(struct lemon *lemp, struct action *ap) { int act; switch( ap->type ){ case SHIFT: act = ap->x.stp->statenum; break; | > | > > > > > > | 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 | ** Return negative if no action should be generated. */ PRIVATE int compute_action(struct lemon *lemp, struct action *ap) { int act; switch( ap->type ){ case SHIFT: act = ap->x.stp->statenum; break; case SHIFTREDUCE: { act = ap->x.rp->iRule + lemp->nstate; /* Since a SHIFT is inherient after a prior REDUCE, convert any ** SHIFTREDUCE action with a nonterminal on the LHS into a simple ** REDUCE action: */ if( ap->sp->index>=lemp->nterminal ) act += lemp->nrule; break; } case REDUCE: act = ap->x.rp->iRule + lemp->nstate+lemp->nrule; break; case ERROR: act = lemp->nstate + lemp->nrule*2; break; case ACCEPT: act = lemp->nstate + lemp->nrule*2 + 1; break; default: act = -1; break; } return act; } |
︙ | ︙ | |||
3417 3418 3419 3420 3421 3422 3423 | str++; } if( str[-1]!='\n' ){ putc('\n',out); (*lineno)++; } if (!lemp->nolinenosflag) { | | | 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 | str++; } if( str[-1]!='\n' ){ putc('\n',out); (*lineno)++; } if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); } return; } /* ** The following routine emits code for the destructor for the ** symbol sp |
︙ | ︙ | |||
3462 3463 3464 3465 3466 3467 3468 | cp++; continue; } if( *cp=='\n' ) (*lineno)++; fputc(*cp,out); } fprintf(out,"\n"); (*lineno)++; | | | | 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 | cp++; continue; } if( *cp=='\n' ) (*lineno)++; fputc(*cp,out); } fprintf(out,"\n"); (*lineno)++; if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); } fprintf(out,"}\n"); (*lineno)++; return; } /* ** Return TRUE (non-zero) if the given symbol has a destructor. |
︙ | ︙ | |||
3586 3587 3588 3589 3590 3591 3592 | rp->codePrefix = Strsafe(append_str(0,0,0,0)); rp->noCode = 0; } }else if( rp->lhsalias==0 ){ /* There is no LHS value symbol. */ lhsdirect = 1; }else if( strcmp(rp->lhsalias,rp->rhsalias[0])==0 ){ | | | | 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 | rp->codePrefix = Strsafe(append_str(0,0,0,0)); rp->noCode = 0; } }else if( rp->lhsalias==0 ){ /* There is no LHS value symbol. */ lhsdirect = 1; }else if( strcmp(rp->lhsalias,rp->rhsalias[0])==0 ){ /* The LHS symbol and the left-most RHS symbol are the same, so ** direct writing is allowed */ lhsdirect = 1; lhsused = 1; used[0] = 1; if( rp->lhs->dtnum!=rp->rhs[0]->dtnum ){ ErrorMsg(lemp->filename,rp->ruleline, "%s(%s) and %s(%s) share the same label but have " "different datatypes.", rp->lhs->name, rp->lhsalias, rp->rhs[0]->name, rp->rhsalias[0]); lemp->errorcnt++; } }else{ lemon_sprintf(zOvwrt, "/*%s-overwrites-%s*/", rp->lhsalias, rp->rhsalias[0]); zSkip = strstr(rp->code, zOvwrt); if( zSkip!=0 ){ /* The code contains a special comment that indicates that it is safe ** for the LHS label to overwrite left-most RHS label. */ |
︙ | ︙ | |||
3736 3737 3738 3739 3740 3741 3742 | rp->codeSuffix = Strsafe(cp); rp->noCode = 0; } return rc; } | | | 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 | rp->codeSuffix = Strsafe(cp); rp->noCode = 0; } return rc; } /* ** Generate code which executes when the rule "rp" is reduced. Write ** the code to "out". Make sure lineno stays up-to-date. */ PRIVATE void emit_code( FILE *out, struct rule *rp, struct lemon *lemp, |
︙ | ︙ | |||
4155 4156 4157 4158 4159 4160 4161 | 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++){ | < | | 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 | 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){ if( ap->type==REDUCE || ap->type==SHIFTREDUCE ){ ap->x.rp->doesReduce = 1; } } } /* Finish rendering the constants now that the action table has ** been computed */ fprintf(out,"#define YYNSTATE %d\n",lemp->nxstate); lineno++; |
︙ | ︙ | |||
4234 4235 4236 4237 4238 4239 4240 | /* Output the yy_shift_ofst[] table */ n = lemp->nxstate; while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", lemp->nactiontab); lineno++; fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++; fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++; fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++; | | | 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 | /* Output the yy_shift_ofst[] table */ n = lemp->nxstate; while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", lemp->nactiontab); lineno++; fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++; fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++; fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++; fprintf(out, "static const %s yy_shift_ofst[] = {\n", minimum_size_type(mnTknOfst, lemp->nterminal+lemp->nactiontab, &sz)); lineno++; lemp->tablesize += n*sz; for(i=j=0; i<n; i++){ int ofst; stp = lemp->sorted[i]; ofst = stp->iTknOfst; |
︙ | ︙ | |||
4261 4262 4263 4264 4265 4266 4267 | /* Output the yy_reduce_ofst[] table */ fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; n = lemp->nxstate; while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--; fprintf(out, "#define YY_REDUCE_COUNT (%d)\n", n-1); lineno++; fprintf(out, "#define YY_REDUCE_MIN (%d)\n", mnNtOfst); lineno++; fprintf(out, "#define YY_REDUCE_MAX (%d)\n", mxNtOfst); lineno++; | | | 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 | /* Output the yy_reduce_ofst[] table */ fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; n = lemp->nxstate; while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--; fprintf(out, "#define YY_REDUCE_COUNT (%d)\n", n-1); lineno++; fprintf(out, "#define YY_REDUCE_MIN (%d)\n", mnNtOfst); lineno++; fprintf(out, "#define YY_REDUCE_MAX (%d)\n", mxNtOfst); lineno++; fprintf(out, "static const %s yy_reduce_ofst[] = {\n", minimum_size_type(mnNtOfst-1, mxNtOfst, &sz)); lineno++; lemp->tablesize += n*sz; for(i=j=0; i<n; i++){ int ofst; stp = lemp->sorted[i]; ofst = stp->iNtOfst; if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; |
︙ | ︙ | |||
4340 4341 4342 4343 4344 4345 4346 | fprintf(out," /* %3d */ \"", i); writeRuleText(out, rp); fprintf(out,"\",\n"); lineno++; } tplt_xfer(lemp->name,in,out,&lineno); /* Generate code which executes every time a symbol is popped from | | | 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 | fprintf(out," /* %3d */ \"", i); writeRuleText(out, rp); fprintf(out,"\",\n"); lineno++; } tplt_xfer(lemp->name,in,out,&lineno); /* Generate code which executes every time a symbol is popped from ** the stack while processing errors or while destroying the parser. ** (In other words, generate the %destructor actions) */ if( lemp->tokendest ){ int once = 1; for(i=0; i<lemp->nsymbol; i++){ struct symbol *sp = lemp->symbols[i]; if( sp==0 || sp->type!=TERMINAL ) continue; |
︙ | ︙ | |||
4406 4407 4408 4409 4410 4411 4412 | } tplt_xfer(lemp->name,in,out,&lineno); /* Generate code which executes whenever the parser stack overflows */ tplt_print(out,lemp,lemp->overflow,&lineno); tplt_xfer(lemp->name,in,out,&lineno); | | | | 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 | } tplt_xfer(lemp->name,in,out,&lineno); /* Generate code which executes whenever the parser stack overflows */ tplt_print(out,lemp,lemp->overflow,&lineno); tplt_xfer(lemp->name,in,out,&lineno); /* Generate the table of rule information ** ** Note: This code depends on the fact that rules are number ** sequentually beginning with 0. */ for(rp=lemp->rule; rp; rp=rp->next){ fprintf(out," { %d, %d },\n",rp->lhs->index,-rp->nrhs); lineno++; } tplt_xfer(lemp->name,in,out,&lineno); /* Generate code which execution during each REDUCE action */ i = 0; for(rp=lemp->rule; rp; rp=rp->next){ i += translate_code(lemp, rp); |
︙ | ︙ | |||
4517 4518 4519 4520 4521 4522 4523 | } } out = file_open(lemp,".h","wb"); if( out ){ for(i=1; i<lemp->nterminal; i++){ fprintf(out,"#define %s%-30s %3d\n",prefix,lemp->symbols[i]->name,i); } | | | 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 | } } out = file_open(lemp,".h","wb"); if( out ){ for(i=1; i<lemp->nterminal; i++){ fprintf(out,"#define %s%-30s %3d\n",prefix,lemp->symbols[i]->name,i); } fclose(out); } return; } /* Reduce the size of the action tables, if possible, by making use ** of defaults. ** |
︙ | ︙ | |||
4564 4565 4566 4567 4568 4569 4570 | if( rp2==rp ) n++; } if( n>nbest ){ nbest = n; rbest = rp; } } | | | 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 | if( rp2==rp ) n++; } if( n>nbest ){ nbest = n; rbest = rp; } } /* Do not make a default if the number of rules to default ** is not at least 1 or if the wildcard token is a possible ** lookahead. */ if( nbest<1 || usesWildcard ) continue; |
︙ | ︙ | |||
4721 4722 4723 4724 4725 4726 4727 | /* Set the set size */ void SetSize(int n) { size = n+1; } /* Allocate a new set */ | | | 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 | /* Set the set size */ void SetSize(int n) { size = n+1; } /* Allocate a new set */ char *SetNew(void){ char *s; s = (char*)calloc( size, 1); if( s==0 ){ extern void memory_error(); memory_error(); } return s; |
︙ | ︙ | |||
4827 4828 4829 4830 4831 4832 4833 | struct s_x1node **from; /* Previous link */ } x1node; /* There is only one instance of the array, which is the following */ static struct s_x1 *x1a; /* Allocate a new associative array */ | | | 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 | struct s_x1node **from; /* Previous link */ } x1node; /* 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*)malloc( sizeof(struct s_x1) ); if( x1a ){ x1a->size = 1024; x1a->count = 0; x1a->tbl = (x1node*)calloc(1024, sizeof(x1node) + sizeof(x1node*)); if( x1a->tbl==0 ){ |
︙ | ︙ | |||
4994 4995 4996 4997 4998 4999 5000 | struct s_x2node **from; /* Previous link */ } x2node; /* There is only one instance of the array, which is the following */ static struct s_x2 *x2a; /* Allocate a new associative array */ | | | 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 | struct s_x2node **from; /* Previous link */ } x2node; /* 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*)malloc( sizeof(struct s_x2) ); if( x2a ){ x2a->size = 128; x2a->count = 0; x2a->tbl = (x2node*)calloc(128, sizeof(x2node) + sizeof(x2node*)); if( x2a->tbl==0 ){ |
︙ | ︙ | |||
5191 5192 5193 5194 5195 5196 5197 | struct s_x3node **from; /* Previous link */ } x3node; /* There is only one instance of the array, which is the following */ static struct s_x3 *x3a; /* Allocate a new associative array */ | | | 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 | struct s_x3node **from; /* Previous link */ } x3node; /* 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*)malloc( sizeof(struct s_x3) ); if( x3a ){ x3a->size = 128; x3a->count = 0; x3a->tbl = (x3node*)calloc(128, sizeof(x3node) + sizeof(x3node*)); if( x3a->tbl==0 ){ |
︙ | ︙ | |||
5285 5286 5287 5288 5289 5290 5291 | } return np ? np->data : 0; } /* Return an array of pointers to all data in the table. ** The array is obtained from malloc. Return NULL if memory allocation ** problems, or if the array is empty. */ | | | 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 | } return np ? np->data : 0; } /* Return an array of pointers to all data in the table. ** The array is obtained from malloc. Return NULL if memory allocation ** 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 **)calloc(arrSize, sizeof(struct state *)); if( array ){ |
︙ | ︙ | |||
5331 5332 5333 5334 5335 5336 5337 | struct s_x4node **from; /* Previous link */ } x4node; /* There is only one instance of the array, which is the following */ static struct s_x4 *x4a; /* Allocate a new associative array */ | | | 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 | struct s_x4node **from; /* Previous link */ } x4node; /* 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*)malloc( sizeof(struct s_x4) ); if( x4a ){ x4a->size = 64; x4a->count = 0; x4a->tbl = (x4node*)calloc(64, sizeof(x4node) + sizeof(x4node*)); if( x4a->tbl==0 ){ |
︙ | ︙ |
Changes to tool/lempar.c.
︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 229 230 | ParseARG_SDECL /* A place to hold %extra_argument */ #if YYSTACKDEPTH<=0 int yystksz; /* Current side of the stack */ yyStackEntry *yystack; /* The parser's stack */ yyStackEntry yystk0; /* First stack entry */ #else yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ #endif }; typedef struct yyParser yyParser; #ifndef NDEBUG #include <stdio.h> static FILE *yyTraceFILE = 0; | > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | ParseARG_SDECL /* A place to hold %extra_argument */ #if YYSTACKDEPTH<=0 int yystksz; /* Current side of the stack */ yyStackEntry *yystack; /* The parser's stack */ yyStackEntry yystk0; /* First stack entry */ #else yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ yyStackEntry *yystackEnd; /* Last entry in the stack */ #endif }; typedef struct yyParser yyParser; #ifndef NDEBUG #include <stdio.h> static FILE *yyTraceFILE = 0; |
︙ | ︙ | |||
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | ** putting an appropriate #define in the %include section of the input ** grammar. */ #ifndef YYMALLOCARGTYPE # define YYMALLOCARGTYPE size_t #endif /* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. ** ** Inputs: ** A pointer to the function used to allocate memory. ** ** Outputs: ** A pointer to a parser. This pointer is used in subsequent calls ** to Parse and ParseFree. */ void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){ yyParser *pParser; pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | < < < < < < < < | | < < < < < < | < < | 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 | ** 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 *yypParser){ yyParser *pParser = (yyParser*)yypParser; #ifdef YYTRACKMAXSTACKDEPTH pParser->yyhwm = 0; #endif #if YYSTACKDEPTH<=0 pParser->yytos = NULL; pParser->yystack = NULL; pParser->yystksz = 0; if( yyGrowStack(pParser) ){ pParser->yystack = &pParser->yystk0; pParser->yystksz = 1; } #endif #ifndef YYNOERRORRECOVERY pParser->yyerrcnt = -1; #endif pParser->yytos = pParser->yystack; pParser->yystack[0].stateno = 0; pParser->yystack[0].major = 0; #if YYSTACKDEPTH>0 pParser->yystackEnd = &pParser->yystack[YYSTACKDEPTH-1]; #endif } #ifndef Parse_ENGINEALWAYSONSTACK /* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. ** ** Inputs: ** A pointer to the function used to allocate memory. ** ** Outputs: ** A pointer to a parser. This pointer is used in subsequent calls ** to Parse and ParseFree. */ void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){ yyParser *pParser; pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); if( pParser ) ParseInit(pParser); return pParser; } #endif /* Parse_ENGINEALWAYSONSTACK */ /* The following function deletes the "minor type" or semantic value ** associated with a symbol. The symbol can be either a terminal ** or nonterminal. "yymajor" is the symbol code, and "yypminor" is ** a pointer to the value to be deleted. The code used to do the ** deletions is derived from the %destructor and/or %token_destructor ** directives of the input grammar. |
︙ | ︙ | |||
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | yyTracePrompt, yyTokenName[yytos->major]); } #endif yy_destructor(pParser, yytos->major, &yytos->minor); } /* ** Deallocate and destroy a parser. Destructors are called for ** all stack elements before shutting the parser down. ** ** If the YYPARSEFREENEVERNULL macro exists (for example because it ** is defined in a %include section of the input grammar) then it is ** assumed that the input pointer is never NULL. */ void ParseFree( void *p, /* The parser to be deleted */ void (*freeProc)(void*) /* Function used to reclaim memory */ ){ | > > > > > > > > > > > > < | | < < < | > | 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 | yyTracePrompt, yyTokenName[yytos->major]); } #endif yy_destructor(pParser, yytos->major, &yytos->minor); } /* ** Clear all secondary memory allocations from the parser */ void ParseFinalize(void *p){ yyParser *pParser = (yyParser*)p; while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); #if YYSTACKDEPTH<=0 if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); #endif } #ifndef Parse_ENGINEALWAYSONSTACK /* ** Deallocate and destroy a parser. Destructors are called for ** all stack elements before shutting the parser down. ** ** If the YYPARSEFREENEVERNULL macro exists (for example because it ** is defined in a %include section of the input grammar) then it is ** assumed that the input pointer is never NULL. */ void ParseFree( void *p, /* The parser to be deleted */ void (*freeProc)(void*) /* Function used to reclaim memory */ ){ #ifndef YYPARSEFREENEVERNULL if( p==0 ) return; #endif ParseFinalize(p); (*freeProc)(p); } #endif /* Parse_ENGINEALWAYSONSTACK */ /* ** Return the peak depth of the stack for a parser. */ #ifdef YYTRACKMAXSTACKDEPTH int ParseStackPeak(void *p){ yyParser *pParser = (yyParser*)p; |
︙ | ︙ | |||
534 535 536 537 538 539 540 | } /* ** The following routine is called if the stack overflows. */ static void yyStackOverflow(yyParser *yypParser){ ParseARG_FETCH; | < | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | } /* ** The following routine is called if the stack overflows. */ static void yyStackOverflow(yyParser *yypParser){ ParseARG_FETCH; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); } #endif while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will execute if the parser |
︙ | ︙ | |||
587 588 589 590 591 592 593 | #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif #if YYSTACKDEPTH>0 | | > > | | | 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 | #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif #if YYSTACKDEPTH>0 if( yypParser->yytos>yypParser->yystackEnd ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } #else if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ if( yyGrowStack(yypParser) ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } } #endif if( yyNewState > YY_MAX_SHIFT ){ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; } yytos = yypParser->yytos; yytos->stateno = (YYACTIONTYPE)yyNewState; yytos->major = (YYCODETYPE)yyMajor; yytos->minor.yy0 = yyMinor; yyTraceShift(yypParser, yyNewState); } /* The following table contains information about every rule that ** is used during the reduce. */ static const struct { YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ signed char nrhs; /* Negative of the number of RHS symbols in the rule */ } yyRuleInfo[] = { %% }; static void yy_accept(yyParser*); /* Forward Declaration */ /* |
︙ | ︙ | |||
639 640 641 642 643 644 645 | int yysize; /* Amount to pop the stack */ ParseARG_FETCH; yymsp = yypParser->yytos; #ifndef NDEBUG if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ yysize = yyRuleInfo[yyruleno].nrhs; fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt, | | | | 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 | int yysize; /* Amount to pop the stack */ ParseARG_FETCH; yymsp = yypParser->yytos; #ifndef NDEBUG if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ yysize = yyRuleInfo[yyruleno].nrhs; fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt, yyRuleName[yyruleno], yymsp[yysize].stateno); } #endif /* NDEBUG */ /* Check that the stack is large enough to grow by a single entry ** if the RHS of the rule is empty. This ensures that there is room ** enough on the stack to push the LHS value */ if( yyRuleInfo[yyruleno].nrhs==0 ){ #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); } #endif #if YYSTACKDEPTH>0 if( yypParser->yytos>=yypParser->yystackEnd ){ yyStackOverflow(yypParser); return; } #else if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); |
︙ | ︙ | |||
685 686 687 688 689 690 691 | /********** Begin reduce actions **********************************************/ %% /********** End reduce actions ************************************************/ }; assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) ); yygoto = yyRuleInfo[yyruleno].lhs; yysize = yyRuleInfo[yyruleno].nrhs; | | | > > | | > > | > > > > | < < < < | 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 | /********** Begin reduce actions **********************************************/ %% /********** End reduce actions ************************************************/ }; assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) ); yygoto = yyRuleInfo[yyruleno].lhs; yysize = yyRuleInfo[yyruleno].nrhs; yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto); /* There are no SHIFTREDUCE actions on nonterminals because the table ** generator has simplified them to pure REDUCE actions. */ assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) ); /* It is not possible for a REDUCE to be followed by an error */ assert( yyact!=YY_ERROR_ACTION ); if( yyact==YY_ACCEPT_ACTION ){ yypParser->yytos += yysize; yy_accept(yypParser); }else{ yymsp += yysize+1; yypParser->yytos = yymsp; yymsp->stateno = (YYACTIONTYPE)yyact; yymsp->major = (YYCODETYPE)yygoto; yyTraceShift(yypParser, yyact); } } /* ** The following code executes when the parse fails */ #ifndef YYNOERRORRECOVERY |
︙ | ︙ |
Added tool/max-limits.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 | /* ** Link this program against an SQLite library of unknown provenance in order ** to display the compile-time maximum values for various settings. */ #include "sqlite3.h" #include <stdio.h> static const struct { int eCode; char *zName; } aLimit[] = { { SQLITE_LIMIT_LENGTH, "SQLITE_MAX_LENGTH" }, { SQLITE_LIMIT_SQL_LENGTH, "SQLITE_MAX_SQL_LENGTH" }, { SQLITE_LIMIT_COLUMN, "SQLITE_MAX_COLUMN" }, { SQLITE_LIMIT_EXPR_DEPTH, "SQLITE_MAX_EXPR_DEPTH" }, { SQLITE_LIMIT_COMPOUND_SELECT, "SQLITE_MAX_COMPOUND_SELECT" }, { SQLITE_LIMIT_VDBE_OP, "SQLITE_MAX_VDBE_OP" }, { SQLITE_LIMIT_FUNCTION_ARG, "SQLITE_MAX_FUNCTION_ARG" }, { SQLITE_LIMIT_ATTACHED, "SQLITE_MAX_ATTACHED" }, { SQLITE_LIMIT_LIKE_PATTERN_LENGTH, "SQLITE_MAX_LIKE_PATTERN_LENGTH" }, { SQLITE_LIMIT_VARIABLE_NUMBER, "SQLITE_MAX_VARIABLE_NUMBER" }, { SQLITE_LIMIT_TRIGGER_DEPTH, "SQLITE_MAX_TRIGGER_DEPTH" }, { SQLITE_LIMIT_WORKER_THREADS, "SQLITE_MAX_WORKER_THREADS" }, }; static int maxLimit(sqlite3 *db, int eCode){ int iOrig = sqlite3_limit(db, eCode, 0x7fffffff); return sqlite3_limit(db, eCode, iOrig); } int main(int argc, char **argv){ sqlite3 *db; int j, rc; rc = sqlite3_open(":memory:", &db); if( rc==SQLITE_OK ){ for(j=0; j<sizeof(aLimit)/sizeof(aLimit[0]); j++){ printf("%-35s %10d\n", aLimit[j].zName, maxLimit(db, aLimit[j].eCode)); } sqlite3_close(db); } } |
Added tool/mkctimec.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 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 | #!/usr/bin/tclsh # # To build the # # const char **azCompileOpt[] # # declaration used in src/ctime.c, run this script. # # All Boolean compile time options. # set boolean_options { SQLITE_32BIT_ROWID SQLITE_4_BYTE_ALIGNED_MALLOC SQLITE_64BIT_STATS SQLITE_ALLOW_COVERING_INDEX_SCAN SQLITE_ALLOW_URI_AUTHORITY SQLITE_BUG_COMPATIBLE_20160819 SQLITE_CASE_SENSITIVE_LIKE SQLITE_CHECK_PAGES SQLITE_COVERAGE_TEST SQLITE_DEBUG SQLITE_DEFAULT_AUTOMATIC_INDEX SQLITE_DEFAULT_AUTOVACUUM SQLITE_DEFAULT_CKPTFULLFSYNC SQLITE_DEFAULT_FOREIGN_KEYS SQLITE_DEFAULT_LOCKING_MODE SQLITE_DEFAULT_MEMSTATUS SQLITE_DEFAULT_RECURSIVE_TRIGGERS SQLITE_DEFAULT_SYNCHRONOUS SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DIRECT_OVERFLOW_READ SQLITE_DISABLE_DIRSYNC SQLITE_DISABLE_FTS3_UNICODE SQLITE_DISABLE_FTS4_DEFERRED SQLITE_DISABLE_INTRINSIC SQLITE_DISABLE_LFS SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS SQLITE_DISABLE_SKIPAHEAD_DISTINCT SQLITE_ENABLE_8_3_NAMES SQLITE_ENABLE_API_ARMOR SQLITE_ENABLE_ATOMIC_WRITE SQLITE_ENABLE_CEROD SQLITE_ENABLE_COLUMN_METADATA SQLITE_ENABLE_COLUMN_USED_MASK SQLITE_ENABLE_COSTMULT SQLITE_ENABLE_CURSOR_HINTS SQLITE_ENABLE_DBSTAT_VTAB SQLITE_ENABLE_EXPENSIVE_ASSERT SQLITE_ENABLE_FTS1 SQLITE_ENABLE_FTS2 SQLITE_ENABLE_FTS3 SQLITE_ENABLE_FTS3_PARENTHESIS SQLITE_ENABLE_FTS3_TOKENIZER SQLITE_ENABLE_FTS4 SQLITE_ENABLE_FTS5 SQLITE_ENABLE_HIDDEN_COLUMNS SQLITE_ENABLE_ICU SQLITE_ENABLE_IOTRACE SQLITE_ENABLE_JSON1 SQLITE_ENABLE_LOAD_EXTENSION SQLITE_ENABLE_LOCKING_STYLE SQLITE_ENABLE_MEMORY_MANAGEMENT SQLITE_ENABLE_MEMSYS3 SQLITE_ENABLE_MEMSYS5 SQLITE_ENABLE_MULTIPLEX SQLITE_ENABLE_NULL_TRIM SQLITE_ENABLE_OVERSIZE_CELL_CHECK SQLITE_ENABLE_PREUPDATE_HOOK SQLITE_ENABLE_RBU SQLITE_ENABLE_RTREE SQLITE_ENABLE_SELECTTRACE SQLITE_ENABLE_SESSION SQLITE_ENABLE_SNAPSHOT SQLITE_ENABLE_SQLLOG SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION SQLITE_ENABLE_UNLOCK_NOTIFY SQLITE_ENABLE_UPDATE_DELETE_LIMIT SQLITE_ENABLE_URI_00_ERROR SQLITE_ENABLE_VFSTRACE SQLITE_ENABLE_WHERETRACE SQLITE_ENABLE_ZIPVFS SQLITE_EXPLAIN_ESTIMATED_ROWS SQLITE_EXTRA_IFNULLROW SQLITE_FTS5_ENABLE_TEST_MI SQLITE_FTS5_NO_WITHOUT_ROWID SQLITE_HAS_CODEC SQLITE_HOMEGROWN_RECURSIVE_MUTEX SQLITE_IGNORE_AFP_LOCK_ERRORS SQLITE_IGNORE_FLOCK_LOCK_ERRORS SQLITE_INLINE_MEMCPY SQLITE_INT64_TYPE SQLITE_LIKE_DOESNT_MATCH_BLOBS SQLITE_LOCK_TRACE SQLITE_LOG_CACHE_SPILL SQLITE_MEMDEBUG SQLITE_MIXED_ENDIAN_64BIT_FLOAT SQLITE_MMAP_READWRITE SQLITE_MUTEX_NOOP SQLITE_MUTEX_NREF SQLITE_MUTEX_OMIT SQLITE_MUTEX_PTHREADS SQLITE_MUTEX_W32 SQLITE_NEED_ERR_NAME SQLITE_NOINLINE SQLITE_NO_SYNC SQLITE_OMIT_ALTERTABLE SQLITE_OMIT_ANALYZE SQLITE_OMIT_ATTACH SQLITE_OMIT_AUTHORIZATION SQLITE_OMIT_AUTOINCREMENT SQLITE_OMIT_AUTOINIT SQLITE_OMIT_AUTOMATIC_INDEX SQLITE_OMIT_AUTORESET SQLITE_OMIT_AUTOVACUUM SQLITE_OMIT_BETWEEN_OPTIMIZATION SQLITE_OMIT_BLOB_LITERAL SQLITE_OMIT_BTREECOUNT SQLITE_OMIT_CAST SQLITE_OMIT_CHECK SQLITE_OMIT_COMPLETE SQLITE_OMIT_COMPOUND_SELECT SQLITE_OMIT_CONFLICT_CLAUSE SQLITE_OMIT_CTE SQLITE_OMIT_DATETIME_FUNCS SQLITE_OMIT_DECLTYPE SQLITE_OMIT_DEPRECATED SQLITE_OMIT_DISKIO SQLITE_OMIT_EXPLAIN SQLITE_OMIT_FLAG_PRAGMAS SQLITE_OMIT_FLOATING_POINT SQLITE_OMIT_FOREIGN_KEY SQLITE_OMIT_GET_TABLE SQLITE_OMIT_HEX_INTEGER SQLITE_OMIT_INCRBLOB SQLITE_OMIT_INTEGRITY_CHECK SQLITE_OMIT_LIKE_OPTIMIZATION SQLITE_OMIT_LOAD_EXTENSION SQLITE_OMIT_LOCALTIME SQLITE_OMIT_LOOKASIDE SQLITE_OMIT_MEMORYDB SQLITE_OMIT_OR_OPTIMIZATION SQLITE_OMIT_PAGER_PRAGMAS SQLITE_OMIT_PARSER_TRACE SQLITE_OMIT_POPEN SQLITE_OMIT_PRAGMA SQLITE_OMIT_PROGRESS_CALLBACK SQLITE_OMIT_QUICKBALANCE SQLITE_OMIT_REINDEX SQLITE_OMIT_SCHEMA_PRAGMAS SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS SQLITE_OMIT_SHARED_CACHE SQLITE_OMIT_SHUTDOWN_DIRECTORIES SQLITE_OMIT_SUBQUERY SQLITE_OMIT_TCL_VARIABLE SQLITE_OMIT_TEMPDB SQLITE_OMIT_TEST_CONTROL SQLITE_OMIT_TRACE SQLITE_OMIT_TRIGGER SQLITE_OMIT_TRUNCATE_OPTIMIZATION SQLITE_OMIT_UTF16 SQLITE_OMIT_VACUUM SQLITE_OMIT_VIEW SQLITE_OMIT_VIRTUALTABLE SQLITE_OMIT_WAL SQLITE_OMIT_WSD SQLITE_OMIT_XFER_OPT SQLITE_PCACHE_SEPARATE_HEADER SQLITE_PERFORMANCE_TRACE SQLITE_POWERSAFE_OVERWRITE SQLITE_PREFER_PROXY_LOCKING SQLITE_PROXY_DEBUG SQLITE_REVERSE_UNORDERED_SELECTS SQLITE_RTREE_INT_ONLY SQLITE_SECURE_DELETE SQLITE_SMALL_STACK SQLITE_SOUNDEX SQLITE_SUBSTR_COMPATIBILITY SQLITE_SYSTEM_MALLOC SQLITE_TCL SQLITE_TEST SQLITE_UNLINK_AFTER_CLOSE SQLITE_UNTESTABLE SQLITE_USE_ALLOCA SQLITE_USE_FCNTL_TRACE SQLITE_USER_AUTHENTICATION SQLITE_USE_URI SQLITE_VDBE_COVERAGE SQLITE_WIN32_MALLOC SQLITE_ZERO_MALLOC } # All compile time options for which the assigned value is other than boolean. # set value_options { SQLITE_BITMASK_TYPE SQLITE_DEFAULT_CACHE_SIZE SQLITE_DEFAULT_FILE_FORMAT SQLITE_DEFAULT_FILE_PERMISSIONS SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT SQLITE_DEFAULT_LOCKING_MODE SQLITE_DEFAULT_LOOKASIDE SQLITE_DEFAULT_MMAP_SIZE SQLITE_DEFAULT_PAGE_SIZE SQLITE_DEFAULT_PCACHE_INITSZ SQLITE_DEFAULT_PROXYDIR_PERMISSIONS SQLITE_DEFAULT_ROWEST SQLITE_DEFAULT_SECTOR_SIZE SQLITE_DEFAULT_SYNCHRONOUS SQLITE_DEFAULT_WAL_AUTOCHECKPOINT SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DEFAULT_WORKER_THREADS SQLITE_ENABLE_8_3_NAMES SQLITE_ENABLE_LOCKING_STYLE SQLITE_EXTRA_INIT SQLITE_EXTRA_SHUTDOWN SQLITE_FTS3_MAX_EXPR_DEPTH SQLITE_INTEGRITY_CHECK_ERROR_MAX SQLITE_MALLOC_SOFT_LIMIT SQLITE_MAX_ATTACHED SQLITE_MAX_COLUMN SQLITE_MAX_COMPOUND_SELECT SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_EXPR_DEPTH SQLITE_MAX_FUNCTION_ARG SQLITE_MAX_LENGTH SQLITE_MAX_LIKE_PATTERN_LENGTH SQLITE_MAX_MEMORY SQLITE_MAX_MMAP_SIZE SQLITE_MAX_MMAP_SIZE_ SQLITE_MAX_PAGE_COUNT SQLITE_MAX_PAGE_SIZE SQLITE_MAX_SCHEMA_RETRY SQLITE_MAX_SQL_LENGTH SQLITE_MAX_TRIGGER_DEPTH SQLITE_MAX_VARIABLE_NUMBER SQLITE_MAX_VDBE_OP SQLITE_MAX_WORKER_THREADS SQLITE_SORTER_PMASZ SQLITE_STAT4_SAMPLES SQLITE_STMTJRNL_SPILL SQLITE_TEMP_STORE } # Options that require custom code. # set options(ENABLE_STAT3) { #if defined(SQLITE_ENABLE_STAT4) "ENABLE_STAT4", #elif defined(SQLITE_ENABLE_STAT3) "ENABLE_STAT3", #endif } set options(COMPILER) { #if defined(__clang__) && defined(__clang_major__) "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "." CTIMEOPT_VAL(__clang_minor__) "." CTIMEOPT_VAL(__clang_patchlevel__), #elif defined(_MSC_VER) "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER), #elif defined(__GNUC__) && defined(__VERSION__) "COMPILER=gcc-" __VERSION__, #endif } set options(HAVE_ISNAN) { #if HAVE_ISNAN || SQLITE_HAVE_ISNAN "HAVE_ISNAN", #endif } set options(THREADSAFE) { #if defined(SQLITE_THREADSAFE) "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), #elif defined(THREADSAFE) "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), #else "THREADSAFE=1" #endif } proc trim_name {in} { set ret $in if {[string range $in 0 6]=="SQLITE_"} { set ret [string range $in 7 end] } return $ret } foreach b $boolean_options { set name [trim_name $b] set options($name) [subst { #if $b "$name", #endif }] } foreach v $value_options { set name [trim_name $v] set options($name) [subst { #ifdef $v "$name=" CTIMEOPT_VAL($v), #endif }] } foreach o [lsort [array names options]] { puts [string trim $options($o)] } |
Changes to tool/mkkeywordhash.c.
︙ | ︙ | |||
332 333 334 335 336 337 338 | */ int main(int argc, char **argv){ int i, j, k, h; int bestSize, bestCount; int count; int nChar; int totalLen = 0; | | | | 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | */ int main(int argc, char **argv){ int i, j, k, h; int bestSize, bestCount; int count; int nChar; int totalLen = 0; int aKWHash[1000]; /* 1000 is much bigger than nKeyword */ char zKWText[2000]; /* Remove entries from the list of keywords that have mask==0 */ for(i=j=0; i<nKeyword; i++){ if( aKeywordTable[i].mask==0 ) continue; if( j<i ){ aKeywordTable[j] = aKeywordTable[i]; } |
︙ | ︙ | |||
437 438 439 440 441 442 443 | qsort(aKeywordTable, nKeyword, sizeof(aKeywordTable[0]), keywordCompare3); /* Figure out how big to make the hash table in order to minimize the ** number of collisions */ bestSize = nKeyword; bestCount = nKeyword*nKeyword; for(i=nKeyword/2; i<=2*nKeyword; i++){ | | | | | | | | < | | | | | | | | | > | | | | > > > | | | > | | | > > | | | > | | | | > > > | | | | | 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 | qsort(aKeywordTable, nKeyword, sizeof(aKeywordTable[0]), keywordCompare3); /* Figure out how big to make the hash table in order to minimize the ** number of collisions */ bestSize = nKeyword; bestCount = nKeyword*nKeyword; for(i=nKeyword/2; i<=2*nKeyword; i++){ for(j=0; j<i; j++) aKWHash[j] = 0; for(j=0; j<nKeyword; j++){ h = aKeywordTable[j].hash % i; aKWHash[h] *= 2; aKWHash[h]++; } for(j=count=0; j<i; j++) count += aKWHash[j]; if( count<bestCount ){ bestCount = count; bestSize = i; } } /* Compute the hash */ for(i=0; i<bestSize; i++) aKWHash[i] = 0; for(i=0; i<nKeyword; i++){ h = aKeywordTable[i].hash % bestSize; aKeywordTable[i].iNext = aKWHash[h]; aKWHash[h] = i+1; } /* Begin generating code */ printf("%s", zHdr); printf("/* Hash score: %d */\n", bestCount); printf("/* zKWText[] encodes %d bytes of keyword text in %d bytes */\n", totalLen + nKeyword, nChar+1 ); for(i=j=k=0; i<nKeyword; i++){ Keyword *p = &aKeywordTable[i]; if( p->substrId ) continue; memcpy(&zKWText[k], p->zName, p->len); k += p->len; if( j+p->len>70 ){ printf("%*s */\n", 74-j, ""); j = 0; } if( j==0 ){ printf("/* "); j = 8; } printf("%s", p->zName); j += p->len; } if( j>0 ){ printf("%*s */\n", 74-j, ""); } printf("static const char zKWText[%d] = {\n", nChar); zKWText[nChar] = 0; for(i=j=0; i<k; i++){ if( j==0 ){ printf(" "); } if( zKWText[i]==0 ){ printf("0"); }else{ printf("'%c',", zKWText[i]); } j += 4; if( j>68 ){ printf("\n"); j = 0; } } if( j>0 ) printf("\n"); printf("};\n"); printf("/* aKWHash[i] is the hash value for the i-th keyword */\n"); printf("static const unsigned char aKWHash[%d] = {\n", bestSize); for(i=j=0; i<bestSize; i++){ if( j==0 ) printf(" "); printf(" %3d,", aKWHash[i]); j++; if( j>12 ){ printf("\n"); j = 0; } } printf("%s};\n", j==0 ? "" : "\n"); printf("/* aKWNext[] forms the hash collision chain. If aKWHash[i]==0\n"); printf("** then the i-th keyword has no more hash collisions. Otherwise,\n"); printf("** the next keyword with the same hash is aKWHash[i]-1. */\n"); printf("static const unsigned char aKWNext[%d] = {\n", nKeyword); for(i=j=0; i<nKeyword; i++){ if( j==0 ) printf(" "); printf(" %3d,", aKeywordTable[i].iNext); j++; if( j>12 ){ printf("\n"); j = 0; } } printf("%s};\n", j==0 ? "" : "\n"); printf("/* aKWLen[i] is the length (in bytes) of the i-th keyword */\n"); printf("static const unsigned char aKWLen[%d] = {\n", nKeyword); for(i=j=0; i<nKeyword; i++){ if( j==0 ) printf(" "); printf(" %3d,", aKeywordTable[i].len+aKeywordTable[i].prefix); j++; if( j>12 ){ printf("\n"); j = 0; } } printf("%s};\n", j==0 ? "" : "\n"); printf("/* aKWOffset[i] is the index into zKWText[] of the start of\n"); printf("** the text for the i-th keyword. */\n"); printf("static const unsigned short int aKWOffset[%d] = {\n", nKeyword); for(i=j=0; i<nKeyword; i++){ if( j==0 ) printf(" "); printf(" %3d,", aKeywordTable[i].offset); j++; if( j>12 ){ printf("\n"); j = 0; } } printf("%s};\n", j==0 ? "" : "\n"); printf("/* aKWCode[i] is the parser symbol code for the i-th keyword */\n"); printf("static const unsigned char aKWCode[%d] = {\n", nKeyword); for(i=j=0; i<nKeyword; i++){ char *zToken = aKeywordTable[i].zTokenType; if( j==0 ) printf(" "); printf("%s,%*s", zToken, (int)(14-strlen(zToken)), ""); j++; if( j>=5 ){ printf("\n"); j = 0; } } printf("%s};\n", j==0 ? "" : "\n"); printf("/* Check to see if z[0..n-1] is a keyword. If it is, write the\n"); printf("** parser symbol code for that keyword into *pType. Always\n"); printf("** return the integer n (the length of the token). */\n"); printf("static int keywordCode(const char *z, int n, int *pType){\n"); printf(" int i, j;\n"); printf(" const char *zKW;\n"); printf(" if( n>=2 ){\n"); printf(" i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) %% %d;\n", bestSize); printf(" for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){\n"); printf(" if( aKWLen[i]!=n ) continue;\n"); printf(" j = 0;\n"); printf(" zKW = &zKWText[aKWOffset[i]];\n"); printf("#ifdef SQLITE_ASCII\n"); printf(" while( j<n && (z[j]&~0x20)==zKW[j] ){ j++; }\n"); printf("#endif\n"); printf("#ifdef SQLITE_EBCDIC\n"); printf(" while( j<n && toupper(z[j])==zKW[j] ){ j++; }\n"); printf("#endif\n"); printf(" if( j<n ) continue;\n"); for(i=0; i<nKeyword; i++){ printf(" testcase( i==%d ); /* %s */\n", i, aKeywordTable[i].zOrigName); } printf(" *pType = aKWCode[i];\n"); printf(" break;\n"); printf(" }\n"); printf(" }\n"); printf(" return n;\n"); printf("}\n"); printf("int sqlite3KeywordCode(const unsigned char *z, int n){\n"); printf(" int id = TK_ID;\n"); |
︙ | ︙ |
Changes to tool/mkmsvcmin.tcl.
︙ | ︙ | |||
79 80 81 82 83 84 85 | set blocks(2) [string trimleft [string map [list \\\\ \\] { Replace.exe: $(CSC) /target:exe $(TOP)\Replace.cs sqlite3.def: Replace.exe $(LIBOBJ) echo EXPORTS > sqlite3.def dumpbin /all $(LIBOBJ) \\ | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | set blocks(2) [string trimleft [string map [list \\\\ \\] { Replace.exe: $(CSC) /target:exe $(TOP)\Replace.cs sqlite3.def: Replace.exe $(LIBOBJ) echo EXPORTS > sqlite3.def dumpbin /all $(LIBOBJ) \\ | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \\ | sort >> sqlite3.def }]] set data "#### DO NOT EDIT ####\n" append data "# This makefile is automatically " append data "generated from the [file tail $fromFileName] at\n" append data "# the root of the canonical SQLite source tree (not the\n" |
︙ | ︙ |
Changes to tool/mkopcodeh.tcl.
︙ | ︙ | |||
188 189 190 191 192 193 194 | incr cnt while {[info exists used($cnt)]} {incr cnt} set op($name) $cnt set used($cnt) 1 set def($cnt) $name } } | | > | > > > > | | < < < < | < | | > > > > < > > | | | | | | > | 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 | incr cnt while {[info exists used($cnt)]} {incr cnt} set op($name) $cnt set used($cnt) 1 set def($cnt) $name } } set max [lindex [lsort -decr -integer [array names used]] 0] for {set i 0} {$i<=$max} {incr i} { 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 {$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)" } if {[llength $com]} { puts -nonewline [format " /* %-42s */" [join $com {, }]] } puts "" } if {$max>255} { error "More than 255 opcodes - VdbeOp.opcode is of type u8!" } # Generate the bitvectors: # set bv(0) 0 for {set i 0} {$i<=$max} {incr i} { set x 0 set name $def($i) if {[string match OP_NotUsed* $name]==0} { 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} } 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 "*/" |
︙ | ︙ |
Changes to tool/mkpragmatab.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/usr/bin/tclsh # # Run this script to generate the pragma name lookup table C code. # # To add new pragmas, first add the name and other relevant attributes # of the pragma to the "pragma_def" object below. Then run this script # to generate the ../src/pragma.h header file that contains macros and # the lookup table needed for pragma name lookup in the pragma.c module. # Then add the extra "case PragTyp_XXXXX:" and subsequent code for the # new pragma in ../src/pragma.c. # set pragma_def { NAME: full_column_names TYPE: FLAG ARG: SQLITE_FullColNames IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) NAME: short_column_names | > > > > > > > > > > | 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 | #!/usr/bin/tclsh # # Run this script to generate the pragma name lookup table C code. # # To add new pragmas, first add the name and other relevant attributes # of the pragma to the "pragma_def" object below. Then run this script # to generate the ../src/pragma.h header file that contains macros and # the lookup table needed for pragma name lookup in the pragma.c module. # Then add the extra "case PragTyp_XXXXX:" and subsequent code for the # new pragma in ../src/pragma.c. # # Flag meanings: set flagMeaning(NeedSchema) {Force schema load before running} set flagMeaning(ReadOnly) {Read-only HEADER_VALUE} set flagMeaning(Result0) {Acts as query when no argument} set flagMeaning(Result1) {Acts as query when has one argument} set flagMeaning(SchemaReq) {Schema required - "main" is default} set flagMeaning(SchemaOpt) {Schema restricts name search if present} set flagMeaning(NoColumns) {OP_ResultRow called with zero columns} set flagMeaning(NoColumns1) {zero columns if RHS argument is present} set pragma_def { NAME: full_column_names TYPE: FLAG ARG: SQLITE_FullColNames IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) NAME: short_column_names |
︙ | ︙ | |||
43 44 45 46 47 48 49 50 51 52 53 54 55 56 | NAME: checkpoint_fullfsync TYPE: FLAG ARG: SQLITE_CkptFullFSync IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) NAME: cache_spill IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) NAME: reverse_unordered_selects TYPE: FLAG ARG: SQLITE_ReverseOrder IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) | > | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | NAME: checkpoint_fullfsync TYPE: FLAG ARG: SQLITE_CkptFullFSync IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) NAME: cache_spill FLAG: Result0 SchemaReq NoColumns1 IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) NAME: reverse_unordered_selects TYPE: FLAG ARG: SQLITE_ReverseOrder IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
︙ | ︙ | |||
105 106 107 108 109 110 111 | TYPE: FLAG ARG: SQLITE_IgnoreChecks IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) IF: !defined(SQLITE_OMIT_CHECK) NAME: writable_schema TYPE: FLAG | | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | TYPE: FLAG ARG: SQLITE_IgnoreChecks IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) IF: !defined(SQLITE_OMIT_CHECK) NAME: writable_schema TYPE: FLAG ARG: SQLITE_WriteSchema IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) NAME: read_uncommitted TYPE: FLAG ARG: SQLITE_ReadUncommit IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) NAME: recursive_triggers TYPE: FLAG ARG: SQLITE_RecTriggers IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
︙ | ︙ | |||
135 136 137 138 139 140 141 | IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) NAME: cell_size_check TYPE: FLAG ARG: SQLITE_CellSizeCk NAME: default_cache_size | | > > > | | > | > | | | > > > > | | > | > | | > | > | > | > > > > > > > > > > > > > > > > > > > > > | > | > > | | > > > > > > > > > | | > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > | 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 | IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) NAME: cell_size_check TYPE: FLAG ARG: SQLITE_CellSizeCk NAME: default_cache_size FLAG: NeedSchema Result0 SchemaReq NoColumns1 COLS: cache_size IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) NAME: page_size FLAG: Result0 SchemaReq NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: secure_delete FLAG: Result0 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: page_count FLAG: NeedSchema Result0 SchemaReq IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: max_page_count TYPE: PAGE_COUNT FLAG: NeedSchema Result0 SchemaReq IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: locking_mode FLAG: Result0 SchemaReq IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: journal_mode FLAG: NeedSchema Result0 SchemaReq IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: journal_size_limit FLAG: Result0 SchemaReq IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: cache_size FLAG: NeedSchema Result0 SchemaReq NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: mmap_size IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: auto_vacuum FLAG: NeedSchema Result0 SchemaReq NoColumns1 IF: !defined(SQLITE_OMIT_AUTOVACUUM) NAME: incremental_vacuum FLAG: NeedSchema NoColumns IF: !defined(SQLITE_OMIT_AUTOVACUUM) NAME: temp_store FLAG: Result0 NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: temp_store_directory FLAG: NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: data_store_directory FLAG: NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN NAME: lock_proxy_file FLAG: NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE NAME: synchronous FLAG: NeedSchema Result0 SchemaReq NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: table_info FLAG: NeedSchema Result1 SchemaOpt COLS: cid name type notnull dflt_value pk IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: stats FLAG: NeedSchema Result0 SchemaReq COLS: tbl idx wdth hght flgs IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) NAME: index_info TYPE: INDEX_INFO ARG: 0 FLAG: NeedSchema Result1 SchemaOpt COLS: seqno cid name IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: index_xinfo TYPE: INDEX_INFO ARG: 1 FLAG: NeedSchema Result1 SchemaOpt COLS: seqno cid name desc coll key IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: index_list FLAG: NeedSchema Result1 SchemaOpt COLS: seq name unique origin partial IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: database_list FLAG: NeedSchema Result0 COLS: seq name file IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: function_list FLAG: Result0 COLS: name builtin IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) IF: defined(SQLITE_INTROSPECTION_PRAGMAS) NAME: module_list FLAG: Result0 COLS: name IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) IF: !defined(SQLITE_OMIT_VIRTUALTABLE) IF: defined(SQLITE_INTROSPECTION_PRAGMAS) NAME: pragma_list FLAG: Result0 COLS: name IF: defined(SQLITE_INTROSPECTION_PRAGMAS) NAME: collation_list FLAG: Result0 COLS: seq name IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: foreign_key_list FLAG: NeedSchema Result1 SchemaOpt COLS: id seq table from to on_update on_delete match IF: !defined(SQLITE_OMIT_FOREIGN_KEY) NAME: foreign_key_check FLAG: NeedSchema Result0 COLS: table rowid parent fkid IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) NAME: parser_trace IF: defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_PARSER_TRACE) NAME: case_sensitive_like FLAG: NoColumns NAME: integrity_check FLAG: NeedSchema Result0 Result1 IF: !defined(SQLITE_OMIT_INTEGRITY_CHECK) NAME: quick_check TYPE: INTEGRITY_CHECK FLAG: NeedSchema Result0 Result1 IF: !defined(SQLITE_OMIT_INTEGRITY_CHECK) NAME: est_count FLAG: NeedSchema NAME: btree_sample FLAG: NeedSchema NAME: encoding FLAG: Result0 NoColumns1 IF: !defined(SQLITE_OMIT_UTF16) NAME: schema_version TYPE: HEADER_VALUE ARG: BTREE_SCHEMA_VERSION FLAG: NoColumns1 Result0 IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: user_version TYPE: HEADER_VALUE ARG: BTREE_USER_VERSION FLAG: NoColumns1 Result0 IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: data_version TYPE: HEADER_VALUE ARG: BTREE_DATA_VERSION FLAG: ReadOnly Result0 IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: freelist_count TYPE: HEADER_VALUE ARG: BTREE_FREE_PAGE_COUNT FLAG: ReadOnly Result0 IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: application_id TYPE: HEADER_VALUE ARG: BTREE_APPLICATION_ID FLAG: NoColumns1 Result0 IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: compile_options FLAG: Result0 IF: !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) NAME: wal_checkpoint FLAG: NeedSchema COLS: busy log checkpointed IF: !defined(SQLITE_OMIT_WAL) NAME: wal_autocheckpoint IF: !defined(SQLITE_OMIT_WAL) NAME: shrink_memory FLAG: NoColumns NAME: busy_timeout FLAG: Result0 COLS: timeout NAME: lock_status FLAG: Result0 COLS: database status IF: defined(SQLITE_DEBUG) || defined(SQLITE_TEST) NAME: key IF: defined(SQLITE_HAS_CODEC) NAME: rekey IF: defined(SQLITE_HAS_CODEC) NAME: hexkey IF: defined(SQLITE_HAS_CODEC) NAME: hexrekey TYPE: HEXKEY IF: defined(SQLITE_HAS_CODEC) NAME: activate_extensions IF: defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) NAME: soft_heap_limit FLAG: Result0 NAME: threads FLAG: Result0 NAME: optimize FLAG: Result1 NeedSchema } # Open the output file # set destfile "[file dir [file dir [file normal $argv0]]]/src/pragma.h" puts "Overwriting $destfile with new pragma table..." set fd [open $destfile wb] puts $fd {/* DO NOT EDIT! ** This file is automatically generated by the script at ** ../tool/mkpragmatab.tcl. To update the set of pragmas, edit ** that script and rerun it. */} # Parse the PRAGMA table above. # set name {} set type {} set if {} set flags {} set cols {} set cols_list {} set arg 0 proc record_one {} { global name type if arg allbyname typebyif flags cols allcols global cols_list colUsedBy if {$name==""} return if {$cols!=""} { if {![info exists allcols($cols)]} { lappend cols_list $cols set allcols($cols) [llength $cols_list] } set cx $allcols($cols) lappend colUsedBy($cols) $name } else { set cx 0 } set allbyname($name) [list $type $arg $if $flags $cx] set name {} set type {} set if {} set flags {} set cols {} set arg 0 } foreach line [split $pragma_def \n] { set line [string trim $line] if {$line==""} continue foreach {id val} [split $line :] break set val [string trim $val] if {$id=="NAME"} { record_one set name $val set type [string toupper $val] } elseif {$id=="TYPE"} { set type $val if {$type=="FLAG"} { lappend flags Result0 NoColumns1 } } elseif {$id=="ARG"} { set arg $val } elseif {$id=="COLS"} { set cols $val } elseif {$id=="IF"} { lappend if $val } elseif {$id=="FLAG"} { foreach term [split $val] { lappend flags $term set allflags($term) 1 } } else { error "bad pragma_def line: $line" } } record_one set allnames [lsort [array names allbyname]] # Generate #defines for all pragma type names. Group the pragmas that are # omit in default builds (defined(SQLITE_DEBUG) and defined(SQLITE_HAS_CODEC)) # at the end. # puts $fd "\n/* The various pragma types */" set pnum 0 foreach name $allnames { set type [lindex $allbyname($name) 0] if {[info exists seentype($type)]} continue set if [lindex $allbyname($name) 2] if {[regexp SQLITE_DEBUG $if] || [regexp SQLITE_HAS_CODEC $if]} continue set seentype($type) 1 |
︙ | ︙ | |||
403 404 405 406 407 408 409 410 411 | set seentype($type) 1 puts $fd [format {#define %-35s %4d} PragTyp_$type $pnum] incr pnum } # Generate #defines for flags # set fv 1 foreach f [lsort [array names allflags]] { | > | > > > > > > > > > > > > > > > > > > > > > | | | | > > > | | > | > > > > > > > | | | | > | | 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 | set seentype($type) 1 puts $fd [format {#define %-35s %4d} PragTyp_$type $pnum] incr pnum } # Generate #defines for flags # puts $fd "\n/* Property flags associated with various pragma. */" set fv 1 foreach f [lsort [array names allflags]] { puts $fd [format {#define PragFlg_%-10s 0x%02x /* %s */} \ $f $fv $flagMeaning($f)] set fv [expr {$fv*2}] } # Generate the array of column names used by pragmas that act like # queries. # puts $fd "\n/* Names of columns for pragmas that return multi-column result" puts $fd "** or that return single-column results where the name of the" puts $fd "** result column is different from the name of the pragma\n*/" puts $fd "static const char *const pragCName\[\] = {" set offset 0 foreach cols $cols_list { set cols_offset($allcols($cols)) $offset set ub " /* Used by: $colUsedBy($cols) */" foreach c $cols { puts $fd [format " /* %3d */ %-14s%s" $offset \"$c\", $ub] set ub "" incr offset } } puts $fd "\175;" # Generate the lookup table # puts $fd "\n/* Definitions of all built-in pragmas */" puts $fd "typedef struct PragmaName \173" puts $fd " const char *const zName; /* Name of pragma */" puts $fd " u8 ePragTyp; /* PragTyp_XXX value */" puts $fd " u8 mPragFlg; /* Zero or more PragFlg_XXX values */" puts $fd { u8 iPragCName; /* Start of column names in pragCName[] */} puts $fd " u8 nPragCName; \ /* Num of col names. 0 means use pragma name */" puts $fd " u32 iArg; /* Extra argument */" puts $fd "\175 PragmaName;" puts $fd "static const PragmaName aPragmaName\[\] = \173" set current_if {} set spacer [format { %26s } {}] foreach name $allnames { foreach {type arg if flag cx} $allbyname($name) break if {$cx==0} { set cy 0 set nx 0 } else { set cy $cols_offset($cx) set nx [llength [lindex $cols_list [expr {$cx-1}]]] } if {$if!=$current_if} { if {$current_if!=""} { foreach this_if $current_if { puts $fd "#endif" } } set current_if $if if {$current_if!=""} { foreach this_if $current_if { puts $fd "#if $this_if" } } } set typex [format PragTyp_%-23s $type,] if {$flag==""} { set flagx "0" } else { set flagx PragFlg_[join $flag {|PragFlg_}] } puts $fd " \173/* zName: */ \"$name\"," puts $fd " /* ePragTyp: */ PragTyp_$type," puts $fd " /* ePragFlg: */ $flagx," puts $fd " /* ColNames: */ $cy, $nx," puts $fd " /* iArg: */ $arg \175," } if {$current_if!=""} { foreach this_if $current_if { puts $fd "#endif" } } puts $fd "\175;" |
︙ | ︙ |
Added tool/mkshellc.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 | #!/usr/bin/tclsh # # Run this script to generate the "src/shell.c" source file from # constituent parts. # set topdir [file dir [file dir [file normal $argv0]]] puts "Overwriting $topdir/src/shell.c with new shell source code..." set out [open $topdir/src/shell.c wb] puts $out {/* DO NOT EDIT! ** This file is automatically generated by the script in the canonical ** SQLite source tree at tool/mkshellc.tcl. That script combines source ** code from various constituent source files of SQLite into this single ** "shell.c" file used to implement the SQLite command-line shell. ** ** Most of the code found below comes from the "src/shell.c.in" file in ** the canonical SQLite source tree. That main file contains "INCLUDE" ** lines that specify other files in the canonical source tree that are ** inserted to getnerate this complete program source file. ** ** The code from multiple files is combined into this single "shell.c" ** source file to help make the command-line program easier to compile. ** ** To modify this program, get a copy of the canonical SQLite source tree, ** edit the src/shell.c.in" and/or some of the other files that are included ** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script. */} set in [open $topdir/src/shell.c.in rb] while {1} { set lx [gets $in] if {[eof $in]} break; if {[regexp {^INCLUDE } $lx]} { set cfile [lindex $lx 1] puts $out "/************************* Begin $cfile ******************/" set in2 [open $topdir/src/$cfile rb] while {![eof $in2]} { set lx [gets $in2] if {[regexp {^#include "sqlite} $lx]} continue set lx [string map [list __declspec(dllexport) {}] $lx] puts $out $lx } close $in2 puts $out "/************************* End $cfile ********************/" continue } puts $out $lx } close $in close $out |
Added tool/mksourceid.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 | /* ** Run this program with a single argument which is the name of the ** Fossil "manifest" file for a project, and this program will emit on ** standard output the "source id" for for the program. ** ** (1) The "source id" is the date of check-in together with the ** SHA3 hash of the manifest file. ** ** (2) All individual file hashes in the manifest are verified. If any ** source file has changed, the SHA3 hash ends with "modified". ** */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <ctype.h> /* Portable 64-bit unsigned integers */ #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 u64; #else typedef unsigned long long int u64; #endif /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** ** For best performance, an attempt is made to guess at the byte-order ** using C-preprocessor macros. If that is unsuccessful, or if ** -DBYTEORDER=0 is set, then byte-order is determined ** at run-time. */ #ifndef BYTEORDER # if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__arm__) # define BYTEORDER 1234 # elif defined(sparc) || defined(__ppc__) # define BYTEORDER 4321 # else # define BYTEORDER 0 # endif #endif /* ** State structure for a SHA3 hash in progress */ typedef struct SHA3Context SHA3Context; struct SHA3Context { union { u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */ unsigned char x[1600]; /* ... or 1600 bytes */ } u; unsigned nRate; /* Bytes of input accepted per Keccak iteration */ unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ }; /* ** A single step of the Keccak mixing function for a 1600-bit state */ static void KeccakF1600Step(SHA3Context *p){ int i; u64 B0, B1, B2, B3, B4; u64 C0, C1, C2, C3, C4; u64 D0, D1, D2, D3, D4; static const u64 RC[] = { 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL }; # define A00 (p->u.s[0]) # define A01 (p->u.s[1]) # define A02 (p->u.s[2]) # define A03 (p->u.s[3]) # define A04 (p->u.s[4]) # define A10 (p->u.s[5]) # define A11 (p->u.s[6]) # define A12 (p->u.s[7]) # define A13 (p->u.s[8]) # define A14 (p->u.s[9]) # define A20 (p->u.s[10]) # define A21 (p->u.s[11]) # define A22 (p->u.s[12]) # define A23 (p->u.s[13]) # define A24 (p->u.s[14]) # define A30 (p->u.s[15]) # define A31 (p->u.s[16]) # define A32 (p->u.s[17]) # define A33 (p->u.s[18]) # define A34 (p->u.s[19]) # define A40 (p->u.s[20]) # define A41 (p->u.s[21]) # define A42 (p->u.s[22]) # define A43 (p->u.s[23]) # define A44 (p->u.s[24]) # define ROL64(a,x) ((a<<x)|(a>>(64-x))) for(i=0; i<24; i+=4){ C0 = A00^A10^A20^A30^A40; C1 = A01^A11^A21^A31^A41; C2 = A02^A12^A22^A32^A42; C3 = A03^A13^A23^A33^A43; C4 = A04^A14^A24^A34^A44; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A11^D1), 44); B2 = ROL64((A22^D2), 43); B3 = ROL64((A33^D3), 21); B4 = ROL64((A44^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i]; A11 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B2 = ROL64((A20^D0), 3); B3 = ROL64((A31^D1), 45); B4 = ROL64((A42^D2), 61); B0 = ROL64((A03^D3), 28); B1 = ROL64((A14^D4), 20); A20 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A40^D0), 18); B0 = ROL64((A01^D1), 1); B1 = ROL64((A12^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A34^D4), 8); A40 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B1 = ROL64((A10^D0), 36); B2 = ROL64((A21^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A43^D3), 56); B0 = ROL64((A04^D4), 27); A10 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B3 = ROL64((A30^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A02^D2), 62); B1 = ROL64((A13^D3), 55); B2 = ROL64((A24^D4), 39); A30 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); C0 = A00^A20^A40^A10^A30; C1 = A11^A31^A01^A21^A41; C2 = A22^A42^A12^A32^A02; C3 = A33^A03^A23^A43^A13; C4 = A44^A14^A34^A04^A24; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A31^D1), 44); B2 = ROL64((A12^D2), 43); B3 = ROL64((A43^D3), 21); B4 = ROL64((A24^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+1]; A31 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B2 = ROL64((A40^D0), 3); B3 = ROL64((A21^D1), 45); B4 = ROL64((A02^D2), 61); B0 = ROL64((A33^D3), 28); B1 = ROL64((A14^D4), 20); A40 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A30^D0), 18); B0 = ROL64((A11^D1), 1); B1 = ROL64((A42^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A04^D4), 8); A30 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B1 = ROL64((A20^D0), 36); B2 = ROL64((A01^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A13^D3), 56); B0 = ROL64((A44^D4), 27); A20 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B3 = ROL64((A10^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A22^D2), 62); B1 = ROL64((A03^D3), 55); B2 = ROL64((A34^D4), 39); A10 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); C0 = A00^A40^A30^A20^A10; C1 = A31^A21^A11^A01^A41; C2 = A12^A02^A42^A32^A22; C3 = A43^A33^A23^A13^A03; C4 = A24^A14^A04^A44^A34; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A21^D1), 44); B2 = ROL64((A42^D2), 43); B3 = ROL64((A13^D3), 21); B4 = ROL64((A34^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+2]; A21 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B2 = ROL64((A30^D0), 3); B3 = ROL64((A01^D1), 45); B4 = ROL64((A22^D2), 61); B0 = ROL64((A43^D3), 28); B1 = ROL64((A14^D4), 20); A30 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A10^D0), 18); B0 = ROL64((A31^D1), 1); B1 = ROL64((A02^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A44^D4), 8); A10 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B1 = ROL64((A40^D0), 36); B2 = ROL64((A11^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A03^D3), 56); B0 = ROL64((A24^D4), 27); A40 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B3 = ROL64((A20^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A12^D2), 62); B1 = ROL64((A33^D3), 55); B2 = ROL64((A04^D4), 39); A20 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); C0 = A00^A30^A10^A40^A20; C1 = A21^A01^A31^A11^A41; C2 = A42^A22^A02^A32^A12; C3 = A13^A43^A23^A03^A33; C4 = A34^A14^A44^A24^A04; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A01^D1), 44); B2 = ROL64((A02^D2), 43); B3 = ROL64((A03^D3), 21); B4 = ROL64((A04^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+3]; A01 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B2 = ROL64((A10^D0), 3); B3 = ROL64((A11^D1), 45); B4 = ROL64((A12^D2), 61); B0 = ROL64((A13^D3), 28); B1 = ROL64((A14^D4), 20); A10 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A20^D0), 18); B0 = ROL64((A21^D1), 1); B1 = ROL64((A22^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A24^D4), 8); A20 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B1 = ROL64((A30^D0), 36); B2 = ROL64((A31^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A33^D3), 56); B0 = ROL64((A34^D4), 27); A30 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B3 = ROL64((A40^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A42^D2), 62); B1 = ROL64((A43^D3), 55); B2 = ROL64((A44^D4), 39); A40 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); } } /* ** Initialize a new hash. iSize determines the size of the hash ** in bits and should be one of 224, 256, 384, or 512. Or iSize ** can be zero to use the default hash size of 256 bits. */ static void SHA3Init(SHA3Context *p, int iSize){ memset(p, 0, sizeof(*p)); if( iSize>=128 && iSize<=512 ){ p->nRate = (1600 - ((iSize + 31)&~31)*2)/8; }else{ p->nRate = (1600 - 2*256)/8; } #if BYTEORDER==1234 /* Known to be little-endian at compile-time. No-op */ #elif BYTEORDER==4321 p->ixMask = 7; /* Big-endian */ #else { static unsigned int one = 1; if( 1==*(unsigned char*)&one ){ /* Little endian. No byte swapping. */ p->ixMask = 0; }else{ /* Big endian. Byte swap. */ p->ixMask = 7; } } #endif } /* ** Make consecutive calls to the SHA3Update function to add new content ** to the hash */ static void SHA3Update( SHA3Context *p, const unsigned char *aData, unsigned int nData ){ unsigned int i = 0; #if BYTEORDER==1234 if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){ for(; i+7<nData; i+=8){ p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i]; p->nLoaded += 8; if( p->nLoaded>=p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } } #endif for(; i<nData; i++){ #if BYTEORDER==1234 p->u.x[p->nLoaded] ^= aData[i]; #elif BYTEORDER==4321 p->u.x[p->nLoaded^0x07] ^= aData[i]; #else p->u.x[p->nLoaded^p->ixMask] ^= aData[i]; #endif p->nLoaded++; if( p->nLoaded==p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } } /* ** After all content has been added, invoke SHA3Final() to compute ** the final hash. The function returns a pointer to the binary ** hash value. */ static unsigned char *SHA3Final(SHA3Context *p){ unsigned int i; if( p->nLoaded==p->nRate-1 ){ const unsigned char c1 = 0x86; SHA3Update(p, &c1, 1); }else{ const unsigned char c2 = 0x06; const unsigned char c3 = 0x80; SHA3Update(p, &c2, 1); p->nLoaded = p->nRate - 1; SHA3Update(p, &c3, 1); } for(i=0; i<p->nRate; i++){ p->u.x[i+p->nRate] = p->u.x[i^p->ixMask]; } return &p->u.x[p->nRate]; } /* ** Convert a digest into base-16. digest should be declared as ** "unsigned char digest[20]" in the calling function. The SHA3 ** digest is stored in the first 20 bytes. zBuf should ** be "char zBuf[41]". */ static void DigestToBase16(unsigned char *digest, char *zBuf, int nByte){ static const char zEncode[] = "0123456789abcdef"; int ix; for(ix=0; ix<nByte; ix++){ *zBuf++ = zEncode[(*digest>>4)&0xf]; *zBuf++ = zEncode[*digest++ & 0xf]; } *zBuf = '\0'; } /* ** Compute the SHA3 checksum of a file on disk. Store the resulting ** checksum in the blob pCksum. pCksum is assumed to be initialized. ** ** Return the number of errors. */ static int sha3sum_file(const char *zFilename, int iSize, char *pCksum){ FILE *in; SHA3Context ctx; char zBuf[10240]; in = fopen(zFilename,"rb"); if( in==0 ){ return 1; } SHA3Init(&ctx, iSize); for(;;){ int n = (int)fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; SHA3Update(&ctx, (unsigned char*)zBuf, (unsigned)n); } fclose(in); DigestToBase16(SHA3Final(&ctx), pCksum, iSize/8); return 0; } /* ** The SHA1 implementation below is adapted from: ** ** $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $ ** $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ ** ** SHA-1 in C ** By Steve Reid <steve@edmweb.com> ** 100% Public Domain */ typedef struct SHA1Context SHA1Context; struct SHA1Context { unsigned int state[5]; unsigned int count[2]; unsigned char buffer[64]; }; /* * blk0() and blk() perform the initial expand. * I got the idea of expanding during the round function from SSLeay * * blk0le() for little-endian and blk0be() for big-endian. */ #if __GNUC__ && (defined(__i386__) || defined(__x86_64__)) /* * GCC by itself only generates left rotates. Use right rotates if * possible to be kinder to dinky implementations with iterative rotate * instructions. */ #define SHA_ROT(op, x, k) \ ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; }) #define rol(x,k) SHA_ROT("roll", x, k) #define ror(x,k) SHA_ROT("rorl", x, k) #else /* Generic C equivalent */ #define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) #define rol(x,k) SHA_ROT(x,k,32-(k)) #define ror(x,k) SHA_ROT(x,32-(k),k) #endif #define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ |(rol(block[i],8)&0x00FF00FF)) #define blk0be(i) block[i] #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ ^block[(i+2)&15]^block[i&15],1)) /* * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 * * Rl0() for little-endian and Rb0() for big-endian. Endianness is * determined at run-time. */ #define Rl0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); #define Rb0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R1(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R2(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); #define R3(v,w,x,y,z,i) \ z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); #define R4(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); /* * Hash a single 512-bit block. This is the core of the algorithm. */ #define a qq[0] #define b qq[1] #define c qq[2] #define d qq[3] #define e qq[4] static void SHA1Transform( unsigned int state[5], const unsigned char buffer[64] ){ unsigned int qq[5]; /* a, b, c, d, e; */ static int one = 1; unsigned int block[16]; memcpy(block, buffer, 64); memcpy(qq,state,5*sizeof(unsigned int)); /* Copy context->state[] to working vars */ /* a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; */ /* 4 rounds of 20 operations each. Loop unrolled. */ if( 1 == *(unsigned char*)&one ){ Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); }else{ Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); } R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } /* * SHA1Init - Initialize new context */ static void SHA1Init(SHA1Context *context){ /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* * Run your data through this. */ static void SHA1Update( SHA1Context *context, const unsigned char *data, unsigned int len ){ unsigned int i, j; j = context->count[0]; if ((context->count[0] += len << 3) < j) context->count[1] += (len>>29)+1; j = (j >> 3) & 63; if ((j + len) > 63) { (void)memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) SHA1Transform(context->state, &data[i]); j = 0; } else { i = 0; } (void)memcpy(&context->buffer[j], &data[i], len - i); } /* * Add padding and return the message digest. */ static void SHA1Final(unsigned char *digest, SHA1Context *context){ unsigned int i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1Update(context, (const unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) SHA1Update(context, (const unsigned char *)"\0", 1); SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ if (digest) { for (i = 0; i < 20; i++) digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } } /* ** Compute the SHA1 checksum of a file on disk. Store the resulting ** checksum in the blob pCksum. pCksum is assumed to be initialized. ** ** Return the number of errors. */ static int sha1sum_file(const char *zFilename, char *pCksum){ FILE *in; SHA1Context ctx; unsigned char zResult[20]; char zBuf[10240]; in = fopen(zFilename,"rb"); if( in==0 ){ return 1; } SHA1Init(&ctx); for(;;){ int n = (int)fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n); } fclose(in); SHA1Final(zResult, &ctx); DigestToBase16(zResult, pCksum, 20); return 0; } /* ** Print a usage comment and quit. */ static void usage(const char *argv0){ fprintf(stderr, "Usage: %s manifest\n" "Options:\n" " -v Diagnostic output\n" , argv0); exit(1); } /* ** Find the first whitespace character in a string. Set that whitespace ** to a \000 terminator and return a pointer to the next character. */ static char *nextToken(char *z){ while( *z && !isspace(*z) ) z++; if( *z==0 ) return z; *z = 0; return &z[1]; } int main(int argc, char **argv){ const char *zManifest = 0; int i; int bVerbose = 0; FILE *in; int allValid = 1; int rc; SHA3Context ctx; char zDate[50]; char zHash[100]; char zLine[20000]; for(i=1; i<argc; i++){ const char *z = argv[i]; if( z[0]=='-' ){ if( z[1]=='-' ) z++; if( strcmp(z, "-v")==0 ){ bVerbose = 1; }else { fprintf(stderr, "unknown option \"%s\"", argv[i]); exit(1); } }else if( zManifest!=0 ){ usage(argv[0]); }else{ zManifest = z; } } if( zManifest==0 ) usage(argv[0]); zDate[0] = 0; in = fopen(zManifest, "rb"); if( in==0 ){ fprintf(stderr, "cannot open \"%s\" for reading\n", zManifest); exit(1); } SHA3Init(&ctx, 256); while( fgets(zLine, sizeof(zLine), in) ){ if( strncmp(zLine,"# Remove this line", 18)!=0 ){ SHA3Update(&ctx, (unsigned char*)zLine, (unsigned)strlen(zLine)); } if( strncmp(zLine, "D 20", 4)==0 ){ memcpy(zDate, &zLine[2], 10); zDate[10] = ' '; memcpy(&zDate[11], &zLine[13], 8); zDate[19] = 0; continue; } if( strncmp(zLine, "F ", 2)==0 ){ char *zFilename = &zLine[2]; char *zMHash = nextToken(zFilename); nextToken(zMHash); if( strlen(zMHash)==40 ){ rc = sha1sum_file(zFilename, zHash); }else{ rc = sha3sum_file(zFilename, 256, zHash); } if( rc ){ allValid = 0; if( bVerbose ){ printf("hash failed: %s\n", zFilename); } }else if( strcmp(zHash, zMHash)!=0 ){ allValid = 0; if( bVerbose ){ printf("wrong hash: %s\n", zFilename); printf("... expected: %s\n", zMHash); printf("... got: %s\n", zHash); } } } } fclose(in); DigestToBase16(SHA3Final(&ctx), zHash, 256/8); if( !allValid ){ printf("%s %.60salt1\n", zDate, zHash); }else{ printf("%s %s\n", zDate, zHash); } return 0; } |
Changes to tool/mksqlite3c.tcl.
︙ | ︙ | |||
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | } elseif {$addstatic && ![regexp {^(static|typedef|SQLITE_PRIVATE)} $line]} { # Skip adding the SQLITE_PRIVATE or SQLITE_API keyword before # functions if this header file does not need it. if {![info exists varonly_hdr($tail)] && [regexp $declpattern $line all rettype funcname rest]} { regsub {^SQLITE_API } $line {} line # Add the SQLITE_PRIVATE or SQLITE_API keyword before functions. # so that linkage can be modified at compile-time. if {[regexp {^sqlite3[a-z]*_} $funcname]} { set line SQLITE_API append line " " [string trim $rettype] if {[string index $rettype end] ne "*"} { append line " " } if {$useapicall} { if {[lsearch -exact $cdecllist $funcname] >= 0} { append line SQLITE_CDECL " " } else { append line SQLITE_APICALL " " } } append line $funcname $rest | > > > > > > > | > | 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 | } elseif {$addstatic && ![regexp {^(static|typedef|SQLITE_PRIVATE)} $line]} { # Skip adding the SQLITE_PRIVATE or SQLITE_API keyword before # functions if this header file does not need it. if {![info exists varonly_hdr($tail)] && [regexp $declpattern $line all rettype funcname rest]} { regsub {^SQLITE_API } $line {} line regsub {^SQLITE_API } $rettype {} rettype # Add the SQLITE_PRIVATE or SQLITE_API keyword before functions. # so that linkage can be modified at compile-time. if {[regexp {^sqlite3[a-z]*_} $funcname]} { set line SQLITE_API append line " " [string trim $rettype] if {[string index $rettype end] ne "*"} { append line " " } if {$useapicall} { if {[lsearch -exact $cdecllist $funcname] >= 0} { append line SQLITE_CDECL " " } else { append line SQLITE_APICALL " " } } append line $funcname $rest if {$funcname=="sqlite3_sourceid" && !$linemacros} { # The sqlite3_sourceid() routine is synthesized at the end of # the amalgamation puts $out "/* $line */" } else { puts $out $line } } else { puts $out "SQLITE_PRIVATE $line" } } elseif {[regexp $varpattern $line all varname]} { # Add the SQLITE_PRIVATE before variable declarations or # definitions for internal use regsub {^SQLITE_API } $line {} line |
︙ | ︙ | |||
278 279 280 281 282 283 284 285 286 287 | # Process the source files. Process files containing commonly # used subroutines first in order to help the compiler find # inlining opportunities. # foreach file { sqliteInt.h global.c | > < | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | # Process the source files. Process files containing commonly # used subroutines first in order to help the compiler find # inlining opportunities. # foreach file { ctime.c sqliteInt.h global.c status.c date.c os.c fault.c mem0.c mem1.c |
︙ | ︙ | |||
385 386 387 388 389 390 391 392 393 394 395 396 | icu.c fts3_icu.c sqlite3rbu.c dbstat.c sqlite3session.c json1.c fts5.c } { copy_file tsrc/$file } close $out | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | icu.c fts3_icu.c sqlite3rbu.c dbstat.c sqlite3session.c json1.c fts5.c stmt.c } { copy_file tsrc/$file } # Synthesize an alternative sqlite3_sourceid() implementation that # that tries to detects changes in the amalgamation source text # and modify returns a modified source-id if changes are detected. # # The only detection mechanism we have is the __LINE__ macro. So only # edits that changes the number of lines of source code are detected. # if {!$linemacros} { flush $out set in2 [open sqlite3.c] set cnt 0 set oldsrcid {} while {![eof $in2]} { incr cnt gets $in2 line if {[regexp {^#define SQLITE_SOURCE_ID } $line]} {set oldsrcid $line} } close $in2 regsub {[0-9a-flt]{4}"} $oldsrcid {alt2"} oldsrcid puts $out \ "#if __LINE__!=[expr {$cnt+0}] #undef SQLITE_SOURCE_ID $oldsrcid #endif /* 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/mksqlite3h.tcl.
︙ | ︙ | |||
47 48 49 50 51 52 53 | # Get the SQLite version number (ex: 3.6.18) from the $TOP/VERSION file. # set in [open $TOP/VERSION] set zVersion [string trim [read $in]] close $in set nVersion [eval format "%d%03d%03d" [split $zVersion .]] | | > | | | < > < | | < < < < | > > | | | < | | | | 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 | # Get the SQLite version number (ex: 3.6.18) from the $TOP/VERSION file. # set in [open $TOP/VERSION] set zVersion [string trim [read $in]] close $in set nVersion [eval format "%d%03d%03d" [split $zVersion .]] # Get the source-id # set PWD [pwd] cd $TOP set zSourceId [exec $PWD/mksourceid manifest] cd $PWD # Set up patterns for recognizing API declarations. # set varpattern {^[a-zA-Z][a-zA-Z_0-9 *]+sqlite3_[_a-zA-Z0-9]+(\[|;| =)} set declpattern1 {^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3_[_a-zA-Z0-9]+)(\(.*)$} set declpattern2 \ {^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3session_[_a-zA-Z0-9]+)(\(.*)$} set declpattern3 \ {^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3changeset_[_a-zA-Z0-9]+)(\(.*)$} set declpattern4 \ {^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3changegroup_[_a-zA-Z0-9]+)(\(.*)$} # Force the output to use unix line endings, even on Windows. fconfigure stdout -translation lf set filelist [subst { $TOP/src/sqlite.h.in $TOP/ext/rtree/sqlite3rtree.h |
︙ | ︙ | |||
112 113 114 115 116 117 118 | # File sqlite3rtree.h contains a line "#include <sqlite3.h>". Omit this # line when copying sqlite3rtree.h into sqlite3.h. # if {[string match {*#include*[<"]sqlite3.h[>"]*} $line]} continue regsub -- --VERS-- $line $zVersion line regsub -- --VERSION-NUMBER-- $line $nVersion line | | > > > | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # File sqlite3rtree.h contains a line "#include <sqlite3.h>". Omit this # line when copying sqlite3rtree.h into sqlite3.h. # if {[string match {*#include*[<"]sqlite3.h[>"]*} $line]} continue regsub -- --VERS-- $line $zVersion line regsub -- --VERSION-NUMBER-- $line $nVersion line regsub -- --SOURCE-ID-- $line "$zSourceId" line if {[regexp $varpattern $line] && ![regexp {^ *typedef} $line]} { set line "SQLITE_API $line" } else { if {[regexp $declpattern1 $line all rettype funcname rest] || \ [regexp $declpattern2 $line all rettype funcname rest] || \ [regexp $declpattern3 $line all rettype funcname rest] || \ [regexp $declpattern4 $line all rettype funcname rest]} { set line SQLITE_API append line " " [string trim $rettype] if {[string index $rettype end] ne "*"} { append line " " } if {$useapicall} { if {[lsearch -exact $cdecllist $funcname] >= 0} { |
︙ | ︙ |
Changes to tool/mkvsix.tcl.
︙ | ︙ | |||
73 74 75 76 77 78 79 | # the package. Currently, the only supported versions are "2012" and "2013". # The package flavors "WinRT81" and "WP81" are only supported when the Visual # Studio version is "2013". Typically, when on Windows, this script is # executed using commands similar to the following from a normal Windows # command prompt: # # CD /D C:\dev\sqlite\core | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | # the package. Currently, the only supported versions are "2012" and "2013". # The package flavors "WinRT81" and "WP81" are only supported when the Visual # Studio version is "2013". Typically, when on Windows, this script is # executed using commands similar to the following from a normal Windows # command prompt: # # CD /D C:\dev\sqlite\core # tclsh tool\mkvsix.tcl C:\Temp # # In the example above, "C:\dev\sqlite\core" represents the root of the source # tree for SQLite and "C:\Temp" represents the top-level directory containing # the executable and other compiled binary files, organized into a directory # tree as described in item 6 of the PREREQUISITES section, above. # # This script should work on non-Windows platforms as well, provided that all |
︙ | ︙ |
Changes to tool/omittest.tcl.
|
| < < < | 1 2 3 4 5 6 7 | # Documentation for this script. This may be output to stderr # if the script is invoked incorrectly. set ::USAGE_MESSAGE { This Tcl script is used to test the various compile time options available for omitting code (the SQLITE_OMIT_xxx options). It should be invoked as follows: |
︙ | ︙ | |||
130 131 132 133 134 135 136 | } else { set ::MAKEFILE ./Makefile.linux-gcc ;# Default value } set ::SKIP_RUN 0 ;# Default to attempt test set ::TARGET testfixture ;# Default thing to build for {set i 0} {$i < [llength $argv]} {incr i} { | | | | | | > > > > > > > > > > > | 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 | } else { set ::MAKEFILE ./Makefile.linux-gcc ;# Default value } set ::SKIP_RUN 0 ;# Default to attempt test set ::TARGET testfixture ;# Default thing to build for {set i 0} {$i < [llength $argv]} {incr i} { switch -regexp -- [lindex $argv $i] { -{1,2}makefile { incr i set ::MAKEFILE [lindex $argv $i] } -{1,2}nmake { set ::MAKEBIN nmake set ::MAKEFILE ./Makefile.msc } -{1,2}target { incr i set ::TARGET [lindex $argv $i] } -{1,2}skip_run { set ::SKIP_RUN 1 } -{1,2}help { puts $::USAGE_MESSAGE exit } -.* { puts stderr "Unknown option: [lindex $argv i]" puts stderr $::USAGE_MESSAGE exit 1 } default { if {[info exists ::SYMBOL]} { puts stderr [string trim $::USAGE_MESSAGE] exit -1 } set ::SYMBOL [lindex $argv $i] |
︙ | ︙ |
Changes to tool/showdb.c.
︙ | ︙ | |||
531 532 533 534 535 536 537 | if( nLocal>0 ){ i = decodeVarint(x, &nHdr); printBytes(a, x, i); printf("record-header-size: %d\n", (int)nHdr); j = i; nCol = 0; k = nHdr; | | | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | if( nLocal>0 ){ i = decodeVarint(x, &nHdr); printBytes(a, x, i); printf("record-header-size: %d\n", (int)nHdr); j = i; nCol = 0; k = nHdr; while( x+j<=end && j<nHdr ){ const char *zTypeName; int sz = 0; char zNm[30]; i = decodeVarint(x+j, &iType); printBytes(a, x+j, i); printf("typecode[%d]: %d - ", nCol, (int)iType); switch( iType ){ |
︙ | ︙ |
Changes to tool/showstat4.c.
︙ | ︙ | |||
114 115 116 117 118 119 120 121 | }else if( iVal<=7 ){ v = (signed char)aSample[y]; for(j=1; j<sz; j++){ v = (v<<8) + aSample[y+j]; } if( iVal==7 ){ double r; memcpy(&r, &v, sizeof(r)); | > > | > | | | | 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 | }else if( iVal<=7 ){ v = (signed char)aSample[y]; for(j=1; j<sz; j++){ v = (v<<8) + aSample[y+j]; } if( iVal==7 ){ double r; char *z; memcpy(&r, &v, sizeof(r)); z = sqlite3_mprintf("%s%!.15g", zSep, r); printf("%s", z); sqlite3_free(z); }else{ printf("%s%lld", zSep, v); } }else if( (iVal&1)==0 ){ printf("%sx'", zSep); for(j=0; j<sz; j++){ printf("%02x", aSample[y+j]); } printf("'"); }else{ printf("%s'", zSep); for(j=0; j<sz; j++){ char c = (char)aSample[y+j]; if( ISPRINT(c) ){ if( c=='\'' || c=='\\' ) putchar('\\'); putchar(c); }else if( c=='\n' ){ printf("\\n"); }else if( c=='\t' ){ printf("\\t"); }else if( c=='\r' ){ printf("\\r"); }else{ printf("\\%03o", c); } } printf("'"); } zSep = ","; y += sz; } printf("\n"); } sqlite3_free(zIdx); |
︙ | ︙ |
Changes to tool/showwal.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <fcntl.h> #define ISDIGIT(X) isdigit((unsigned char)(X)) #define ISPRINT(X) isprint((unsigned char)(X)) #if !defined(_MSC_VER) #include <unistd.h> #else #include <io.h> #endif #include <stdlib.h> #include <string.h> | > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <fcntl.h> #define ISDIGIT(X) isdigit((unsigned char)(X)) #define ISPRINT(X) isprint((unsigned char)(X)) #if !defined(_MSC_VER) #include <unistd.h> #include <sys/types.h> #else #include <io.h> #endif #include <stdlib.h> #include <string.h> |
︙ | ︙ | |||
575 576 577 578 579 580 581 582 583 584 585 586 587 588 | nByte = pagesize; } ofst = 32 + hdrSize + (iStart-1)*(pagesize+24) + 24; a = getContent(ofst, nByte); decode_btree_page(a, iStart, hdrSize, zLeft+1); free(a); continue; }else{ iEnd = iStart; } if( iStart<1 || iEnd<iStart || iEnd>mxFrame ){ fprintf(stderr, "Page argument should be LOWER?..UPPER?. Range 1 to %d\n", mxFrame); | > > > > > > > > | 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 | nByte = pagesize; } ofst = 32 + hdrSize + (iStart-1)*(pagesize+24) + 24; a = getContent(ofst, nByte); decode_btree_page(a, iStart, hdrSize, zLeft+1); free(a); continue; #if !defined(_MSC_VER) }else if( zLeft && strcmp(zLeft,"truncate")==0 ){ /* Frame number followed by "truncate" truncates the WAL file ** after that frame */ off_t newSize = 32 + iStart*(pagesize+24); truncate(argv[1], newSize); continue; #endif }else{ iEnd = iStart; } if( iStart<1 || iEnd<iStart || iEnd>mxFrame ){ fprintf(stderr, "Page argument should be LOWER?..UPPER?. Range 1 to %d\n", mxFrame); |
︙ | ︙ |
Changes to tool/spaceanal.tcl.
︙ | ︙ | |||
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | # total_pages_percent: Pages consumed as a percentage of the file. # storage: Bytes consumed. # payload_percent: Payload bytes used as a percentage of $storage. # total_unused: Unused bytes on pages. # avg_payload: Average payload per btree entry. # avg_fanout: Average fanout for internal pages. # avg_unused: Average unused bytes per btree entry. # ovfl_cnt_percent: Percentage of btree entries that use overflow pages. # set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}] set total_pages_percent [percent $total_pages $file_pgcnt] set storage [expr {$total_pages*$pageSize}] set payload_percent [percent $payload $storage {of storage consumed}] set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}] set avg_payload [divide $payload $nentry] set avg_unused [divide $total_unused $nentry] if {$int_pages>0} { # TODO: Is this formula correct? set nTab [mem eval " SELECT count(*) FROM ( SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0 ) "] | > > > > > | 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 | # total_pages_percent: Pages consumed as a percentage of the file. # storage: Bytes consumed. # payload_percent: Payload bytes used as a percentage of $storage. # total_unused: Unused bytes on pages. # avg_payload: Average payload per btree entry. # avg_fanout: Average fanout for internal pages. # avg_unused: Average unused bytes per btree entry. # avg_meta: Average metadata overhead per entry. # ovfl_cnt_percent: Percentage of btree entries that use overflow pages. # set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}] set total_pages_percent [percent $total_pages $file_pgcnt] set storage [expr {$total_pages*$pageSize}] set payload_percent [percent $payload $storage {of storage consumed}] set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}] set avg_payload [divide $payload $nentry] set avg_unused [divide $total_unused $nentry] set total_meta [expr {$storage - $payload - $total_unused}] set total_meta [expr {$total_meta + 4*($ovfl_pages - $ovfl_cnt)}] set meta_percent [percent $total_meta $storage {of metadata}] set avg_meta [divide $total_meta $nentry] if {$int_pages>0} { # TODO: Is this formula correct? set nTab [mem eval " SELECT count(*) FROM ( SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0 ) "] |
︙ | ︙ | |||
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | if {$compressed_size!=$storage} { set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}] set pct [expr {$compressed_size*100.0/$storage}] set pct [format {%5.1f%%} $pct] statline {Bytes used after compression} $compressed_size $pct } statline {Bytes of payload} $payload $payload_percent if {$cnt==1} {statline {B-tree depth} $depth} statline {Average payload per entry} $avg_payload statline {Average unused bytes per entry} $avg_unused if {[info exists avg_fanout]} { statline {Average fanout} $avg_fanout } if {$showFrag && $total_pages>1} { set fragmentation [percent $gap_cnt [expr {$total_pages-1}]] statline {Non-sequential pages} $gap_cnt $fragmentation } | > > | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | if {$compressed_size!=$storage} { set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}] set pct [expr {$compressed_size*100.0/$storage}] set pct [format {%5.1f%%} $pct] statline {Bytes used after compression} $compressed_size $pct } statline {Bytes of payload} $payload $payload_percent statline {Bytes of metadata} $total_meta $meta_percent if {$cnt==1} {statline {B-tree depth} $depth} statline {Average payload per entry} $avg_payload statline {Average unused bytes per entry} $avg_unused statline {Average metadata per entry} $avg_meta if {[info exists avg_fanout]} { statline {Average fanout} $avg_fanout } if {$showFrag && $total_pages>1} { set fragmentation [percent $gap_cnt [expr {$total_pages-1}]] statline {Non-sequential pages} $gap_cnt $fragmentation } |
︙ | ︙ | |||
753 754 755 756 757 758 759 760 761 762 763 764 765 766 | Bytes of payload The amount of payload stored under this category. Payload is the data part of table entries and the key part of index entries. The percentage at the right is the bytes of payload divided by the bytes of storage consumed. Average payload per entry The average amount of payload on each entry. This is just the bytes of payload divided by the number of entries. Average unused bytes per entry | > > > > > > > > > > | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | Bytes of payload The amount of payload stored under this category. Payload is the data part of table entries and the key part of index entries. The percentage at the right is the bytes of payload divided by the bytes of storage consumed. Bytes of metadata The amount of formatting and structural information stored in the table or index. Metadata includes the btree page header, the cell pointer array, the size field for each cell, the left child pointer or non-leaf cells, the overflow pointers for overflow cells, and the rowid value for rowid table cells. In other words, metadata is everything that is neither unused space nor content. The record header in the payload is counted as content, not metadata. Average payload per entry The average amount of payload on each entry. This is just the bytes of payload divided by the number of entries. Average unused bytes per entry |
︙ | ︙ |
Changes to tool/speed-check.sh.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 | echo "Usage: $0 OUTPUTFILE [OPTIONS]" exit fi NAME=$1 shift #CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5" CC_OPTS="-DSQLITE_ENABLE_MEMSYS5" SPEEDTEST_OPTS="--shrink-memory --reprepare --stats --heap 10000000 64" SIZE=5 LEAN_OPTS="-DSQLITE_THREADSAFE=0" LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_MEMSTATUS=0" LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1" LEAN_OPTS="$LEAN_OPTS -DSQLITE_LIKE_DOESNT_MATCH_BLOB" LEAN_OPTS="$LEAN_OPTS -DSQLITE_MAX_EXPR_DEPTH=0" | > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | echo "Usage: $0 OUTPUTFILE [OPTIONS]" exit fi NAME=$1 shift #CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5" CC_OPTS="-DSQLITE_ENABLE_MEMSYS5" CC=gcc SPEEDTEST_OPTS="--shrink-memory --reprepare --stats --heap 10000000 64" SIZE=5 LEAN_OPTS="-DSQLITE_THREADSAFE=0" LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_MEMSTATUS=0" LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1" LEAN_OPTS="$LEAN_OPTS -DSQLITE_LIKE_DOESNT_MATCH_BLOB" LEAN_OPTS="$LEAN_OPTS -DSQLITE_MAX_EXPR_DEPTH=0" |
︙ | ︙ | |||
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 | rm -f vdbe_profile.out CC_OPTS="$CC_OPTS -DVDBE_PROFILE" doCachegrind=0 ;; --lean) CC_OPTS="$CC_OPTS $LEAN_OPTS" ;; --heap) CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_MEMSYS5" shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --heap $1 64" ;; --repeat) CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RCACHE" shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --repeat $1" ;; *) CC_OPTS="$CC_OPTS $1" ;; esac shift done SPEEDTEST_OPTS="$SPEEDTEST_OPTS --size $SIZE" echo "NAME = $NAME" | tee summary-$NAME.txt echo "SPEEDTEST_OPTS = $SPEEDTEST_OPTS" | tee -a summary-$NAME.txt echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt rm -f cachegrind.out.* speedtest1 speedtest1.db sqlite3.o | > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > | 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 | rm -f vdbe_profile.out CC_OPTS="$CC_OPTS -DVDBE_PROFILE" doCachegrind=0 ;; --lean) CC_OPTS="$CC_OPTS $LEAN_OPTS" ;; --clang) CC=clang ;; --icc) CC=/home/drh/intel/bin/icc ;; --gcc7) CC=gcc-7 ;; --heap) CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_MEMSYS5" shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --heap $1 64" ;; --lookaside) shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --lookaside $1 $2" shift; ;; --repeat) CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RCACHE" shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --repeat $1" ;; --mmap) shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --mmap $1" ;; --rtree) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset rtree" CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RTREE" ;; --orm) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset orm" ;; *) CC_OPTS="$CC_OPTS $1" ;; esac shift done SPEEDTEST_OPTS="$SPEEDTEST_OPTS --size $SIZE" echo "NAME = $NAME" | tee summary-$NAME.txt echo "SPEEDTEST_OPTS = $SPEEDTEST_OPTS" | tee -a summary-$NAME.txt echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt rm -f cachegrind.out.* speedtest1 speedtest1.db sqlite3.o $CC -g -Os -Wall -I. $CC_OPTS -c sqlite3.c size sqlite3.o | tee -a summary-$NAME.txt if test $doExplain -eq 1; then $CC -g -Os -Wall -I. $CC_OPTS \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ ./shell.c ./sqlite3.c -o sqlite3 -ldl -lpthread fi SRC=./speedtest1.c $CC -g -Os -Wall -I. $CC_OPTS $SRC ./sqlite3.o -o speedtest1 -ldl -lpthread ls -l speedtest1 | tee -a summary-$NAME.txt if test $doCachegrind -eq 1; then valgrind --tool=cachegrind ./speedtest1 speedtest1.db \ $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt else ./speedtest1 speedtest1.db $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt fi size sqlite3.o | tee -a summary-$NAME.txt wc sqlite3.c if test $doCachegrind -eq 1; then cg_anno.tcl cachegrind.out.* >cout-$NAME.txt echo '*****************************************************' >>cout-$NAME.txt sed 's/^[0-9=-]\{9\}/==00000==/' summary-$NAME.txt >>cout-$NAME.txt fi if test $doExplain -eq 1; then ./speedtest1 --explain $SPEEDTEST_OPTS | ./sqlite3 >explain-$NAME.txt fi if test "$NAME" != "trunk"; then fossil test-diff --tk -c 20 cout-trunk.txt cout-$NAME.txt fi |
Changes to tool/sqldiff.c.
︙ | ︙ | |||
1663 1664 1665 1666 1667 1668 1669 | if( g.fDebug & DEBUG_DIFF_SQL ){ printf("SQL for %s:\n%s\n", zId, sql.z); goto end_changeset_one_table; } putc('T', out); putsVarint(out, (sqlite3_uint64)nCol); | | | 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 | if( g.fDebug & DEBUG_DIFF_SQL ){ printf("SQL for %s:\n%s\n", zId, sql.z); 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", sql.z); while( SQLITE_ROW==sqlite3_step(pStmt) ){ int iType = sqlite3_column_int(pStmt,0); putc(iType, out); |
︙ | ︙ |
Changes to tool/warnings.sh.
1 2 3 4 5 6 | #/bin/sh # # Run this script in a directory with a working makefile to check for # compiler warnings in SQLite. # | > | | | | | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #/bin/sh # # Run this script in a directory with a working makefile to check for # compiler warnings in SQLite. # if uname | grep -i openbsd ; then # Use these for testing on OpenBSD: WARNING_OPTS=-Wall WARNING_ANDROID_OPTS=-Wall else # Use these for testing on Linux and Mac OSX: WARNING_OPTS="-Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long" WARNING_ANDROID_OPTS="-Wshadow -Wall -Wextra" fi rm -f sqlite3.c make sqlite3.c echo '********** No optimizations. Includes FTS4/5, RTREE, JSON1 ***' echo '********** ' Options: $WARNING_OPTS gcc -c $WARNING_OPTS -std=c89 \ -ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE \ |
︙ | ︙ |