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 fd81d8a4 to 8d2a1cca
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: 8d2a1cca 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: 2c03d8b8 user: mistachkin tags: trunk) | |
2017-02-16
| ||
14:02 | Merge recent enhancements from trunk. (check-in: 325ccfa9 user: drh tags: est_count_pragma) | |
2016-10-20
| ||
22:02 | Experimental est_count pragma. (check-in: 340822af 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: fd81d8a4 user: drh tags: multi-arg-pragma) | |
2016-10-18
| ||
16:36 | Minor simplification of the comparison opcodes. (check-in: 56474ebc 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.